From 16cd1c07fff0bda27f552fc0ce61fd2ba96ec108 Mon Sep 17 00:00:00 2001 From: Michael Bernardi Date: Mon, 29 Sep 2025 02:05:00 +1000 Subject: [PATCH] Swap Duktape for quickjs-ng Co-authored-by: Basssiiie Co-authored-by: Tulio Leao --- .github/workflows/clang-tidy.yml | 1 + CMakeLists.txt | 8 +- openrct2.common.props | 5 +- .../app/src/main/CMakeLists.txt | 10 +- src/openrct2-ui/CMakeLists.txt | 2 +- src/openrct2-ui/WindowManager.cpp | 8 +- src/openrct2-ui/interface/FileBrowser.cpp | 22 +- src/openrct2-ui/interface/FileBrowser.h | 6 +- src/openrct2-ui/libopenrct2ui.vcxproj | 2 + src/openrct2-ui/scripting/CustomImages.cpp | 208 +- src/openrct2-ui/scripting/CustomImages.h | 10 +- src/openrct2-ui/scripting/CustomListView.cpp | 183 +- src/openrct2-ui/scripting/CustomListView.h | 40 +- src/openrct2-ui/scripting/CustomMenu.cpp | 214 +- src/openrct2-ui/scripting/CustomMenu.h | 31 +- src/openrct2-ui/scripting/CustomWindow.cpp | 322 +- src/openrct2-ui/scripting/CustomWindow.h | 5 +- .../scripting/ScGraphicsContext.hpp | 301 +- src/openrct2-ui/scripting/ScImageManager.hpp | 176 +- src/openrct2-ui/scripting/ScTileSelection.hpp | 128 +- src/openrct2-ui/scripting/ScTitleSequence.hpp | 534 +- src/openrct2-ui/scripting/ScUi.hpp | 561 +- src/openrct2-ui/scripting/ScViewport.hpp | 300 +- src/openrct2-ui/scripting/ScWidget.hpp | 978 +- src/openrct2-ui/scripting/ScWindow.cpp | 406 + src/openrct2-ui/scripting/ScWindow.h | 108 + src/openrct2-ui/scripting/ScWindow.hpp | 374 - src/openrct2-ui/scripting/UiExtensions.cpp | 93 +- src/openrct2-ui/scripting/UiExtensions.h | 1 + src/openrct2-ui/windows/LoadSave.cpp | 8 +- src/openrct2-ui/windows/Windows.h | 2 +- src/openrct2/CMakeLists.txt | 78 +- src/openrct2/Context.cpp | 2 +- src/openrct2/actions/GameAction.cpp | 21 +- src/openrct2/actions/GameActionRunner.cpp | 1 - src/openrct2/actions/general/CustomAction.cpp | 7 +- src/openrct2/actions/general/CustomAction.h | 6 +- src/openrct2/core/File.cpp | 2 + src/openrct2/entity/Guest.cpp | 10 +- src/openrct2/interface/Window.cpp | 15 +- src/openrct2/interface/Window.h | 1 + src/openrct2/libopenrct2.vcxproj | 14 +- src/openrct2/network/NetworkBase.cpp | 49 +- src/openrct2/park/ParkFile.cpp | 2 +- src/openrct2/ride/RideRatings.cpp | 20 +- src/openrct2/ride/Vehicle.Crash.cpp | 11 +- src/openrct2/scripting/Duktape.hpp | 504 - src/openrct2/scripting/HookEngine.cpp | 48 +- src/openrct2/scripting/HookEngine.h | 25 +- src/openrct2/scripting/Plugin.cpp | 180 +- src/openrct2/scripting/Plugin.h | 24 +- src/openrct2/scripting/ScriptEngine.cpp | 1184 +- src/openrct2/scripting/ScriptEngine.h | 167 +- src/openrct2/scripting/ScriptUtil.hpp | 614 + .../scripting/bindings/entity/ScBalloon.cpp | 35 +- .../scripting/bindings/entity/ScBalloon.hpp | 14 +- .../scripting/bindings/entity/ScEntity.cpp | 234 + .../scripting/bindings/entity/ScEntity.hpp | 194 +- .../scripting/bindings/entity/ScGuest.cpp | 717 +- .../scripting/bindings/entity/ScGuest.hpp | 146 +- .../scripting/bindings/entity/ScLitter.cpp | 58 +- .../scripting/bindings/entity/ScLitter.hpp | 16 +- .../bindings/entity/ScMoneyEffect.cpp | 36 +- .../bindings/entity/ScMoneyEffect.hpp | 15 +- .../scripting/bindings/entity/ScParticle.cpp | 209 +- .../scripting/bindings/entity/ScParticle.hpp | 35 +- .../scripting/bindings/entity/ScPeep.hpp | 155 +- .../scripting/bindings/entity/ScStaff.cpp | 489 +- .../scripting/bindings/entity/ScStaff.hpp | 113 +- .../scripting/bindings/entity/ScVehicle.cpp | 508 +- .../scripting/bindings/entity/ScVehicle.hpp | 103 +- .../scripting/bindings/game/ScCheats.hpp | 472 +- .../bindings/game/ScConfiguration.hpp | 284 +- .../scripting/bindings/game/ScConsole.hpp | 83 +- .../scripting/bindings/game/ScContext.hpp | 534 +- .../scripting/bindings/game/ScDisposable.hpp | 43 +- .../scripting/bindings/game/ScPlugin.hpp | 84 +- .../scripting/bindings/game/ScProfiler.hpp | 110 +- .../scripting/bindings/network/ScNetwork.cpp | 206 +- .../scripting/bindings/network/ScNetwork.hpp | 85 +- .../scripting/bindings/network/ScPlayer.cpp | 113 +- .../scripting/bindings/network/ScPlayer.hpp | 51 +- .../bindings/network/ScPlayerGroup.cpp | 114 +- .../bindings/network/ScPlayerGroup.hpp | 34 +- .../scripting/bindings/network/ScSocket.hpp | 707 +- .../bindings/object/ScInstalledObject.cpp | 2 + .../bindings/object/ScInstalledObject.hpp | 142 +- .../scripting/bindings/object/ScObject.hpp | 1117 +- .../bindings/object/ScObjectManager.cpp | 186 +- .../bindings/object/ScObjectManager.h | 27 +- .../scripting/bindings/ride/ScRide.cpp | 865 +- .../scripting/bindings/ride/ScRide.hpp | 291 +- .../scripting/bindings/ride/ScRideStation.cpp | 199 +- .../scripting/bindings/ride/ScRideStation.hpp | 46 +- .../bindings/ride/ScTrackIterator.cpp | 141 +- .../scripting/bindings/ride/ScTrackIterator.h | 38 +- .../bindings/ride/ScTrackSegment.cpp | 378 +- .../scripting/bindings/ride/ScTrackSegment.h | 81 +- .../scripting/bindings/world/ScAward.cpp | 72 +- .../scripting/bindings/world/ScAward.hpp | 35 +- .../scripting/bindings/world/ScDate.hpp | 129 +- .../scripting/bindings/world/ScMap.cpp | 250 +- .../scripting/bindings/world/ScMap.hpp | 60 +- .../scripting/bindings/world/ScPark.cpp | 432 +- .../scripting/bindings/world/ScPark.hpp | 169 +- .../bindings/world/ScParkMessage.cpp | 175 +- .../bindings/world/ScParkMessage.hpp | 72 +- .../scripting/bindings/world/ScResearch.cpp | 224 +- .../scripting/bindings/world/ScResearch.hpp | 49 +- .../scripting/bindings/world/ScScenario.hpp | 263 +- .../scripting/bindings/world/ScTile.cpp | 214 +- .../scripting/bindings/world/ScTile.hpp | 55 +- .../bindings/world/ScTileElement.cpp | 2753 +- .../bindings/world/ScTileElement.hpp | 248 +- .../scripting/bindings/world/ScWeather.hpp | 88 +- src/openrct2/ui/DummyWindowManager.cpp | 1 + src/openrct2/ui/WindowManager.h | 1 + src/openrct2/world/Park.cpp | 13 +- src/thirdparty/base64.hpp | 721 + src/thirdparty/dukglue/detail_class_proto.h | 201 - src/thirdparty/dukglue/detail_constructor.h | 73 - src/thirdparty/dukglue/detail_function.h | 102 - src/thirdparty/dukglue/detail_method.h | 183 - .../dukglue/detail_primitive_types.h | 256 - src/thirdparty/dukglue/detail_refs.h | 199 - src/thirdparty/dukglue/detail_stack.h | 52 - src/thirdparty/dukglue/detail_traits.h | 122 - src/thirdparty/dukglue/detail_typeinfo.h | 64 - src/thirdparty/dukglue/detail_types.h | 158 - src/thirdparty/dukglue/dukexception.h | 41 - src/thirdparty/dukglue/dukglue.h | 14 - src/thirdparty/dukglue/dukvalue.h | 638 - src/thirdparty/dukglue/public_util.h | 316 - src/thirdparty/dukglue/register_class.h | 202 - src/thirdparty/dukglue/register_function.h | 35 - src/thirdparty/dukglue/register_property.h | 124 - src/thirdparty/duktape/README.md | 23 - src/thirdparty/duktape/duk_config.h | 3213 - src/thirdparty/duktape/duk_source_meta.json | 1911 - src/thirdparty/duktape/duktape.cpp | 101206 --------------- src/thirdparty/duktape/duktape.h | 1456 - src/thirdparty/quickjs-ng/quickjs-amalgam.c | 82922 ++++++++++++ src/thirdparty/quickjs-ng/quickjs.h | 1405 + 143 files changed, 97500 insertions(+), 120982 deletions(-) create mode 100644 src/openrct2-ui/scripting/ScWindow.cpp create mode 100644 src/openrct2-ui/scripting/ScWindow.h delete mode 100644 src/openrct2-ui/scripting/ScWindow.hpp delete mode 100644 src/openrct2/scripting/Duktape.hpp create mode 100644 src/openrct2/scripting/ScriptUtil.hpp create mode 100644 src/openrct2/scripting/bindings/entity/ScEntity.cpp create mode 100644 src/thirdparty/base64.hpp delete mode 100644 src/thirdparty/dukglue/detail_class_proto.h delete mode 100644 src/thirdparty/dukglue/detail_constructor.h delete mode 100644 src/thirdparty/dukglue/detail_function.h delete mode 100644 src/thirdparty/dukglue/detail_method.h delete mode 100644 src/thirdparty/dukglue/detail_primitive_types.h delete mode 100644 src/thirdparty/dukglue/detail_refs.h delete mode 100644 src/thirdparty/dukglue/detail_stack.h delete mode 100644 src/thirdparty/dukglue/detail_traits.h delete mode 100644 src/thirdparty/dukglue/detail_typeinfo.h delete mode 100644 src/thirdparty/dukglue/detail_types.h delete mode 100644 src/thirdparty/dukglue/dukexception.h delete mode 100644 src/thirdparty/dukglue/dukglue.h delete mode 100644 src/thirdparty/dukglue/dukvalue.h delete mode 100644 src/thirdparty/dukglue/public_util.h delete mode 100644 src/thirdparty/dukglue/register_class.h delete mode 100644 src/thirdparty/dukglue/register_function.h delete mode 100644 src/thirdparty/dukglue/register_property.h delete mode 100644 src/thirdparty/duktape/README.md delete mode 100644 src/thirdparty/duktape/duk_config.h delete mode 100644 src/thirdparty/duktape/duk_source_meta.json delete mode 100644 src/thirdparty/duktape/duktape.cpp delete mode 100644 src/thirdparty/duktape/duktape.h create mode 100644 src/thirdparty/quickjs-ng/quickjs-amalgam.c create mode 100644 src/thirdparty/quickjs-ng/quickjs.h diff --git a/.github/workflows/clang-tidy.yml b/.github/workflows/clang-tidy.yml index 2bd6ea1a11..d5e95b4bf9 100644 --- a/.github/workflows/clang-tidy.yml +++ b/.github/workflows/clang-tidy.yml @@ -19,3 +19,4 @@ jobs: with: build_dir: 'build' cmake_args: '-G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Debug -DDISABLE_DISCORD_RPC=ON' + file_filter: 'cpp,c,cc,cxx' diff --git a/CMakeLists.txt b/CMakeLists.txt index a79fe8893d..6c80503e1f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,12 +48,13 @@ if (APPLE) set(CMAKE_SYSTEM_PROCESSOR "${CMAKE_OSX_ARCHITECTURES}" CACHE STRING "") endif () -project(openrct2 CXX) +# C Language required for quickjs-ng +project(openrct2 CXX C) include(cmake/platform.cmake) include(CMakeDependentOption) -# vcpkg includes its own copy of duktapeConfig.cmake; only include ours as needed. +# vcpkg includes its own copy of *.cmake; only include ours as needed. if (NOT MSVC) include(FindPkgConfig) set(CMAKE_PREFIX_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}") @@ -267,7 +268,7 @@ endfunction () # Add check flags to a compile TARGET function (SET_CHECK_CXX_FLAGS _TARGET) - target_compile_options("${_TARGET}" PRIVATE "${SUPPORTED_CHECK_CXX_COMPILER_FLAGS}") + target_compile_options("${_TARGET}" PRIVATE $<$:${SUPPORTED_CHECK_CXX_COMPILER_FLAGS}>) endfunction() # Check if a flag exists and add it to the compiler and the linker options @@ -395,7 +396,6 @@ endif() # graphics files (g2.dat, fonts.dat, palettes.dat, tracks.dat) if (NOT CMAKE_CROSSCOMPILING) set(graphics_files "g2" "fonts" "palettes" "tracks") - foreach(graphics_file ${graphics_files}) set(output_file "${graphics_file}.dat") set(json_file "${ROOT_DIR}/resources/${graphics_file}/sprites.json") diff --git a/openrct2.common.props b/openrct2.common.props index 2025a0f66e..0554ac58e0 100644 --- a/openrct2.common.props +++ b/openrct2.common.props @@ -69,7 +69,8 @@ MultiThreadedDLL true true - /utf-8 /std:c++20 /permissive- /Zc:externConstexpr /Zc:char8_t- + /utf-8 /permissive- /Zc:externConstexpr /Zc:char8_t- + stdcpp20 true @@ -119,7 +120,7 @@ - $(SolutionDir)src;$(SolutionDir)src\thirdparty;$(SolutionDir)src\thirdparty\duktape;$(SolutionDir)lib\$(Platform)\include;$(SolutionDir)lib\$(Platform)\include\SDL2;$(SolutionDir)lib\$(Platform)\include\crashpad;$(IncludePath) + $(SolutionDir)src;$(SolutionDir)src\thirdparty;$(SolutionDir)src\thirdparty\quickjs-ng;$(SolutionDir)lib\$(Platform)\include;$(SolutionDir)lib\$(Platform)\include\SDL2;$(SolutionDir)lib\$(Platform)\include\crashpad;$(IncludePath) $(SolutionDir)lib\$(Platform)\debug\lib;$(LibraryPath) $(SolutionDir)lib\$(Platform)\lib;$(LibraryPath) diff --git a/src/openrct2-android/app/src/main/CMakeLists.txt b/src/openrct2-android/app/src/main/CMakeLists.txt index 2ed448a79d..28cfc374c4 100644 --- a/src/openrct2-android/app/src/main/CMakeLists.txt +++ b/src/openrct2-android/app/src/main/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.10) -project(openrct2-android CXX) +project(openrct2-android CXX C) set(CMAKE_VERBOSE_MAKEFILE on) @@ -226,7 +226,7 @@ if (CCache_FOUND) endif (CCache_FOUND) file(GLOB_RECURSE LIBOPENRCT2_SOURCES - "${ORCT2_ROOT}/src/thirdparty/duktape/duktape.cpp" + "${ORCT2_ROOT}/src/thirdparty/quickjs-ng/quickjs-amalgam.c" "${ORCT2_ROOT}/src/openrct2/*.cpp" "${ORCT2_ROOT}/src/openrct2/*.h" "${ORCT2_ROOT}/src/openrct2/*.hpp") @@ -255,13 +255,13 @@ target_link_libraries(openrct2-ui openrct2 android stdc++ GLESv1_CM GLESv2 SDL2m add_executable(openrct2-cli ${OPENRCT2_CLI_SOURCES}) target_link_libraries(openrct2-cli openrct2 android stdc++ GLESv1_CM GLESv2) -target_include_directories(openrct2 PRIVATE "${ORCT2_ROOT}/src/thirdparty/duktape/") +target_include_directories(openrct2 SYSTEM PRIVATE "${ORCT2_ROOT}/src/thirdparty/quickjs-ng") target_include_directories(openrct2 SYSTEM PRIVATE "${ORCT2_ROOT}/src/thirdparty") -target_include_directories(openrct2-ui PRIVATE "${ORCT2_ROOT}/src/thirdparty/duktape/") +target_include_directories(openrct2-ui SYSTEM PRIVATE "${ORCT2_ROOT}/src/thirdparty/quickjs-ng") target_include_directories(openrct2-ui PRIVATE "${ORCT2_ROOT}/src") target_include_directories(openrct2-ui SYSTEM PRIVATE "${ORCT2_ROOT}/src/thirdparty") target_include_directories(openrct2-cli PRIVATE "${ORCT2_ROOT}/src") target_include_directories(openrct2-cli SYSTEM PRIVATE "${ORCT2_ROOT}/src/thirdparty") target_include_directories(openrct2 PRIVATE "/opt/openrct2/include/nlohmann/../") -target_include_directories(openrct2-ui PRIVATE "/opt/openrct2/include/nlohmann/../") +target_include_directories(openrct2-ui PRIVATE "/opt/openrct2/include/nlohmann/../") \ No newline at end of file diff --git a/src/openrct2-ui/CMakeLists.txt b/src/openrct2-ui/CMakeLists.txt index 39863d8e2d..8fd3f0bfcc 100644 --- a/src/openrct2-ui/CMakeLists.txt +++ b/src/openrct2-ui/CMakeLists.txt @@ -156,7 +156,7 @@ if (ENABLE_HEADERS_CHECK AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") add_library(openrct2-headers-check OBJECT ${OPENRCT2_HEADERS_CHECK}) set_target_properties(openrct2-headers-check PROPERTIES LINKER_LANGUAGE CXX) set_source_files_properties(${OPENRCT2_HEADERS_CHECK} PROPERTIES LANGUAGE CXX) - add_definitions("-x c++ -Wno-pragma-once-outside-header -Wno-unused-const-variable") + target_compile_options(openrct2-headers-check PRIVATE -Wno-pragma-once-outside-header -Wno-unused-const-variable) get_target_property(OPENRCT2_INCLUDE_DIRS openrct2 INCLUDE_DIRECTORIES) set_target_properties(openrct2-headers-check PROPERTIES INCLUDE_DIRECTORIES "${OPENRCT2_INCLUDE_DIRS}") endif () diff --git a/src/openrct2-ui/WindowManager.cpp b/src/openrct2-ui/WindowManager.cpp index 64e911b881..801c8c5cf1 100644 --- a/src/openrct2-ui/WindowManager.cpp +++ b/src/openrct2-ui/WindowManager.cpp @@ -271,7 +271,7 @@ public: callback(result, std::string(path).c_str()); } }, - trackDesign); + false, trackDesign); return w; } case WindowClass::manageTrackDesign: @@ -1111,6 +1111,12 @@ public: CloseByClass(WindowClass::trackDesignPlace); } + void Cleanup() override + { + CloseByCondition([](WindowBase* w) { return true; }); + WindowCullDead(); + } + /** * Finds the first window with the specified window class. * rct2: 0x006EA8A0 diff --git a/src/openrct2-ui/interface/FileBrowser.cpp b/src/openrct2-ui/interface/FileBrowser.cpp index 6f476a7357..0fc8c9b2c7 100644 --- a/src/openrct2-ui/interface/FileBrowser.cpp +++ b/src/openrct2-ui/interface/FileBrowser.cpp @@ -41,12 +41,12 @@ extern void EmscriptenSaveGame(bool isTrackDesign, bool isAutosave, LoadSaveType namespace OpenRCT2::Ui::FileBrowser { static LoadSaveCallback _loadSaveCallback; + static bool _loadSaveCallbackIsJs = false; WindowBase* OpenPreferred( - LoadSaveAction action, LoadSaveType type, u8string defaultPath, LoadSaveCallback callback, TrackDesign* trackDesign) + LoadSaveAction action, LoadSaveType type, u8string defaultPath, LoadSaveCallback callback, bool isJsCallback, + TrackDesign* trackDesign) { - RegisterCallback(callback); - #ifdef __EMSCRIPTEN__ if (action == LoadSaveAction::save) { @@ -69,16 +69,18 @@ namespace OpenRCT2::Ui::FileBrowser const bool isSave = (action == LoadSaveAction::save); const auto defaultDirectory = GetDir(type); + RegisterCallback(callback, isJsCallback); const u8string path = OpenSystemFileBrowser(isSave, type, defaultDirectory, defaultPath, trackDesign); if (!path.empty()) { Select(path.c_str(), action, type, trackDesign); } + UnregisterJSCallback(); return nullptr; } // Use built-in load/save window - return Windows::LoadsaveOpen(action, type, defaultPath, callback, trackDesign); + return Windows::LoadsaveOpen(action, type, defaultPath, callback, isJsCallback, trackDesign); } bool ListItemSort(LoadSaveListItem& a, LoadSaveListItem& b) @@ -227,9 +229,19 @@ namespace OpenRCT2::Ui::FileBrowser return result; } - void RegisterCallback(LoadSaveCallback callback) + void RegisterCallback(LoadSaveCallback callback, bool isJsCallback) { _loadSaveCallback = callback; + _loadSaveCallbackIsJs = isJsCallback; + } + + void UnregisterJSCallback() + { + // This is very hacky but for a silly confluence of reasons we have to clear the callback if it is a callback to + // javascript plugin code, but not if it is to normal C++ code. + // https://github.com/mrmbernardi/OpenRCT2/pull/11#issuecomment-3448197606 + if (_loadSaveCallbackIsJs) + _loadSaveCallback = {}; } void InvokeCallback(ModalResult result, const utf8* path) diff --git a/src/openrct2-ui/interface/FileBrowser.h b/src/openrct2-ui/interface/FileBrowser.h index 45feb0c80a..874a791512 100644 --- a/src/openrct2-ui/interface/FileBrowser.h +++ b/src/openrct2-ui/interface/FileBrowser.h @@ -58,14 +58,16 @@ namespace OpenRCT2::Ui::FileBrowser u8string GetFilterPatternByType(LoadSaveType type, bool isSave, const TrackDesign* trackDesign = nullptr); u8string RemovePatternWildcard(u8string_view pattern); u8string GetDir(LoadSaveType type); - void RegisterCallback(LoadSaveCallback callback); + void RegisterCallback(LoadSaveCallback callback, bool isJsCallback); + void UnregisterJSCallback(); void InvokeCallback(ModalResult result, const utf8* path); void Select(const char* path, LoadSaveAction action, LoadSaveType type, TrackDesign* trackDesignPtr); StringId GetTitleStringId(LoadSaveType type, bool isSave); u8string OpenSystemFileBrowser( bool isSave, LoadSaveType type, u8string defaultDirectory, u8string defaultPath, const TrackDesign* trackDesign); WindowBase* OpenPreferred( - LoadSaveAction action, LoadSaveType type, u8string defaultPath, LoadSaveCallback callback, TrackDesign* trackDesign); + LoadSaveAction action, LoadSaveType type, u8string defaultPath, LoadSaveCallback callback, bool isJsCallback, + TrackDesign* trackDesign); } // namespace OpenRCT2::Ui::FileBrowser #ifdef __EMSCRIPTEN__ diff --git a/src/openrct2-ui/libopenrct2ui.vcxproj b/src/openrct2-ui/libopenrct2ui.vcxproj index c845a3b418..88c586bbfd 100644 --- a/src/openrct2-ui/libopenrct2ui.vcxproj +++ b/src/openrct2-ui/libopenrct2ui.vcxproj @@ -94,6 +94,7 @@ + @@ -155,6 +156,7 @@ + diff --git a/src/openrct2-ui/scripting/CustomImages.cpp b/src/openrct2-ui/scripting/CustomImages.cpp index 626ae251e4..3c14c16c10 100644 --- a/src/openrct2-ui/scripting/CustomImages.cpp +++ b/src/openrct2-ui/scripting/CustomImages.cpp @@ -18,6 +18,7 @@ #include #include #include + #include using namespace OpenRCT2::Drawing; @@ -47,7 +48,7 @@ namespace OpenRCT2::Scripting int32_t Height; int32_t Stride; PixelDataPaletteKind Palette; - DukValue Data; + JSValue Data; }; struct AllocatedImageList @@ -141,37 +142,37 @@ namespace OpenRCT2::Scripting scriptEngine.SubscribeToPluginStoppedEvent([](std::shared_ptr plugin) -> void { FreeCustomImages(plugin); }); } - DukValue DukGetImageInfo(duk_context* ctx, ImageIndex id) + JSValue JSGetImageInfo(JSContext* ctx, ImageIndex id) { auto* g1 = GfxGetG1Element(id); if (g1 == nullptr) { - return ToDuk(ctx, undefined); + return JS_UNDEFINED; } - DukObject obj(ctx); - obj.Set("id", id); - obj.Set("offset", ToDuk(ctx, { g1->xOffset, g1->yOffset })); - obj.Set("width", g1->width); - obj.Set("height", g1->height); + JSValue obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "id", JS_NewInt32(ctx, id)); + JS_SetPropertyStr(ctx, obj, "offset", ToJSValue(ctx, ScreenCoordsXY{ g1->xOffset, g1->yOffset })); + JS_SetPropertyStr(ctx, obj, "width", JS_NewInt32(ctx, g1->width)); + JS_SetPropertyStr(ctx, obj, "height", JS_NewInt32(ctx, g1->height)); - obj.Set("hasTransparent", g1->flags.has(G1Flag::hasTransparency)); - obj.Set("isRLE", g1->flags.has(G1Flag::hasRLECompression)); - obj.Set("isPalette", g1->flags.has(G1Flag::isPalette)); - obj.Set("noZoom", g1->flags.has(G1Flag::noZoomDraw)); + JS_SetPropertyStr(ctx, obj, "hasTransparent", JS_NewBool(ctx, g1->flags.has(G1Flag::hasTransparency))); + JS_SetPropertyStr(ctx, obj, "isRLE", JS_NewBool(ctx, g1->flags.has(G1Flag::hasRLECompression))); + JS_SetPropertyStr(ctx, obj, "isPalette", JS_NewBool(ctx, g1->flags.has(G1Flag::isPalette))); + JS_SetPropertyStr(ctx, obj, "noZoom", JS_NewBool(ctx, g1->flags.has(G1Flag::noZoomDraw))); if (g1->flags.has(G1Flag::hasZoomSprite)) { - obj.Set("nextZoomId", id - g1->zoomedOffset); + JS_SetPropertyStr(ctx, obj, "nextZoomId", JS_NewInt32(ctx, id - g1->zoomedOffset)); } else { - obj.Set("nextZoomId", undefined); + JS_SetPropertyStr(ctx, obj, "nextZoomId", JS_UNDEFINED); } - return obj.Take(); + return obj; } - static const char* GetPixelDataTypeForG1(const G1Element& g1) + static std::string_view GetPixelDataTypeForG1(const G1Element& g1) { if (g1.flags.has(G1Flag::hasRLECompression)) return "rle"; @@ -180,83 +181,55 @@ namespace OpenRCT2::Scripting return "raw"; } - DukValue DukGetImagePixelData(duk_context* ctx, ImageIndex id) + JSValue JSGetImagePixelData(JSContext* ctx, ImageIndex id) { auto* g1 = GfxGetG1Element(id); if (g1 == nullptr) { - return ToDuk(ctx, undefined); + return JS_UNDEFINED; } auto dataSize = G1CalculateDataSize(g1); - auto* type = GetPixelDataTypeForG1(*g1); + auto type = GetPixelDataTypeForG1(*g1); // Copy the G1 data to a JS buffer wrapped in a Uint8Array - duk_push_fixed_buffer(ctx, dataSize); - duk_size_t bufferSize{}; - auto* buffer = duk_get_buffer_data(ctx, -1, &bufferSize); - if (buffer != nullptr && bufferSize == dataSize) - { - std::memcpy(buffer, g1->offset, dataSize); - } - duk_push_buffer_object(ctx, -1, 0, dataSize, DUK_BUFOBJ_UINT8ARRAY); - duk_remove(ctx, -2); - auto data = DukValue::take_from_stack(ctx, -1); + JSValue data = JS_NewUint8ArrayCopy(ctx, g1->offset, dataSize); - DukObject obj(ctx); - obj.Set("type", type); - obj.Set("width", g1->width); - obj.Set("height", g1->height); - obj.Set("data", data); - return obj.Take(); + JSValue obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "type", JSFromStdString(ctx, type)); + JS_SetPropertyStr(ctx, obj, "width", JS_NewInt32(ctx, g1->width)); + JS_SetPropertyStr(ctx, obj, "height", JS_NewInt32(ctx, g1->height)); + JS_SetPropertyStr(ctx, obj, "data", data); + return obj; } - static std::vector GetBufferFromDukStack(duk_context* ctx) + static std::vector GetDataFromBufferLikeObject(JSContext* ctx, JSValue data) { std::vector result; - duk_size_t bufferLen{}; - const auto* buffer = reinterpret_cast(duk_get_buffer_data(ctx, -1, &bufferLen)); - if (buffer != nullptr) - { - result.resize(bufferLen); - std::memcpy(result.data(), buffer, bufferLen); - } - return result; - } - - static std::vector DukGetDataFromBufferLikeObject(const DukValue& data) - { - std::vector result; - auto ctx = data.context(); - if (data.is_array()) + if (JS_IsArray(data)) { // From array of numbers - data.push(); - auto len = duk_get_length(ctx, -1); - result.resize(len); - for (duk_uarridx_t i = 0; i < len; i++) + int64_t arrSz = 0; + JS_GetLength(ctx, data, &arrSz); + if (arrSz > 0) { - if (duk_get_prop_index(ctx, -1, i)) - { - result[i] = duk_get_int(ctx, -1) & 0xFF; - duk_pop(ctx); - } + result.resize(arrSz); + JSIterateArray(ctx, data, [&result](JSContext* ctx2, JSValue val) { result.push_back(JSToInt(ctx2, val)); }); } - duk_pop(ctx); } - else if (data.type() == DukValue::Type::STRING) + else if (JS_IsString(data)) { - // From base64 string - data.push(); - duk_base64_decode(ctx, -1); - result = GetBufferFromDukStack(ctx); - duk_pop(ctx); + std::string str = JSToStdString(ctx, data); + result = base64::decode_into>(str); } - else if (data.type() == DukValue::Type::OBJECT) + else if (JS_IsArrayBuffer(data)) { // From Uint8Array - data.push(); - result = GetBufferFromDukStack(ctx); - duk_pop(ctx); + size_t sz = 0; + uint8_t* arr = JS_GetUint8Array(ctx, &sz, data); + if (arr) + { + result = std::vector(arr, arr + sz); + } } return result; } @@ -290,14 +263,14 @@ namespace OpenRCT2::Scripting } } - static std::vector GetBufferFromPixelData(duk_context* ctx, PixelData& pixelData) + static std::vector GetBufferFromPixelData(JSContext* ctx, PixelData& pixelData) { std::vector imageData; switch (pixelData.Type) { case PixelDataKind::Raw: { - auto data = DukGetDataFromBufferLikeObject(pixelData.Data); + auto data = GetDataFromBufferLikeObject(ctx, pixelData.Data); if (pixelData.Stride != pixelData.Width) { // Make sure data is expected size for RemovePadding @@ -312,7 +285,7 @@ namespace OpenRCT2::Scripting } case PixelDataKind::Rle: { - imageData = DukGetDataFromBufferLikeObject(pixelData.Data); + imageData = GetDataFromBufferLikeObject(ctx, pixelData.Data); break; } case PixelDataKind::Png: @@ -320,7 +293,7 @@ namespace OpenRCT2::Scripting auto imageFormat = pixelData.Palette == PixelDataPaletteKind::Keep ? ImageFormat::png : ImageFormat::png32; auto palette = pixelData.Palette == PixelDataPaletteKind::Keep ? Palette::KeepIndices : Palette::OpenRCT2; auto importMode = getImportModeFromPalette(pixelData.Palette); - auto pngData = DukGetDataFromBufferLikeObject(pixelData.Data); + auto pngData = GetDataFromBufferLikeObject(ctx, pixelData.Data); auto image = Imaging::ReadFromBuffer(pngData, imageFormat); constexpr ImportFlags flags = { ImportFlag::rle }; ImageImportMeta meta = { { 0, 0 }, palette, flags, importMode }; @@ -341,49 +314,40 @@ namespace OpenRCT2::Scripting return imageData; } - template<> - PixelDataKind FromDuk(const DukValue& d) + static PixelDataKind PixelDataKindFromJS(const std::string& s) { - if (d.type() == DukValue::Type::STRING) - { - auto& s = d.as_string(); - if (s == "raw") - return PixelDataKind::Raw; - if (s == "rle") - return PixelDataKind::Rle; - if (s == "palette") - return PixelDataKind::Palette; - if (s == "png") - return PixelDataKind::Png; - } + if (s == "raw") + return PixelDataKind::Raw; + if (s == "rle") + return PixelDataKind::Rle; + if (s == "palette") + return PixelDataKind::Palette; + if (s == "png") + return PixelDataKind::Png; return PixelDataKind::Unknown; } - template<> - PixelDataPaletteKind FromDuk(const DukValue& d) + static PixelDataPaletteKind PixelDataPaletteKindFromJS(const std::string& s) { - if (d.type() == DukValue::Type::STRING) - { - auto& s = d.as_string(); - if (s == "keep") - return PixelDataPaletteKind::Keep; - if (s == "closest") - return PixelDataPaletteKind::Closest; - if (s == "dither") - return PixelDataPaletteKind::Dither; - } + if (s == "keep") + return PixelDataPaletteKind::Keep; + if (s == "closest") + return PixelDataPaletteKind::Closest; + if (s == "dither") + return PixelDataPaletteKind::Dither; return PixelDataPaletteKind::None; } - static PixelData GetPixelDataFromDuk(const DukValue& dukPixelData) + static PixelData GetPixelDataFromJS(JSContext* ctx, JSValue jsPixelData) { PixelData pixelData; - pixelData.Type = FromDuk(dukPixelData["type"]); - pixelData.Palette = FromDuk(dukPixelData["palette"]); - pixelData.Width = AsOrDefault(dukPixelData["width"], 0); - pixelData.Height = AsOrDefault(dukPixelData["height"], 0); - pixelData.Stride = AsOrDefault(dukPixelData["stride"], pixelData.Width); - pixelData.Data = dukPixelData["data"]; + pixelData.Type = PixelDataKindFromJS(JSToStdString(ctx, jsPixelData, "type")); + pixelData.Palette = PixelDataPaletteKindFromJS(JSToStdString(ctx, jsPixelData, "palette")); + pixelData.Width = AsOrDefault(ctx, jsPixelData, "width", static_cast(0)); + pixelData.Height = AsOrDefault(ctx, jsPixelData, "height", static_cast(0)); + pixelData.Stride = AsOrDefault(ctx, jsPixelData, "stride", static_cast(pixelData.Width)); + // Note: this must be JS_FreeValued + pixelData.Data = JS_GetPropertyStr(ctx, jsPixelData, "data"); return pixelData; } @@ -414,21 +378,15 @@ namespace OpenRCT2::Scripting DrawingEngineInvalidateImage(id); } - void DukSetPixelData(duk_context* ctx, ImageIndex id, const DukValue& dukPixelData) + void JSSetPixelData(JSContext* ctx, ImageIndex id, JSValue jsPixelData) { - auto pixelData = GetPixelDataFromDuk(dukPixelData); - try - { - auto newData = GetBufferFromPixelData(ctx, pixelData); - ReplacePixelDataForImage(id, pixelData, std::move(newData)); - } - catch (const std::runtime_error& e) - { - duk_error(ctx, DUK_ERR_ERROR, e.what()); - } + auto pixelData = GetPixelDataFromJS(ctx, jsPixelData); + auto newData = GetBufferFromPixelData(ctx, pixelData); + ReplacePixelDataForImage(id, pixelData, std::move(newData)); + JS_FreeValue(ctx, pixelData.Data); } - void DukDrawCustomImage(ScriptEngine& scriptEngine, ImageIndex id, ScreenSize size, const DukValue& callback) + void JSDrawCustomImage(ScriptEngine& scriptEngine, ImageIndex id, ScreenSize size, const JSCallback& callback) { auto* ctx = scriptEngine.GetContext(); auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin(); @@ -464,10 +422,12 @@ namespace OpenRCT2::Scripting rt.bits = reinterpret_cast(g1->offset); } - auto dukG = GetObjectAsDukValue(ctx, std::make_shared(ctx, rt)); - drawingEngine->BeginDraw(); - scriptEngine.ExecutePluginCall(plugin, callback, { dukG }, false); - drawingEngine->EndDraw(); + if (callback.IsValid()) + { + drawingEngine->BeginDraw(); + scriptEngine.ExecutePluginCall(plugin, callback.callback, { gScGraphicsContext.New(ctx, rt) }, false); + drawingEngine->EndDraw(); + } if (createNewImage) { diff --git a/src/openrct2-ui/scripting/CustomImages.h b/src/openrct2-ui/scripting/CustomImages.h index 29833dcb56..b4fd8aa362 100644 --- a/src/openrct2-ui/scripting/CustomImages.h +++ b/src/openrct2-ui/scripting/CustomImages.h @@ -13,8 +13,6 @@ #include #include - #include - #include #include #include @@ -24,10 +22,10 @@ namespace OpenRCT2::Scripting std::optional AllocateCustomImages(const std::shared_ptr& plugin, uint32_t count); bool FreeCustomImages(const std::shared_ptr& plugin, ImageList range); bool DoesPluginOwnImage(const std::shared_ptr& plugin, ImageIndex index); - DukValue DukGetImageInfo(duk_context* ctx, ImageIndex id); - DukValue DukGetImagePixelData(duk_context* ctx, ImageIndex id); - void DukSetPixelData(duk_context* ctx, ImageIndex id, const DukValue& dukPixelData); - void DukDrawCustomImage(ScriptEngine& scriptEngine, ImageIndex id, ScreenSize size, const DukValue& callback); + JSValue JSGetImageInfo(JSContext* ctx, ImageIndex id); + JSValue JSGetImagePixelData(JSContext* ctx, ImageIndex id); + void JSSetPixelData(JSContext* ctx, ImageIndex id, JSValue jsPixelData); + void JSDrawCustomImage(ScriptEngine& scriptEngine, ImageIndex id, ScreenSize size, const JSCallback& callback); } // namespace OpenRCT2::Scripting diff --git a/src/openrct2-ui/scripting/CustomListView.cpp b/src/openrct2-ui/scripting/CustomListView.cpp index 63fc24244a..5e26d14525 100644 --- a/src/openrct2-ui/scripting/CustomListView.cpp +++ b/src/openrct2-ui/scripting/CustomListView.cpp @@ -34,12 +34,11 @@ namespace OpenRCT2::Scripting { constexpr size_t kColumnHeaderHeight = kListRowHeight + 1; - template<> - ColumnSortOrder FromDuk(const DukValue& d) + ColumnSortOrder ColumnSortOrderFromJS(JSContext* ctx, JSValue d) { - if (d.type() == DukValue::Type::STRING) + if (JS_IsString(d)) { - auto s = d.as_string(); + auto s = JSToStdString(ctx, d); if (s == "ascending") return ColumnSortOrder::Ascending; if (s == "descending") @@ -48,44 +47,36 @@ namespace OpenRCT2::Scripting return ColumnSortOrder::None; } - template<> - DukValue ToDuk(duk_context* ctx, const ColumnSortOrder& value) + static JSValue ColumnSortOrderToJS(JSContext* ctx, ColumnSortOrder value) { switch (value) { case ColumnSortOrder::Ascending: - return ToDuk(ctx, "ascending"); + return JSFromStdString(ctx, "ascending"); case ColumnSortOrder::Descending: - return ToDuk(ctx, "descending"); + return JSFromStdString(ctx, "descending"); default: - return ToDuk(ctx, "none"); + return JSFromStdString(ctx, "none"); } } - template<> - std::optional FromDuk(const DukValue& d) - { - if (d.type() == DukValue::Type::NUMBER) - { - return d.as_int(); - } - return std::nullopt; - } - - template<> - ListViewColumn FromDuk(const DukValue& d) + ListViewColumn ListViewColumnFromJS(JSContext* ctx, JSValue d) { ListViewColumn result; - result.CanSort = AsOrDefault(d["canSort"], false); - result.SortOrder = FromDuk(d["sortOrder"]); - result.Header = AsOrDefault(d["header"], ""); - result.HeaderTooltip = AsOrDefault(d["headerTooltip"], ""); - result.MinWidth = FromDuk>(d["minWidth"]); - result.MaxWidth = FromDuk>(d["maxWidth"]); - result.RatioWidth = FromDuk>(d["ratioWidth"]); - if (d["width"].type() == DukValue::Type::NUMBER) + result.CanSort = AsOrDefault(ctx, d, "canSort", false); + JSValue sortOrderVal = JS_GetPropertyStr(ctx, d, "sortOrder"); + result.SortOrder = ColumnSortOrderFromJS(ctx, sortOrderVal); + JS_FreeValue(ctx, sortOrderVal); + result.Header = AsOrDefault(ctx, d, "header", ""); + result.HeaderTooltip = AsOrDefault(ctx, d, "headerTooltip", ""); + result.MinWidth = JSToOptionalInt(ctx, d, "minWidth"); + result.MaxWidth = JSToOptionalInt(ctx, d, "maxWidth"); + result.RatioWidth = JSToOptionalInt(ctx, d, "ratioWidth"); + + auto width = JSToOptionalInt(ctx, d, "width"); + if (width.has_value()) { - result.MinWidth = d["width"].as_int(); + result.MinWidth = width.value(); result.MaxWidth = result.MinWidth; result.RatioWidth = std::nullopt; } @@ -96,45 +87,43 @@ namespace OpenRCT2::Scripting return result; } - template<> - DukValue ToDuk(duk_context* ctx, const ListViewColumn& value) + JSValue ListViewColumnToJS(JSContext* ctx, const ListViewColumn& value) { - DukObject obj(ctx); - obj.Set("canSort", value.CanSort); - obj.Set("sortOrder", ToDuk(ctx, value.SortOrder)); - obj.Set("header", value.Header); - obj.Set("headerTooltip", value.HeaderTooltip); - obj.Set("minWidth", value.MinWidth); - obj.Set("maxWidth", value.MaxWidth); - obj.Set("ratioWidth", value.RatioWidth); - obj.Set("width", value.Width); - return obj.Take(); + JSValue obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "canSort", JS_NewBool(ctx, value.CanSort)); + JS_SetPropertyStr(ctx, obj, "sortOrder", ColumnSortOrderToJS(ctx, value.SortOrder)); + JS_SetPropertyStr(ctx, obj, "header", JSFromStdString(ctx, value.Header)); + JS_SetPropertyStr(ctx, obj, "headerTooltip", JSFromStdString(ctx, value.HeaderTooltip)); + JS_SetPropertyStr( + ctx, obj, "minWidth", value.MinWidth.has_value() ? JS_NewInt32(ctx, value.MinWidth.value()) : JS_NULL); + JS_SetPropertyStr( + ctx, obj, "maxWidth", value.MaxWidth.has_value() ? JS_NewInt32(ctx, value.MaxWidth.value()) : JS_NULL); + JS_SetPropertyStr( + ctx, obj, "ratioWidth", value.RatioWidth.has_value() ? JS_NewInt32(ctx, value.RatioWidth.value()) : JS_NULL); + JS_SetPropertyStr(ctx, obj, "width", JS_NewInt32(ctx, value.Width)); + return obj; } - template<> - ListViewItem FromDuk(const DukValue& d) + ListViewItem ListViewItemFromJS(JSContext* ctx, JSValue d) { ListViewItem result; - if (d.type() == DukValue::Type::STRING) + if (JS_IsString(d)) { - result = ListViewItem(ProcessString(d)); + result = ListViewItem(JSToStdString(ctx, d)); } - else if (d.is_array()) + else if (JS_IsArray(d)) { std::vector cells; - for (const auto& dukCell : d.as_array()) - { - cells.push_back(ProcessString(dukCell)); - } + JSIterateArray(ctx, d, [&cells](JSContext* ctx2, JSValue x) { cells.push_back(JSToStdString(ctx2, x)); }); result = ListViewItem(std::move(cells)); } - else if (d.type() == DukValue::Type::OBJECT) + else if (JS_IsObject(d)) { - auto type = ProcessString(d["type"]); + auto type = JSToStdString(ctx, d, "type"); // This type was misspelt between 2020 and 2025. if (type == "separator" || type == "seperator") { - auto text = ProcessString(d["text"]); + auto text = JSToStdString(ctx, d, "text"); result = ListViewItem(text); result.IsSeparator = true; } @@ -142,64 +131,51 @@ namespace OpenRCT2::Scripting return result; } - template<> - std::vector FromDuk(const DukValue& d) + std::vector ListViewColumnVecFromJS(JSContext* ctx, JSValue d) { std::vector result; - if (d.is_array()) + if (JS_IsArray(d)) { - auto dukColumns = d.as_array(); - for (const auto& dukColumn : dukColumns) - { - result.push_back(FromDuk(dukColumn)); - } + JSIterateArray(ctx, d, [&result](JSContext* ctx2, JSValue x) { result.push_back(ListViewColumnFromJS(ctx2, x)); }); } return result; } - template<> - std::vector FromDuk(const DukValue& d) + std::vector ListViewItemVecFromJS(JSContext* ctx, JSValue d) { std::vector result; - if (d.is_array()) + if (JS_IsArray(d)) { - auto dukItems = d.as_array(); - for (const auto& dukItem : dukItems) - { - result.push_back(FromDuk(dukItem)); - } + JSIterateArray(ctx, d, [&result](JSContext* ctx2, JSValue x) { result.push_back(ListViewItemFromJS(ctx2, x)); }); } return result; } - template<> - std::optional FromDuk(const DukValue& d) + std::optional RowColumnFromJS(JSContext* ctx, JSValue d) { - if (d.type() == DukValue::Type::OBJECT) + if (JS_IsObject(d)) { - auto dukRow = d["row"]; - auto dukColumn = d["column"]; - if (dukRow.type() == DukValue::Type::NUMBER && dukColumn.type() == DukValue::Type::NUMBER) + auto row = JSToOptionalInt(ctx, d, "row"); + auto column = JSToOptionalInt(ctx, d, "column"); + if (row.has_value() && column.has_value()) { - return RowColumn(dukRow.as_int(), dukColumn.as_int()); + return RowColumn(row.value(), column.value()); } } return std::nullopt; } - template<> - DukValue ToDuk(duk_context* ctx, const RowColumn& value) + JSValue RowColumnToJS(JSContext* ctx, const RowColumn value) { - DukObject obj(ctx); - obj.Set("row", value.Row); - obj.Set("column", value.Column); - return obj.Take(); + JSValue obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "row", JS_NewInt32(ctx, value.Row)); + JS_SetPropertyStr(ctx, obj, "column", JS_NewInt32(ctx, value.Column)); + return obj; } - template<> - ScrollbarType FromDuk(const DukValue& d) + ScrollbarType ScrollbarTypeFromJS(JSContext* ctx, JSValue d) { - auto value = AsOrDefault(d, ""); + auto value = JSToStdString(ctx, d); if (value == "horizontal") return ScrollbarType::Horizontal; if (value == "vertical") @@ -209,20 +185,19 @@ namespace OpenRCT2::Scripting return ScrollbarType::None; } - template<> - DukValue ToDuk(duk_context* ctx, const ScrollbarType& value) + JSValue ScrollbarTypeToJS(JSContext* ctx, const ScrollbarType value) { switch (value) { default: case ScrollbarType::None: - return ToDuk(ctx, "none"); + return JSFromStdString(ctx, "none"); case ScrollbarType::Horizontal: - return ToDuk(ctx, "horizontal"); + return JSFromStdString(ctx, "horizontal"); case ScrollbarType::Vertical: - return ToDuk(ctx, "vertical"); + return JSFromStdString(ctx, "vertical"); case ScrollbarType::Both: - return ToDuk(ctx, "both"); + return JSFromStdString(ctx, "both"); } } @@ -479,15 +454,13 @@ void CustomListView::MouseOver(const ScreenCoordsXY& pos, bool isMouseDown) HighlightedCell = hitResult; if (HighlightedCell != LastHighlightedCell) { - if (hitResult->Row != kHeaderRow && OnHighlight.context() != nullptr && OnHighlight.is_function()) + if (hitResult->Row != kHeaderRow && OnHighlight.context != nullptr && OnHighlight.IsValid()) { - auto ctx = OnHighlight.context(); - duk_push_int(ctx, static_cast(HighlightedCell->Row)); - auto dukRow = DukValue::take_from_stack(ctx, -1); - duk_push_int(ctx, static_cast(HighlightedCell->Column)); - auto dukColumn = DukValue::take_from_stack(ctx, -1); + auto ctx = OnHighlight.context; + JSValue jsRow = JS_NewInt32(ctx, HighlightedCell->Row); + JSValue jsColumn = JS_NewInt32(ctx, HighlightedCell->Column); auto& scriptEngine = GetContext()->GetScriptEngine(); - scriptEngine.ExecutePluginCall(Owner, OnHighlight, { dukRow, dukColumn }, false); + scriptEngine.ExecutePluginCall(Owner, OnHighlight.callback, { jsRow, jsColumn }, false); } Invalidate(); } @@ -526,15 +499,13 @@ void CustomListView::MouseDown(const ScreenCoordsXY& pos) Invalidate(); } - auto ctx = OnClick.context(); - if (ctx != nullptr && OnClick.is_function()) + auto ctx = OnClick.context; + if (ctx != nullptr && OnClick.IsValid()) { - duk_push_int(ctx, static_cast(hitResult->Row)); - auto dukRow = DukValue::take_from_stack(ctx, -1); - duk_push_int(ctx, static_cast(hitResult->Column)); - auto dukColumn = DukValue::take_from_stack(ctx, -1); + JSValue jsRow = JS_NewInt32(ctx, hitResult->Row); + JSValue jsColumn = JS_NewInt32(ctx, hitResult->Column); auto& scriptEngine = GetContext()->GetScriptEngine(); - scriptEngine.ExecutePluginCall(Owner, OnClick, { dukRow, dukColumn }, false); + scriptEngine.ExecutePluginCall(Owner, OnClick.callback, { jsRow, jsColumn }, false); } } } diff --git a/src/openrct2-ui/scripting/CustomListView.h b/src/openrct2-ui/scripting/CustomListView.h index 3aa4ea00ed..fe650d14e3 100644 --- a/src/openrct2-ui/scripting/CustomListView.h +++ b/src/openrct2-ui/scripting/CustomListView.h @@ -15,7 +15,6 @@ #include #include - #include #include #include #include @@ -119,8 +118,8 @@ namespace OpenRCT2::Ui::Windows bool IsMouseDown{}; bool CanSelect{}; - DukValue OnClick; - DukValue OnHighlight; + JSCallback OnClick; + JSCallback OnHighlight; CustomListView(WindowBase* parent, size_t scrollIndex); ScrollbarType GetScrollbars() const; @@ -155,44 +154,29 @@ namespace OpenRCT2::Ui::Windows }; } // namespace OpenRCT2::Ui::Windows -class DukValue; - namespace OpenRCT2::Scripting { using namespace OpenRCT2::Ui::Windows; - template<> - ColumnSortOrder FromDuk(const DukValue& d); + ColumnSortOrder ColumnSortOrderFromJS(JSContext* ctx, JSValue d); - template<> - std::optional FromDuk(const DukValue& d); + ListViewColumn ListViewColumnFromJS(JSContext* ctx, JSValue d); - template<> - ListViewColumn FromDuk(const DukValue& d); + ListViewItem ListViewItemFromJS(JSContext* ctx, JSValue d); - template<> - ListViewItem FromDuk(const DukValue& d); + std::vector ListViewColumnVecFromJS(JSContext* ctx, JSValue d); - template<> - std::vector FromDuk(const DukValue& d); + std::vector ListViewItemVecFromJS(JSContext* ctx, JSValue d); - template<> - std::vector FromDuk(const DukValue& d); + std::optional RowColumnFromJS(JSContext* ctx, JSValue d); - template<> - std::optional FromDuk(const DukValue& d); + JSValue RowColumnToJS(JSContext* ctx, RowColumn value); - template<> - DukValue ToDuk(duk_context* ctx, const RowColumn& value); + JSValue ListViewColumnToJS(JSContext* ctx, const ListViewColumn& value); - template<> - DukValue ToDuk(duk_context* ctx, const ListViewColumn& value); + ScrollbarType ScrollbarTypeFromJS(JSContext* ctx, JSValue d); - template<> - ScrollbarType FromDuk(const DukValue& d); - - template<> - DukValue ToDuk(duk_context* ctx, const ScrollbarType& value); + JSValue ScrollbarTypeToJS(JSContext* ctx, ScrollbarType value); } // namespace OpenRCT2::Scripting #endif diff --git a/src/openrct2-ui/scripting/CustomMenu.cpp b/src/openrct2-ui/scripting/CustomMenu.cpp index 0cb6c4d76a..bb92a9a758 100644 --- a/src/openrct2-ui/scripting/CustomMenu.cpp +++ b/src/openrct2-ui/scripting/CustomMenu.cpp @@ -16,6 +16,8 @@ #include #include #include + #include + #include #include #include #include @@ -31,7 +33,7 @@ namespace OpenRCT2::Scripting CustomShortcut::CustomShortcut( std::shared_ptr owner, std::string_view id, std::string_view text, const std::vector& bindings, - DukValue callback) + JSCallback callback) : Owner(owner) , Id(id) , Text(text) @@ -59,7 +61,7 @@ namespace OpenRCT2::Scripting void CustomShortcut::Invoke() const { auto& scriptEngine = GetContext()->GetScriptEngine(); - scriptEngine.ExecutePluginCall(Owner, Callback, {}, false); + scriptEngine.ExecutePluginCall(Owner, Callback.callback, {}, false); } static constexpr std::array CursorNames = { @@ -69,47 +71,57 @@ namespace OpenRCT2::Scripting "volcano_down", "walk_down", "paint_down", "entrance_down", "hand_open", "hand_closed", }; - static const DukEnumMap ToolFilterMap( - { - { "terrain", ViewportInteractionItem::terrain }, - { "entity", ViewportInteractionItem::entity }, - { "ride", ViewportInteractionItem::ride }, - { "water", ViewportInteractionItem::water }, - { "scenery", ViewportInteractionItem::scenery }, - { "footpath", ViewportInteractionItem::footpath }, - { "footpath_item", ViewportInteractionItem::pathAddition }, - { "park_entrance", ViewportInteractionItem::parkEntrance }, - { "wall", ViewportInteractionItem::wall }, - { "large_scenery", ViewportInteractionItem::largeScenery }, - { "label", ViewportInteractionItem::label }, - { "banner", ViewportInteractionItem::banner }, - }); - - template<> - DukValue ToDuk(duk_context* ctx, const CursorID& cursorId) + JSValue CursorIDToJSValue(JSContext* ctx, CursorID id) { - auto value = EnumValue(cursorId); - if (value < std::size(CursorNames)) + auto idVal = EnumValue(id); + if (idVal < CursorNames.size()) { - auto str = CursorNames[value]; - duk_push_lstring(ctx, str.data(), str.size()); - return DukValue::take_from_stack(ctx); + return JSFromStdString(ctx, CursorNames[idVal]); } - return ToDuk(ctx, undefined); + return JS_UNDEFINED; } - template<> - CursorID FromDuk(const DukValue& s) + static CursorID CursorJSValToID(JSContext* ctx, JSValue value) { - if (s.type() == DukValue::Type::STRING) + if (JS_IsString(value)) { - auto it = std::find(std::begin(CursorNames), std::end(CursorNames), s.as_c_string()); - if (it != std::end(CursorNames)) + std::string valueStr = JSToStdString(ctx, value); + for (uint8_t i = 0; i < EnumValue(CursorID::Count); i++) { - return static_cast(std::distance(std::begin(CursorNames), it)); + if (CursorNames[i] == valueStr) + { + return static_cast(i); + } } } - return CursorID::Undefined; + return CursorID::Arrow; + } + + static const EnumMap ToolFilterMap{ + { "terrain", ViewportInteractionItem::terrain }, + { "entity", ViewportInteractionItem::entity }, + { "ride", ViewportInteractionItem::ride }, + { "water", ViewportInteractionItem::water }, + { "scenery", ViewportInteractionItem::scenery }, + { "footpath", ViewportInteractionItem::footpath }, + { "footpath_item", ViewportInteractionItem::pathAddition }, + { "park_entrance", ViewportInteractionItem::parkEntrance }, + { "wall", ViewportInteractionItem::wall }, + { "large_scenery", ViewportInteractionItem::largeScenery }, + { "label", ViewportInteractionItem::label }, + { "banner", ViewportInteractionItem::banner }, + }; + + static ViewportInteractionItem FilterJSValToEnum(JSContext* ctx, JSValue value) + { + if (JS_IsString(value)) + { + if (auto val = ToolFilterMap.TryGet(JSToStdString(ctx, value)); val.has_value()) + { + return val.value(); + } + } + return ViewportInteractionItem::none; } static void RemoveMenuItemsAndTool(std::shared_ptr owner) @@ -157,13 +169,13 @@ namespace OpenRCT2::Scripting void CustomTool::OnUpdate(const ScreenCoordsXY& screenCoords) { - InvokeEventHandler(onMove, screenCoords); + InvokeEventHandler(onMove.callback, screenCoords); } void CustomTool::OnDown(const ScreenCoordsXY& screenCoords) { MouseDown = true; - InvokeEventHandler(onDown, screenCoords); + InvokeEventHandler(onDown.callback, screenCoords); } void CustomTool::OnDrag(const ScreenCoordsXY& screenCoords) @@ -173,36 +185,36 @@ namespace OpenRCT2::Scripting void CustomTool::Start() { auto& scriptEngine = GetContext()->GetScriptEngine(); - scriptEngine.ExecutePluginCall(Owner, onStart, {}, false); + scriptEngine.ExecutePluginCall(Owner, onStart.callback, {}, false); } void CustomTool::OnUp(const ScreenCoordsXY& screenCoords) { MouseDown = false; - InvokeEventHandler(onUp, screenCoords); + InvokeEventHandler(onUp.callback, screenCoords); } void CustomTool::OnAbort() { auto& scriptEngine = GetContext()->GetScriptEngine(); - scriptEngine.ExecutePluginCall(Owner, onFinish, {}, false); + scriptEngine.ExecutePluginCall(Owner, onFinish.callback, {}, false); } - void CustomTool::InvokeEventHandler(const DukValue& dukHandler, const ScreenCoordsXY& screenCoords) + void CustomTool::InvokeEventHandler(JSValue handler, const ScreenCoordsXY& screenCoords) { - if (dukHandler.is_function()) + JSContext* ctx = Owner->GetContext(); + if (JS_IsFunction(ctx, handler)) { - auto ctx = dukHandler.context(); auto info = GetMapCoordinatesFromPos(screenCoords, Filter); - DukObject obj(dukHandler.context()); - obj.Set("isDown", MouseDown); - obj.Set("screenCoords", ToDuk(ctx, screenCoords)); - obj.Set("mapCoords", ToDuk(ctx, info.Loc)); + JSValue obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "isDown", JS_NewBool(ctx, MouseDown)); + JS_SetPropertyStr(ctx, obj, "screenCoords", ToJSValue(ctx, screenCoords)); + JS_SetPropertyStr(ctx, obj, "mapCoords", ToJSValue(ctx, info.Loc)); if (info.interactionType == ViewportInteractionItem::entity && info.Entity != nullptr) { - obj.Set("entityId", info.Entity->Id.ToUnderlying()); + JS_SetPropertyStr(ctx, obj, "entityId", JS_NewInt32(ctx, info.Entity->Id.ToUnderlying())); } else if (info.Element != nullptr) { @@ -214,7 +226,7 @@ namespace OpenRCT2::Scripting { if (el == info.Element) { - obj.Set("tileElementIndex", index); + JS_SetPropertyStr(ctx, obj, "tileElementIndex", JS_NewInt32(ctx, index)); break; } index++; @@ -223,71 +235,75 @@ namespace OpenRCT2::Scripting } auto& scriptEngine = GetContext()->GetScriptEngine(); - std::vector args; - args.emplace_back(obj.Take()); - scriptEngine.ExecutePluginCall(Owner, dukHandler, args, false); + scriptEngine.ExecutePluginCall(Owner, handler, { obj }, false); } } - void InitialiseCustomTool(ScriptEngine& scriptEngine, const DukValue& dukValue) + [[nodiscard]] JSValue InitialiseCustomTool(ScriptEngine& scriptEngine, JSContext* ctx, JSValue value) { - try + std::shared_ptr currentPlugin = scriptEngine.GetExecInfo().GetCurrentPlugin(); + + if (JS_IsObject(value)) { - if (dukValue.type() == DukValue::Type::OBJECT) + JSValue idVal = JS_GetPropertyStr(ctx, value, "id"); + if (!JS_IsString(idVal)) { - CustomTool customTool; - customTool.Owner = scriptEngine.GetExecInfo().GetCurrentPlugin(); - customTool.Id = dukValue["id"].as_string(); - customTool.Cursor = FromDuk(dukValue["cursor"]); - if (customTool.Cursor == CursorID::Undefined) - { - customTool.Cursor = CursorID::Arrow; - } + JS_FreeValue(ctx, idVal); + JS_ThrowTypeError(ctx, "Invalid id. Expected string"); + return JS_EXCEPTION; + } - auto dukFilter = dukValue["filter"]; - if (dukFilter.is_array()) - { - customTool.Filter = 0; - auto dukItems = dukFilter.as_array(); - for (const auto& dukItem : dukItems) - { - if (dukItem.type() == DukValue::Type::STRING) - { - auto value = ToolFilterMap[dukItem.as_string()]; - customTool.Filter |= static_cast(EnumToFlag(value)); - } - } - } - else - { - customTool.Filter = kViewportInteractionItemAll; - } + CustomTool customTool; + customTool.Owner = currentPlugin; - customTool.onStart = dukValue["onStart"]; - customTool.onDown = dukValue["onDown"]; - customTool.onMove = dukValue["onMove"]; - customTool.onUp = dukValue["onUp"]; - customTool.onFinish = dukValue["onFinish"]; + customTool.Id = JSToStdString(ctx, idVal); + JS_FreeValue(ctx, idVal); - auto* windowMgr = GetWindowManager(); - auto toolbarWindow = windowMgr->FindByClass(WindowClass::topToolbar); - if (toolbarWindow != nullptr) + JSValue cursorVal = JS_GetPropertyStr(ctx, value, "cursor"); + customTool.Cursor = CursorJSValToID(ctx, cursorVal); + JS_FreeValue(ctx, cursorVal); + + JSValue filter = JS_GetPropertyStr(ctx, value, "filter"); + if (JS_IsArray(filter)) + { + customTool.Filter = 0; + int64_t len = -1; + JS_GetLength(ctx, filter, &len); + for (int64_t i = 0; i < len; i++) { - // Use a widget that does not exist on top toolbar but also make sure it isn't - // kWidgetIndexNull, as that prevents abort from being called. - // TODO: refactor this to not leech on the top toolbar. - WidgetIndex widgetIndex = 0xFFFE; - ToolCancel(); - ToolSet(*toolbarWindow, widgetIndex, static_cast(customTool.Cursor)); - ActiveCustomTool = std::move(customTool); - ActiveCustomTool->Start(); + JSValue curFilter = JS_GetPropertyInt64(ctx, filter, i); + auto elem = FilterJSValToEnum(ctx, curFilter); + customTool.Filter |= static_cast(EnumToFlag(elem)); + JS_FreeValue(ctx, curFilter); } } + else + { + customTool.Filter = kViewportInteractionItemAll; + } + JS_FreeValue(ctx, filter); + + customTool.onStart = JSToCallback(ctx, value, "onStart"); + customTool.onDown = JSToCallback(ctx, value, "onDown"); + customTool.onMove = JSToCallback(ctx, value, "onMove"); + customTool.onUp = JSToCallback(ctx, value, "onUp"); + customTool.onFinish = JSToCallback(ctx, value, "onFinish"); + + auto* windowMgr = GetWindowManager(); + auto toolbarWindow = windowMgr->FindByClass(WindowClass::topToolbar); + if (toolbarWindow != nullptr) + { + // Use a widget that does not exist on top toolbar but also make sure it isn't + // kWidgetIndexNull, as that prevents abort from being called. + // TODO: refactor this to not leech on the top toolbar. + WidgetIndex widgetIndex = 0xFFFE; + ToolCancel(); + ToolSet(*toolbarWindow, widgetIndex, static_cast(customTool.Cursor)); + ActiveCustomTool = std::move(customTool); + ActiveCustomTool->Start(); + } } - catch (const DukException&) - { - duk_error(scriptEngine.GetContext(), DUK_ERR_ERROR, "Invalid parameters."); - } + return JS_UNDEFINED; } } // namespace OpenRCT2::Scripting diff --git a/src/openrct2-ui/scripting/CustomMenu.h b/src/openrct2-ui/scripting/CustomMenu.h index 0a62209aa9..faaf54c0b4 100644 --- a/src/openrct2-ui/scripting/CustomMenu.h +++ b/src/openrct2-ui/scripting/CustomMenu.h @@ -14,7 +14,6 @@ #include #include #include - #include #include #include #include @@ -35,10 +34,10 @@ namespace OpenRCT2::Scripting std::shared_ptr Owner; CustomToolbarMenuItemKind Kind; std::string Text; - DukValue Callback; + JSCallback Callback; CustomToolbarMenuItem( - std::shared_ptr owner, CustomToolbarMenuItemKind kind, const std::string& text, DukValue callback) + std::shared_ptr owner, CustomToolbarMenuItemKind kind, const std::string& text, JSCallback callback) : Owner(owner) , Kind(kind) , Text(text) @@ -49,7 +48,7 @@ namespace OpenRCT2::Scripting void Invoke() const { auto& scriptEngine = GetContext()->GetScriptEngine(); - scriptEngine.ExecutePluginCall(Owner, Callback, {}, false); + scriptEngine.ExecutePluginCall(Owner, Callback.callback, {}, false); } }; @@ -60,11 +59,11 @@ namespace OpenRCT2::Scripting std::string Id; std::string Text; std::vector Bindings; - DukValue Callback; + JSCallback Callback; CustomShortcut( std::shared_ptr owner, std::string_view id, std::string_view text, const std::vector& bindings, - DukValue callback); + JSCallback callback); CustomShortcut(CustomShortcut&&) = default; CustomShortcut(const CustomShortcut&) = delete; ~CustomShortcut(); @@ -84,11 +83,11 @@ namespace OpenRCT2::Scripting bool MouseDown{}; // Event handlers - DukValue onStart; - DukValue onDown; - DukValue onMove; - DukValue onUp; - DukValue onFinish; + JSCallback onStart; + JSCallback onDown; + JSCallback onMove; + JSCallback onUp; + JSCallback onFinish; void Start(); void OnUpdate(const ScreenCoordsXY& screenCoords); @@ -98,7 +97,7 @@ namespace OpenRCT2::Scripting void OnAbort(); private: - void InvokeEventHandler(const DukValue& dukHandler, const ScreenCoordsXY& screenCoords); + void InvokeEventHandler(JSValue handler, const ScreenCoordsXY& screenCoords); }; extern std::optional ActiveCustomTool; @@ -106,13 +105,9 @@ namespace OpenRCT2::Scripting extern std::vector> CustomShortcuts; void InitialiseCustomMenuItems(ScriptEngine& scriptEngine); - void InitialiseCustomTool(ScriptEngine& scriptEngine, const DukValue& dukValue); - - template<> - DukValue ToDuk(duk_context* ctx, const CursorID& value); - template<> - CursorID FromDuk(const DukValue& s); + JSValue InitialiseCustomTool(ScriptEngine& scriptEngine, JSContext* ctx, JSValue value); + JSValue CursorIDToJSValue(JSContext* ctx, CursorID id); } // namespace OpenRCT2::Scripting #endif diff --git a/src/openrct2-ui/scripting/CustomWindow.cpp b/src/openrct2-ui/scripting/CustomWindow.cpp index 76c0ebc1a0..1d27afd204 100644 --- a/src/openrct2-ui/scripting/CustomWindow.cpp +++ b/src/openrct2-ui/scripting/CustomWindow.cpp @@ -17,13 +17,14 @@ #include "../windows/Windows.h" #include "CustomListView.h" #include "ScUi.hpp" - #include "ScWindow.hpp" + #include "ScWindow.h" #include #include #include #include #include + #include #include #include #include @@ -82,114 +83,118 @@ namespace OpenRCT2::Ui::Windows bool CanSelect{}; // Event handlers - DukValue OnClick; - DukValue OnChange; - DukValue OnDraw; - DukValue OnIncrement; - DukValue OnDecrement; - DukValue OnHighlight; + JSCallback OnClick; + JSCallback OnChange; + JSCallback OnDraw; + JSCallback OnIncrement; + JSCallback OnDecrement; + JSCallback OnHighlight; - static CustomWidgetDesc FromDukValue(DukValue desc) + static CustomWidgetDesc FromJSValue(JSContext* ctx, JSValue desc) { CustomWidgetDesc result; - result.Type = desc["type"].as_string(); - result.X = desc["x"].as_int(); - result.Y = desc["y"].as_int(); - result.Width = desc["width"].as_int(); - result.Height = desc["height"].as_int(); - result.IsDisabled = AsOrDefault(desc["isDisabled"], false); - result.IsVisible = AsOrDefault(desc["isVisible"], true); - result.Name = AsOrDefault(desc["name"], ""); - result.Tooltip = AsOrDefault(desc["tooltip"], ""); + result.Type = JSToStdString(ctx, desc, "type"); + result.X = JSToInt(ctx, desc, "x"); + result.Y = JSToInt(ctx, desc, "y"); + result.Width = JSToInt(ctx, desc, "width"); + result.Height = JSToInt(ctx, desc, "height"); + result.IsDisabled = AsOrDefault(ctx, desc, "isDisabled", false); + result.IsVisible = AsOrDefault(ctx, desc, "isVisible", true); + result.Name = AsOrDefault(ctx, desc, "name", ""); + result.Tooltip = AsOrDefault(ctx, desc, "tooltip", ""); if (result.Type == "button") { - auto dukImage = desc["image"]; - if (dukImage.type() == DukValue::Type::STRING || dukImage.type() == DukValue::Type::NUMBER) + JSValue jsImage = JS_GetPropertyStr(ctx, desc, "image"); + if (JS_IsString(jsImage) || JS_IsNumber(jsImage)) { - result.Image = ImageId(ImageFromDuk(dukImage)); + result.Image = ImageId(ImageFromJSValue(ctx, jsImage)); result.HasBorder = false; } else { - result.Text = ProcessString(desc["text"]); + result.Text = AsOrDefault(ctx, desc, "text", ""); result.HasBorder = true; } - result.IsPressed = AsOrDefault(desc["isPressed"], false); - result.OnClick = desc["onClick"]; + JS_FreeValue(ctx, jsImage); + result.IsPressed = AsOrDefault(ctx, desc, "isPressed", false); + result.OnClick = JSToCallback(ctx, desc, "onClick"); } else if (result.Type == "checkbox") { - result.Text = ProcessString(desc["text"]); - result.IsChecked = AsOrDefault(desc["isChecked"], false); - result.OnChange = desc["onChange"]; + result.Text = AsOrDefault(ctx, desc, "text", ""); + result.IsChecked = AsOrDefault(ctx, desc, "isChecked", false); + result.OnChange = JSToCallback(ctx, desc, "onChange"); } else if (result.Type == "colourpicker") { - auto colour = AsOrDefault(desc["colour"], 0); + auto colour = AsOrDefault(ctx, desc, "colour", 0); if (colour < kColourNumTotal) { result.Colour = static_cast(colour); } - result.OnChange = desc["onChange"]; + result.OnChange = JSToCallback(ctx, desc, "onChange"); } else if (result.Type == "custom") { - result.OnDraw = desc["onDraw"]; + result.OnDraw = JSToCallback(ctx, desc, "onDraw"); } else if (result.Type == "dropdown") { - if (desc["items"].is_array()) - { - auto dukItems = desc["items"].as_array(); - for (const auto& dukItem : dukItems) - { - result.Items.push_back(ProcessString(dukItem)); - } - } - result.SelectedIndex = AsOrDefault(desc["selectedIndex"], 0); - result.OnChange = desc["onChange"]; + JSIterateArray(ctx, desc, "items", [&result](JSContext* ctx2, JSValue val) { + result.Items.push_back(JSToStdString(ctx2, val)); + }); + result.SelectedIndex = AsOrDefault(ctx, desc, "selectedIndex", 0); + result.OnChange = JSToCallback(ctx, desc, "onChange"); } else if (result.Type == "groupbox") { - result.Text = ProcessString(desc["text"]); + result.Text = AsOrDefault(ctx, desc, "text", ""); } else if (result.Type == "label") { - result.Text = ProcessString(desc["text"]); - if (ProcessString(desc["textAlign"]) == "centred") + result.Text = AsOrDefault(ctx, desc, "text", ""); + if (JSToStdString(ctx, desc, "textAlign") == "centred") { result.TextAlign = TextAlignment::centre; } } else if (result.Type == "listview") { - result.ListViewColumns = FromDuk>(desc["columns"]); - result.ListViewItems = FromDuk>(desc["items"]); - result.SelectedCell = FromDuk>(desc["selectedCell"]); - result.ShowColumnHeaders = AsOrDefault(desc["showColumnHeaders"], false); - result.IsStriped = AsOrDefault(desc["isStriped"], false); - result.OnClick = desc["onClick"]; - result.OnHighlight = desc["onHighlight"]; - result.CanSelect = AsOrDefault(desc["canSelect"], false); - if (desc["scrollbars"].type() == DukValue::UNDEFINED) + JSValue cols = JS_GetPropertyStr(ctx, desc, "columns"); + JSValue items = JS_GetPropertyStr(ctx, desc, "items"); + JSValue selected = JS_GetPropertyStr(ctx, desc, "selectedCell"); + JSValue scrollbars = JS_GetPropertyStr(ctx, desc, "scrollbars"); + result.ListViewColumns = ListViewColumnVecFromJS(ctx, cols); + result.ListViewItems = ListViewItemVecFromJS(ctx, items); + result.SelectedCell = RowColumnFromJS(ctx, selected); + result.ShowColumnHeaders = AsOrDefault(ctx, desc, "showColumnHeaders", false); + result.IsStriped = AsOrDefault(ctx, desc, "isStriped", false); + result.OnClick = JSToCallback(ctx, desc, "onClick"); + result.OnHighlight = JSToCallback(ctx, desc, "onHighlight"); + result.CanSelect = AsOrDefault(ctx, desc, "canSelect", false); + if (JS_IsUndefined(scrollbars)) result.Scrollbars = ScrollbarType::Vertical; else - result.Scrollbars = FromDuk(desc["scrollbars"]); + result.Scrollbars = ScrollbarTypeFromJS(ctx, scrollbars); + JS_FreeValue(ctx, cols); + JS_FreeValue(ctx, items); + JS_FreeValue(ctx, selected); + JS_FreeValue(ctx, scrollbars); } else if (result.Type == "spinner") { - result.Text = ProcessString(desc["text"]); - result.OnIncrement = desc["onIncrement"]; - result.OnDecrement = desc["onDecrement"]; - result.OnClick = desc["onClick"]; + result.Text = AsOrDefault(ctx, desc, "text", ""); + result.OnIncrement = JSToCallback(ctx, desc, "onIncrement"); + result.OnDecrement = JSToCallback(ctx, desc, "onDecrement"); + result.OnClick = JSToCallback(ctx, desc, "onClick"); } else if (result.Type == "textbox") { - result.Text = ProcessString(desc["text"]); - result.MaxLength = AsOrDefault(desc["maxLength"], 32); - result.OnChange = desc["onChange"]; + result.Text = AsOrDefault(ctx, desc, "text", ""); + result.MaxLength = AsOrDefault(ctx, desc, "maxLength", 32); + result.OnChange = JSToCallback(ctx, desc, "onChange"); } - result.HasBorder = AsOrDefault(desc["border"], result.HasBorder); + result.HasBorder = AsOrDefault(ctx, desc, "border", result.HasBorder); return result; } }; @@ -202,53 +207,53 @@ namespace OpenRCT2::Ui::Windows ScreenCoordsXY offset; std::vector Widgets; - static CustomTabDesc FromDukValue(const DukValue& desc) + static CustomTabDesc FromJSValue(JSContext* ctx, JSValue desc) { CustomTabDesc result; - auto dukImage = desc["image"]; - if (dukImage.type() == DukValue::Type::STRING || dukImage.type() == DukValue::Type::NUMBER) + JSValue jsImage = JS_GetPropertyStr(ctx, desc, "image"); + if (JS_IsString(jsImage) || JS_IsNumber(jsImage)) { - result.imageFrameBase = ImageId(ImageFromDuk(dukImage)); + result.imageFrameBase = ImageId(ImageFromJSValue(ctx, jsImage)); result.imageFrameCount = 0; result.imageFrameDuration = 0; } - else if (dukImage.type() == DukValue::Type::OBJECT) + else if (JS_IsObject(jsImage)) { - result.imageFrameBase = ImageId(dukImage["frameBase"].as_uint()); - result.imageFrameCount = AsOrDefault(dukImage["frameCount"], 0); - result.imageFrameDuration = AsOrDefault(dukImage["frameDuration"], 0); + result.imageFrameBase = ImageId(JSToInt64(ctx, jsImage, "frameBase")); + result.imageFrameCount = AsOrDefault(ctx, jsImage, "frameCount", 0); + result.imageFrameDuration = AsOrDefault(ctx, jsImage, "frameDuration", 0); - if (dukImage["primaryColour"].type() == DukValue::Type::NUMBER) + auto primaryCol = JSToOptionalInt64(ctx, jsImage, "primaryColour"); + if (primaryCol.has_value()) { - result.imageFrameBase = result.imageFrameBase.WithPrimary( - static_cast(dukImage["primaryColour"].as_uint())); + result.imageFrameBase = result.imageFrameBase.WithPrimary(static_cast(primaryCol.value())); - if (dukImage["secondaryColour"].type() == DukValue::Type::NUMBER) + auto secondaryCol = JSToOptionalInt64(ctx, jsImage, "secondaryColour"); + if (secondaryCol.has_value()) { - result.imageFrameBase = result.imageFrameBase.WithSecondary( - static_cast(dukImage["secondaryColour"].as_uint())); + result.imageFrameBase = result.imageFrameBase.WithSecondary(static_cast(secondaryCol.value())); - if (dukImage["tertiaryColour"].type() == DukValue::Type::NUMBER) + auto tertiaryCol = JSToOptionalInt64(ctx, jsImage, "tertiaryColour"); + if (tertiaryCol.has_value()) { result.imageFrameBase = result.imageFrameBase.WithTertiary( - static_cast(dukImage["tertiaryColour"].as_uint())); + static_cast(tertiaryCol.value())); } } } - auto dukCoord = dukImage["offset"]; - if (dukCoord.type() == DukValue::Type::OBJECT) + JSValue jsCoord = JS_GetPropertyStr(ctx, jsImage, "offset"); + if (JS_IsObject(jsCoord)) { - result.offset = { AsOrDefault(dukCoord["x"], 0), AsOrDefault(dukCoord["y"], 0) }; + result.offset = { AsOrDefault(ctx, jsCoord, "x", 0), AsOrDefault(ctx, jsCoord, "y", 0) }; } + JS_FreeValue(ctx, jsCoord); } - if (desc["widgets"].is_array()) - { - auto dukWidgets = desc["widgets"].as_array(); - std::transform(dukWidgets.begin(), dukWidgets.end(), std::back_inserter(result.Widgets), [](const DukValue& w) { - return CustomWidgetDesc::FromDukValue(w); - }); - } + JS_FreeValue(ctx, jsImage); + + JSIterateArray(ctx, desc, "widgets", [&result](JSContext* ctx2, JSValue x) { + result.Widgets.push_back(CustomWidgetDesc::FromJSValue(ctx2, x)); + }); return result; } }; @@ -271,9 +276,9 @@ namespace OpenRCT2::Ui::Windows std::optional TabIndex; // Event handlers - DukValue OnClose; - DukValue OnUpdate; - DukValue OnTabChange; + JSCallback OnClose; + JSCallback OnUpdate; + JSCallback OnTabChange; CustomWindowDesc() = default; @@ -282,65 +287,49 @@ namespace OpenRCT2::Ui::Windows return MinWidth || MinHeight || MaxWidth || MaxHeight; } - static CustomWindowDesc FromDukValue(DukValue desc) + static CustomWindowDesc FromJSValue(JSContext* ctx, JSValue desc) { CustomWindowDesc result; - result.Classification = desc["classification"].as_string(); - result.X = GetOptionalInt(desc["x"]); - result.Y = GetOptionalInt(desc["y"]); - result.size.width = desc["width"].as_int(); - result.size.height = desc["height"].as_int(); - result.MinWidth = GetOptionalInt(desc["minWidth"]); - result.MaxWidth = GetOptionalInt(desc["maxWidth"]); - result.MinHeight = GetOptionalInt(desc["minHeight"]); - result.MaxHeight = GetOptionalInt(desc["maxHeight"]); - result.Title = desc["title"].as_string(); - result.Id = GetOptionalInt(desc["id"]); - result.TabIndex = GetOptionalInt(desc["tabIndex"]); + result.Classification = JSToStdString(ctx, desc, "classification"); + result.X = JSToOptionalInt(ctx, desc, "x"); + result.Y = JSToOptionalInt(ctx, desc, "y"); + result.size.width = JSToInt(ctx, desc, "width"); + result.size.height = JSToInt(ctx, desc, "height"); + result.MinWidth = JSToOptionalInt(ctx, desc, "minWidth"); + result.MaxWidth = JSToOptionalInt(ctx, desc, "maxWidth"); + result.MinHeight = JSToOptionalInt(ctx, desc, "minHeight"); + result.MaxHeight = JSToOptionalInt(ctx, desc, "maxHeight"); + result.Title = JSToStdString(ctx, desc, "title"); + result.Id = JSToOptionalInt(ctx, desc, "id"); + result.TabIndex = JSToOptionalInt(ctx, desc, "tabIndex"); - if (desc["widgets"].is_array()) - { - auto dukWidgets = desc["widgets"].as_array(); - std::transform(dukWidgets.begin(), dukWidgets.end(), std::back_inserter(result.Widgets), [](const DukValue& w) { - return CustomWidgetDesc::FromDukValue(w); - }); - } + JSIterateArray(ctx, desc, "widgets", [&result](JSContext* ctx2, JSValue val) { + result.Widgets.push_back(CustomWidgetDesc::FromJSValue(ctx2, val)); + }); - if (desc["tabs"].is_array()) - { - auto dukTabs = desc["tabs"].as_array(); - std::transform(dukTabs.begin(), dukTabs.end(), std::back_inserter(result.Tabs), [](const DukValue& w) { - return CustomTabDesc::FromDukValue(w); - }); - } + JSIterateArray(ctx, desc, "tabs", [&result](JSContext* ctx2, JSValue x) { + result.Tabs.push_back(CustomTabDesc::FromJSValue(ctx2, x)); + }); - if (desc["colours"].is_array()) - { - auto dukColours = desc["colours"].as_array(); - std::transform(dukColours.begin(), dukColours.end(), std::back_inserter(result.Colours), [](const DukValue& w) { - ColourWithFlags c = { Colour::black }; - if (w.type() == DukValue::Type::NUMBER) - { - uint8_t colour = (w.as_uint() & ~kLegacyColourFlagTranslucent) % kColourNumTotal; - bool isTranslucent = (w.as_uint() & kLegacyColourFlagTranslucent); - c.colour = static_cast(colour); - c.flags.set(ColourFlag::translucent, isTranslucent); - } - return c; - }); - } + JSIterateArray(ctx, desc, "colours", [&result](JSContext* ctx2, JSValue x) { + ColourWithFlags c = { Colour::black }; + if (JS_IsNumber(x)) + { + int32_t xValue = JSToInt(ctx2, x); + uint8_t colour = (xValue & ~kLegacyColourFlagTranslucent) % kColourNumTotal; + bool isTranslucent = (xValue & kLegacyColourFlagTranslucent); + c.colour = static_cast(colour); + c.flags.set(ColourFlag::translucent, isTranslucent); + } + result.Colours.push_back(c); + }); - result.OnClose = desc["onClose"]; - result.OnUpdate = desc["onUpdate"]; - result.OnTabChange = desc["onTabChange"]; + result.OnClose = JSToCallback(ctx, desc, "onClose"); + result.OnUpdate = JSToCallback(ctx, desc, "onUpdate"); + result.OnTabChange = JSToCallback(ctx, desc, "onTabChange"); return result; } - - static std::optional GetOptionalInt(DukValue input) - { - return input.type() == DukValue::Type::NUMBER ? std::make_optional(input.as_int()) : std::nullopt; - } }; class CustomWindowInfo @@ -392,9 +381,9 @@ namespace OpenRCT2::Ui::Windows class CustomWindow; static CustomWindowInfo& GetInfo(CustomWindow* w); - static void InvokeEventHandler(const std::shared_ptr& owner, const DukValue& dukHandler); + static void InvokeEventHandler(const std::shared_ptr& owner, const JSCallback& jsCallback); static void InvokeEventHandler( - const std::shared_ptr& owner, const DukValue& dukHandler, const std::vector& args); + const std::shared_ptr& owner, const JSCallback& jsCallback, const std::vector& args); class CustomWindow final : public Window { @@ -559,18 +548,19 @@ namespace OpenRCT2::Ui::Windows if (widgetDesc != nullptr && widgetDesc->Type == "custom") { auto& onDraw = widgetDesc->OnDraw; - if (onDraw.is_function()) + if (onDraw.IsValid()) { RenderTarget widgetRT; if (ClipRenderTarget( widgetRT, rt, { windowPos.x + widget.left, windowPos.y + widget.top }, widget.width() - 1, widget.height() - 1)) { - auto ctx = onDraw.context(); - auto dukWidget = ScWidget::ToDukValue(ctx, this, widgetIndex); - auto dukG = GetObjectAsDukValue(ctx, std::make_shared(ctx, widgetRT)); + auto ctx = onDraw.context; + auto jsWidget = gScWidget.New(ctx, this, widgetIndex); + auto gfx = gScGraphicsContext.New(ctx, widgetRT); auto& scriptEngine = GetContext()->GetScriptEngine(); - scriptEngine.ExecutePluginCall(_info.Owner, widgetDesc->OnDraw, dukWidget, { dukG }, false); + scriptEngine.ExecutePluginCall(_info.Owner, onDraw.callback, jsWidget, { gfx }, false); + JS_FreeValue(ctx, jsWidget); } } } @@ -614,10 +604,8 @@ namespace OpenRCT2::Ui::Windows widgetSetCheckboxValue(*this, widgetIndex, isChecked); - std::vector args; - auto ctx = widgetDesc->OnChange.context(); - duk_push_boolean(ctx, isChecked); - args.push_back(DukValue::take_from_stack(ctx)); + std::vector args; + args.push_back(JS_NewBool(widgetDesc->OnChange.context, isChecked)); InvokeEventHandler(_info.Owner, widgetDesc->OnChange, args); } else if (widgetDesc->Type == "spinner") @@ -707,10 +695,9 @@ namespace OpenRCT2::Ui::Windows { UpdateWidgetText(this, widgetIndex, text); - std::vector args; - auto ctx = widgetDesc->OnChange.context(); - duk_push_lstring(ctx, text.data(), text.size()); - args.push_back(DukValue::take_from_stack(ctx)); + std::vector args; + std::string textStr(text); + args.push_back(JSFromStdString(widgetDesc->OnChange.context, textStr)); InvokeEventHandler(_info.Owner, widgetDesc->OnChange, args); } } @@ -1127,9 +1114,9 @@ namespace OpenRCT2::Ui::Windows WindowNumber CustomWindow::_nextWindowNumber; - WindowBase* WindowCustomOpen(std::shared_ptr owner, DukValue dukDesc) + WindowBase* WindowCustomOpen(JSContext* ctx, std::shared_ptr owner, JSValue descVal) { - auto desc = CustomWindowDesc::FromDukValue(dukDesc); + auto desc = CustomWindowDesc::FromJSValue(ctx, descVal); WindowFlags windowFlags = { WindowFlag::resizable, WindowFlag::transparent }; auto* windowMgr = GetWindowManager(); @@ -1151,17 +1138,17 @@ namespace OpenRCT2::Ui::Windows return w->getInfo(); } - static void InvokeEventHandler(const std::shared_ptr& owner, const DukValue& dukHandler) + static void InvokeEventHandler(const std::shared_ptr& owner, const JSCallback& jsCallback) { - std::vector args; - InvokeEventHandler(owner, dukHandler, args); + std::vector args; + InvokeEventHandler(owner, jsCallback, args); } static void InvokeEventHandler( - const std::shared_ptr& owner, const DukValue& dukHandler, const std::vector& args) + const std::shared_ptr& owner, const JSCallback& jsCallback, const std::vector& args) { auto& scriptEngine = GetContext()->GetScriptEngine(); - scriptEngine.ExecutePluginCall(owner, dukHandler, args, false); + scriptEngine.ExecutePluginCall(owner, jsCallback.callback, args, false); } std::string GetWindowTitle(WindowBase* w) @@ -1245,10 +1232,8 @@ namespace OpenRCT2::Ui::Windows auto* windowMgr = GetWindowManager(); windowMgr->InvalidateWidget(*w, widgetIndex); - std::vector args; - auto ctx = customWidgetInfo->OnChange.context(); - duk_push_int(ctx, EnumValue(colour)); - args.push_back(DukValue::take_from_stack(ctx)); + std::vector args; + args.push_back(JS_NewInt32(customWidgetInfo->OnChange.context, EnumValue(colour))); InvokeEventHandler(customInfo.Owner, customWidgetInfo->OnChange, args); } } @@ -1293,10 +1278,8 @@ namespace OpenRCT2::Ui::Windows if (lastSelectedIndex != selectedIndex) { - std::vector args; - auto ctx = customWidgetInfo->OnChange.context(); - duk_push_int(ctx, selectedIndex); - args.push_back(DukValue::take_from_stack(ctx)); + std::vector args; + args.push_back(JS_NewInt32(customWidgetInfo->OnChange.context, selectedIndex)); InvokeEventHandler(customInfo.Owner, customWidgetInfo->OnChange, args); } } @@ -1499,7 +1482,6 @@ namespace OpenRCT2::Ui::Windows windowMgr->Close(*window); } } - } // namespace OpenRCT2::Ui::Windows #endif diff --git a/src/openrct2-ui/scripting/CustomWindow.h b/src/openrct2-ui/scripting/CustomWindow.h index f4ed9cead0..daab409a26 100644 --- a/src/openrct2-ui/scripting/CustomWindow.h +++ b/src/openrct2-ui/scripting/CustomWindow.h @@ -11,8 +11,6 @@ #ifdef ENABLE_SCRIPTING - #include "../interface/Window.h" - #include #include #include @@ -40,7 +38,8 @@ namespace OpenRCT2::Ui::Windows CustomListView* GetCustomListView(WindowBase* w, WidgetIndex widgetIndex); int32_t GetWidgetMaxLength(WindowBase* w, WidgetIndex widgetIndex); void SetWidgetMaxLength(WindowBase* w, WidgetIndex widgetIndex, int32_t value); - void CloseWindowsOwnedByPlugin(std::shared_ptr plugin); + void CloseWindowsOwnedByPlugin(std::shared_ptr plugin); + WindowBase* WindowCustomOpen(JSContext* ctx, std::shared_ptr owner, JSValue descVal); } // namespace OpenRCT2::Ui::Windows #endif diff --git a/src/openrct2-ui/scripting/ScGraphicsContext.hpp b/src/openrct2-ui/scripting/ScGraphicsContext.hpp index 67878f9d37..3dc77b9f73 100644 --- a/src/openrct2-ui/scripting/ScGraphicsContext.hpp +++ b/src/openrct2-ui/scripting/ScGraphicsContext.hpp @@ -18,230 +18,321 @@ #include #include #include - #include + #include + #include namespace OpenRCT2::Scripting { - class ScGraphicsContext + class ScGraphicsContext; + extern ScGraphicsContext gScGraphicsContext; + class ScGraphicsContext final : public ScBase { private: - duk_context* _ctx{}; - Drawing::RenderTarget _rt{}; + struct GraphicsData + { + Drawing::RenderTarget _rt{}; - std::optional _colour{}; - std::optional _secondaryColour{}; - std::optional _tertiaryColour{}; - std::optional _paletteId{}; - Drawing::PaletteIndex _stroke{}; - Drawing::PaletteIndex _fill{}; + std::optional _colour{}; + std::optional _secondaryColour{}; + std::optional _tertiaryColour{}; + std::optional _paletteId{}; + Drawing::PaletteIndex _stroke{}; + Drawing::PaletteIndex _fill{}; + }; public: - ScGraphicsContext(duk_context* ctx, const Drawing::RenderTarget& rt) - : _ctx(ctx) - , _rt(rt) + void Register(JSContext* ctx) { + RegisterBaseStr(ctx, "GraphicsContext", Finalize); } - static void Register(duk_context* ctx) + static void Finalize(JSRuntime* rt, JSValue thisVal) { - dukglue_register_property(ctx, &ScGraphicsContext::colour_get, &ScGraphicsContext::colour_set, "colour"); - dukglue_register_property( - ctx, &ScGraphicsContext::secondaryColour_get, &ScGraphicsContext::secondaryColour_set, "secondaryColour"); - dukglue_register_property( - ctx, &ScGraphicsContext::tertiaryColour_get, &ScGraphicsContext::tertiaryColour_set, "ternaryColour"); - dukglue_register_property( - ctx, &ScGraphicsContext::tertiaryColour_get, &ScGraphicsContext::tertiaryColour_set, "tertiaryColour"); - dukglue_register_property(ctx, &ScGraphicsContext::paletteId_get, &ScGraphicsContext::paletteId_set, "paletteId"); - dukglue_register_property(ctx, &ScGraphicsContext::fill_get, &ScGraphicsContext::fill_set, "fill"); - dukglue_register_property(ctx, &ScGraphicsContext::stroke_get, &ScGraphicsContext::stroke_set, "stroke"); - dukglue_register_property(ctx, &ScGraphicsContext::width_get, nullptr, "width"); - dukglue_register_property(ctx, &ScGraphicsContext::height_get, nullptr, "height"); + GraphicsData* data = gScGraphicsContext.GetOpaque(thisVal); + if (data) + delete data; + } - dukglue_register_method(ctx, &ScGraphicsContext::getImage, "getImage"); - dukglue_register_method(ctx, &ScGraphicsContext::measureText, "measureText"); + JSValue New(JSContext* ctx, const Drawing::RenderTarget& rt) + { + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("colour", ScGraphicsContext::colour_get, ScGraphicsContext::colour_set), + JS_CGETSET_DEF( + "secondaryColour", ScGraphicsContext::secondaryColour_get, ScGraphicsContext::secondaryColour_set), + JS_CGETSET_DEF("ternaryColour", ScGraphicsContext::tertiaryColour_get, ScGraphicsContext::tertiaryColour_set), + JS_CGETSET_DEF("tertiaryColour", ScGraphicsContext::tertiaryColour_get, ScGraphicsContext::tertiaryColour_set), + JS_CGETSET_DEF("paletteId", ScGraphicsContext::paletteId_get, ScGraphicsContext::paletteId_set), + JS_CGETSET_DEF("fill", ScGraphicsContext::fill_get, ScGraphicsContext::fill_set), + JS_CGETSET_DEF("stroke", ScGraphicsContext::stroke_get, ScGraphicsContext::stroke_set), + JS_CGETSET_DEF("width", ScGraphicsContext::width_get, nullptr), + JS_CGETSET_DEF("height", ScGraphicsContext::height_get, nullptr), - dukglue_register_method(ctx, &ScGraphicsContext::box, "box"); - dukglue_register_method(ctx, &ScGraphicsContext::clear, "clear"); - dukglue_register_method(ctx, &ScGraphicsContext::clip, "clip"); - dukglue_register_method(ctx, &ScGraphicsContext::image, "image"); - dukglue_register_method(ctx, &ScGraphicsContext::line, "line"); - dukglue_register_method(ctx, &ScGraphicsContext::rect, "rect"); - dukglue_register_method(ctx, &ScGraphicsContext::text, "text"); - dukglue_register_method(ctx, &ScGraphicsContext::well, "well"); + JS_CFUNC_DEF("getImage", 1, ScGraphicsContext::getImage), + JS_CFUNC_DEF("measureText", 1, ScGraphicsContext::measureText), + + JS_CFUNC_DEF("box", 4, ScGraphicsContext::box), + JS_CFUNC_DEF("clear", 0, ScGraphicsContext::clear), + JS_CFUNC_DEF("clip", 4, ScGraphicsContext::clip), + JS_CFUNC_DEF("image", 3, ScGraphicsContext::image), + JS_CFUNC_DEF("line", 4, ScGraphicsContext::lineJS), + JS_CFUNC_DEF("rect", 4, ScGraphicsContext::rect), + JS_CFUNC_DEF("text", 3, ScGraphicsContext::text), + JS_CFUNC_DEF("well", 4, ScGraphicsContext::well), + }; + return MakeWithOpaque(ctx, funcs, new GraphicsData{ rt }); } private: - DukValue colour_get() const + static JSValue colour_get(JSContext* ctx, JSValue thisVal) { - return ToDuk(_ctx, _colour); + GraphicsData* data = gScGraphicsContext.GetOpaque(thisVal); + return ToJSValue(ctx, data->_colour); } - void colour_set(DukValue value) + static JSValue colour_set(JSContext* ctx, JSValue thisVal, JSValue value) { - if (value.type() == DukValue::NUMBER) - _colour = static_cast(value.as_uint()); + GraphicsData* data = gScGraphicsContext.GetOpaque(thisVal); + if (JS_IsNumber(value)) + data->_colour = static_cast(JSToInt(ctx, value)); else - _colour = {}; + data->_colour = {}; + return JS_UNDEFINED; } - DukValue secondaryColour_get() const + static JSValue secondaryColour_get(JSContext* ctx, JSValue thisVal) { - return ToDuk(_ctx, _secondaryColour); + GraphicsData* data = gScGraphicsContext.GetOpaque(thisVal); + return ToJSValue(ctx, data->_secondaryColour); } - void secondaryColour_set(DukValue value) + static JSValue secondaryColour_set(JSContext* ctx, JSValue thisVal, JSValue value) { - if (value.type() == DukValue::NUMBER) - _secondaryColour = static_cast(value.as_uint()); + GraphicsData* data = gScGraphicsContext.GetOpaque(thisVal); + if (JS_IsNumber(value)) + data->_secondaryColour = static_cast(JSToInt(ctx, value)); else - _secondaryColour = {}; + data->_secondaryColour = {}; + return JS_UNDEFINED; } - DukValue tertiaryColour_get() const + static JSValue tertiaryColour_get(JSContext* ctx, JSValue thisVal) { - return ToDuk(_ctx, _tertiaryColour); + GraphicsData* data = gScGraphicsContext.GetOpaque(thisVal); + return ToJSValue(ctx, data->_tertiaryColour); } - void tertiaryColour_set(DukValue value) + static JSValue tertiaryColour_set(JSContext* ctx, JSValue thisVal, JSValue value) { - if (value.type() == DukValue::NUMBER) - _tertiaryColour = static_cast(value.as_uint()); + GraphicsData* data = gScGraphicsContext.GetOpaque(thisVal); + if (JS_IsNumber(value)) + data->_tertiaryColour = static_cast(JSToInt(ctx, value)); else - _tertiaryColour = {}; + data->_tertiaryColour = {}; + return JS_UNDEFINED; } - DukValue paletteId_get() const + static JSValue paletteId_get(JSContext* ctx, JSValue thisVal) { - return ToDuk(_ctx, _paletteId); + GraphicsData* data = gScGraphicsContext.GetOpaque(thisVal); + return ToJSValue(ctx, data->_paletteId); } - void paletteId_set(DukValue value) + static JSValue paletteId_set(JSContext* ctx, JSValue thisVal, JSValue value) { - if (value.type() == DukValue::NUMBER) - _paletteId = static_cast(value.as_uint()); + GraphicsData* data = gScGraphicsContext.GetOpaque(thisVal); + if (JS_IsNumber(value)) + data->_paletteId = static_cast(JSToInt(ctx, value)); else - _paletteId = {}; + data->_paletteId = {}; + return JS_UNDEFINED; } - uint8_t fill_get() const + static JSValue fill_get(JSContext* ctx, JSValue thisVal) { - return EnumValue(_fill); + GraphicsData* data = gScGraphicsContext.GetOpaque(thisVal); + return JS_NewInt32(ctx, EnumValue(data->_fill)); } - void fill_set(uint8_t value) + static JSValue fill_set(JSContext* ctx, JSValue thisVal, JSValue value) { - _fill = static_cast(value); + JS_UNPACK_INT32(valueInt, ctx, value) + GraphicsData* data = gScGraphicsContext.GetOpaque(thisVal); + data->_fill = static_cast(valueInt); + return JS_UNDEFINED; } - uint8_t stroke_get() const + static JSValue stroke_get(JSContext* ctx, JSValue thisVal) { - return EnumValue(_stroke); + GraphicsData* data = gScGraphicsContext.GetOpaque(thisVal); + return JS_NewInt32(ctx, EnumValue(data->_stroke)); } - void stroke_set(uint8_t value) + static JSValue stroke_set(JSContext* ctx, JSValue thisVal, JSValue value) { - _stroke = static_cast(value); + JS_UNPACK_INT32(valueInt, ctx, value); + GraphicsData* data = gScGraphicsContext.GetOpaque(thisVal); + data->_stroke = static_cast(valueInt); + return JS_UNDEFINED; } - int32_t width_get() const + static JSValue width_get(JSContext* ctx, JSValue thisVal) { - return _rt.width; + GraphicsData* data = gScGraphicsContext.GetOpaque(thisVal); + return JS_NewInt32(ctx, data->_rt.width); } - int32_t height_get() const + static JSValue height_get(JSContext* ctx, JSValue thisVal) { - return _rt.height; + GraphicsData* data = gScGraphicsContext.GetOpaque(thisVal); + return JS_NewInt32(ctx, data->_rt.height); } - DukValue getImage(uint32_t id) + static JSValue getImage(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - return DukGetImageInfo(_ctx, id); + JS_UNPACK_UINT32(id, ctx, argv[0]); + + return JSGetImageInfo(ctx, id); } - DukValue measureText(const std::string& text) + static JSValue measureText(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { + JS_UNPACK_STR(text, ctx, argv[0]) + auto width = Drawing::getStringWidth(text, FontStyle::medium); auto height = Drawing::getStringHeightRaw(text.c_str(), FontStyle::medium); - return ToDuk(_ctx, { width, height }); + return ToJSValue(ctx, ScreenSize{ width, height }); } - void box(int32_t x, int32_t y, int32_t width, int32_t height) + static JSValue box(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { + JS_UNPACK_INT32(x, ctx, argv[0]); + JS_UNPACK_INT32(y, ctx, argv[1]); + JS_UNPACK_INT32(width, ctx, argv[2]); + JS_UNPACK_INT32(height, ctx, argv[3]); + GraphicsData* data = gScGraphicsContext.GetOpaque(thisVal); + Drawing::Rectangle::fillInset( - _rt, { x, y, x + width - 1, y + height - 1 }, { static_cast(_colour.value_or(0)) }); + data->_rt, { x, y, x + width - 1, y + height - 1 }, { static_cast(data->_colour.value_or(0)) }); + return JS_UNDEFINED; } - void well(int32_t x, int32_t y, int32_t width, int32_t height) + static JSValue well(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { + JS_UNPACK_INT32(x, ctx, argv[0]); + JS_UNPACK_INT32(y, ctx, argv[1]); + JS_UNPACK_INT32(width, ctx, argv[2]); + JS_UNPACK_INT32(height, ctx, argv[3]); + GraphicsData* data = gScGraphicsContext.GetOpaque(thisVal); + Drawing::Rectangle::fillInset( - _rt, { x, y, x + width - 1, y + height - 1 }, { static_cast(_colour.value_or(0)) }, + data->_rt, { x, y, x + width - 1, y + height - 1 }, { static_cast(data->_colour.value_or(0)) }, Drawing::Rectangle::BorderStyle::inset, Drawing::Rectangle::FillBrightness::light, Drawing::Rectangle::FillMode::dontLightenWhenInset); + return JS_UNDEFINED; } - void clear() + static JSValue clear(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - GfxClear(_rt, _fill); + GraphicsData* data = gScGraphicsContext.GetOpaque(thisVal); + GfxClear(data->_rt, data->_fill); + return JS_UNDEFINED; } - void clip(int32_t x, int32_t y, int32_t width, int32_t height) + static JSValue clip(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { + JS_UNPACK_INT32(x, ctx, argv[0]); + JS_UNPACK_INT32(y, ctx, argv[1]); + JS_UNPACK_INT32(width, ctx, argv[2]); + JS_UNPACK_INT32(height, ctx, argv[3]); + GraphicsData* data = gScGraphicsContext.GetOpaque(thisVal); + Drawing::RenderTarget newRT; - ClipRenderTarget(newRT, _rt, { x, y }, width, height); - _rt = newRT; + ClipRenderTarget(newRT, data->_rt, { x, y }, width, height); + data->_rt = newRT; + return JS_UNDEFINED; } - void image(uint32_t id, int32_t x, int32_t y) + static JSValue image(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { + JS_UNPACK_UINT32(id, ctx, argv[0]); + JS_UNPACK_INT32(x, ctx, argv[1]); + JS_UNPACK_INT32(y, ctx, argv[2]); + GraphicsData* data = gScGraphicsContext.GetOpaque(thisVal); + ImageId img; img = img.WithIndex(id); - if (_paletteId) + if (data->_paletteId) { - img = img.WithRemap(*_paletteId); + img = img.WithRemap(*data->_paletteId); } else { - if (_colour) + if (data->_colour) { - img = img.WithPrimary(static_cast(*_colour)); + img = img.WithPrimary(static_cast(*data->_colour)); } - if (_secondaryColour) + if (data->_secondaryColour) { - img = img.WithSecondary(static_cast(*_secondaryColour)); + img = img.WithSecondary(static_cast(*data->_secondaryColour)); } } - GfxDrawSprite(_rt, img.WithTertiary(static_cast(_tertiaryColour.value_or(0))), { x, y }); + GfxDrawSprite( + data->_rt, img.WithTertiary(static_cast(data->_tertiaryColour.value_or(0))), { x, y }); + return JS_UNDEFINED; } - void line(int32_t x1, int32_t y1, int32_t x2, int32_t y2) + static void line(GraphicsData* data, int32_t x1, int32_t y1, int32_t x2, int32_t y2) { - GfxDrawLine(_rt, { { x1, y1 }, { x2, y2 } }, _stroke); + GfxDrawLine(data->_rt, { { x1, y1 }, { x2, y2 } }, data->_stroke); } - void rect(int32_t x, int32_t y, int32_t width, int32_t height) + static JSValue lineJS(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - if (_stroke != Drawing::PaletteIndex::transparent) + JS_UNPACK_INT32(x1, ctx, argv[0]); + JS_UNPACK_INT32(y1, ctx, argv[1]); + JS_UNPACK_INT32(x2, ctx, argv[2]); + JS_UNPACK_INT32(y2, ctx, argv[3]); + GraphicsData* data = gScGraphicsContext.GetOpaque(thisVal); + line(data, x1, y1, x2, y2); + return JS_UNDEFINED; + } + + static JSValue rect(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) + { + JS_UNPACK_INT32(x, ctx, argv[0]); + JS_UNPACK_INT32(y, ctx, argv[1]); + JS_UNPACK_INT32(width, ctx, argv[2]); + JS_UNPACK_INT32(height, ctx, argv[3]); + GraphicsData* data = gScGraphicsContext.GetOpaque(thisVal); + + if (data->_stroke != Drawing::PaletteIndex::transparent) { - line(x, y, x + width, y); - line(x + width - 1, y + 1, x + width - 1, y + height - 1); - line(x, y + height - 1, x + width, y + height - 1); - line(x, y + 1, x, y + height - 1); + line(data, x, y, x + width, y); + line(data, x + width - 1, y + 1, x + width - 1, y + height - 1); + line(data, x, y + height - 1, x + width, y + height - 1); + line(data, x, y + 1, x, y + height - 1); x++; y++; width -= 2; height -= 2; } - if (_fill != Drawing::PaletteIndex::transparent) + if (data->_fill != Drawing::PaletteIndex::transparent) { - Drawing::Rectangle::fill(_rt, { x, y, x + width - 1, y + height - 1 }, _fill); + Drawing::Rectangle::fill(data->_rt, { x, y, x + width - 1, y + height - 1 }, data->_fill); } + return JS_UNDEFINED; } - void text(const std::string& text, int32_t x, int32_t y) + static JSValue text(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - drawText(_rt, { x, y }, text, { static_cast(_colour.value_or(0)) }); + JS_UNPACK_STR(text, ctx, argv[0]); + JS_UNPACK_INT32(x, ctx, argv[1]); + JS_UNPACK_INT32(y, ctx, argv[2]); + GraphicsData* data = gScGraphicsContext.GetOpaque(thisVal); + drawText(data->_rt, { x, y }, text, { static_cast(data->_colour.value_or(0)) }); + return JS_UNDEFINED; } }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2-ui/scripting/ScImageManager.hpp b/src/openrct2-ui/scripting/ScImageManager.hpp index b746daa817..580eda46c5 100644 --- a/src/openrct2-ui/scripting/ScImageManager.hpp +++ b/src/openrct2-ui/scripting/ScImageManager.hpp @@ -16,147 +16,185 @@ #include #include #include - #include namespace OpenRCT2::Scripting { - class ScImageManager + class ScImageManager; + extern ScImageManager gScImageManager; + + class ScImageManager final : public ScBase { - private: - duk_context* _ctx{}; - public: - ScImageManager(duk_context* ctx) - : _ctx(ctx) + void Register(JSContext* ctx) { + RegisterBaseStr(ctx, "ImageManager"); } - static void Register(duk_context* ctx) + JSValue New(JSContext* ctx) { - dukglue_register_method(ctx, &ScImageManager::getPredefinedRange, "getPredefinedRange"); - dukglue_register_method(ctx, &ScImageManager::getAvailableAllocationRanges, "getAvailableAllocationRanges"); - dukglue_register_method(ctx, &ScImageManager::allocate, "allocate"); - dukglue_register_method(ctx, &ScImageManager::free, "free"); - dukglue_register_method(ctx, &ScImageManager::getImageInfo, "getImageInfo"); - dukglue_register_method(ctx, &ScImageManager::getPixelData, "getPixelData"); - dukglue_register_method(ctx, &ScImageManager::setPixelData, "setPixelData"); - dukglue_register_method(ctx, &ScImageManager::draw, "draw"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CFUNC_DEF("getPredefinedRange", 1, ScImageManager::getPredefinedRange), + JS_CFUNC_DEF("getAvailableAllocationRanges", 0, ScImageManager::getAvailableAllocationRanges), + JS_CFUNC_DEF("allocate", 1, ScImageManager::allocate), + JS_CFUNC_DEF("free", 1, ScImageManager::free), + JS_CFUNC_DEF("getImageInfo", 1, ScImageManager::getImageInfo), + JS_CFUNC_DEF("getPixelData", 1, ScImageManager::getPixelData), + JS_CFUNC_DEF("setPixelData", 2, ScImageManager::setPixelData), + JS_CFUNC_DEF("draw", 3, ScImageManager::draw) + }; + return MakeWithOpaque(ctx, funcs, nullptr); } private: - DukValue getPredefinedRange(const std::string& name) const + static JSValue getPredefinedRange(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { + JS_UNPACK_STR(name, ctx, argv[0]); if (name == "g1") { - return CreateImageIndexRange(0, SPR_G1_END); + return CreateImageIndexRange(ctx, 0, SPR_G1_END); } else if (name == "g2") { - return CreateImageIndexRange(SPR_G2_BEGIN, SPR_G2_END - SPR_G2_BEGIN); + return CreateImageIndexRange(ctx, SPR_G2_BEGIN, SPR_G2_END - SPR_G2_BEGIN); } else if (name == "fonts") { - return CreateImageIndexRange(SPR_FONTS_BEGIN, SPR_FONTS_END - SPR_FONTS_BEGIN); + return CreateImageIndexRange(ctx, SPR_FONTS_BEGIN, SPR_FONTS_END - SPR_FONTS_BEGIN); } else if (name == "tracks") { - return CreateImageIndexRange(SPR_TRACKS_BEGIN, SPR_TRACKS_END - SPR_TRACKS_BEGIN); + return CreateImageIndexRange(ctx, SPR_TRACKS_BEGIN, SPR_TRACKS_END - SPR_TRACKS_BEGIN); } else if (name == "csg") { - return CreateImageIndexRange(SPR_CSG_BEGIN, SPR_CSG_END - SPR_CSG_BEGIN); + return CreateImageIndexRange(ctx, SPR_CSG_BEGIN, SPR_CSG_END - SPR_CSG_BEGIN); } else if (name == "allocated") { - return CreateImageIndexRange(SPR_IMAGE_LIST_BEGIN, SPR_IMAGE_LIST_END - SPR_IMAGE_LIST_BEGIN); + return CreateImageIndexRange(ctx, SPR_IMAGE_LIST_BEGIN, SPR_IMAGE_LIST_END - SPR_IMAGE_LIST_BEGIN); } else { - return ToDuk(_ctx, nullptr); + return JS_NULL; } } - DukValue getAvailableAllocationRanges() const + static JSValue getAvailableAllocationRanges(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { auto ranges = GetAvailableAllocationRanges(); - duk_push_array(_ctx); - duk_uarridx_t index = 0; + JSValue arr = JS_NewArray(ctx); + int64_t index = 0; for (const auto& range : ranges) { - auto value = CreateImageIndexRange(range.BaseId, range.Count); - value.push(); - duk_put_prop_index(_ctx, /* duk stack index */ -2, index); + JS_SetPropertyInt64(ctx, arr, index, CreateImageIndexRange(ctx, range.BaseId, range.Count)); index++; } - return DukValue::take_from_stack(_ctx); + return arr; } - DukValue allocate(int32_t count) + static JSValue allocate(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { + JS_UNPACK_INT32(count, ctx, argv[0]) + auto& scriptEngine = GetContext()->GetScriptEngine(); auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin(); auto range = AllocateCustomImages(plugin, count); - return range ? CreateImageIndexRange(range->BaseId, range->Count) : ToDuk(_ctx, undefined); + return range ? CreateImageIndexRange(ctx, range->BaseId, range->Count) : JS_UNDEFINED; } - void free(const DukValue& dukRange) + static JSValue free(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto start = dukRange["start"].as_uint(); - auto count = dukRange["count"].as_uint(); + auto start = JSToOptionalInt64(ctx, argv[0], "start"); + auto count = JSToOptionalInt64(ctx, argv[0], "count"); + if (!start.has_value() || !count.has_value()) + { + JS_ThrowPlainError(ctx, "Invalid argument"); + return JS_EXCEPTION; + } - ImageList range(start, count); + ImageList range(start.value(), count.value()); auto& scriptEngine = GetContext()->GetScriptEngine(); auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin(); if (!FreeCustomImages(plugin, range)) { - duk_error(_ctx, DUK_ERR_ERROR, "This plugin did not allocate the specified image range."); - } - } - - DukValue getImageInfo(int32_t id) - { - return DukGetImageInfo(_ctx, id); - } - - DukValue getPixelData(int32_t id) - { - return DukGetImagePixelData(_ctx, id); - } - - void setPixelData(int32_t id, const DukValue& pixelData) - { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin(); - if (!DoesPluginOwnImage(plugin, id)) - { - duk_error(_ctx, DUK_ERR_ERROR, "This plugin did not allocate the specified image."); + JS_ThrowPlainError(ctx, "This plugin did not allocate the specified image range."); + return JS_EXCEPTION; } - DukSetPixelData(_ctx, id, pixelData); + return JS_UNDEFINED; } - void draw(int32_t id, const DukValue& dukSize, const DukValue& callback) + static JSValue getImageInfo(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto width = dukSize["width"].as_int(); - auto height = dukSize["height"].as_int(); + JS_UNPACK_INT32(id, ctx, argv[0]); + + return JSGetImageInfo(ctx, id); + } + + static JSValue getPixelData(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) + { + JS_UNPACK_INT32(id, ctx, argv[0]); + + return JSGetImagePixelData(ctx, id); + } + + static JSValue setPixelData(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) + { + JS_UNPACK_INT32(id, ctx, argv[0]); + JSValue pixelData = argv[1]; auto& scriptEngine = GetContext()->GetScriptEngine(); auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin(); if (!DoesPluginOwnImage(plugin, id)) { - duk_error(_ctx, DUK_ERR_ERROR, "This plugin did not allocate the specified image."); + JS_ThrowPlainError(ctx, "This plugin did not allocate the specified image range."); + return JS_EXCEPTION; } - DukDrawCustomImage(scriptEngine, id, { width, height }, callback); + try + { + JSSetPixelData(ctx, id, pixelData); + } + catch (const std::runtime_error& e) + { + JS_ThrowInternalError(ctx, "%s", e.what()); + return JS_EXCEPTION; + } + return JS_UNDEFINED; } - DukValue CreateImageIndexRange(size_t start, size_t count) const + static JSValue draw(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - DukObject obj(_ctx); - obj.Set("start", static_cast(start)); - obj.Set("count", static_cast(count)); - return obj.Take(); + JS_UNPACK_INT32(id, ctx, argv[0]) + auto width = JSToOptionalInt64(ctx, argv[1], "width"); + auto height = JSToOptionalInt64(ctx, argv[1], "height"); + if (!width.has_value() || !height.has_value()) + { + JS_ThrowPlainError(ctx, "Invalid size argument"); + return JS_EXCEPTION; + } + JSCallback callback(ctx, argv[2]); + + auto& scriptEngine = GetContext()->GetScriptEngine(); + auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin(); + if (!DoesPluginOwnImage(plugin, id)) + { + JS_ThrowPlainError(ctx, "This plugin did not allocate the specified image range."); + return JS_EXCEPTION; + } + + JSDrawCustomImage( + scriptEngine, id, { static_cast(width.value()), static_cast(height.value()) }, callback); + return JS_UNDEFINED; + } + + static JSValue CreateImageIndexRange(JSContext* ctx, size_t start, size_t count) + { + JSValue obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "start", JS_NewInt64(ctx, static_cast(start))); + JS_SetPropertyStr(ctx, obj, "count", JS_NewInt64(ctx, static_cast(count))); + return obj; } }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2-ui/scripting/ScTileSelection.hpp b/src/openrct2-ui/scripting/ScTileSelection.hpp index d484c15cc5..98e05dbdab 100644 --- a/src/openrct2-ui/scripting/ScTileSelection.hpp +++ b/src/openrct2-ui/scripting/ScTileSelection.hpp @@ -11,49 +11,56 @@ #ifdef ENABLE_SCRIPTING - #include #include namespace OpenRCT2::Scripting { - class ScTileSelection - { - private: - duk_context* _ctx{}; + class ScTileSelection; + extern ScTileSelection gScTileSelection; + class ScTileSelection final : public ScBase + { public: - ScTileSelection(duk_context* ctx) - : _ctx(ctx) + void Register(JSContext* ctx) { + RegisterBaseStr(ctx, "TileSelection"); } - DukValue range_get() const + JSValue New(JSContext* ctx) + { + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("range", ScTileSelection::range_get, ScTileSelection::range_set), + JS_CGETSET_DEF("tiles", ScTileSelection::tiles_get, ScTileSelection::tiles_set), + }; + return MakeWithOpaque(ctx, funcs, nullptr); + } + + static JSValue range_get(JSContext* ctx, JSValue thisVal) { if (gMapSelectFlags.has(MapSelectFlag::enable)) { - DukObject range(_ctx); + JSValue range = JS_NewObject(ctx); - DukObject leftTop(_ctx); - leftTop.Set("x", gMapSelectPositionA.x); - leftTop.Set("y", gMapSelectPositionA.y); - range.Set("leftTop", leftTop.Take()); + JSValue leftTop = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, leftTop, "x", JS_NewInt32(ctx, gMapSelectPositionA.x)); + JS_SetPropertyStr(ctx, leftTop, "y", JS_NewInt32(ctx, gMapSelectPositionA.y)); + JS_SetPropertyStr(ctx, range, "leftTop", leftTop); - DukObject rightBottom(_ctx); - rightBottom.Set("x", gMapSelectPositionB.x); - rightBottom.Set("y", gMapSelectPositionB.y); - range.Set("rightBottom", rightBottom.Take()); - return range.Take(); + JSValue rightBottom = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, rightBottom, "x", JS_NewInt32(ctx, gMapSelectPositionB.x)); + JS_SetPropertyStr(ctx, rightBottom, "y", JS_NewInt32(ctx, gMapSelectPositionB.y)); + JS_SetPropertyStr(ctx, range, "rightBottom", rightBottom); + return range; } - duk_push_null(_ctx); - return DukValue::take_from_stack(_ctx); + return JS_NULL; } - void range_set(DukValue value) + static JSValue range_set(JSContext* ctx, JSValue thisVal, JSValue value) { - if (value.type() == DukValue::Type::OBJECT) + if (JS_IsObject(value)) { - auto range = GetMapRange(value); + auto range = GetMapRange(ctx, value); if (range) { gMapSelectPositionA.x = range->GetX1(); @@ -68,48 +75,40 @@ namespace OpenRCT2::Scripting { gMapSelectFlags.unset(MapSelectFlag::enable); } + + return JS_UNDEFINED; } - DukValue tiles_get() const + static JSValue tiles_get(JSContext* ctx, JSValue thisVal) { - duk_push_array(_ctx); + JSValue tiles = JS_NewArray(ctx); if (gMapSelectFlags.has(MapSelectFlag::enableConstruct)) { - duk_uarridx_t index = 0; + int64_t index = 0; for (const auto& tile : MapSelection::getSelectedTiles()) { - duk_push_object(_ctx); - duk_push_int(_ctx, tile.x); - duk_put_prop_string(_ctx, -2, "x"); - duk_push_int(_ctx, tile.y); - duk_put_prop_string(_ctx, -2, "y"); - duk_put_prop_index(_ctx, -2, index); + JSValue val = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, val, "x", JS_NewInt32(ctx, tile.x)); + JS_SetPropertyStr(ctx, val, "y", JS_NewInt32(ctx, tile.y)); + JS_SetPropertyInt64(ctx, tiles, index, val); index++; } } - return DukValue::take_from_stack(_ctx); + return tiles; } - void tiles_set(DukValue value) + static JSValue tiles_set(JSContext* ctx, JSValue thisVal, JSValue value) { MapSelection::clearSelectedTiles(); - if (value.is_array()) + if (JS_IsArray(value)) { - value.push(); - auto arrayLen = duk_get_length(_ctx, -1); - for (duk_uarridx_t i = 0; i < arrayLen; i++) - { - if (duk_get_prop_index(_ctx, -1, i)) + JSIterateArray(ctx, value, [](JSContext* ctx2, JSValue v) { + auto coords = GetCoordsXY(ctx2, v); + if (coords.has_value()) { - auto dukElement = DukValue::take_from_stack(_ctx); - auto coords = GetCoordsXY(dukElement); - if (coords) - { - MapSelection::addSelectedTile(*coords); - } + MapSelection::addSelectedTile(coords.value()); } - } - duk_pop(_ctx); + }); } if (MapSelection::getSelectedTiles().empty()) @@ -121,40 +120,37 @@ namespace OpenRCT2::Scripting { gMapSelectFlags.set(MapSelectFlag::enableConstruct); } - } - static void Register(duk_context* ctx) - { - dukglue_register_property(ctx, &ScTileSelection::range_get, &ScTileSelection::range_set, "range"); - dukglue_register_property(ctx, &ScTileSelection::tiles_get, &ScTileSelection::tiles_set, "tiles"); + return JS_UNDEFINED; } private: - static std::optional GetCoordsXY(const DukValue& dukCoords) + static std::optional GetCoordsXY(JSContext* ctx, JSValue jsCoords) { - if (dukCoords.type() == DukValue::Type::OBJECT) + if (JS_IsObject(jsCoords)) { - auto dukX = dukCoords["x"]; - if (dukX.type() == DukValue::Type::NUMBER) + auto x = JSToOptionalInt(ctx, jsCoords, "x"); + auto y = JSToOptionalInt(ctx, jsCoords, "y"); + if (x.has_value() && y.has_value()) { - auto dukY = dukCoords["y"]; - if (dukY.type() == DukValue::Type::NUMBER) - { - return CoordsXY(dukX.as_int(), dukY.as_int()); - } + return CoordsXY(x.value(), y.value()); } } return std::nullopt; } - static std::optional GetMapRange(const DukValue& dukMapRange) + static std::optional GetMapRange(JSContext* ctx, JSValue jsMapRange) { - if (dukMapRange.type() == DukValue::Type::OBJECT) + if (JS_IsObject(jsMapRange)) { - auto leftTop = GetCoordsXY(dukMapRange["leftTop"]); + JSValue jsLeftTop = JS_GetPropertyStr(ctx, jsMapRange, "leftTop"); + auto leftTop = GetCoordsXY(ctx, jsLeftTop); + JS_FreeValue(ctx, jsLeftTop); if (leftTop.has_value()) { - auto rightBottom = GetCoordsXY(dukMapRange["rightBottom"]); + JSValue jsRightBottom = JS_GetPropertyStr(ctx, jsMapRange, "rightBottom"); + auto rightBottom = GetCoordsXY(ctx, jsRightBottom); + JS_FreeValue(ctx, jsRightBottom); if (rightBottom.has_value()) { return MapRange(leftTop->x, leftTop->y, rightBottom->x, rightBottom->y); diff --git a/src/openrct2-ui/scripting/ScTitleSequence.hpp b/src/openrct2-ui/scripting/ScTitleSequence.hpp index 3e5d59fe29..a66998f8a4 100644 --- a/src/openrct2-ui/scripting/ScTitleSequence.hpp +++ b/src/openrct2-ui/scripting/ScTitleSequence.hpp @@ -17,8 +17,8 @@ #include #include #include + #include #include - #include #include #include #include @@ -48,7 +48,7 @@ namespace OpenRCT2::Scripting LoadSc, }; - static const DukEnumMap TitleScriptMap( + static const EnumMap TitleScriptMap( { { Title::LoadParkCommand::ScriptingName, TitleScript::Load }, { Title::SetLocationCommand::ScriptingName, TitleScript::Location }, @@ -62,193 +62,224 @@ namespace OpenRCT2::Scripting { Title::EndCommand::ScriptingName, TitleScript::End }, }); - template<> - DukValue ToDuk(duk_context* ctx, const TitleScript& value) + inline JSValue TitleScriptToJS(JSContext* ctx, TitleScript value) { - return ToDuk(ctx, TitleScriptMap[value]); + return JSFromStdString(ctx, TitleScriptMap[value]); } - template<> - DukValue ToDuk(duk_context* ctx, const Title::TitleCommand& value) + inline JSValue TitleCommandToJS(JSContext* ctx, const Title::TitleCommand& value) { using namespace OpenRCT2::Title; - DukObject obj(ctx); + JSValue obj = JS_NewObject(ctx); std::visit( - [&obj](auto&& command) { + [ctx, &obj](auto&& command) { using T = std::decay_t; - obj.Set("type", T::ScriptingName); + JS_SetPropertyStr(ctx, obj, "type", JSFromStdString(ctx, T::ScriptingName)); if constexpr (std::is_same_v) { - obj.Set("index", command.SaveIndex); + JS_SetPropertyStr(ctx, obj, "index", JS_NewInt32(ctx, command.SaveIndex)); } else if constexpr (std::is_same_v) { - obj.Set("x", command.Location.X); - obj.Set("y", command.Location.Y); + JS_SetPropertyStr(ctx, obj, "x", JS_NewInt32(ctx, command.Location.X)); + JS_SetPropertyStr(ctx, obj, "y", JS_NewInt32(ctx, command.Location.Y)); } else if constexpr (std::is_same_v) { - obj.Set("rotations", command.Rotations); + JS_SetPropertyStr(ctx, obj, "rotations", JS_NewInt32(ctx, command.Rotations)); } else if constexpr (std::is_same_v) { - obj.Set("zoom", command.Zoom); + JS_SetPropertyStr(ctx, obj, "zoom", JS_NewInt32(ctx, command.Zoom)); } else if constexpr (std::is_same_v) { if (command.Follow.SpriteIndex.IsNull()) - obj.Set("id", nullptr); + JS_SetPropertyStr(ctx, obj, "id", JS_NULL); else - obj.Set("id", command.Follow.SpriteIndex.ToUnderlying()); + JS_SetPropertyStr(ctx, obj, "id", JS_NewInt32(ctx, command.Follow.SpriteIndex.ToUnderlying())); } else if constexpr (std::is_same_v) { - obj.Set("speed", command.Speed); + JS_SetPropertyStr(ctx, obj, "speed", JS_NewInt32(ctx, command.Speed)); } else if constexpr (std::is_same_v) { - obj.Set("duration", command.Milliseconds); + JS_SetPropertyStr(ctx, obj, "duration", JS_NewInt32(ctx, command.Milliseconds)); } else if constexpr (std::is_same_v) { - obj.Set("scenario", String::toStringView(command.Scenario, sizeof(command.Scenario))); + JS_SetPropertyStr(ctx, obj, "scenario", JSFromStdString(ctx, command.Scenario)); } }, value); - return obj.Take(); + return obj; } - template<> - TitleScript FromDuk(const DukValue& value) - { - if (value.type() == DukValue::Type::STRING) - return TitleScriptMap[value.as_string()]; - throw DukException() << "Invalid title command id"; - } - - template<> - Title::TitleCommand FromDuk(const DukValue& value) + inline std::optional TitleCommandFromJS(JSContext* ctx, JSValue value) { using namespace OpenRCT2::Title; - auto type = FromDuk(value["type"]); - TitleCommand command{}; - switch (type) + auto type = TitleScriptMap.TryGet(JSToStdString(ctx, value, "type")); + if (!type.has_value()) + return std::nullopt; + + switch (type.value()) { case TitleScript::Load: - command = LoadParkCommand{ static_cast(value["index"].as_uint()) }; - break; - case TitleScript::Location: - command = SetLocationCommand{ - static_cast(value["x"].as_uint()), - static_cast(value["y"].as_uint()), - }; - break; - case TitleScript::Rotate: - command = RotateViewCommand{ static_cast(value["rotations"].as_uint()) }; - break; - case TitleScript::Zoom: - command = SetZoomCommand{ static_cast(value["zoom"].as_uint()) }; - break; - case TitleScript::Follow: { - auto dukId = value["id"]; - if (dukId.type() == DukValue::Type::NUMBER) + auto index = JSToOptionalInt(ctx, value, "index"); + if (!index.has_value()) + return std::nullopt; + + return LoadParkCommand{ static_cast(index.value()) }; + } + case TitleScript::Location: + { + auto x = JSToOptionalInt(ctx, value, "x"); + auto y = JSToOptionalInt(ctx, value, "y"); + if (!x.has_value() || !y.has_value()) + return std::nullopt; + + return SetLocationCommand{ + static_cast(x.value()), + static_cast(y.value()), + }; + } + case TitleScript::Rotate: + { + auto rotations = JSToOptionalInt(ctx, value, "rotations"); + if (!rotations.has_value()) + return std::nullopt; + + return RotateViewCommand{ static_cast(rotations.value()) }; + } + case TitleScript::Zoom: + { + auto zoom = JSToOptionalInt(ctx, value, "zoom"); + if (!zoom.has_value()) + return std::nullopt; + + return SetZoomCommand{ static_cast(zoom.value()) }; + } + case TitleScript::Follow: + if (auto id = JSToOptionalInt(ctx, value, "id"); id.has_value()) { - command = FollowEntityCommand{ EntityId::FromUnderlying(dukId.as_uint()) }; + return FollowEntityCommand{ EntityId::FromUnderlying(id.value()) }; } else { - command = FollowEntityCommand{ EntityId::GetNull() }; + return FollowEntityCommand{ EntityId::GetNull() }; } - break; - } case TitleScript::Speed: - command = SetSpeedCommand{ static_cast(value["speed"].as_uint()) }; - break; + { + auto speed = JSToOptionalInt(ctx, value, "speed"); + if (!speed.has_value()) + return std::nullopt; + + return SetSpeedCommand{ static_cast(speed.value()) }; + } case TitleScript::Wait: - command = WaitCommand{ static_cast(value["duration"].as_uint()) }; - break; + { + auto duration = JSToOptionalInt(ctx, value, "duration"); + if (!duration.has_value()) + return std::nullopt; + + return WaitCommand{ static_cast(duration.value()) }; + } case TitleScript::LoadSc: { + auto scenario = JSToOptionalStdString(ctx, value, "scenario"); + if (!scenario.has_value()) + return std::nullopt; + auto loadScenarioCommand = LoadScenarioCommand{}; - String::set( - loadScenarioCommand.Scenario, sizeof(loadScenarioCommand.Scenario), value["scenario"].as_c_string()); - command = loadScenarioCommand; - break; + String::set(loadScenarioCommand.Scenario, sizeof(loadScenarioCommand.Scenario), scenario.value().c_str()); + return loadScenarioCommand; } case TitleScript::Restart: - command = RestartCommand{}; - break; + return RestartCommand{}; case TitleScript::End: - command = EndCommand{}; - break; + return EndCommand{}; default: - break; + return std::nullopt; } - return command; } - class ScTitleSequencePark + class ScTitleSequencePark; + extern ScTitleSequencePark gScTitleSequencePark; + class ScTitleSequencePark : public ScBase { - private: - std::string _titleSequencePath; - std::string _fileName; - - public: - ScTitleSequencePark(std::string_view path, std::string_view fileName) - : _titleSequencePath(path) - , _fileName(fileName) + struct OpaqueData { + std::string _titleSequencePath{}; + std::string _fileName{}; + }; + + static JSValue fileName_get(JSContext* ctx, JSValue thisVal) + { + OpaqueData* data = gScTitleSequencePark.GetOpaque(thisVal); + if (!data) + return JS_UNDEFINED; + return JSFromStdString(ctx, data->_fileName); } - private: - std::string fileName_get() const + static JSValue fileName_set(JSContext* ctx, JSValue thisVal, JSValue value) { - return _fileName; - } + JS_UNPACK_STR(valueStr, ctx, value); + OpaqueData* data = gScTitleSequencePark.GetOpaque(thisVal); + if (!data) + return JS_UNDEFINED; - void fileName_set(const std::string& value) - { - if (value == _fileName) - return; + if (valueStr == data->_fileName) + return JS_UNDEFINED; - auto seq = Title::LoadTitleSequence(_titleSequencePath); + auto seq = Title::LoadTitleSequence(data->_titleSequencePath); if (seq != nullptr) { // Check if name already in use - auto index = GetIndex(*seq, value); + auto index = GetIndex(*seq, valueStr); if (!index) { - index = GetIndex(*seq, _fileName); + index = GetIndex(*seq, data->_fileName); if (index) { - TitleSequenceRenamePark(*seq, *index, value.c_str()); + TitleSequenceRenamePark(*seq, *index, valueStr.c_str()); TitleSequenceSave(*seq); } } } + return JS_UNDEFINED; } - void delete_() + static JSValue delete_(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto seq = Title::LoadTitleSequence(_titleSequencePath); + OpaqueData* data = gScTitleSequencePark.GetOpaque(thisVal); + if (!data) + return JS_UNDEFINED; + + auto seq = Title::LoadTitleSequence(data->_titleSequencePath); if (seq != nullptr) { - auto index = GetIndex(*seq, _fileName); + auto index = GetIndex(*seq, data->_fileName); if (index) { OpenRCT2::Title::TitleSequenceRemovePark(*seq, *index); OpenRCT2::Title::TitleSequenceSave(*seq); } } + return JS_UNDEFINED; } - void load() + static JSValue load(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto seq = Title::LoadTitleSequence(_titleSequencePath); + OpaqueData* data = gScTitleSequencePark.GetOpaque(thisVal); + if (!data) + return JS_UNDEFINED; + + auto seq = Title::LoadTitleSequence(data->_titleSequencePath); if (seq != nullptr) { - auto index = GetIndex(*seq, _fileName); + auto index = GetIndex(*seq, data->_fileName); if (index) { auto handle = OpenRCT2::Title::TitleSequenceGetParkHandle(*seq, *index); @@ -281,22 +312,39 @@ namespace OpenRCT2::Scripting } catch (const std::exception&) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - duk_error(ctx, DUK_ERR_ERROR, "Unable to load park."); + JS_ThrowPlainError(ctx, "Unable to load park."); + return JS_EXCEPTION; } } } + return JS_UNDEFINED; } public: - static void Register(duk_context* ctx) + JSValue New(JSContext* ctx, std::string_view path, std::string_view fileName) { - dukglue_register_property(ctx, &ScTitleSequencePark::fileName_get, &ScTitleSequencePark::fileName_set, "fileName"); - dukglue_register_method(ctx, &ScTitleSequencePark::delete_, "delete"); - dukglue_register_method(ctx, &ScTitleSequencePark::load, "load"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("fileName", ScTitleSequencePark::fileName_get, ScTitleSequencePark::fileName_set), + JS_CFUNC_DEF("delete", 0, ScTitleSequencePark::delete_), + JS_CFUNC_DEF("load", 0, ScTitleSequencePark::load), + }; + + return MakeWithOpaque(ctx, funcs, new OpaqueData{ std::string(path), std::string(fileName) }); + } + + void Register(JSContext* ctx) + { + RegisterBaseStr(ctx, "TitleSequencePark", Finalize); } private: + static void Finalize(JSRuntime* rt, JSValue thisVal) + { + OpaqueData* data = gScTitleSequencePark.GetOpaque(thisVal); + if (data) + delete data; + } + static std::optional GetIndex(const Title::TitleSequence& seq, const std::string_view needle) { for (size_t i = 0; i < seq.Saves.size(); i++) @@ -310,189 +358,236 @@ namespace OpenRCT2::Scripting } }; - class ScTitleSequence + class ScTitleSequence; + extern ScTitleSequence gScTitleSequence; + class ScTitleSequence : public ScBase { - private: - std::string _path; - - public: - ScTitleSequence(const std::string& path) + struct OpaqueData { - _path = path; - } + std::string _path; + }; - private: - std::string name_get() const + static JSValue name_get(JSContext* ctx, JSValue thisVal) { - const auto* item = GetItem(); + const auto* item = GetItem(ctx, thisVal); if (item != nullptr) { - return item->Name; + return JSFromStdString(ctx, item->Name); } - return {}; + return JSFromStdString(ctx, {}); } - void name_set(const std::string& value) + static JSValue name_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto index = GetManagerIndex(); + JS_UNPACK_STR(valueStr, ctx, value) + OpaqueData* data = gScTitleSequence.GetOpaque(thisVal); + if (!data) + return JS_UNDEFINED; + + auto index = GetManagerIndex(ctx, thisVal); if (index) { - auto newIndex = TitleSequenceManager::RenameItem(*index, value.c_str()); + auto newIndex = TitleSequenceManager::RenameItem(*index, valueStr.c_str()); // Update path to new value auto newItem = TitleSequenceManager::GetItem(newIndex); - _path = newItem != nullptr ? newItem->Path : std::string(); + data->_path = newItem != nullptr ? newItem->Path : std::string(); } + return JS_UNDEFINED; } - std::string path_get() const + static JSValue path_get(JSContext* ctx, JSValue thisVal) { - const auto* item = GetItem(); + const auto* item = GetItem(ctx, thisVal); if (item != nullptr) { - return item->Path; + return JSFromStdString(ctx, item->Path); } - return {}; + return JSFromStdString(ctx, {}); } - bool isDirectory_get() const + static JSValue isDirectory_get(JSContext* ctx, JSValue thisVal) { - const auto* item = GetItem(); + const auto* item = GetItem(ctx, thisVal); if (item != nullptr) { - return !item->IsZip; + return JS_NewBool(ctx, !item->IsZip); } - return {}; + return JS_NewBool(ctx, {}); } - bool isReadOnly_get() const + static JSValue isReadOnly_get(JSContext* ctx, JSValue thisVal) { - const auto* item = GetItem(); + const auto* item = GetItem(ctx, thisVal); if (item != nullptr) { - return item->PredefinedIndex != TitleSequenceManager::kPredefinedIndexCustom; + return JS_NewBool(ctx, item->PredefinedIndex != TitleSequenceManager::kPredefinedIndexCustom); } - return {}; + return JS_NewBool(ctx, {}); } - std::vector> parks_get() const + static JSValue parks_get(JSContext* ctx, JSValue thisVal) { - std::vector> result; - auto titleSeq = Title::LoadTitleSequence(_path); + JSValue result = JS_NewArray(ctx); + OpaqueData* data = gScTitleSequence.GetOpaque(thisVal); + if (!data) + return result; + + auto titleSeq = Title::LoadTitleSequence(data->_path); if (titleSeq != nullptr) { for (size_t i = 0; i < titleSeq->Saves.size(); i++) { - result.push_back(std::make_shared(_path, titleSeq->Saves[i])); + JS_SetPropertyInt64(ctx, result, i, gScTitleSequencePark.New(ctx, data->_path, titleSeq->Saves[i])); } } return result; } - std::vector commands_get() const + static JSValue commands_get(JSContext* ctx, JSValue thisVal) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto ctx = scriptEngine.GetContext(); + JSValue result = JS_NewArray(ctx); + OpaqueData* data = gScTitleSequence.GetOpaque(thisVal); + if (!data) + return result; - std::vector result; - auto titleSeq = Title::LoadTitleSequence(_path); + auto titleSeq = Title::LoadTitleSequence(data->_path); if (titleSeq != nullptr) { + int64_t idx = 0; for (const auto& command : titleSeq->Commands) { - result.push_back(ToDuk(ctx, command)); + JS_SetPropertyInt64(ctx, result, idx++, TitleCommandToJS(ctx, command)); } } return result; } - void commands_set(const std::vector& value) + static JSValue commands_set(JSContext* ctx, JSValue thisVal, JSValue value) { + JS_UNPACK_ARRAY(array, ctx, value); + OpaqueData* data = gScTitleSequence.GetOpaque(thisVal); + if (!data) + return JS_UNDEFINED; + + int64_t length; + if (JS_GetLength(ctx, array, &length) != 0) + return JS_UNDEFINED; std::vector commands; - for (const auto& v : value) + commands.reserve(length); + + for (int64_t i = 0; i < length; i++) { - auto command = FromDuk(v); - commands.push_back(std::move(command)); + JSValue element = JS_GetPropertyInt64(ctx, array, i); + auto command = TitleCommandFromJS(ctx, element); + JS_FreeValue(ctx, element); + if (!command.has_value()) + { + JS_ThrowPlainError(ctx, "Invalid command"); + return JS_EXCEPTION; + } + commands.push_back(command.value()); } - auto titleSeq = Title::LoadTitleSequence(_path); + auto titleSeq = Title::LoadTitleSequence(data->_path); titleSeq->Commands = commands; OpenRCT2::Title::TitleSequenceSave(*titleSeq); + return JS_UNDEFINED; } - void addPark(const std::string& path, const std::string& fileName) + static JSValue addPark(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto titleSeq = Title::LoadTitleSequence(_path); + JS_UNPACK_STR(path, ctx, argv[0]); + JS_UNPACK_STR(fileName, ctx, argv[1]); + OpaqueData* data = gScTitleSequence.GetOpaque(thisVal); + if (!data) + return JS_UNDEFINED; + + auto titleSeq = Title::LoadTitleSequence(data->_path); OpenRCT2::Title::TitleSequenceAddPark(*titleSeq, path.c_str(), fileName.c_str()); OpenRCT2::Title::TitleSequenceSave(*titleSeq); + return JS_UNDEFINED; } - std::shared_ptr clone(const std::string& name) const + static JSValue clone(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto copyIndex = GetManagerIndex(); + JS_UNPACK_STR(name, ctx, argv[0]); + + auto copyIndex = GetManagerIndex(ctx, thisVal); if (copyIndex) { auto index = TitleSequenceManager::DuplicateItem(*copyIndex, name.c_str()); auto* item = TitleSequenceManager::GetItem(index); if (item != nullptr) { - return std::make_shared(item->Path); + return gScTitleSequence.New(ctx, item->Path); } } - return nullptr; + return JS_NULL; } - void delete_() + static JSValue delete_(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto index = GetManagerIndex(); + OpaqueData* data = gScTitleSequence.GetOpaque(thisVal); + if (!data) + return JS_UNDEFINED; + + auto index = GetManagerIndex(ctx, thisVal); if (index) { TitleSequenceManager::DeleteItem(*index); } - _path = {}; + data->_path = {}; + return JS_UNDEFINED; } - bool isPlaying_get() const + static bool IsPlaying(JSContext* ctx, JSValue thisVal) { - auto index = GetManagerIndex(); + auto index = GetManagerIndex(ctx, thisVal); return index && TitleIsPreviewingSequence() && *index == TitleGetCurrentSequence(); } - DukValue position_get() const + static JSValue isPlaying_get(JSContext* ctx, JSValue thisVal) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - if (isPlaying_get()) + return JS_NewBool(ctx, IsPlaying(ctx, thisVal)); + } + + static JSValue position_get(JSContext* ctx, JSValue thisVal) + { + if (IsPlaying(ctx, thisVal)) { auto* player = static_cast(TitleGetSequencePlayer()); if (player != nullptr) { - return ToDuk(ctx, player->GetCurrentPosition()); + return JS_NewInt32(ctx, player->GetCurrentPosition()); } } - return ToDuk(ctx, nullptr); + return JS_NULL; } - void play() + static JSValue play(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - auto index = GetManagerIndex(); + auto index = GetManagerIndex(ctx, thisVal); if (index && (!TitleIsPreviewingSequence() || *index != TitleGetCurrentSequence())) { if (!TitlePreviewSequence(*index)) { - duk_error(ctx, DUK_ERR_ERROR, "Failed to load title sequence"); + JS_ThrowPlainError(ctx, "Failed to load title sequence"); + return JS_EXCEPTION; } else if (gLegacyScene != LegacyScene::titleSequence) { gPreviewingTitleSequenceInGame = true; } } + return JS_UNDEFINED; } - void seek(int32_t position) + static JSValue seek(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - if (isPlaying_get()) + JS_UNPACK_INT32(position, ctx, argv[0]); + + if (IsPlaying(ctx, thisVal)) { auto* player = static_cast(TitleGetSequencePlayer()); try @@ -502,47 +597,71 @@ namespace OpenRCT2::Scripting } catch (...) { - duk_error(ctx, DUK_ERR_ERROR, "Failed to seek"); + JS_ThrowPlainError(ctx, "Failed to seek"); + return JS_EXCEPTION; } } + return JS_UNDEFINED; } - void stop() + static JSValue stop(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - if (isPlaying_get()) + if (IsPlaying(ctx, thisVal)) { TitleStopPreviewingSequence(); } + return JS_UNDEFINED; } public: - static void Register(duk_context* ctx) + JSValue New(JSContext* ctx, std::string_view path) { - dukglue_register_property(ctx, &ScTitleSequence::name_get, &ScTitleSequence::name_set, "name"); - dukglue_register_property(ctx, &ScTitleSequence::path_get, nullptr, "path"); - dukglue_register_property(ctx, &ScTitleSequence::isDirectory_get, nullptr, "isDirectory"); - dukglue_register_property(ctx, &ScTitleSequence::isReadOnly_get, nullptr, "isReadOnly"); - dukglue_register_property(ctx, &ScTitleSequence::parks_get, nullptr, "parks"); - dukglue_register_property(ctx, &ScTitleSequence::commands_get, &ScTitleSequence::commands_set, "commands"); - dukglue_register_property(ctx, &ScTitleSequence::isPlaying_get, nullptr, "isPlaying"); - dukglue_register_property(ctx, &ScTitleSequence::position_get, nullptr, "position"); - dukglue_register_method(ctx, &ScTitleSequence::addPark, "addPark"); - dukglue_register_method(ctx, &ScTitleSequence::clone, "clone"); - dukglue_register_method(ctx, &ScTitleSequence::delete_, "delete"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("name", ScTitleSequence::name_get, ScTitleSequence::name_set), + JS_CGETSET_DEF("path", ScTitleSequence::path_get, nullptr), + JS_CGETSET_DEF("isDirectory", ScTitleSequence::isDirectory_get, nullptr), + JS_CGETSET_DEF("isReadOnly", ScTitleSequence::isReadOnly_get, nullptr), + JS_CGETSET_DEF("parks", ScTitleSequence::parks_get, nullptr), + JS_CGETSET_DEF("commands", ScTitleSequence::commands_get, ScTitleSequence::commands_set), + JS_CGETSET_DEF("isPlaying", ScTitleSequence::isPlaying_get, nullptr), + JS_CGETSET_DEF("position", ScTitleSequence::position_get, nullptr), - dukglue_register_method(ctx, &ScTitleSequence::play, "play"); - dukglue_register_method(ctx, &ScTitleSequence::seek, "seek"); - dukglue_register_method(ctx, &ScTitleSequence::stop, "stop"); + JS_CFUNC_DEF("addPark", 2, ScTitleSequence::addPark), + JS_CFUNC_DEF("clone", 1, ScTitleSequence::clone), + JS_CFUNC_DEF("delete", 0, ScTitleSequence::delete_), + + JS_CFUNC_DEF("play", 0, ScTitleSequence::play), + JS_CFUNC_DEF("seek", 1, ScTitleSequence::seek), + JS_CFUNC_DEF("stop", 0, ScTitleSequence::stop), + }; + + return MakeWithOpaque(ctx, funcs, new OpaqueData{ std::string(path) }); + } + + void Register(JSContext* ctx) + { + RegisterBaseStr(ctx, "TitleSequence", Finalize); } private: - std::optional GetManagerIndex() const + static void Finalize(JSRuntime* rt, JSValue thisVal) { + OpaqueData* data = gScTitleSequence.GetOpaque(thisVal); + if (data) + delete data; + } + + static std::optional GetManagerIndex(JSContext* ctx, JSValue thisVal) + { + OpaqueData* data = gScTitleSequence.GetOpaque(thisVal); + if (!data) + return std::nullopt; + auto count = TitleSequenceManager::GetCount(); for (size_t i = 0; i < count; i++) { auto item = TitleSequenceManager::GetItem(i); - if (item != nullptr && item->Path == _path) + if (item != nullptr && item->Path == data->_path) { return i; } @@ -550,9 +669,9 @@ namespace OpenRCT2::Scripting return std::nullopt; } - const TitleSequenceManager::Item* GetItem() const + static const TitleSequenceManager::Item* GetItem(JSContext* ctx, JSValue thisVal) { - auto index = GetManagerIndex(); + auto index = GetManagerIndex(ctx, thisVal); if (index) { return TitleSequenceManager::GetItem(*index); @@ -561,37 +680,48 @@ namespace OpenRCT2::Scripting } }; - class ScTitleSequenceManager + class ScTitleSequenceManager; + extern ScTitleSequenceManager gScTitleSequenceManager; + class ScTitleSequenceManager : public ScBase { - private: - std::vector> titleSequences_get() const + static JSValue titleSequences_get(JSContext* ctx, JSValue thisVal) { - std::vector> result; + JSValue result = JS_NewArray(ctx); auto count = TitleSequenceManager::GetCount(); for (size_t i = 0; i < count; i++) { const auto& path = TitleSequenceManager::GetItem(i)->Path; - result.push_back(std::make_shared(path)); + JS_SetPropertyInt64(ctx, result, i, gScTitleSequence.New(ctx, path)); } return result; } - std::shared_ptr create(const std::string& name) + static JSValue create(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { + JS_UNPACK_STR(name, ctx, argv[0]) + auto index = TitleSequenceManager::CreateItem(name.c_str()); auto* item = TitleSequenceManager::GetItem(index); if (item != nullptr) { - return std::make_shared(item->Path); + return gScTitleSequence.New(ctx, item->Path); } - return nullptr; + return JS_NULL; } public: - static void Register(duk_context* ctx) + JSValue New(JSContext* ctx) { - dukglue_register_property(ctx, &ScTitleSequenceManager::titleSequences_get, nullptr, "titleSequences"); - dukglue_register_method(ctx, &ScTitleSequenceManager::create, "create"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("titleSequences", ScTitleSequenceManager::titleSequences_get, nullptr), + JS_CFUNC_DEF("create", 1, ScTitleSequenceManager::create), + }; + return MakeWithOpaque(ctx, funcs, nullptr); + } + + void Register(JSContext* ctx) + { + RegisterBaseStr(ctx, "TitleSequenceManager"); } }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2-ui/scripting/ScUi.hpp b/src/openrct2-ui/scripting/ScUi.hpp index 09dba5246e..f53fbc2fa2 100644 --- a/src/openrct2-ui/scripting/ScUi.hpp +++ b/src/openrct2-ui/scripting/ScUi.hpp @@ -11,12 +11,14 @@ #ifdef ENABLE_SCRIPTING + #include "../interface/Window.h" #include "../windows/Windows.h" #include "CustomMenu.h" + #include "CustomWindow.h" #include "ScImageManager.hpp" #include "ScTileSelection.hpp" #include "ScViewport.hpp" - #include "ScWindow.hpp" + #include "ScWindow.h" #include #include @@ -24,416 +26,495 @@ #include #include #include - #include #include #include namespace OpenRCT2::Scripting { class Plugin; -} + class ScUi; + extern ScUi gScUi; + class ScTool; + extern ScTool gScTool; -namespace OpenRCT2::Ui::Windows -{ - WindowBase* WindowCustomOpen(std::shared_ptr owner, DukValue dukDesc); -} + static const EnumMap ScenarioCategoryMap{ { + { "beginner", Scenario::Category::beginner }, + { "challenging", Scenario::Category::challenging }, + { "expert", Scenario::Category::expert }, + { "real", Scenario::Category::real }, + { "other", Scenario::Category::other }, + { "dlc", Scenario::Category::dlc }, + { "build_your_own", Scenario::Category::buildYourOwn }, + { "competitions", Scenario::Category::competitions }, + } }; -namespace OpenRCT2::Scripting -{ - static const DukEnumMap ScenarioCategoryMap( - { - { "beginner", Scenario::Category::beginner }, - { "challenging", Scenario::Category::challenging }, - { "expert", Scenario::Category::expert }, - { "real", Scenario::Category::real }, - { "other", Scenario::Category::other }, - { "dlc", Scenario::Category::dlc }, - { "build_your_own", Scenario::Category::buildYourOwn }, - { "competitions", Scenario::Category::competitions }, - }); + static const EnumMap ScenarioSourceMap{ + { "rct1", ScenarioSource::RCT1 }, { "rct1_aa", ScenarioSource::RCT1_AA }, { "rct1_ll", ScenarioSource::RCT1_LL }, + { "rct2", ScenarioSource::RCT2 }, { "rct2_ww", ScenarioSource::RCT2_WW }, { "rct2_tt", ScenarioSource::RCT2_TT }, + { "real", ScenarioSource::Real }, { "extras", ScenarioSource::Extras }, { "other", ScenarioSource::Other }, + }; - static const DukEnumMap ScenarioSourceMap( - { - { "rct1", ScenarioSource::RCT1 }, - { "rct1_aa", ScenarioSource::RCT1_AA }, - { "rct1_ll", ScenarioSource::RCT1_LL }, - { "rct2", ScenarioSource::RCT2 }, - { "rct2_ww", ScenarioSource::RCT2_WW }, - { "rct2_tt", ScenarioSource::RCT2_TT }, - { "real", ScenarioSource::Real }, - { "extras", ScenarioSource::Extras }, - { "other", ScenarioSource::Other }, - }); - - template<> - inline DukValue ToDuk(duk_context* ctx, const Scenario::Category& value) + inline JSValue ScenarioCategoryToJS(JSContext* ctx, Scenario::Category value) { const auto& entry = ScenarioCategoryMap.find(value); if (entry != ScenarioCategoryMap.end()) - return ToDuk(ctx, entry->first); - return ToDuk(ctx, ScenarioCategoryMap[Scenario::Category::other]); + return JSFromStdString(ctx, entry->first); + return JSFromStdString(ctx, ScenarioCategoryMap[Scenario::Category::other]); } - template<> - inline DukValue ToDuk(duk_context* ctx, const ScenarioSource& value) + inline JSValue ScenarioSourceToJS(JSContext* ctx, ScenarioSource value) { const auto& entry = ScenarioSourceMap.find(value); if (entry != ScenarioSourceMap.end()) - return ToDuk(ctx, entry->first); - return ToDuk(ctx, ScenarioSourceMap[ScenarioSource::Other]); + return JSFromStdString(ctx, entry->first); + return JSFromStdString(ctx, ScenarioSourceMap[ScenarioSource::Other]); } - class ScTool + class ScTool final : public ScBase { private: - duk_context* _ctx{}; - - public: - ScTool(duk_context* ctx) - : _ctx(ctx) + static JSValue id_get(JSContext* ctx, JSValue thisVal) { + return JSFromStdString(ctx, ActiveCustomTool ? ActiveCustomTool->Id : ""); } - static void Register(duk_context* ctx) + static JSValue cursor_get(JSContext* ctx, JSValue thisVal) { - dukglue_register_property(ctx, &ScTool::id_get, nullptr, "id"); - dukglue_register_property(ctx, &ScTool::cursor_get, nullptr, "cursor"); - dukglue_register_method(ctx, &ScTool::cancel, "cancel"); + return CursorIDToJSValue(ctx, static_cast(gCurrentToolId)); } - private: - std::string id_get() const - { - return ActiveCustomTool ? ActiveCustomTool->Id : ""; - } - - DukValue cursor_get() const - { - return ToDuk(_ctx, static_cast(gCurrentToolId)); - } - - void cancel() + static JSValue cancel(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { ToolCancel(); + return JS_UNDEFINED; + } + + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("id", ScTool::id_get, nullptr), + JS_CGETSET_DEF("cursor", ScTool::cursor_get, nullptr), + JS_CFUNC_DEF("cancel", 0, ScTool::cancel), + }; + + public: + JSValue New(JSContext* ctx) + { + return MakeWithOpaque(ctx, funcs, nullptr); + } + + void Register(JSContext* ctx) + { + RegisterBaseStr(ctx, "Tool"); } }; - class ScUi + class ScUi final : public ScBase { private: - ScriptEngine& _scriptEngine; - - public: - ScUi(ScriptEngine& scriptEngine) - : _scriptEngine(scriptEngine) + static JSValue width_get(JSContext* ctx, JSValue thisVal) { + return JS_NewInt32(ctx, ContextGetWidth()); + } + static JSValue height_get(JSContext* ctx, JSValue thisVal) + { + return JS_NewInt32(ctx, ContextGetHeight()); + } + static JSValue windows_get(JSContext* ctx, JSValue thisVal) + { + return JS_NewInt32(ctx, static_cast(gWindowList.size())); } - private: - int32_t width_get() const + static JSValue mainViewport_get(JSContext* ctx, JSValue thisVal) { - return ContextGetWidth(); - } - int32_t height_get() const - { - return ContextGetHeight(); - } - int32_t windows_get() const - { - return static_cast(gWindowList.size()); + return gScViewport.New(ctx, WindowClass::mainWindow); } - std::shared_ptr mainViewport_get() const + static JSValue tileSelection_get(JSContext* ctx, JSValue thisVal) { - return std::make_shared(WindowClass::mainWindow); + return gScTileSelection.New(ctx); } - std::shared_ptr tileSelection_get() const - { - return std::make_shared(_scriptEngine.GetContext()); - } - - std::shared_ptr tool_get() const + static JSValue tool_get(JSContext* ctx, JSValue thisVal) { if (gInputFlags.has(InputFlag::toolActive)) { - return std::make_shared(_scriptEngine.GetContext()); + return gScTool.New(ctx); } - return {}; + return JS_NULL; } - std::shared_ptr imageManager_get() const + static JSValue imageManager_get(JSContext* ctx, JSValue thisVal) { - return std::make_shared(_scriptEngine.GetContext()); + return gScImageManager.New(ctx); } - std::shared_ptr openWindow(DukValue desc) + static JSValue openWindow(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { using namespace OpenRCT2::Ui::Windows; - auto& execInfo = _scriptEngine.GetExecInfo(); + ScriptEngine* scriptEngine = gScUi.GetOpaque(thisVal); + if (!scriptEngine) + { + JS_ThrowInternalError(ctx, "No script engine"); + return JS_EXCEPTION; + } + + auto& execInfo = scriptEngine->GetExecInfo(); auto owner = execInfo.GetCurrentPlugin(); - owner->ThrowIfStopping(); + if (owner->IsStopping()) + { + JS_ThrowInternalError(ctx, "Plugin is stopping."); + return JS_EXCEPTION; + } - std::shared_ptr scWindow = nullptr; - auto w = WindowCustomOpen(owner, desc); + auto w = WindowCustomOpen(ctx, owner, argv[0]); if (w != nullptr) { - scWindow = std::make_shared(w); + return gScWindow.New(ctx, w); } - return scWindow; + return JS_NULL; } - void closeWindows(std::string classification, DukValue id) + static JSValue closeWindows(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { + JS_UNPACK_STR(classification, ctx, argv[0]); + JSValue id = argv[1]; + auto* windowMgr = Ui::GetWindowManager(); auto cls = GetClassification(classification); if (cls != WindowClass::null) { - if (id.type() == DukValue::Type::NUMBER) + if (JS_IsNumber(id)) { - windowMgr->CloseByNumber(cls, id.as_uint()); + windowMgr->CloseByNumber(cls, static_cast(JSToInt(ctx, id))); } else { windowMgr->CloseByClass(cls); } } + return JS_UNDEFINED; } - void closeAllWindows() + static JSValue closeAllWindows(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { auto* windowMgr = Ui::GetWindowManager(); windowMgr->CloseAll(); + return JS_UNDEFINED; } - std::shared_ptr getWindow(DukValue a) const + static JSValue getWindow(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - if (a.type() == DukValue::Type::NUMBER) + if (JS_IsNumber(argv[0])) { - auto index = a.as_uint(); - size_t i = 0; + int32_t index = -1; + JS_ToInt32(ctx, &index, argv[0]); + int32_t i = 0; for (const auto& w : gWindowList) { if (i == index) { - return std::make_shared(w.get()); + return gScWindow.New(ctx, w.get()); } i++; } } - else if (a.type() == DukValue::Type::STRING) + else if (JS_IsString(argv[0])) { - const auto& classification = a.as_string(); - auto w = FindCustomWindowByClassification(classification); + std::string classification = JSToStdString(ctx, argv[0]); + auto w = Ui::Windows::FindCustomWindowByClassification(classification); if (w != nullptr) { - return std::make_shared(w); + return gScWindow.New(ctx, w); } } - return {}; + return JS_NULL; } - void showError(const std::string& title, const std::string& message) + static JSValue showError(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - ErrorOpen(title, message); + if (argc < 2 || !JS_IsString(argv[0]) || !JS_IsString(argv[1])) + { + JS_ThrowTypeError(ctx, "Invalid arguments"); + return JS_EXCEPTION; + } + std::string title = JSToStdString(ctx, argv[0]); + std::string message = JSToStdString(ctx, argv[1]); + Ui::Windows::ErrorOpen(title, message); + return JS_UNDEFINED; } - void showTextInput(const DukValue& desc) + static JSValue showTextInput(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - try + JSValue desc = argv[0]; + if (!JS_IsObject(desc)) { - constexpr int32_t kMaxLengthAllowed = 4096; - auto plugin = _scriptEngine.GetExecInfo().GetCurrentPlugin(); - auto title = desc["title"].as_string(); - auto description = desc["description"].as_string(); - auto initialValue = AsOrDefault(desc["initialValue"], ""); - auto maxLength = AsOrDefault(desc["maxLength"], kMaxLengthAllowed); - auto callback = desc["callback"]; - WindowTextInputOpen( - title, description, initialValue, std::clamp(maxLength, 0, kMaxLengthAllowed), - [this, plugin, callback](std::string_view value) { - auto dukValue = ToDuk(_scriptEngine.GetContext(), value); - _scriptEngine.ExecutePluginCall(plugin, callback, { dukValue }, false); - }, - {}); + JS_ThrowInternalError(ctx, "Invalid parameters."); + return JS_EXCEPTION; } - catch (const DukException&) + + ScriptEngine* scriptEngine = gScUi.GetOpaque(thisVal); + if (!scriptEngine) { - duk_error(_scriptEngine.GetContext(), DUK_ERR_ERROR, "Invalid parameters."); + JS_ThrowInternalError(ctx, "No script engine"); + return JS_EXCEPTION; } + constexpr int32_t kMaxLengthAllowed = 4096; + auto plugin = scriptEngine->GetExecInfo().GetCurrentPlugin(); + auto title = JSToStdString(ctx, desc, "title"); + auto description = JSToStdString(ctx, desc, "description"); + auto initialValue = AsOrDefault(ctx, desc, "initialValue", ""); + auto maxLength = AsOrDefault(ctx, desc, "maxLength", kMaxLengthAllowed); + auto callback = JSToCallback(ctx, desc, "callback"); + Ui::Windows::WindowTextInputOpen( + title, description, initialValue, std::clamp(maxLength, 0, kMaxLengthAllowed), + [scriptEngine, ctx, plugin, callback](std::string_view value) { + scriptEngine->ExecutePluginCall(plugin, callback.callback, { JSFromStdString(ctx, value) }, false); + }, + {}); + return JS_UNDEFINED; } - void showFileBrowse(const DukValue& desc) + static JSValue showFileBrowse(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - try + JSValue desc = argv[0]; + if (!JS_IsObject(desc)) { - auto plugin = _scriptEngine.GetExecInfo().GetCurrentPlugin(); - auto type = desc["type"].as_string(); - auto fileType = desc["fileType"].as_string(); - auto defaultPath = AsOrDefault(desc["defaultPath"], ""); - auto callback = desc["callback"]; - - auto loadSaveAction = LoadSaveAction::load; - if (type == "load") - loadSaveAction = LoadSaveAction::load; - else - throw DukException(); - - LoadSaveType loadSaveType; - if (fileType == "game") - loadSaveType = LoadSaveType::park; - else if (fileType == "heightmap") - loadSaveType = LoadSaveType::heightmap; - else - throw DukException(); - - LoadsaveOpen( - loadSaveAction, loadSaveType, defaultPath, - [this, plugin, callback](ModalResult result, std::string_view path) { - if (result == ModalResult::ok) - { - auto dukValue = ToDuk(_scriptEngine.GetContext(), path); - _scriptEngine.ExecutePluginCall(plugin, callback, { dukValue }, false); - } - }, - nullptr); + JS_ThrowInternalError(ctx, "Invalid parameters."); + return JS_EXCEPTION; } - catch (const DukException&) + + ScriptEngine* scriptEngine = gScUi.GetOpaque(thisVal); + if (!scriptEngine) { - duk_error(_scriptEngine.GetContext(), DUK_ERR_ERROR, "Invalid parameters."); + JS_ThrowInternalError(ctx, "No script engine"); + return JS_EXCEPTION; } + auto plugin = scriptEngine->GetExecInfo().GetCurrentPlugin(); + auto type = JSToStdString(ctx, desc, "type"); + auto fileType = JSToStdString(ctx, desc, "fileType"); + auto defaultPath = AsOrDefault(ctx, desc, "defaultPath", ""); + auto callback = JSToCallback(ctx, desc, "callback"); + + auto loadSaveAction = LoadSaveAction::load; + if (type == "load") + loadSaveAction = LoadSaveAction::load; + else + { + JS_ThrowPlainError(ctx, "Invalid type parameter"); + return JS_EXCEPTION; + } + + LoadSaveType loadSaveType; + if (fileType == "game") + loadSaveType = LoadSaveType::park; + else if (fileType == "heightmap") + loadSaveType = LoadSaveType::heightmap; + else + { + JS_ThrowPlainError(ctx, "Invalid fileType parameter"); + return JS_EXCEPTION; + } + + Ui::Windows::LoadsaveOpen( + loadSaveAction, loadSaveType, defaultPath, + [scriptEngine, ctx, plugin, callback](ModalResult result, std::string_view path) { + if (result == ModalResult::ok) + { + scriptEngine->ExecutePluginCall(plugin, callback.callback, { JSFromStdString(ctx, path) }, false); + } + }, + true, nullptr); + return JS_UNDEFINED; } - void showScenarioSelect(const DukValue& desc) + static JSValue showScenarioSelect(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto plugin = _scriptEngine.GetExecInfo().GetCurrentPlugin(); - auto callback = desc["callback"]; + JSValue desc = argv[0]; + if (!JS_IsObject(desc)) + { + JS_ThrowInternalError(ctx, "Invalid parameters."); + return JS_EXCEPTION; + } - ScenarioselectOpen([this, plugin, callback](std::string_view path) { - auto dukValue = GetScenarioFile(path); - _scriptEngine.ExecutePluginCall(plugin, callback, { dukValue }, false); + ScriptEngine* scriptEngine = gScUi.GetOpaque(thisVal); + if (!scriptEngine) + { + JS_ThrowInternalError(ctx, "No script engine"); + return JS_EXCEPTION; + } + auto plugin = scriptEngine->GetExecInfo().GetCurrentPlugin(); + auto callback = JSToCallback(ctx, desc, "callback"); + + Ui::Windows::ScenarioselectOpen([scriptEngine, ctx, plugin, callback](std::string_view path) { + scriptEngine->ExecutePluginCall(plugin, callback.callback, { GetScenarioFile(ctx, path) }, false); }); + return JS_UNDEFINED; } - void activateTool(const DukValue& desc) + static JSValue activateTool(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - InitialiseCustomTool(_scriptEngine, desc); + ScriptEngine* scriptEngine = gScUi.GetOpaque(thisVal); + if (!scriptEngine) + { + JS_ThrowInternalError(ctx, "No script engine"); + return JS_EXCEPTION; + } + return InitialiseCustomTool(*scriptEngine, ctx, argv[0]); } - void registerMenuItem(std::string text, DukValue callback) + static JSValue registerMenuItem(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto& execInfo = _scriptEngine.GetExecInfo(); + ScriptEngine* scriptEngine = gScUi.GetOpaque(thisVal); + if (!scriptEngine) + { + JS_ThrowInternalError(ctx, "No script engine"); + return JS_EXCEPTION; + } + if (argc < 2 || !JS_IsString(argv[0]) || !JS_IsFunction(ctx, argv[1])) + { + JS_ThrowTypeError(ctx, "Invalid arguments"); + return JS_EXCEPTION; + } + auto& execInfo = scriptEngine->GetExecInfo(); auto owner = execInfo.GetCurrentPlugin(); - CustomMenuItems.emplace_back(owner, CustomToolbarMenuItemKind::Standard, text, callback); + std::string text = JSToStdString(ctx, argv[0]); + assert(owner->GetContext() == ctx); + CustomMenuItems.emplace_back(owner, CustomToolbarMenuItemKind::Standard, text, JSCallback(ctx, argv[1])); std::ranges::sort(CustomMenuItems, [](auto&& a, auto&& b) { return a.Text < b.Text; }); + + return JS_UNDEFINED; } - void registerToolboxMenuItem(const std::string& text, DukValue callback) + static JSValue registerToolboxMenuItem(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto& execInfo = _scriptEngine.GetExecInfo(); + ScriptEngine* scriptEngine = gScUi.GetOpaque(thisVal); + if (!scriptEngine) + { + JS_ThrowInternalError(ctx, "No script engine"); + return JS_EXCEPTION; + } + if (argc < 2 || !JS_IsString(argv[0]) || !JS_IsFunction(ctx, argv[1])) + { + JS_ThrowTypeError(ctx, "Invalid arguments"); + return JS_EXCEPTION; + } + + auto& execInfo = scriptEngine->GetExecInfo(); auto owner = execInfo.GetCurrentPlugin(); if (owner->GetMetadata().Type == PluginType::Intransient) { - CustomMenuItems.emplace_back(owner, CustomToolbarMenuItemKind::Toolbox, text, callback); + CustomMenuItems.emplace_back( + owner, CustomToolbarMenuItemKind::Toolbox, JSToStdString(ctx, argv[0]), JSCallback(ctx, argv[1])); std::ranges::sort(CustomMenuItems, [](auto&& a, auto&& b) { return a.Text < b.Text; }); } else { - duk_error(_scriptEngine.GetContext(), DUK_ERR_ERROR, "Plugin must be intransient."); + JS_ThrowPlainError(ctx, "Plugin must be intransient."); + return JS_EXCEPTION; } + return JS_UNDEFINED; } - void registerShortcut(DukValue desc) + static JSValue registerShortcut(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - try + JSValue desc = argv[0]; + if (!JS_IsObject(desc)) { - auto& execInfo = _scriptEngine.GetExecInfo(); - auto owner = execInfo.GetCurrentPlugin(); - auto id = desc["id"].as_string(); - auto text = desc["text"].as_string(); - - std::vector bindings; - auto dukBindings = desc["bindings"]; - if (dukBindings.is_array()) - { - for (auto binding : dukBindings.as_array()) - { - bindings.push_back(binding.as_string()); - } - } - - auto callback = desc["callback"]; - CustomShortcuts.emplace_back(std::make_unique(owner, id, text, bindings, callback)); + JS_ThrowInternalError(ctx, "Invalid parameters."); + return JS_EXCEPTION; } - catch (const DukException&) + + ScriptEngine* scriptEngine = gScUi.GetOpaque(thisVal); + if (!scriptEngine) { - duk_error(_scriptEngine.GetContext(), DUK_ERR_ERROR, "Invalid parameters."); + JS_ThrowInternalError(ctx, "No script engine"); + return JS_EXCEPTION; } + + auto owner = scriptEngine->GetExecInfo().GetCurrentPlugin(); + auto id = JSToStdString(ctx, desc, "id"); + auto text = JSToStdString(ctx, desc, "text"); + + std::vector bindings; + JSIterateArray( + ctx, desc, "bindings", [&bindings](JSContext* ctx2, JSValue x) { bindings.push_back(JSToStdString(ctx2, x)); }); + + auto callback = JSToCallback(ctx, desc, "callback"); + CustomShortcuts.emplace_back(std::make_unique(owner, id, text, bindings, callback)); + return JS_UNDEFINED; } + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("height", ScUi::height_get, nullptr), + JS_CGETSET_DEF("width", ScUi::width_get, nullptr), + JS_CGETSET_DEF("windows", ScUi::windows_get, nullptr), + JS_CGETSET_DEF("mainViewport", ScUi::mainViewport_get, nullptr), + JS_CGETSET_DEF("tileSelection", ScUi::tileSelection_get, nullptr), + JS_CGETSET_DEF("tool", ScUi::tool_get, nullptr), + JS_CGETSET_DEF("imageManager", ScUi::imageManager_get, nullptr), + + JS_CFUNC_DEF("openWindow", 1, ScUi::openWindow), + JS_CFUNC_DEF("closeWindows", 2, ScUi::closeWindows), + JS_CFUNC_DEF("closeAllWindows", 0, ScUi::closeAllWindows), + JS_CFUNC_DEF("getWindow", 1, ScUi::getWindow), + JS_CFUNC_DEF("showError", 2, ScUi::showError), + JS_CFUNC_DEF("showTextInput", 1, ScUi::showTextInput), + JS_CFUNC_DEF("showFileBrowse", 1, ScUi::showFileBrowse), + JS_CFUNC_DEF("showScenarioSelect", 1, ScUi::showScenarioSelect), + JS_CFUNC_DEF("activateTool", 1, ScUi::activateTool), + JS_CFUNC_DEF("registerMenuItem", 2, ScUi::registerMenuItem), + JS_CFUNC_DEF("registerToolboxMenuItem", 2, ScUi::registerToolboxMenuItem), + JS_CFUNC_DEF("registerShortcut", 1, ScUi::registerShortcut), + }; + public: - static void Register(duk_context* ctx) + JSValue New(JSContext* ctx, ScriptEngine* scriptEngine) { - dukglue_register_property(ctx, &ScUi::height_get, nullptr, "height"); - dukglue_register_property(ctx, &ScUi::width_get, nullptr, "width"); - dukglue_register_property(ctx, &ScUi::windows_get, nullptr, "windows"); - dukglue_register_property(ctx, &ScUi::mainViewport_get, nullptr, "mainViewport"); - dukglue_register_property(ctx, &ScUi::tileSelection_get, nullptr, "tileSelection"); - dukglue_register_property(ctx, &ScUi::tool_get, nullptr, "tool"); - dukglue_register_property(ctx, &ScUi::imageManager_get, nullptr, "imageManager"); - dukglue_register_method(ctx, &ScUi::openWindow, "openWindow"); - dukglue_register_method(ctx, &ScUi::closeWindows, "closeWindows"); - dukglue_register_method(ctx, &ScUi::closeAllWindows, "closeAllWindows"); - dukglue_register_method(ctx, &ScUi::getWindow, "getWindow"); - dukglue_register_method(ctx, &ScUi::showError, "showError"); - dukglue_register_method(ctx, &ScUi::showTextInput, "showTextInput"); - dukglue_register_method(ctx, &ScUi::showFileBrowse, "showFileBrowse"); - dukglue_register_method(ctx, &ScUi::showScenarioSelect, "showScenarioSelect"); - dukglue_register_method(ctx, &ScUi::activateTool, "activateTool"); - dukglue_register_method(ctx, &ScUi::registerMenuItem, "registerMenuItem"); - dukglue_register_method(ctx, &ScUi::registerToolboxMenuItem, "registerToolboxMenuItem"); - dukglue_register_method(ctx, &ScUi::registerShortcut, "registerShortcut"); + return MakeWithOpaque(ctx, funcs, scriptEngine); + } + + void Register(JSContext* ctx) + { + RegisterBaseStr(ctx, "Ui"); } private: - WindowClass GetClassification(const std::string& key) const + static WindowClass GetClassification(const std::string& key) { return WindowClass::null; } - DukValue GetScenarioFile(std::string_view path) + static JSValue GetScenarioFile(JSContext* ctx, std::string_view path) { - auto ctx = _scriptEngine.GetContext(); - DukObject obj(ctx); - obj.Set("path", path); + JSValue obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "path", JSFromStdString(ctx, path)); auto* scenarioRepo = GetScenarioRepository(); auto entry = scenarioRepo->GetByPath(std::string(path).c_str()); if (entry != nullptr) { - obj.Set("id", entry->ScenarioId); - obj.Set("category", ToDuk(ctx, entry->Category)); - obj.Set("sourceGame", ToDuk(ctx, entry->SourceGame)); - obj.Set("internalName", entry->InternalName); - obj.Set("name", entry->Name); - obj.Set("details", entry->Details); + JS_SetPropertyStr(ctx, obj, "id", JS_NewInt32(ctx, entry->ScenarioId)); + JS_SetPropertyStr(ctx, obj, "category", ScenarioCategoryToJS(ctx, entry->Category)); + JS_SetPropertyStr(ctx, obj, "sourceGame", ScenarioSourceToJS(ctx, entry->SourceGame)); + JS_SetPropertyStr(ctx, obj, "internalName", JSFromStdString(ctx, entry->InternalName)); + JS_SetPropertyStr(ctx, obj, "name", JSFromStdString(ctx, entry->Name)); + JS_SetPropertyStr(ctx, obj, "details", JSFromStdString(ctx, entry->Details)); auto* highscore = entry->Highscore; if (highscore == nullptr) { - obj.Set("highscore", nullptr); + JS_SetPropertyStr(ctx, obj, "highscore", JS_NULL); } else { - DukObject dukHighscore(ctx); - dukHighscore.Set("name", highscore->name); - dukHighscore.Set("companyValue", highscore->company_value); - obj.Set("highscore", dukHighscore.Take()); + JSValue jsHighscore = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, jsHighscore, "name", JSFromStdString(ctx, highscore->name)); + JS_SetPropertyStr(ctx, jsHighscore, "companyValue", JS_NewInt64(ctx, highscore->company_value)); + JS_SetPropertyStr(ctx, obj, "highscore", jsHighscore); } } - return obj.Take(); + return obj; } }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2-ui/scripting/ScViewport.hpp b/src/openrct2-ui/scripting/ScViewport.hpp index 41b7d6d47d..a51c9d9726 100644 --- a/src/openrct2-ui/scripting/ScViewport.hpp +++ b/src/openrct2-ui/scripting/ScViewport.hpp @@ -13,252 +13,287 @@ #include "../interface/Window.h" - #include #include #include - #include #include #include #include namespace OpenRCT2::Scripting { - class ScViewport + class ScViewport; + extern ScViewport gScViewport; + class ScViewport final : public ScBase { private: - WindowClass _class{}; - WindowNumber _number{}; - - public: - ScViewport(WindowClass c, WindowNumber n = 0) - : _class(c) - , _number(n) + struct OpaqueWindowData { + WindowClass _class{}; + WindowNumber _number{}; + }; + + static JSValue left_get(JSContext* ctx, JSValue thisVal) + { + auto viewport = GetViewport(thisVal); + if (viewport != nullptr) + { + return JS_NewInt32(ctx, viewport->viewPos.x); + } + return JS_NewInt32(ctx, 0); + } + static JSValue left_set(JSContext* ctx, JSValue thisVal, JSValue value) + { + JS_UNPACK_INT32(valueInt, ctx, value); + + WindowBase* w; + auto viewport = GetViewport(thisVal, &w); + if (w != nullptr && viewport != nullptr) + { + SetViewLeftTop(w, viewport, valueInt, viewport->viewPos.y); + } + return JS_UNDEFINED; } - private: - int32_t left_get() const + static JSValue top_get(JSContext* ctx, JSValue thisVal) { - auto viewport = GetViewport(); + auto viewport = GetViewport(thisVal); if (viewport != nullptr) { - return viewport->viewPos.x; + return JS_NewInt32(ctx, viewport->viewPos.y); } - return 0; + return JS_NewInt32(ctx, 0); } - void left_set(int32_t value) + static JSValue top_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto viewport = GetViewport(); - if (viewport != nullptr) + JS_UNPACK_INT32(valueInt, ctx, value); + + WindowBase* w; + auto viewport = GetViewport(thisVal, &w); + if (w != nullptr && viewport != nullptr) { - SetViewLeftTop(value, viewport->viewPos.y); + SetViewLeftTop(w, viewport, viewport->viewPos.x, valueInt); } + return JS_UNDEFINED; } - int32_t top_get() const + static JSValue right_get(JSContext* ctx, JSValue thisVal) { - auto viewport = GetViewport(); + auto viewport = GetViewport(thisVal); if (viewport != nullptr) { - return viewport->viewPos.y; + return JS_NewInt32(ctx, viewport->viewPos.x + viewport->ViewWidth()); } - return 0; + return JS_NewInt32(ctx, 0); } - void top_set(int32_t value) + static JSValue right_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto viewport = GetViewport(); - if (viewport != nullptr) + JS_UNPACK_INT32(valueInt, ctx, value); + + WindowBase* w; + auto viewport = GetViewport(thisVal, &w); + if (w != nullptr && viewport != nullptr) { - SetViewLeftTop(viewport->viewPos.x, value); + SetViewLeftTop(w, viewport, valueInt - viewport->ViewWidth(), viewport->viewPos.y); } + return JS_UNDEFINED; } - int32_t right_get() const + static JSValue bottom_get(JSContext* ctx, JSValue thisVal) { - auto viewport = GetViewport(); + auto viewport = GetViewport(thisVal); if (viewport != nullptr) { - return viewport->viewPos.x + viewport->ViewWidth(); + return JS_NewInt32(ctx, viewport->viewPos.y + viewport->ViewHeight()); } - return 0; + return JS_NewInt32(ctx, 0); } - void right_set(int32_t value) + static JSValue bottom_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto viewport = GetViewport(); - if (viewport != nullptr) + JS_UNPACK_INT32(valueInt, ctx, value); + + WindowBase* w; + auto viewport = GetViewport(thisVal, &w); + if (w != nullptr && viewport != nullptr) { - SetViewLeftTop(value - viewport->ViewWidth(), viewport->viewPos.y); + SetViewLeftTop(w, viewport, viewport->viewPos.x, valueInt - viewport->ViewHeight()); } + return JS_UNDEFINED; } - int32_t bottom_get() const + static JSValue rotation_get(JSContext* ctx, JSValue thisVal) { - auto viewport = GetViewport(); + auto viewport = GetViewport(thisVal); if (viewport != nullptr) { - return viewport->viewPos.y + viewport->ViewHeight(); + return JS_NewInt32(ctx, viewport->rotation); } - return 0; + return JS_NewInt32(ctx, 0); } - void bottom_set(int32_t value) + static JSValue rotation_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto viewport = GetViewport(); - if (viewport != nullptr) - { - SetViewLeftTop(viewport->viewPos.x, value - viewport->ViewHeight()); - } - } + JS_UNPACK_INT32(valueInt, ctx, value); - int32_t rotation_get() const - { - auto viewport = GetViewport(); - if (viewport != nullptr) + if (valueInt >= 0 && valueInt < 4) { - return viewport->rotation; - } - return 0; - } - void rotation_set(int32_t value) - { - if (value >= 0 && value < 4) - { - auto w = GetWindow(); + auto w = GetWindow(thisVal); if (w != nullptr) { - while (w->viewport->rotation != value) + while (w->viewport->rotation != valueInt) { ViewportRotateSingle(w, 1); } } } + return JS_UNDEFINED; } - int32_t zoom_get() const + static JSValue zoom_get(JSContext* ctx, JSValue thisVal) { - auto viewport = GetViewport(); + auto viewport = GetViewport(thisVal); if (viewport != nullptr) { - return static_cast(viewport->zoom); + return JS_NewInt32(ctx, static_cast(viewport->zoom)); } - return 0; + return JS_NewInt32(ctx, 0); } - void zoom_set(int32_t value) + static JSValue zoom_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto w = GetWindow(); + JS_UNPACK_INT32(valueInt, ctx, value); + + auto w = GetWindow(thisVal); if (w != nullptr) { - auto i8Value = static_cast(value); + auto i8Value = static_cast(valueInt); WindowZoomSet(*w, ZoomLevel{ i8Value }, false); } + return JS_UNDEFINED; } - uint32_t visibilityFlags_get() const + static JSValue visibilityFlags_get(JSContext* ctx, JSValue thisVal) { - auto viewport = GetViewport(); + auto viewport = GetViewport(thisVal); if (viewport != nullptr) { - return viewport->flags; + return JS_NewInt64(ctx, viewport->flags); } - return 0; + return JS_NewInt32(ctx, 0); } - void visibilityFlags_set(uint32_t value) + static JSValue visibilityFlags_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto w = GetWindow(); + JS_UNPACK_UINT32(valueUint, ctx, value) + + auto w = GetWindow(thisVal); if (w != nullptr) { auto viewport = w->viewport; if (viewport != nullptr) { - if (viewport->flags != value) + if (viewport->flags != valueUint) { - viewport->flags = value; + viewport->flags = valueUint; w->invalidate(); } } } + return JS_UNDEFINED; } - DukValue getCentrePosition() const + static JSValue getCentrePosition(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto viewport = GetViewport(); + auto viewport = GetViewport(thisVal); if (viewport != nullptr) { auto centre = viewport->viewPos + ScreenCoordsXY{ viewport->ViewWidth() / 2, viewport->ViewHeight() / 2 }; auto coords = ViewportPosToMapPos(centre, 24, viewport->rotation); - auto ctx = GetContext()->GetScriptEngine().GetContext(); - auto obj = duk_push_object(ctx); - duk_push_number(ctx, coords.x); - duk_put_prop_string(ctx, obj, "x"); - duk_push_number(ctx, coords.y); - duk_put_prop_string(ctx, obj, "y"); - return DukValue::take_from_stack(ctx); + JSValue obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "x", JS_NewInt32(ctx, coords.x)); + JS_SetPropertyStr(ctx, obj, "y", JS_NewInt32(ctx, coords.y)); + return obj; } - return {}; + return JS_UNDEFINED; } - void moveTo(DukValue position) + static JSValue moveTo(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto w = GetWindow(); + auto w = GetWindow(thisVal); if (w != nullptr) { auto viewport = w->viewport; if (viewport != nullptr) { - auto coords = GetCoordsFromObject(position); + auto coords = GetCoordsFromObject(ctx, argv[0]); if (coords) { auto screenCoords = Translate3DTo2DWithZ(viewport->rotation, *coords); auto left = screenCoords.x - (viewport->ViewWidth() / 2); auto top = screenCoords.y - (viewport->ViewHeight() / 2); - SetViewLeftTop(left, top); + SetViewLeftTop(w, viewport, left, top); } } } + return JS_UNDEFINED; } - void scrollTo(DukValue position) + static JSValue scrollTo(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto w = GetWindow(); + auto w = GetWindow(thisVal); if (w != nullptr) { - auto coords = GetCoordsFromObject(position); + auto coords = GetCoordsFromObject(ctx, argv[0]); if (coords) { WindowScrollToLocation(*w, *coords); } } + return JS_UNDEFINED; } public: - static void Register(duk_context* ctx) + JSValue New(JSContext* ctx, WindowClass wClass, WindowNumber wNum = 0) { - dukglue_register_property(ctx, &ScViewport::left_get, &ScViewport::left_set, "left"); - dukglue_register_property(ctx, &ScViewport::top_get, &ScViewport::top_set, "top"); - dukglue_register_property(ctx, &ScViewport::right_get, &ScViewport::right_set, "right"); - dukglue_register_property(ctx, &ScViewport::bottom_get, &ScViewport::bottom_set, "bottom"); - dukglue_register_property(ctx, &ScViewport::rotation_get, &ScViewport::rotation_set, "rotation"); - dukglue_register_property(ctx, &ScViewport::zoom_get, &ScViewport::zoom_set, "zoom"); - dukglue_register_property( - ctx, &ScViewport::visibilityFlags_get, &ScViewport::visibilityFlags_set, "visibilityFlags"); - dukglue_register_method(ctx, &ScViewport::getCentrePosition, "getCentrePosition"); - dukglue_register_method(ctx, &ScViewport::moveTo, "moveTo"); - dukglue_register_method(ctx, &ScViewport::scrollTo, "scrollTo"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("left", ScViewport::left_get, ScViewport::left_set), + JS_CGETSET_DEF("top", ScViewport::top_get, ScViewport::top_set), + JS_CGETSET_DEF("right", ScViewport::right_get, ScViewport::right_set), + JS_CGETSET_DEF("bottom", ScViewport::bottom_get, ScViewport::bottom_set), + JS_CGETSET_DEF("rotation", ScViewport::rotation_get, ScViewport::rotation_set), + JS_CGETSET_DEF("zoom", ScViewport::zoom_get, ScViewport::zoom_set), + JS_CGETSET_DEF("visibilityFlags", ScViewport::visibilityFlags_get, ScViewport::visibilityFlags_set), + JS_CFUNC_DEF("getCentrePosition", 0, ScViewport::getCentrePosition), + JS_CFUNC_DEF("moveTo", 1, ScViewport::moveTo), + JS_CFUNC_DEF("scrollTo", 1, ScViewport::scrollTo), + }; + return MakeWithOpaque(ctx, funcs, new OpaqueWindowData{ wClass, wNum }); + } + + void Register(JSContext* ctx) + { + RegisterBaseStr(ctx, "Viewport", Finalize); + } + + static void Finalize(JSRuntime* rt, JSValue thisVal) + { + OpaqueWindowData* data = gScViewport.GetOpaque(thisVal); + if (data) + delete data; } private: - WindowBase* GetWindow() const + static WindowBase* GetWindow(JSValue thisVal) { - if (_class == WindowClass::mainWindow) - return WindowGetMain(); - + OpaqueWindowData* data = gScViewport.GetOpaque(thisVal); + if (!data) + return nullptr; auto* windowMgr = Ui::GetWindowManager(); - return windowMgr->FindByNumber(_class, _number); + return windowMgr->FindByNumber(data->_class, data->_number); } - Viewport* GetViewport() const + static Viewport* GetViewport(JSValue thisVal, WindowBase** wOut = nullptr) { - auto w = GetWindow(); + auto w = GetWindow(thisVal); + if (wOut) + *wOut = w; if (w != nullptr) { return w->viewport; @@ -266,42 +301,35 @@ namespace OpenRCT2::Scripting return nullptr; } - void SetViewLeftTop(int32_t left, int32_t top) + static void SetViewLeftTop(WindowBase* w, Viewport* viewport, int32_t left, int32_t top) { - auto w = GetWindow(); - if (w != nullptr) + if (viewport->viewPos != ScreenCoordsXY(left, top)) { - auto viewport = w->viewport; - if (viewport != nullptr && viewport->viewPos != ScreenCoordsXY(left, top)) - { - viewport->viewPos.x = left; - viewport->viewPos.y = top; - w->flags.unset(WindowFlag::scrollingToLocation); - w->savedViewPos.x = viewport->viewPos.x; - w->savedViewPos.y = viewport->viewPos.y; - viewport->Invalidate(); - } + viewport->viewPos.x = left; + viewport->viewPos.y = top; + w->flags.unset(WindowFlag::scrollingToLocation); + w->savedViewPos.x = viewport->viewPos.x; + w->savedViewPos.y = viewport->viewPos.y; + viewport->Invalidate(); } } - std::optional GetCoordsFromObject(DukValue position) const + static std::optional GetCoordsFromObject(JSContext* ctx, JSValue position) { - if (position.type() == DukValue::Type::OBJECT) + if (JS_IsObject(position)) { - auto dukX = position["x"]; - auto dukY = position["y"]; - auto dukZ = position["z"]; - if (dukX.type() == DukValue::Type::NUMBER && dukY.type() == DukValue::Type::NUMBER) + auto x = JSToOptionalInt(ctx, position, "x"); + auto y = JSToOptionalInt(ctx, position, "y"); + auto z = JSToOptionalInt(ctx, position, "z"); + if (x.has_value() && y.has_value()) { - auto x = dukX.as_int(); - auto y = dukY.as_int(); - if (dukZ.type() == DukValue::Type::NUMBER) + if (z.has_value()) { - return CoordsXYZ(x, y, dukZ.as_int()); + return CoordsXYZ(x.value(), y.value(), z.value()); } - auto z = TileElementHeight(CoordsXY(x, y)); - return CoordsXYZ(x, y, z); + auto zTile = TileElementHeight(CoordsXY(x.value(), y.value())); + return CoordsXYZ(x.value(), y.value(), zTile); } } return std::nullopt; diff --git a/src/openrct2-ui/scripting/ScWidget.hpp b/src/openrct2-ui/scripting/ScWidget.hpp index 53caa21c75..e29fd36679 100644 --- a/src/openrct2-ui/scripting/ScWidget.hpp +++ b/src/openrct2-ui/scripting/ScWidget.hpp @@ -15,131 +15,152 @@ #include "CustomListView.h" #include "CustomWindow.h" #include "ScViewport.hpp" + #include "ScWindow.h" #include #include - #include #include #include #include namespace OpenRCT2::Scripting { - class ScWindow; - class ScWidget + inline uint32_t ImageFromJSValue(JSContext* ctx, JSValue value) + { + uint32_t img{}; + if (JS_IsNumber(value)) + { + JS_ToUint32(ctx, &img, value); + if (GetTargetAPIVersion() <= kApiVersionG2Reorder) + { + img = NewIconIndex(img); + } + } + else if (JS_IsString(value)) + { + img = GetIconByName(JSToStdString(ctx, value)); + } + return img; + } + + class ScWidget; + extern ScWidget gScWidget; + + struct WidgetData { - protected: WindowClass _class{}; WindowNumber _number{}; WidgetIndex _widgetIndex{}; + }; - public: - ScWidget(WindowClass c, WindowNumber n, WidgetIndex widgetIndex) - : _class(c) - , _number(n) - , _widgetIndex(widgetIndex) - { - } - - static DukValue ToDukValue(duk_context* ctx, WindowBase* w, WidgetIndex widgetIndex); - + class ScWidget : public ScBase + { private: - std::shared_ptr window_get() const; - - std::string name_get() const + static JSValue window_get(JSContext* ctx, JSValue thisVal) { - auto w = GetWindow(); - if (w != nullptr && IsCustomWindow()) - { - return Ui::Windows::GetWidgetName(w, _widgetIndex); - } - return {}; + WidgetData* data = gScWidget.GetOpaque(thisVal); + return gScWindow.New(ctx, data->_class, data->_number); } - void name_set(const std::string& value) + static JSValue name_get(JSContext* ctx, JSValue thisVal) { - auto w = GetWindow(); - if (w != nullptr && IsCustomWindow()) + WidgetData* data = gScWidget.GetOpaque(thisVal); + auto w = GetWindow(data->_class, data->_number); + if (w != nullptr && IsCustomWindow(w)) { - Ui::Windows::SetWidgetName(w, _widgetIndex, value); + return JSFromStdString(ctx, GetWidgetName(w, data->_widgetIndex)); } + return JSFromStdString(ctx, ""); } - std::string type_get() const + static JSValue name_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto widget = GetWidget(); + WidgetData* data = gScWidget.GetOpaque(thisVal); + auto w = GetWindow(data->_class, data->_number); + if (w != nullptr && IsCustomWindow(w)) + { + SetWidgetName(w, data->_widgetIndex, JSToStdString(ctx, value)); + } + return JS_UNDEFINED; + } + + static JSValue type_get(JSContext* ctx, JSValue thisVal) + { + auto widget = GetWidget(thisVal); if (widget != nullptr) { switch (widget->type) { case WidgetType::frame: - return "frame"; + return JSFromStdString(ctx, "frame"); case WidgetType::resize: - return "resize"; + return JSFromStdString(ctx, "resize"); case WidgetType::imgBtn: case WidgetType::trnBtn: case WidgetType::flatBtn: case WidgetType::hiddenButton: case WidgetType::button: case WidgetType::closeBox: - return "button"; + return JSFromStdString(ctx, "button"); case WidgetType::colourBtn: - return "colourpicker"; + return JSFromStdString(ctx, "colourpicker"); case WidgetType::tab: - return "tab"; + return JSFromStdString(ctx, "tab"); case WidgetType::labelCentred: case WidgetType::label: - return "label"; + return JSFromStdString(ctx, "label"); case WidgetType::tableHeader: - return "table_header"; + return JSFromStdString(ctx, "table_header"); case WidgetType::spinner: - return "spinner"; + return JSFromStdString(ctx, "spinner"); case WidgetType::dropdownMenu: - return "dropdown"; + return JSFromStdString(ctx, "dropdown"); case WidgetType::viewport: - return "viewport"; + return JSFromStdString(ctx, "viewport"); case WidgetType::groupbox: - return "groupbox"; + return JSFromStdString(ctx, "groupbox"); case WidgetType::caption: - return "caption"; + return JSFromStdString(ctx, "caption"); case WidgetType::scroll: - return "listview"; + return JSFromStdString(ctx, "listview"); case WidgetType::checkbox: - return "checkbox"; + return JSFromStdString(ctx, "checkbox"); case WidgetType::textBox: - return "textbox"; + return JSFromStdString(ctx, "textbox"); case WidgetType::empty: - return "empty"; + return JSFromStdString(ctx, "empty"); case WidgetType::placeholder: - return "placeholder"; + return JSFromStdString(ctx, "placeholder"); case WidgetType::progressBar: - return "progress_bar"; + return JSFromStdString(ctx, "progress_bar"); case WidgetType::horizontalSeparator: - return "horizontal_separator"; + return JSFromStdString(ctx, "horizontal_separator"); case WidgetType::custom: - return "custom"; + return JSFromStdString(ctx, "custom"); } } - return "unknown"; + return JSFromStdString(ctx, "unknown"); } - int32_t x_get() const + static JSValue x_get(JSContext* ctx, JSValue thisVal) { - auto widget = GetWidget(); + auto widget = GetWidget(thisVal); if (widget != nullptr) { - return widget->left; + return JS_NewInt32(ctx, widget->left); } - return 0; + return JS_NewInt32(ctx, 0); } - void x_set(int32_t value) + static JSValue x_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto widget = GetWidget(); + JS_UNPACK_INT32(valueInt, ctx, value); + + auto widget = GetWidget(thisVal); if (widget != nullptr) { - auto delta = value - widget->left; + auto delta = valueInt - widget->left; - Invalidate(); + Invalidate(thisVal); widget->left += delta; widget->right += delta; @@ -159,30 +180,33 @@ namespace OpenRCT2::Scripting downWidget->right += delta; } - Invalidate(widget); + Invalidate(thisVal, widget); } + return JS_UNDEFINED; } - int32_t y_get() const + static JSValue y_get(JSContext* ctx, JSValue thisVal) { - auto widget = GetWidget(); + auto widget = GetWidget(thisVal); if (widget != nullptr) { - auto w = GetWindow(); - return widget->top - w->getTitleBarDiffNormal(); + auto w = GetWindow(thisVal); + return JS_NewInt32(ctx, widget->top - w->getTitleBarDiffNormal()); } - return 0; + return JS_NewInt32(ctx, 0); } - void y_set(int32_t value) + static JSValue y_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto widget = GetWidget(); + JS_UNPACK_INT32(valueInt, ctx, value); + + auto widget = GetWidget(thisVal); if (widget != nullptr) { - auto w = GetWindow(); - value += w->getTitleBarDiffNormal(); - auto delta = value - widget->top; + auto w = GetWindow(thisVal); + valueInt += w->getTitleBarDiffNormal(); + auto delta = valueInt - widget->top; - Invalidate(); + Invalidate(thisVal); widget->top += delta; widget->bottom += delta; @@ -202,27 +226,30 @@ namespace OpenRCT2::Scripting downWidget->bottom += delta; } - Invalidate(widget); + Invalidate(thisVal, widget); } + return JS_UNDEFINED; } - int32_t width_get() const + static JSValue width_get(JSContext* ctx, JSValue thisVal) { - auto widget = GetWidget(); + auto widget = GetWidget(thisVal); if (widget != nullptr) { - return widget->width(); + return JS_NewInt32(ctx, widget->width()); } - return 0; + return JS_NewInt32(ctx, 0); } - void width_set(int32_t value) + static JSValue width_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto widget = GetWidget(); + JS_UNPACK_INT32(valueInt, ctx, value); + + auto widget = GetWidget(thisVal); if (widget != nullptr) { - auto delta = widget->left + value - (widget->right + 1); + auto delta = widget->left + valueInt - (widget->right + 1); - Invalidate(); + Invalidate(thisVal); widget->right += delta; if (widget->type == WidgetType::dropdownMenu) @@ -241,27 +268,30 @@ namespace OpenRCT2::Scripting downWidget->right += delta; } - Invalidate(widget); + Invalidate(thisVal, widget); } + return JS_UNDEFINED; } - int32_t height_get() const + static JSValue height_get(JSContext* ctx, JSValue thisVal) { - auto widget = GetWidget(); + auto widget = GetWidget(thisVal); if (widget != nullptr) { - return widget->height(); + return JS_NewInt32(ctx, widget->height()); } - return 0; + return JS_NewInt32(ctx, 0); } - void height_set(int32_t value) + static JSValue height_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto widget = GetWidget(); + JS_UNPACK_INT32(valueInt, ctx, value); + + auto widget = GetWidget(thisVal); if (widget != nullptr) { - auto delta = widget->top + value - (widget->bottom + 1); + auto delta = widget->top + valueInt - (widget->bottom + 1); - Invalidate(); + Invalidate(thisVal); widget->bottom += delta; if (widget->type == WidgetType::dropdownMenu) @@ -277,143 +307,207 @@ namespace OpenRCT2::Scripting downWidget->bottom += delta; } - Invalidate(widget); + Invalidate(thisVal, widget); } + return JS_UNDEFINED; } - std::string tooltip_get() const + static JSValue tooltip_get(JSContext* ctx, JSValue thisVal) { - auto w = GetWindow(); - if (w != nullptr && IsCustomWindow()) + WidgetData* data = gScWidget.GetOpaque(thisVal); + auto w = GetWindow(data->_class, data->_number); + if (w != nullptr && IsCustomWindow(w)) { - return Ui::Windows::GetWidgetTooltip(w, _widgetIndex); + return JSFromStdString(ctx, GetWidgetTooltip(w, data->_widgetIndex)); } - return {}; + return JSFromStdString(ctx, {}); } - void tooltip_set(const std::string& value) + static JSValue tooltip_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto w = GetWindow(); - if (w != nullptr && IsCustomWindow()) + JS_UNPACK_STR(valueStr, ctx, value); + + WidgetData* data = gScWidget.GetOpaque(thisVal); + auto w = GetWindow(data->_class, data->_number); + if (w != nullptr && IsCustomWindow(w)) { - Ui::Windows::SetWidgetTooltip(w, _widgetIndex, value); + SetWidgetTooltip(w, data->_widgetIndex, valueStr); } + return JS_UNDEFINED; } - bool isDisabled_get() const + static JSValue isDisabled_get(JSContext* ctx, JSValue thisVal) { - auto w = GetWindow(); + WidgetData* data = gScWidget.GetOpaque(thisVal); + auto w = GetWindow(data->_class, data->_number); if (w != nullptr) { - return Ui::widgetIsDisabled(*w, _widgetIndex); + return JS_NewBool(ctx, Ui::widgetIsDisabled(*w, data->_widgetIndex)); } - return false; + return JS_NewBool(ctx, false); } - void isDisabled_set(bool value) + static JSValue isDisabled_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto w = GetWindow(); + JS_UNPACK_BOOL(valueBool, ctx, value); + + WidgetData* data = gScWidget.GetOpaque(thisVal); + auto w = GetWindow(data->_class, data->_number); if (w != nullptr) { - Ui::widgetSetDisabled(*w, _widgetIndex, value); + Ui::widgetSetDisabled(*w, data->_widgetIndex, valueBool); - auto widget = GetWidget(); + auto widget = GetWidget(w, data->_widgetIndex); if (widget != nullptr) { if (widget->type == WidgetType::dropdownMenu) { - Ui::widgetSetDisabled(*w, _widgetIndex + 1, value); + Ui::widgetSetDisabled(*w, data->_widgetIndex + 1, valueBool); } else if (widget->type == WidgetType::spinner) { - Ui::widgetSetDisabled(*w, _widgetIndex + 1, value); - Ui::widgetSetDisabled(*w, _widgetIndex + 2, value); + Ui::widgetSetDisabled(*w, data->_widgetIndex + 1, valueBool); + Ui::widgetSetDisabled(*w, data->_widgetIndex + 2, valueBool); } } - Invalidate(widget); + Invalidate(thisVal, widget); } + return JS_UNDEFINED; } - bool isVisible_get() const + static JSValue isVisible_get(JSContext* ctx, JSValue thisVal) { - auto w = GetWindow(); + WidgetData* data = gScWidget.GetOpaque(thisVal); + auto w = GetWindow(data->_class, data->_number); if (w != nullptr) { - return Ui::widgetIsVisible(*w, _widgetIndex); + return JS_NewBool(ctx, Ui::widgetIsVisible(*w, data->_widgetIndex)); } - return false; + return JS_NewBool(ctx, false); } - void isVisible_set(bool value) + static JSValue isVisible_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto w = GetWindow(); + JS_UNPACK_BOOL(valueBool, ctx, value); + + WidgetData* data = gScWidget.GetOpaque(thisVal); + auto w = GetWindow(data->_class, data->_number); if (w != nullptr) { - Ui::widgetSetVisible(*w, _widgetIndex, value); + Ui::widgetSetVisible(*w, data->_widgetIndex, valueBool); - auto widget = GetWidget(); + auto widget = GetWidget(w, data->_widgetIndex); if (widget != nullptr) { if (widget->type == WidgetType::dropdownMenu) { - Ui::widgetSetVisible(*w, _widgetIndex + 1, value); + Ui::widgetSetVisible(*w, data->_widgetIndex + 1, valueBool); } else if (widget->type == WidgetType::spinner) { - Ui::widgetSetVisible(*w, _widgetIndex + 1, value); - Ui::widgetSetVisible(*w, _widgetIndex + 2, value); + Ui::widgetSetVisible(*w, data->_widgetIndex + 1, valueBool); + Ui::widgetSetVisible(*w, data->_widgetIndex + 2, valueBool); } } - Invalidate(widget); + Invalidate(thisVal, widget); } + return JS_UNDEFINED; } protected: - std::string text_get() const + static JSValue text_get(JSContext* ctx, JSValue thisVal) { - if (IsCustomWindow()) + WidgetData* data = gScWidget.GetOpaque(thisVal); + auto w = GetWindow(data->_class, data->_number); + if (IsCustomWindow(w)) { - auto widget = GetWidget(); + auto widget = GetWidget(w, data->_widgetIndex); if (widget != nullptr && widget->flags.has(WidgetFlag::textIsString) && widget->string != nullptr) { - return widget->string; + return JSFromStdString(ctx, widget->string); } } - return {}; + return JSFromStdString(ctx, ""); } - void text_set(std::string value) + static JSValue text_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto w = GetWindow(); - if (w != nullptr && IsCustomWindow()) + JS_UNPACK_STR(valueStr, ctx, value); + + WidgetData* data = gScWidget.GetOpaque(thisVal); + auto w = GetWindow(data->_class, data->_number); + if (w != nullptr && IsCustomWindow(w)) { - Ui::Windows::UpdateWidgetText(w, _widgetIndex, value); + UpdateWidgetText(w, data->_widgetIndex, valueStr); } + return JS_UNDEFINED; } + private: + static constexpr JSCFunctionListEntry _funcs[] = { + JS_CGETSET_DEF("window", ScWidget::window_get, nullptr), + JS_CGETSET_DEF("name", ScWidget::name_get, ScWidget::name_set), + JS_CGETSET_DEF("type", ScWidget::type_get, nullptr), + JS_CGETSET_DEF("x", ScWidget::x_get, ScWidget::x_set), + JS_CGETSET_DEF("y", ScWidget::y_get, ScWidget::y_set), + JS_CGETSET_DEF("width", ScWidget::width_get, ScWidget::width_set), + JS_CGETSET_DEF("height", ScWidget::height_get, &ScWidget::height_set), + JS_CGETSET_DEF("tooltip", ScWidget::tooltip_get, &ScWidget::tooltip_set), + JS_CGETSET_DEF("isDisabled", ScWidget::isDisabled_get, &ScWidget::isDisabled_set), + JS_CGETSET_DEF("isVisible", ScWidget::isVisible_get, &ScWidget::isVisible_set), + }; + public: - static void Register(duk_context* ctx); + JSValue New(JSContext* ctx, WindowBase* w, WidgetIndex widgetIndex); + + void Register(JSContext* ctx) + { + RegisterBaseStr(ctx, "Widget", Finalize); + } + + private: + static void Finalize(JSRuntime* rt, JSValue thisVal) + { + WidgetData* data = gScWidget.GetOpaque(thisVal); + if (data) + delete data; + } protected: - WindowBase* GetWindow() const + static WidgetData GetWidgetData(JSValue thisVal) { - if (_class == WindowClass::mainWindow) + return *gScWidget.GetOpaque(thisVal); + } + + static WindowBase* GetWindow(const WindowClass wClass, const WindowNumber number) + { + if (wClass == WindowClass::mainWindow) return WindowGetMain(); auto* windowMgr = Ui::GetWindowManager(); - return windowMgr->FindByNumber(_class, _number); + return windowMgr->FindByNumber(wClass, number); } - Widget* GetWidget() const + static WindowBase* GetWindow(JSValue thisVal) + { + WidgetData* data = gScWidget.GetOpaque(thisVal); + return GetWindow(data->_class, data->_number); + } + + static Widget* GetWidget(WindowBase* w, const WidgetIndex wIdx) { - auto w = GetWindow(); if (w != nullptr) { - return &w->widgets[_widgetIndex]; + return &w->widgets[wIdx]; } return nullptr; } - - bool IsCustomWindow() const + static Widget* GetWidget(JSValue thisVal) + { + WidgetData* data = gScWidget.GetOpaque(thisVal); + auto w = GetWindow(data->_class, data->_number); + return GetWidget(w, data->_widgetIndex); + } + + static bool IsCustomWindow(WindowBase* w) { - auto w = GetWindow(); if (w != nullptr) { return w->classification == WindowClass::custom; @@ -421,628 +515,664 @@ namespace OpenRCT2::Scripting return false; } - void Invalidate(const Widget* widget) + static void Invalidate(JSValue thisVal, const Widget* widget) { + WidgetData* data = gScWidget.GetOpaque(thisVal); + auto* windowMgr = Ui::GetWindowManager(); if (widget != nullptr) { - auto* windowMgr = Ui::GetWindowManager(); if (widget->type == WidgetType::dropdownMenu) { - windowMgr->InvalidateWidgetByNumber(_class, _number, _widgetIndex + 1); + windowMgr->InvalidateWidgetByNumber(data->_class, data->_number, data->_widgetIndex + 1); } else if (widget->type == WidgetType::spinner) { - windowMgr->InvalidateWidgetByNumber(_class, _number, _widgetIndex + 1); - windowMgr->InvalidateWidgetByNumber(_class, _number, _widgetIndex + 2); + windowMgr->InvalidateWidgetByNumber(data->_class, data->_number, data->_widgetIndex + 1); + windowMgr->InvalidateWidgetByNumber(data->_class, data->_number, data->_widgetIndex + 2); } } - Invalidate(); + windowMgr->InvalidateWidgetByNumber(data->_class, data->_number, data->_widgetIndex); } - void Invalidate() + static void Invalidate(JSValue thisVal) { + WidgetData* data = gScWidget.GetOpaque(thisVal); auto* windowMgr = Ui::GetWindowManager(); - windowMgr->InvalidateWidgetByNumber(_class, _number, _widgetIndex); + windowMgr->InvalidateWidgetByNumber(data->_class, data->_number, data->_widgetIndex); } }; class ScButtonWidget : public ScWidget { public: - ScButtonWidget(WindowClass c, WindowNumber n, WidgetIndex widgetIndex) - : ScWidget(c, n, widgetIndex) + static void AddFuncs(JSContext* ctx, JSValue obj) { - } - - static void Register(duk_context* ctx) - { - dukglue_set_base_class(ctx); - dukglue_register_property(ctx, &ScButtonWidget::border_get, &ScButtonWidget::border_set, "border"); - dukglue_register_property(ctx, &ScButtonWidget::isPressed_get, &ScButtonWidget::isPressed_set, "isPressed"); - dukglue_register_property(ctx, &ScButtonWidget::image_get, &ScButtonWidget::image_set, "image"); - // Explicit template due to text being a base method - dukglue_register_property( - ctx, &ScButtonWidget::text_get, &ScButtonWidget::text_set, "text"); + // In the future it might be worth properly subclassing the widget type here. + // Not just for this button class but also for all the other widget subclasses. + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("border", ScButtonWidget::border_get, ScButtonWidget::border_set), + JS_CGETSET_DEF("isPressed", ScButtonWidget::isPressed_get, ScButtonWidget::isPressed_set), + JS_CGETSET_DEF("image", ScButtonWidget::image_get, ScButtonWidget::image_set), + JS_CGETSET_DEF("text", ScWidget::text_get, ScWidget::text_set), + }; + JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs)); } private: - bool border_get() const + static JSValue border_get(JSContext* ctx, JSValue thisVal) { - auto widget = GetWidget(); + auto widget = GetWidget(thisVal); if (widget != nullptr) { - return widget->type == WidgetType::imgBtn; + return JS_NewBool(ctx, widget->type == WidgetType::imgBtn); } - return false; + return JS_NewBool(ctx, false); } - void border_set(bool value) + static JSValue border_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto widget = GetWidget(); + JS_UNPACK_BOOL(valueBool, ctx, value); + + auto widget = GetWidget(thisVal); if (widget != nullptr && (widget->type == WidgetType::flatBtn || widget->type == WidgetType::imgBtn)) { - if (value) + if (valueBool) widget->type = WidgetType::imgBtn; else widget->type = WidgetType::flatBtn; - Invalidate(); + Invalidate(thisVal); } + return JS_UNDEFINED; } - bool isPressed_get() const + static JSValue isPressed_get(JSContext* ctx, JSValue thisVal) { - auto w = GetWindow(); + WidgetData data = GetWidgetData(thisVal); + auto w = GetWindow(data._class, data._number); + if (w != nullptr) { - return Ui::widgetIsPressed(*w, _widgetIndex); + return JS_NewBool(ctx, Ui::widgetIsPressed(*w, data._widgetIndex)); } - return false; + return JS_NewBool(ctx, false); } - void isPressed_set(bool value) + static JSValue isPressed_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto w = GetWindow(); + JS_UNPACK_BOOL(valueBool, ctx, value); + + WidgetData data = GetWidgetData(thisVal); + auto w = GetWindow(data._class, data._number); if (w != nullptr) { - Ui::widgetSetCheckboxValue(*w, _widgetIndex, value ? 1 : 0); - Invalidate(); + Ui::widgetSetCheckboxValue(*w, data._widgetIndex, valueBool ? 1 : 0); + Invalidate(thisVal); } + return JS_UNDEFINED; } - uint32_t image_get() const + static JSValue image_get(JSContext* ctx, JSValue thisVal) { - auto widget = GetWidget(); + auto widget = GetWidget(thisVal); if (widget != nullptr && (widget->type == WidgetType::flatBtn || widget->type == WidgetType::imgBtn)) { if (GetTargetAPIVersion() <= kApiVersionG2Reorder) { - return LegacyIconIndex(widget->image.GetIndex()); + return JS_NewUint32(ctx, LegacyIconIndex(widget->image.GetIndex())); } - return widget->image.GetIndex(); + return JS_NewUint32(ctx, widget->image.GetIndex()); } - return 0; + return JS_NewUint32(ctx, 0); } - void image_set(DukValue value) + static JSValue image_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto widget = GetWidget(); + auto widget = GetWidget(thisVal); if (widget != nullptr && (widget->type == WidgetType::flatBtn || widget->type == WidgetType::imgBtn)) { - widget->image = ImageId(ImageFromDuk(value)); - Invalidate(); + widget->image = ImageId(ImageFromJSValue(ctx, value)); + Invalidate(thisVal); } + return JS_UNDEFINED; } }; class ScCheckBoxWidget : public ScWidget { public: - ScCheckBoxWidget(WindowClass c, WindowNumber n, WidgetIndex widgetIndex) - : ScWidget(c, n, widgetIndex) + static void AddFuncs(JSContext* ctx, JSValue obj) { - } - - static void Register(duk_context* ctx) - { - dukglue_set_base_class(ctx); - dukglue_register_property(ctx, &ScCheckBoxWidget::isChecked_get, &ScCheckBoxWidget::isChecked_set, "isChecked"); - // Explicit template due to text being a base method - dukglue_register_property( - ctx, &ScCheckBoxWidget::text_get, &ScCheckBoxWidget::text_set, "text"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("isChecked", ScCheckBoxWidget::isChecked_get, ScCheckBoxWidget::isChecked_set), + JS_CGETSET_DEF("text", ScWidget::text_get, ScWidget::text_set), + }; + JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs)); } private: - bool isChecked_get() const + static JSValue isChecked_get(JSContext* ctx, JSValue thisVal) { - auto w = GetWindow(); + WidgetData data = GetWidgetData(thisVal); + auto w = GetWindow(data._class, data._number); if (w != nullptr) { - return Ui::widgetIsPressed(*w, _widgetIndex); + return JS_NewBool(ctx, Ui::widgetIsPressed(*w, data._widgetIndex)); } - return false; + return JS_NewBool(ctx, false); } - void isChecked_set(bool value) + static JSValue isChecked_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto w = GetWindow(); + JS_UNPACK_BOOL(valueBool, ctx, value); + + WidgetData data = GetWidgetData(thisVal); + auto w = GetWindow(data._class, data._number); if (w != nullptr) { - Ui::widgetSetCheckboxValue(*w, _widgetIndex, value ? 1 : 0); - Invalidate(); + Ui::widgetSetCheckboxValue(*w, data._widgetIndex, valueBool ? 1 : 0); + Invalidate(thisVal); } + return JS_UNDEFINED; } }; class ScColourPickerWidget : public ScWidget { public: - ScColourPickerWidget(WindowClass c, WindowNumber n, WidgetIndex widgetIndex) - : ScWidget(c, n, widgetIndex) + static void AddFuncs(JSContext* ctx, JSValue obj) { - } - - static void Register(duk_context* ctx) - { - dukglue_set_base_class(ctx); - dukglue_register_property(ctx, &ScColourPickerWidget::colour_get, &ScColourPickerWidget::colour_set, "colour"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("colour", ScColourPickerWidget::colour_get, ScColourPickerWidget::colour_set), + }; + JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs)); } private: - uint8_t colour_get() const + static JSValue colour_get(JSContext* ctx, JSValue thisVal) { - auto w = GetWindow(); + WidgetData data = GetWidgetData(thisVal); + auto w = GetWindow(data._class, data._number); if (w != nullptr) { - return EnumValue(GetWidgetColour(w, _widgetIndex)); + return JS_NewInt32(ctx, EnumValue(Ui::Windows::GetWidgetColour(w, data._widgetIndex))); } - return EnumValue(Drawing::Colour::black); + return JS_NewInt32(ctx, EnumValue(Drawing::Colour::black)); } - void colour_set(uint8_t value) + static JSValue colour_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto w = GetWindow(); + JS_UNPACK_INT32(valueInt, ctx, value); + + WidgetData data = GetWidgetData(thisVal); + auto w = GetWindow(data._class, data._number); if (w != nullptr) { - UpdateWidgetColour(w, _widgetIndex, static_cast(value)); - Invalidate(); + UpdateWidgetColour(w, data._widgetIndex, static_cast(valueInt)); + Invalidate(thisVal); } + return JS_UNDEFINED; } }; class ScDropdownWidget : public ScWidget { public: - ScDropdownWidget(WindowClass c, WindowNumber n, WidgetIndex widgetIndex) - : ScWidget(c, n, widgetIndex) + static void AddFuncs(JSContext* ctx, JSValue obj) { - } - - static void Register(duk_context* ctx) - { - dukglue_set_base_class(ctx); - dukglue_register_property(ctx, &ScDropdownWidget::items_get, &ScDropdownWidget::items_set, "items"); - dukglue_register_property( - ctx, &ScDropdownWidget::selectedIndex_get, &ScDropdownWidget::selectedIndex_set, "selectedIndex"); - // Explicit template due to text being a base method - dukglue_register_property( - ctx, &ScDropdownWidget::text_get, &ScDropdownWidget::text_set, "text"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("items", ScDropdownWidget::items_get, ScDropdownWidget::items_set), + JS_CGETSET_DEF("selectedIndex", ScDropdownWidget::selectedIndex_get, ScDropdownWidget::selectedIndex_set), + JS_CGETSET_DEF("text", ScWidget::text_get, ScWidget::text_set), + }; + JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs)); } private: - int32_t selectedIndex_get() const + static JSValue selectedIndex_get(JSContext* ctx, JSValue thisVal) { - auto w = GetWindow(); + WidgetData data = GetWidgetData(thisVal); + auto w = GetWindow(data._class, data._number); if (w != nullptr) { - return GetWidgetSelectedIndex(w, _widgetIndex); + return JS_NewInt32(ctx, Ui::Windows::GetWidgetSelectedIndex(w, data._widgetIndex)); } - return -1; + return JS_NewInt32(ctx, -1); } - void selectedIndex_set(int32_t value) + static JSValue selectedIndex_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto w = GetWindow(); + JS_UNPACK_INT32(valueInt, ctx, value); + + WidgetData data = GetWidgetData(thisVal); + auto w = GetWindow(data._class, data._number); if (w != nullptr) { - UpdateWidgetSelectedIndex(w, _widgetIndex, value); + Ui::Windows::UpdateWidgetSelectedIndex(w, data._widgetIndex, valueInt); } + return JS_UNDEFINED; } - std::vector items_get() const + static JSValue items_get(JSContext* ctx, JSValue thisVal) { - auto w = GetWindow(); + JSValue arr = JS_NewArray(ctx); + WidgetData data = GetWidgetData(thisVal); + auto w = GetWindow(data._class, data._number); if (w != nullptr) { - return GetWidgetItems(w, _widgetIndex); + auto items = Ui::Windows::GetWidgetItems(w, data._widgetIndex); + for (size_t i = 0; i < items.size(); i++) + { + JS_SetPropertyInt64(ctx, arr, i, JSFromStdString(ctx, items[i])); + } } - return {}; + return arr; } - void items_set(const std::vector& value) + static JSValue items_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto w = GetWindow(); + if (!JS_IsArray(value)) + { + JS_ThrowTypeError(ctx, "Expected array"); + return JS_EXCEPTION; + } + + WidgetData data = GetWidgetData(thisVal); + auto w = GetWindow(data._class, data._number); if (w != nullptr) { - UpdateWidgetItems(w, _widgetIndex, value); + std::vector items; + JSIterateArray( + ctx, value, [&items](JSContext* ctx2, JSValue val) { items.push_back(JSToStdString(ctx2, val)); }); + Ui::Windows::UpdateWidgetItems(w, data._widgetIndex, items); } + return JS_UNDEFINED; } }; class ScGroupBoxWidget : public ScWidget { public: - ScGroupBoxWidget(WindowClass c, WindowNumber n, WidgetIndex widgetIndex) - : ScWidget(c, n, widgetIndex) + static void AddFuncs(JSContext* ctx, JSValue obj) { - } - - static void Register(duk_context* ctx) - { - dukglue_set_base_class(ctx); - // Explicit template due to text being a base method - dukglue_register_property( - ctx, &ScGroupBoxWidget::text_get, &ScGroupBoxWidget::text_set, "text"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("text", ScWidget::text_get, ScWidget::text_set), + }; + JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs)); } }; class ScLabelWidget : public ScWidget { public: - ScLabelWidget(WindowClass c, WindowNumber n, WidgetIndex widgetIndex) - : ScWidget(c, n, widgetIndex) + static void AddFuncs(JSContext* ctx, JSValue obj) { - } - - static void Register(duk_context* ctx) - { - dukglue_set_base_class(ctx); - // Explicit template due to text being a base method - dukglue_register_property( - ctx, &ScLabelWidget::text_get, &ScLabelWidget::text_set, "text"); - dukglue_register_property(ctx, &ScLabelWidget::textAlign_get, &ScLabelWidget::textAlign_set, "textAlign"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("text", ScWidget::text_get, ScWidget::text_set), + JS_CGETSET_DEF("textAlign", ScLabelWidget::textAlign_get, ScLabelWidget::textAlign_set), + }; + JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs)); } private: - std::string textAlign_get() const + static JSValue textAlign_get(JSContext* ctx, JSValue thisVal) { - auto* widget = GetWidget(); + auto* widget = GetWidget(thisVal); if (widget != nullptr) { if (widget->type == WidgetType::labelCentred) { - return "centred"; + return JSFromStdString(ctx, "centred"); } } - return "left"; + return JSFromStdString(ctx, "left"); } - void textAlign_set(const std::string& value) + static JSValue textAlign_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto* widget = GetWidget(); + JS_UNPACK_STR(valueStr, ctx, value); + + auto* widget = GetWidget(thisVal); if (widget != nullptr) { - if (value == "centred") + if (valueStr == "centred") widget->type = WidgetType::labelCentred; else widget->type = WidgetType::label; } + return JS_UNDEFINED; } }; - class ScListViewWidget : public ScWidget { public: - ScListViewWidget(WindowClass c, WindowNumber n, WidgetIndex widgetIndex) - : ScWidget(c, n, widgetIndex) + static void AddFuncs(JSContext* ctx, JSValue obj) { - } - - static void Register(duk_context* ctx) - { - dukglue_set_base_class(ctx); - dukglue_register_property(ctx, &ScListViewWidget::canSelect_get, &ScListViewWidget::canSelect_set, "canSelect"); - dukglue_register_property(ctx, &ScListViewWidget::isStriped_get, &ScListViewWidget::isStriped_set, "isStriped"); - dukglue_register_property(ctx, &ScListViewWidget::scrollbars_get, &ScListViewWidget::scrollbars_set, "scrollbars"); - dukglue_register_property( - ctx, &ScListViewWidget::showColumnHeaders_get, &ScListViewWidget::showColumnHeaders_set, "showColumnHeaders"); - dukglue_register_property(ctx, &ScListViewWidget::highlightedCell_get, nullptr, "highlightedCell"); - dukglue_register_property( - ctx, &ScListViewWidget::selectedCell_get, &ScListViewWidget::selectedCell_set, "selectedCell"); - dukglue_register_property(ctx, &ScListViewWidget::columns_get, &ScListViewWidget::columns_set, "columns"); - dukglue_register_property(ctx, &ScListViewWidget::items_get, &ScListViewWidget::items_set, "items"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("canSelect", ScListViewWidget::canSelect_get, ScListViewWidget::canSelect_set), + JS_CGETSET_DEF("isStriped", ScListViewWidget::isStriped_get, ScListViewWidget::isStriped_set), + JS_CGETSET_DEF("scrollbars", ScListViewWidget::scrollbars_get, ScListViewWidget::scrollbars_set), + JS_CGETSET_DEF( + "showColumnHeaders", ScListViewWidget::showColumnHeaders_get, ScListViewWidget::showColumnHeaders_set), + JS_CGETSET_DEF("highlightedCell", ScListViewWidget::highlightedCell_get, nullptr), + JS_CGETSET_DEF("selectedCell", ScListViewWidget::selectedCell_get, ScListViewWidget::selectedCell_set), + JS_CGETSET_DEF("columns", ScListViewWidget::columns_get, ScListViewWidget::columns_set), + JS_CGETSET_DEF("items", ScListViewWidget::items_get, ScListViewWidget::items_set), + }; + JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs)); } private: - bool canSelect_get() const + static JSValue canSelect_get(JSContext* ctx, JSValue thisVal) { - auto listView = GetListView(); + auto listView = GetListView(thisVal); if (listView != nullptr) { - return listView->CanSelect; + return JS_NewBool(ctx, listView->CanSelect); } - return false; + return JS_NewBool(ctx, false); } - void canSelect_set(bool value) + static JSValue canSelect_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto listView = GetListView(); + JS_UNPACK_BOOL(valueBool, ctx, value) + + auto listView = GetListView(thisVal); if (listView != nullptr) { - listView->CanSelect = value; + listView->CanSelect = valueBool; } + return JS_UNDEFINED; } - bool isStriped_get() const + static JSValue isStriped_get(JSContext* ctx, JSValue thisVal) { - auto listView = GetListView(); + auto listView = GetListView(thisVal); if (listView != nullptr) { - return listView->IsStriped; + return JS_NewBool(ctx, listView->IsStriped); } - return false; + return JS_NewBool(ctx, false); } - void isStriped_set(bool value) + static JSValue isStriped_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto listView = GetListView(); + JS_UNPACK_BOOL(valueBool, ctx, value) + + auto listView = GetListView(thisVal); if (listView != nullptr) { - listView->IsStriped = value; + listView->IsStriped = valueBool; } + return JS_UNDEFINED; } - DukValue scrollbars_get() const + static JSValue scrollbars_get(JSContext* ctx, JSValue thisVal) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); auto scrollType = ScrollbarType::None; - auto listView = GetListView(); + auto listView = GetListView(thisVal); if (listView != nullptr) { scrollType = listView->GetScrollbars(); } - return ToDuk(ctx, scrollType); + return ScrollbarTypeToJS(ctx, scrollType); } - void scrollbars_set(const DukValue& value) + static JSValue scrollbars_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto listView = GetListView(); + auto listView = GetListView(thisVal); if (listView != nullptr) { - listView->SetScrollbars(FromDuk(value)); + listView->SetScrollbars(ScrollbarTypeFromJS(ctx, value)); } + return JS_UNDEFINED; } - bool showColumnHeaders_get() const + static JSValue showColumnHeaders_get(JSContext* ctx, JSValue thisVal) { - auto listView = GetListView(); + auto listView = GetListView(thisVal); if (listView != nullptr) { - return listView->ShowColumnHeaders; + return JS_NewBool(ctx, listView->ShowColumnHeaders); } - return false; + return JS_NewBool(ctx, false); } - void showColumnHeaders_set(bool value) + static JSValue showColumnHeaders_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto listView = GetListView(); + JS_UNPACK_BOOL(valueBool, ctx, value) + + auto listView = GetListView(thisVal); if (listView != nullptr) { - listView->ShowColumnHeaders = value; + listView->ShowColumnHeaders = valueBool; } + return JS_UNDEFINED; } - DukValue highlightedCell_get() + static JSValue highlightedCell_get(JSContext* ctx, JSValue thisVal) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - auto listView = GetListView(); - if (listView != nullptr) + auto listView = GetListView(thisVal); + if (listView != nullptr && listView->LastHighlightedCell.has_value()) { - return ToDuk(ctx, listView->LastHighlightedCell); + return RowColumnToJS(ctx, listView->LastHighlightedCell.value()); } - return ToDuk(ctx, nullptr); + return JS_NULL; } - DukValue selectedCell_get() + static JSValue selectedCell_get(JSContext* ctx, JSValue thisVal) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - auto listView = GetListView(); - if (listView != nullptr) + auto listView = GetListView(thisVal); + if (listView != nullptr && listView->SelectedCell.has_value()) { - return ToDuk(ctx, listView->SelectedCell); + return RowColumnToJS(ctx, listView->SelectedCell.value()); } - return ToDuk(ctx, nullptr); + return JS_NULL; } - void selectedCell_set(const DukValue& value) + static JSValue selectedCell_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto listView = GetListView(); + auto listView = GetListView(thisVal); if (listView != nullptr) { - listView->SelectedCell = FromDuk>(value); + listView->SelectedCell = RowColumnFromJS(ctx, value); } + return JS_UNDEFINED; } - std::vector> items_get() + static JSValue items_get(JSContext* ctx, JSValue thisVal) { - std::vector> result; - auto listView = GetListView(); + JSValue arr = JS_NewArray(ctx); + auto listView = GetListView(thisVal); if (listView != nullptr) { - for (const auto& item : listView->GetItems()) + auto items = listView->GetItems(); + for (size_t i = 0; i < items.size(); i++) { - result.push_back(item.Cells); + JSValue cellArr = JS_NewArray(ctx); + auto cells = items[i].Cells; + for (size_t j = 0; j < cells.size(); j++) + { + JS_SetPropertyInt64(ctx, cellArr, j, JSFromStdString(ctx, cells[j])); + } + JS_SetPropertyInt64(ctx, arr, i, cellArr); } } - return result; + return arr; } - void items_set(const DukValue& value) + static JSValue items_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto listView = GetListView(); + auto listView = GetListView(thisVal); if (listView != nullptr) { - listView->SetItems(FromDuk>(value)); + listView->SetItems(ListViewItemVecFromJS(ctx, value)); } + return JS_UNDEFINED; } - std::vector columns_get() + static JSValue columns_get(JSContext* ctx, JSValue thisVal) { - std::vector result; - auto listView = GetListView(); + JSValue result = JS_NewArray(ctx); + auto listView = GetListView(thisVal); if (listView != nullptr) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); + size_t i = 0; for (const auto& column : listView->GetColumns()) { - result.push_back(ToDuk(ctx, column)); + JS_SetPropertyInt64(ctx, result, i++, ListViewColumnToJS(ctx, column)); } } return result; } - void columns_set(const DukValue& value) + static JSValue columns_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto listView = GetListView(); + auto listView = GetListView(thisVal); if (listView != nullptr) { - listView->SetColumns(FromDuk>(value)); + listView->SetColumns(ListViewColumnVecFromJS(ctx, value)); } + return JS_UNDEFINED; } - CustomListView* GetListView() const + static CustomListView* GetListView(JSValue thisVal) { - auto w = GetWindow(); + WidgetData data = GetWidgetData(thisVal); + WindowBase* w = GetWindow(data._class, data._number); if (w != nullptr) { - return GetCustomListView(w, _widgetIndex); + return GetCustomListView(w, data._widgetIndex); } return nullptr; } }; - class ScSpinnerWidget : public ScWidget { public: - ScSpinnerWidget(WindowClass c, WindowNumber n, WidgetIndex widgetIndex) - : ScWidget(c, n, widgetIndex) + static void AddFuncs(JSContext* ctx, JSValue obj) { - } - - static void Register(duk_context* ctx) - { - dukglue_set_base_class(ctx); - // Explicit template due to text being a base method - dukglue_register_property( - ctx, &ScSpinnerWidget::text_get, &ScSpinnerWidget::text_set, "text"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("text", ScWidget::text_get, ScWidget::text_set), + }; + JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs)); } }; class ScTextBoxWidget : public ScWidget { public: - ScTextBoxWidget(WindowClass c, WindowNumber n, WidgetIndex widgetIndex) - : ScWidget(c, n, widgetIndex) + static void AddFuncs(JSContext* ctx, JSValue obj) { - } - - static void Register(duk_context* ctx) - { - dukglue_set_base_class(ctx); - dukglue_register_property(ctx, &ScTextBoxWidget::maxLength_get, &ScTextBoxWidget::maxLength_set, "maxLength"); - // Explicit template due to text being a base method - dukglue_register_property( - ctx, &ScTextBoxWidget::text_get, &ScTextBoxWidget::text_set, "text"); - dukglue_register_method(ctx, &ScTextBoxWidget::focus, "focus"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("maxLength", ScTextBoxWidget::maxLength_get, ScTextBoxWidget::maxLength_set), + JS_CGETSET_DEF("text", ScWidget::text_get, ScWidget::text_set), JS_CFUNC_DEF("focus", 0, ScTextBoxWidget::focus) + }; + JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs)); } private: - int32_t maxLength_get() const + static JSValue maxLength_get(JSContext* ctx, JSValue thisVal) { - auto w = GetWindow(); - if (w != nullptr && IsCustomWindow()) + WidgetData data = GetWidgetData(thisVal); + auto w = GetWindow(data._class, data._number); + if (IsCustomWindow(w)) { - return Ui::Windows::GetWidgetMaxLength(w, _widgetIndex); + return JS_NewInt32(ctx, GetWidgetMaxLength(w, data._widgetIndex)); } - return 0; + return JS_NewInt32(ctx, 0); } - void maxLength_set(int32_t value) + static JSValue maxLength_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto w = GetWindow(); - if (w != nullptr && IsCustomWindow()) + JS_UNPACK_INT32(valueInt, ctx, value); + + WidgetData data = GetWidgetData(thisVal); + auto w = GetWindow(data._class, data._number); + if (IsCustomWindow(w)) { - Ui::Windows::SetWidgetMaxLength(w, _widgetIndex, value); + SetWidgetMaxLength(w, data._widgetIndex, valueInt); } + return JS_UNDEFINED; } - void focus() + static JSValue focus(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto w = GetWindow(); - if (w != nullptr && IsCustomWindow()) + WidgetData data = GetWidgetData(thisVal); + auto w = GetWindow(data._class, data._number); + if (IsCustomWindow(w)) { - WindowStartTextbox(*w, _widgetIndex, GetWidget()->string, Ui::Windows::GetWidgetMaxLength(w, _widgetIndex)); + Ui::Windows::WindowStartTextbox( + *w, data._widgetIndex, GetWidget(thisVal)->string, Ui::Windows::GetWidgetMaxLength(w, data._widgetIndex)); } + return JS_UNDEFINED; } }; class ScViewportWidget : public ScWidget { public: - ScViewportWidget(WindowClass c, WindowNumber n, WidgetIndex widgetIndex) - : ScWidget(c, n, widgetIndex) + static void AddFuncs(JSContext* ctx, JSValue obj) { - } - - static void Register(duk_context* ctx) - { - dukglue_set_base_class(ctx); - dukglue_register_property(ctx, &ScViewportWidget::viewport_get, nullptr, "viewport"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("viewport", ScViewportWidget::viewport_get, nullptr), + }; + JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs)); } private: - std::shared_ptr viewport_get() const + static JSValue viewport_get(JSContext* ctx, JSValue thisVal) { - auto w = GetWindow(); - if (w != nullptr && IsCustomWindow()) + WidgetData data = GetWidgetData(thisVal); + auto w = GetWindow(data._class, data._number); + if (IsCustomWindow(w)) { - auto widget = GetWidget(); + auto widget = GetWidget(w, data._widgetIndex); if (widget != nullptr && widget->type == WidgetType::viewport) { - return std::make_shared(w->classification, w->number); + return gScViewport.New(ctx, w->classification, w->number); } } - return {}; + return JS_NULL; } }; - inline DukValue ScWidget::ToDukValue(duk_context* ctx, WindowBase* w, WidgetIndex widgetIndex) + inline JSValue ScWidget::New(JSContext* ctx, WindowBase* w, WidgetIndex widgetIndex) { - const auto& widget = w->widgets[widgetIndex]; - auto c = w->classification; - auto n = w->number; - switch (widget.type) + JSValue newObj = MakeWithOpaque(ctx, _funcs, new WidgetData{ w->classification, w->number, widgetIndex }); + switch (w->widgets[widgetIndex].type) { case WidgetType::button: case WidgetType::flatBtn: case WidgetType::imgBtn: - return GetObjectAsDukValue(ctx, std::make_shared(c, n, widgetIndex)); + ScButtonWidget::AddFuncs(ctx, newObj); + break; case WidgetType::checkbox: - return GetObjectAsDukValue(ctx, std::make_shared(c, n, widgetIndex)); + ScCheckBoxWidget::AddFuncs(ctx, newObj); + break; case WidgetType::colourBtn: - return GetObjectAsDukValue(ctx, std::make_shared(c, n, widgetIndex)); + ScColourPickerWidget::AddFuncs(ctx, newObj); + break; case WidgetType::dropdownMenu: - return GetObjectAsDukValue(ctx, std::make_shared(c, n, widgetIndex)); + ScDropdownWidget::AddFuncs(ctx, newObj); + break; case WidgetType::groupbox: - return GetObjectAsDukValue(ctx, std::make_shared(c, n, widgetIndex)); + ScGroupBoxWidget::AddFuncs(ctx, newObj); + break; case WidgetType::label: case WidgetType::labelCentred: - return GetObjectAsDukValue(ctx, std::make_shared(c, n, widgetIndex)); + ScLabelWidget::AddFuncs(ctx, newObj); + break; case WidgetType::scroll: - return GetObjectAsDukValue(ctx, std::make_shared(c, n, widgetIndex)); + ScListViewWidget::AddFuncs(ctx, newObj); + break; case WidgetType::spinner: - return GetObjectAsDukValue(ctx, std::make_shared(c, n, widgetIndex)); + ScSpinnerWidget::AddFuncs(ctx, newObj); + break; case WidgetType::textBox: - return GetObjectAsDukValue(ctx, std::make_shared(c, n, widgetIndex)); + ScTextBoxWidget::AddFuncs(ctx, newObj); + break; case WidgetType::viewport: - return GetObjectAsDukValue(ctx, std::make_shared(c, n, widgetIndex)); + ScViewportWidget::AddFuncs(ctx, newObj); + break; default: - return GetObjectAsDukValue(ctx, std::make_shared(c, n, widgetIndex)); + break; } + return newObj; } } // namespace OpenRCT2::Scripting diff --git a/src/openrct2-ui/scripting/ScWindow.cpp b/src/openrct2-ui/scripting/ScWindow.cpp new file mode 100644 index 0000000000..71587969c4 --- /dev/null +++ b/src/openrct2-ui/scripting/ScWindow.cpp @@ -0,0 +1,406 @@ +/***************************************************************************** + * Copyright (c) 2014-2026 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#ifdef ENABLE_SCRIPTING + + #include "ScWindow.h" + + #include "ScWidget.hpp" + + #include + #include + +namespace OpenRCT2::Scripting +{ + using namespace OpenRCT2::Ui::Windows; + + extern ScWindow gScWindow; + + using OpaqueWindowData = struct + { + WindowClass _class; + WindowNumber _number; + }; + + JSValue ScWindow::classification_get(JSContext* ctx, JSValue thisVal) + { + OpaqueWindowData* data = gScWindow.GetOpaque(thisVal); + return JS_NewInt32(ctx, static_cast(data->_class)); + } + + JSValue ScWindow::number_get(JSContext* ctx, JSValue thisVal) + { + OpaqueWindowData* data = gScWindow.GetOpaque(thisVal); + return JS_NewInt32(ctx, data->_number); + } + + JSValue ScWindow::x_get(JSContext* ctx, JSValue thisVal) + { + auto w = GetWindow(thisVal); + if (w != nullptr) + { + return JS_NewInt32(ctx, w->windowPos.x); + } + return JS_NewInt32(ctx, 0); + } + JSValue ScWindow::x_set(JSContext* ctx, JSValue thisVal, JSValue value) + { + JS_UNPACK_INT32(valueInt, ctx, value); + + auto w = GetWindow(thisVal); + if (w != nullptr) + { + WindowSetPosition(*w, { valueInt, w->windowPos.y }); + } + return JS_UNDEFINED; + } + JSValue ScWindow::y_get(JSContext* ctx, JSValue thisVal) + { + auto w = GetWindow(thisVal); + if (w != nullptr) + { + return JS_NewInt32(ctx, w->windowPos.y); + } + return JS_NewInt32(ctx, 0); + } + JSValue ScWindow::y_set(JSContext* ctx, JSValue thisVal, JSValue value) + { + JS_UNPACK_INT32(valueInt, ctx, value); + + auto w = GetWindow(thisVal); + if (w != nullptr) + { + WindowSetPosition(*w, { w->windowPos.x, valueInt }); + } + return JS_UNDEFINED; + } + JSValue ScWindow::width_get(JSContext* ctx, JSValue thisVal) + { + auto w = GetWindow(thisVal); + if (w != nullptr) + { + return JS_NewInt32(ctx, w->width); + } + return JS_NewInt32(ctx, 0); + } + JSValue ScWindow::width_set(JSContext* ctx, JSValue thisVal, JSValue value) + { + JS_UNPACK_INT32(valueInt, ctx, value); + + auto w = GetWindow(thisVal); + if (w != nullptr) + { + if (w->canBeResized()) + { + WindowResizeByDelta(*w, valueInt - w->width, 0); + } + else + { + WindowSetResize(*w, { valueInt, w->minHeight }, { valueInt, w->maxHeight }); + } + } + return JS_UNDEFINED; + } + JSValue ScWindow::height_get(JSContext* ctx, JSValue thisVal) + { + auto w = GetWindow(thisVal); + if (w != nullptr) + { + return JS_NewInt32(ctx, w->height - w->getTitleBarDiffNormal()); + } + return JS_NewInt32(ctx, 0); + } + JSValue ScWindow::height_set(JSContext* ctx, JSValue thisVal, JSValue value) + { + JS_UNPACK_INT32(valueInt, ctx, value); + + auto w = GetWindow(thisVal); + if (w != nullptr) + { + valueInt += w->getTitleBarDiffNormal(); + if (w->canBeResized()) + { + WindowResizeByDelta(*w, 0, valueInt - w->height); + } + else + { + WindowSetResize(*w, { w->minWidth, valueInt }, { w->maxWidth, valueInt }); + } + } + return JS_UNDEFINED; + } + JSValue ScWindow::minWidth_get(JSContext* ctx, JSValue thisVal) + { + auto w = GetWindow(thisVal); + if (w != nullptr) + { + return JS_NewInt32(ctx, w->minWidth); + } + return JS_NewInt32(ctx, 0); + } + JSValue ScWindow::minWidth_set(JSContext* ctx, JSValue thisVal, JSValue value) + { + JS_UNPACK_INT32(valueInt, ctx, value); + + auto w = GetWindow(thisVal); + if (w != nullptr) + { + WindowSetResize(*w, { valueInt, w->minHeight }, { w->maxWidth, w->maxHeight }); + } + return JS_UNDEFINED; + } + JSValue ScWindow::maxWidth_get(JSContext* ctx, JSValue thisVal) + { + auto w = GetWindow(thisVal); + if (w != nullptr) + { + return JS_NewInt32(ctx, w->maxWidth); + } + return JS_NewInt32(ctx, 0); + } + JSValue ScWindow::maxWidth_set(JSContext* ctx, JSValue thisVal, JSValue value) + { + JS_UNPACK_INT32(valueInt, ctx, value); + + auto w = GetWindow(thisVal); + if (w != nullptr) + { + WindowSetResize(*w, { w->minWidth, w->minHeight }, { valueInt, w->maxHeight }); + } + return JS_UNDEFINED; + } + JSValue ScWindow::minHeight_get(JSContext* ctx, JSValue thisVal) + { + auto w = GetWindow(thisVal); + if (w != nullptr) + { + return JS_NewInt32(ctx, w->minHeight - w->getTitleBarDiffNormal()); + } + return JS_NewInt32(ctx, 0); + } + JSValue ScWindow::minHeight_set(JSContext* ctx, JSValue thisVal, JSValue value) + { + JS_UNPACK_INT32(valueInt, ctx, value); + + auto w = GetWindow(thisVal); + if (w != nullptr) + { + valueInt += w->getTitleBarDiffNormal(); + WindowSetResize(*w, { w->minWidth, valueInt }, { w->maxWidth, w->maxHeight }); + } + return JS_UNDEFINED; + } + JSValue ScWindow::maxHeight_get(JSContext* ctx, JSValue thisVal) + { + auto w = GetWindow(thisVal); + if (w != nullptr) + { + return JS_NewInt32(ctx, w->maxHeight - w->getTitleBarDiffNormal()); + } + return JS_NewInt32(ctx, 0); + } + JSValue ScWindow::maxHeight_set(JSContext* ctx, JSValue thisVal, JSValue value) + { + JS_UNPACK_INT32(valueInt, ctx, value); + + auto w = GetWindow(thisVal); + if (w != nullptr) + { + valueInt += w->getTitleBarDiffNormal(); + WindowSetResize(*w, { w->minWidth, w->minHeight }, { w->maxWidth, valueInt }); + } + return JS_UNDEFINED; + } + JSValue ScWindow::isSticky_get(JSContext* ctx, JSValue thisVal) + { + auto w = GetWindow(thisVal); + if (w != nullptr) + { + return JS_NewBool(ctx, w->flags.hasAny(WindowFlag::stickToBack, WindowFlag::stickToFront) != 0); + } + return JS_NewBool(ctx, false); + } + + JSValue ScWindow::widgets_get(JSContext* ctx, JSValue thisVal) + { + JSValue result = JS_NewArray(ctx); + auto w = GetWindow(thisVal); + if (w != nullptr) + { + for (WidgetIndex widgetIndex = 0; widgetIndex < w->widgets.size(); widgetIndex++) + { + JS_SetPropertyInt64(ctx, result, widgetIndex, gScWidget.New(ctx, w, widgetIndex)); + } + } + return result; + } + + JSValue ScWindow::colours_get(JSContext* ctx, JSValue thisVal) + { + JSValue result = JS_NewArray(ctx); + auto w = GetWindow(thisVal); + if (w != nullptr) + { + int64_t i = 0; + for (auto c : w->colours) + { + auto colour = EnumValue(c.colour); + if (c.flags.has(ColourFlag::translucent)) + colour |= kLegacyColourFlagTranslucent; + + JS_SetPropertyInt64(ctx, result, i++, JS_NewInt32(ctx, colour)); + } + } + return result; + } + JSValue ScWindow::colours_set(JSContext* ctx, JSValue thisVal, JSValue colours) + { + auto w = GetWindow(thisVal); + if (w != nullptr && JS_IsArray(colours)) + { + int64_t coloursLen = -1; + JS_GetLength(ctx, colours, &coloursLen); + for (int64_t i = 0; i < std::ssize(w->colours); i++) + { + auto c = ColourWithFlags{ Drawing::Colour::black }; + if (i < coloursLen) + { + JSValue elem = JS_GetPropertyInt64(ctx, colours, i); + if (JS_IsNumber(elem)) + { + int32_t colorInt = -1; + JS_ToInt32(ctx, &colorInt, elem); + uint8_t colour = (colorInt & ~kLegacyColourFlagTranslucent) % Drawing::kColourNumTotal; + bool isTranslucent = (colorInt & kLegacyColourFlagTranslucent); + c.colour = static_cast(colour); + c.flags.set(ColourFlag::translucent, isTranslucent); + } + JS_FreeValue(ctx, elem); + } + w->colours[i] = c; + } + w->invalidate(); + } + return JS_UNDEFINED; + } + + JSValue ScWindow::title_get(JSContext* ctx, JSValue thisVal) + { + auto w = GetWindow(thisVal); + if (w != nullptr && w->classification == WindowClass::custom) + { + return JSFromStdString(ctx, GetWindowTitle(w)); + } + return JSFromStdString(ctx, {}); + } + JSValue ScWindow::title_set(JSContext* ctx, JSValue thisVal, JSValue value) + { + JS_UNPACK_STR(valueStr, ctx, value); + + auto w = GetWindow(thisVal); + if (w != nullptr && w->classification == WindowClass::custom) + { + UpdateWindowTitle(w, valueStr); + } + return JS_UNDEFINED; + } + + JSValue ScWindow::tabIndex_get(JSContext* ctx, JSValue thisVal) + { + auto w = GetWindow(thisVal); + if (w != nullptr && w->classification == WindowClass::custom) + { + return JS_NewInt32(ctx, w->page); + } + return JS_NewInt32(ctx, 0); + } + JSValue ScWindow::tabIndex_set(JSContext* ctx, JSValue thisVal, JSValue tab) + { + JS_UNPACK_INT32(tabNumber, ctx, tab); + + auto w = GetWindow(thisVal); + if (w != nullptr && w->classification == WindowClass::custom) + { + UpdateWindowTab(w, tabNumber); + } + return JS_UNDEFINED; + } + + JSValue ScWindow::close(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) + { + auto w = GetWindow(thisVal); + if (w != nullptr) + { + auto* windowMgr = Ui::GetWindowManager(); + windowMgr->Close(*w); + } + return JS_UNDEFINED; + } + + JSValue ScWindow::findWidget(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) + { + JS_UNPACK_STR(name, ctx, argv[0]); + + auto w = GetWindow(thisVal); + if (w != nullptr) + { + auto widgetIndex = FindWidgetIndexByName(w, name); + if (widgetIndex) + { + return gScWidget.New(ctx, w, *widgetIndex); + } + } + return JS_NULL; + } + + JSValue ScWindow::bringToFront(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) + { + auto* w = GetWindow(thisVal); + if (w != nullptr) + { + auto* windowMgr = Ui::GetWindowManager(); + w = windowMgr->BringToFront(*w); + w->flash(); + } + return JS_UNDEFINED; + } + + JSValue ScWindow::New(JSContext* ctx, WindowBase* w) + { + return New(ctx, w->classification, w->number); + } + + JSValue ScWindow::New(JSContext* ctx, WindowClass wClass, WindowNumber wNum) + { + return MakeWithOpaque(ctx, funcs, new OpaqueWindowData{ wClass, wNum }); + } + + void ScWindow::Register(JSContext* ctx) + { + RegisterBaseStr(ctx, "Window", Finalize); + } + + void ScWindow::Finalize(JSRuntime* rt, JSValue thisVal) + { + OpaqueWindowData* data = gScWindow.GetOpaque(thisVal); + if (data) + delete data; + } + + WindowBase* ScWindow::GetWindow(JSValue thisVal) + { + OpaqueWindowData* data = gScWindow.GetOpaque(thisVal); + if (!data) + return nullptr; + auto* windowMgr = Ui::GetWindowManager(); + return windowMgr->FindByNumber(data->_class, data->_number); + } +} // namespace OpenRCT2::Scripting + +#endif diff --git a/src/openrct2-ui/scripting/ScWindow.h b/src/openrct2-ui/scripting/ScWindow.h new file mode 100644 index 0000000000..6fcf4cb964 --- /dev/null +++ b/src/openrct2-ui/scripting/ScWindow.h @@ -0,0 +1,108 @@ +/***************************************************************************** + * Copyright (c) 2014-2026 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#ifdef ENABLE_SCRIPTING + + #include + #include + #include + +namespace OpenRCT2::Scripting +{ + class ScWindow; + extern ScWindow gScWindow; + + class ScWindow final : public ScBase + { + private: + static JSValue classification_get(JSContext* ctx, JSValue thisVal); + + static JSValue number_get(JSContext* ctx, JSValue thisVal); + + static JSValue x_get(JSContext* ctx, JSValue thisVal); + static JSValue x_set(JSContext* ctx, JSValue thisVal, JSValue value); + + static JSValue y_get(JSContext* ctx, JSValue thisVal); + static JSValue y_set(JSContext* ctx, JSValue thisVal, JSValue value); + + static JSValue width_get(JSContext* ctx, JSValue thisVal); + static JSValue width_set(JSContext* ctx, JSValue thisVal, JSValue value); + + static JSValue height_get(JSContext* ctx, JSValue thisVal); + static JSValue height_set(JSContext* ctx, JSValue thisVal, JSValue value); + + static JSValue minWidth_get(JSContext* ctx, JSValue thisVal); + static JSValue minWidth_set(JSContext* ctx, JSValue thisVal, JSValue value); + + static JSValue maxWidth_get(JSContext* ctx, JSValue thisVal); + static JSValue maxWidth_set(JSContext* ctx, JSValue thisVal, JSValue value); + + static JSValue minHeight_get(JSContext* ctx, JSValue thisVal); + static JSValue minHeight_set(JSContext* ctx, JSValue thisVal, JSValue value); + + static JSValue maxHeight_get(JSContext* ctx, JSValue thisVal); + static JSValue maxHeight_set(JSContext* ctx, JSValue thisVal, JSValue value); + + static JSValue isSticky_get(JSContext* ctx, JSValue thisVal); + + static JSValue widgets_get(JSContext* ctx, JSValue thisVal); + + static JSValue colours_get(JSContext* ctx, JSValue thisVal); + static JSValue colours_set(JSContext* ctx, JSValue thisVal, JSValue colours); + + static JSValue title_get(JSContext* ctx, JSValue thisVal); + static JSValue title_set(JSContext* ctx, JSValue thisVal, JSValue value); + + static JSValue tabIndex_get(JSContext* ctx, JSValue thisVal); + static JSValue tabIndex_set(JSContext* ctx, JSValue thisVal, JSValue tab); + + static JSValue close(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + + static JSValue findWidget(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + + static JSValue bringToFront(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + + public: + JSValue New(JSContext* ctx, WindowBase* w); + JSValue New(JSContext* ctx, WindowClass wClass, WindowNumber wNum); + + void Register(JSContext* ctx); + + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("classification", ScWindow::classification_get, nullptr), + JS_CGETSET_DEF("number", ScWindow::number_get, nullptr), + JS_CGETSET_DEF("x", ScWindow::x_get, ScWindow::x_set), + JS_CGETSET_DEF("y", ScWindow::y_get, ScWindow::y_set), + JS_CGETSET_DEF("width", ScWindow::width_get, ScWindow::width_set), + JS_CGETSET_DEF("height", ScWindow::height_get, ScWindow::height_set), + JS_CGETSET_DEF("minWidth", ScWindow::minWidth_get, ScWindow::minWidth_set), + JS_CGETSET_DEF("maxWidth", ScWindow::maxWidth_get, ScWindow::maxWidth_set), + JS_CGETSET_DEF("minHeight", ScWindow::minHeight_get, ScWindow::minHeight_set), + JS_CGETSET_DEF("maxHeight", ScWindow::maxHeight_get, ScWindow::maxHeight_set), + JS_CGETSET_DEF("isSticky", ScWindow::isSticky_get, nullptr), + JS_CGETSET_DEF("widgets", ScWindow::widgets_get, nullptr), + JS_CGETSET_DEF("colours", ScWindow::colours_get, ScWindow::colours_set), + JS_CGETSET_DEF("title", ScWindow::title_get, ScWindow::title_set), + JS_CGETSET_DEF("tabIndex", ScWindow::tabIndex_get, ScWindow::tabIndex_set), + + JS_CFUNC_DEF("close", 0, ScWindow::close), + JS_CFUNC_DEF("findWidget", 1, ScWindow::findWidget), + JS_CFUNC_DEF("bringToFront", 0, ScWindow::bringToFront) + }; + + private: + static void Finalize(JSRuntime* rt, JSValue thisVal); + + static WindowBase* GetWindow(JSValue thisVal); + }; +} // namespace OpenRCT2::Scripting + +#endif diff --git a/src/openrct2-ui/scripting/ScWindow.hpp b/src/openrct2-ui/scripting/ScWindow.hpp deleted file mode 100644 index 12d9a1fdc9..0000000000 --- a/src/openrct2-ui/scripting/ScWindow.hpp +++ /dev/null @@ -1,374 +0,0 @@ -/***************************************************************************** - * Copyright (c) 2014-2026 OpenRCT2 developers - * - * For a complete list of all authors, please refer to contributors.md - * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 - * - * OpenRCT2 is licensed under the GNU General Public License version 3. - *****************************************************************************/ - -#pragma once - -#ifdef ENABLE_SCRIPTING - - #include "ScWidget.hpp" - - #include - #include - #include - #include - -namespace OpenRCT2::Scripting -{ - using namespace OpenRCT2::Ui::Windows; - - class ScWindow - { - private: - WindowClass _class; - WindowNumber _number; - - public: - ScWindow(WindowBase* w) - : ScWindow(w->classification, w->number) - { - } - - ScWindow(WindowClass c, WindowNumber n) - : _class(c) - , _number(n) - { - } - - int32_t classification_get() const - { - return static_cast(_class); - } - - int32_t number_get() const - { - return static_cast(_number); - } - - int32_t x_get() const - { - auto w = GetWindow(); - if (w != nullptr) - { - return w->windowPos.x; - } - return 0; - } - void x_set(int32_t value) - { - auto w = GetWindow(); - if (w != nullptr) - { - WindowSetPosition(*w, { value, w->windowPos.y }); - } - } - int32_t y_get() const - { - auto w = GetWindow(); - if (w != nullptr) - { - return w->windowPos.y; - } - return 0; - } - void y_set(int32_t value) - { - auto w = GetWindow(); - if (w != nullptr) - { - WindowSetPosition(*w, { w->windowPos.x, value }); - } - } - int32_t width_get() const - { - auto w = GetWindow(); - if (w != nullptr) - { - return w->width; - } - return 0; - } - void width_set(int32_t value) - { - auto w = GetWindow(); - if (w != nullptr) - { - if (w->canBeResized()) - { - WindowResizeByDelta(*w, value - w->width, 0); - } - else - { - WindowSetResize(*w, { value, w->minHeight }, { value, w->maxHeight }); - } - } - } - int32_t height_get() const - { - auto w = GetWindow(); - if (w != nullptr) - { - return w->height - w->getTitleBarDiffNormal(); - } - return 0; - } - void height_set(int32_t value) - { - auto w = GetWindow(); - if (w != nullptr) - { - value += w->getTitleBarDiffNormal(); - if (w->canBeResized()) - { - WindowResizeByDelta(*w, 0, value - w->height); - } - else - { - WindowSetResize(*w, { w->minWidth, value }, { w->maxWidth, value }); - } - } - } - int32_t minWidth_get() const - { - auto w = GetWindow(); - if (w != nullptr) - { - return w->minWidth; - } - return 0; - } - void minWidth_set(int32_t value) - { - auto w = GetWindow(); - if (w != nullptr) - { - WindowSetResize(*w, { value, w->minHeight }, { w->maxWidth, w->maxHeight }); - } - } - int32_t maxWidth_get() const - { - auto w = GetWindow(); - if (w != nullptr) - { - return w->maxWidth; - } - return 0; - } - void maxWidth_set(int32_t value) - { - auto w = GetWindow(); - if (w != nullptr) - { - WindowSetResize(*w, { w->minWidth, w->minHeight }, { value, w->maxHeight }); - } - } - int32_t minHeight_get() const - { - auto w = GetWindow(); - if (w != nullptr) - { - return w->minHeight - w->getTitleBarDiffNormal(); - } - return 0; - } - void minHeight_set(int32_t value) - { - auto w = GetWindow(); - if (w != nullptr) - { - value += w->getTitleBarDiffNormal(); - WindowSetResize(*w, { w->minWidth, value }, { w->maxWidth, w->maxHeight }); - } - } - int32_t maxHeight_get() const - { - auto w = GetWindow(); - if (w != nullptr) - { - return w->maxHeight - w->getTitleBarDiffNormal(); - } - return 0; - } - void maxHeight_set(int32_t value) - { - auto w = GetWindow(); - if (w != nullptr) - { - value += w->getTitleBarDiffNormal(); - WindowSetResize(*w, { w->minWidth, w->minHeight }, { w->maxWidth, value }); - } - } - bool isSticky_get() const - { - auto w = GetWindow(); - if (w != nullptr) - { - return w->flags.hasAny(WindowFlag::stickToBack, WindowFlag::stickToFront); - } - return false; - } - - std::vector widgets_get() const - { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - - std::vector result; - auto w = GetWindow(); - if (w != nullptr) - { - for (WidgetIndex widgetIndex = 0; widgetIndex < w->widgets.size(); widgetIndex++) - { - result.push_back(ScWidget::ToDukValue(ctx, w, widgetIndex)); - } - } - return result; - } - - std::vector colours_get() const - { - std::vector result; - auto w = GetWindow(); - if (w != nullptr) - { - result.reserve(std::size(w->colours)); - for (auto c : w->colours) - { - auto colour = EnumValue(c.colour); - if (c.flags.has(ColourFlag::translucent)) - colour |= kLegacyColourFlagTranslucent; - result.push_back(colour); - } - } - return result; - } - void colours_set(std::vector colours) - { - auto w = GetWindow(); - if (w != nullptr) - { - for (size_t i = 0; i < std::size(w->colours); i++) - { - auto c = ColourWithFlags{ Drawing::Colour::black }; - if (i < colours.size()) - { - auto colour = (colours[i] & ~kLegacyColourFlagTranslucent) % Drawing::kColourNumTotal; - bool isTranslucent = (colours[i] & kLegacyColourFlagTranslucent); - c.colour = static_cast(colour); - c.flags.set(ColourFlag::translucent, isTranslucent); - } - w->colours[i] = c; - } - w->invalidate(); - } - } - - std::string title_get() const - { - auto w = GetWindow(); - if (w != nullptr && w->classification == WindowClass::custom) - { - return GetWindowTitle(w); - } - return {}; - } - void title_set(std::string value) - { - auto w = GetWindow(); - if (w != nullptr && w->classification == WindowClass::custom) - { - UpdateWindowTitle(w, value); - } - } - - int32_t tabIndex_get() const - { - auto w = GetWindow(); - if (w != nullptr && w->classification == WindowClass::custom) - { - return w->page; - } - return 0; - } - void tabIndex_set(int32_t tab) - { - auto w = GetWindow(); - if (w != nullptr && w->classification == WindowClass::custom) - { - UpdateWindowTab(w, tab); - } - } - - void close() - { - auto w = GetWindow(); - if (w != nullptr) - { - auto* windowMgr = Ui::GetWindowManager(); - windowMgr->Close(*w); - } - } - - DukValue findWidget(std::string name) const - { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - auto w = GetWindow(); - if (w != nullptr) - { - auto widgetIndex = FindWidgetIndexByName(w, name); - if (widgetIndex) - { - return ScWidget::ToDukValue(ctx, w, *widgetIndex); - } - } - return GetObjectAsDukValue(ctx, nullptr); - } - - void bringToFront() - { - auto* w = GetWindow(); - if (w != nullptr) - { - auto* windowMgr = Ui::GetWindowManager(); - w = windowMgr->BringToFront(*w); - w->flash(); - } - } - - static void Register(duk_context* ctx) - { - dukglue_register_property(ctx, &ScWindow::classification_get, nullptr, "classification"); - dukglue_register_property(ctx, &ScWindow::number_get, nullptr, "number"); - dukglue_register_property(ctx, &ScWindow::x_get, &ScWindow::x_set, "x"); - dukglue_register_property(ctx, &ScWindow::y_get, &ScWindow::y_set, "y"); - dukglue_register_property(ctx, &ScWindow::width_get, &ScWindow::width_set, "width"); - dukglue_register_property(ctx, &ScWindow::height_get, &ScWindow::height_set, "height"); - dukglue_register_property(ctx, &ScWindow::minWidth_get, &ScWindow::minWidth_set, "minWidth"); - dukglue_register_property(ctx, &ScWindow::maxWidth_get, &ScWindow::maxWidth_set, "maxWidth"); - dukglue_register_property(ctx, &ScWindow::minHeight_get, &ScWindow::minHeight_set, "minHeight"); - dukglue_register_property(ctx, &ScWindow::maxHeight_get, &ScWindow::maxHeight_set, "maxHeight"); - dukglue_register_property(ctx, &ScWindow::isSticky_get, nullptr, "isSticky"); - dukglue_register_property(ctx, &ScWindow::widgets_get, nullptr, "widgets"); - dukglue_register_property(ctx, &ScWindow::colours_get, &ScWindow::colours_set, "colours"); - dukglue_register_property(ctx, &ScWindow::title_get, &ScWindow::title_set, "title"); - dukglue_register_property(ctx, &ScWindow::tabIndex_get, &ScWindow::tabIndex_set, "tabIndex"); - - dukglue_register_method(ctx, &ScWindow::close, "close"); - dukglue_register_method(ctx, &ScWindow::findWidget, "findWidget"); - dukglue_register_method(ctx, &ScWindow::bringToFront, "bringToFront"); - } - - private: - WindowBase* GetWindow() const - { - auto* windowMgr = Ui::GetWindowManager(); - return windowMgr->FindByNumber(_class, _number); - } - }; -} // namespace OpenRCT2::Scripting - -#endif diff --git a/src/openrct2-ui/scripting/UiExtensions.cpp b/src/openrct2-ui/scripting/UiExtensions.cpp index 3f05f7b021..03c18fc0a7 100644 --- a/src/openrct2-ui/scripting/UiExtensions.cpp +++ b/src/openrct2-ui/scripting/UiExtensions.cpp @@ -19,66 +19,75 @@ #include "ScTitleSequence.hpp" #include "ScUi.hpp" #include "ScWidget.hpp" - #include "ScWindow.hpp" + #include "ScWindow.h" #include using namespace OpenRCT2::Scripting; +ScGraphicsContext OpenRCT2::Scripting::gScGraphicsContext; +ScImageManager OpenRCT2::Scripting::gScImageManager; +ScTileSelection OpenRCT2::Scripting::gScTileSelection; +ScTool OpenRCT2::Scripting::gScTool; +ScUi OpenRCT2::Scripting::gScUi; +ScViewport OpenRCT2::Scripting::gScViewport; + +ScWidget OpenRCT2::Scripting::gScWidget; + +ScTitleSequence OpenRCT2::Scripting::gScTitleSequence; +ScTitleSequenceManager OpenRCT2::Scripting::gScTitleSequenceManager; +ScTitleSequencePark OpenRCT2::Scripting::gScTitleSequencePark; +ScWindow OpenRCT2::Scripting::gScWindow; + +static void InitialiseContext(JSContext* ctx) +{ + JSValue glb = JS_GetGlobalObject(ctx); + JS_SetPropertyStr(ctx, glb, "titleSequenceManager", gScTitleSequenceManager.New(ctx)); + JS_SetPropertyStr(ctx, glb, "ui", gScUi.New(ctx, &OpenRCT2::GetContext()->GetScriptEngine())); + JS_FreeValue(ctx, glb); +} + void UiScriptExtensions::Extend(ScriptEngine& scriptEngine) { - auto ctx = scriptEngine.GetContext(); + JSContext* ctx = scriptEngine.GetContext(); - dukglue_register_global(ctx, std::make_shared(), "titleSequenceManager"); - dukglue_register_global(ctx, std::make_shared(scriptEngine), "ui"); + gScGraphicsContext.Register(ctx); + gScImageManager.Register(ctx); + gScTileSelection.Register(ctx); + gScTool.Register(ctx); + gScUi.Register(ctx); + gScViewport.Register(ctx); - ScGraphicsContext::Register(ctx); - ScImageManager::Register(ctx); - ScTileSelection::Register(ctx); - ScTool::Register(ctx); - ScUi::Register(ctx); - ScViewport::Register(ctx); + gScWidget.Register(ctx); - ScWidget::Register(ctx); - ScButtonWidget::Register(ctx); - ScColourPickerWidget::Register(ctx); - ScCheckBoxWidget::Register(ctx); - ScDropdownWidget::Register(ctx); - ScGroupBoxWidget::Register(ctx); - ScLabelWidget::Register(ctx); - ScListViewWidget::Register(ctx); - ScSpinnerWidget::Register(ctx); - ScTextBoxWidget::Register(ctx); - ScViewportWidget::Register(ctx); - - ScTitleSequence::Register(ctx); - ScTitleSequenceManager::Register(ctx); - ScTitleSequencePark::Register(ctx); - ScWindow::Register(ctx); + gScTitleSequence.Register(ctx); + gScTitleSequenceManager.Register(ctx); + gScTitleSequencePark.Register(ctx); + gScWindow.Register(ctx); InitialiseCustomImages(scriptEngine); InitialiseCustomMenuItems(scriptEngine); scriptEngine.SubscribeToPluginStoppedEvent( - [](std::shared_ptr plugin) -> void { CloseWindowsOwnedByPlugin(plugin); }); + [](std::shared_ptr plugin) -> void { Ui::Windows::CloseWindowsOwnedByPlugin(plugin); }); + + scriptEngine.RegisterExtension(InitialiseContext, Unregister); } -std::shared_ptr ScWidget::window_get() const +void UiScriptExtensions::Unregister() { - return std::make_shared(_class, _number); -} + gScGraphicsContext.Unregister(); + gScImageManager.Unregister(); + gScTileSelection.Unregister(); + gScTool.Unregister(); + gScUi.Unregister(); + gScViewport.Unregister(); -void ScWidget::Register(duk_context* ctx) -{ - dukglue_register_property(ctx, &ScWidget::window_get, nullptr, "window"); - dukglue_register_property(ctx, &ScWidget::name_get, &ScWidget::name_set, "name"); - dukglue_register_property(ctx, &ScWidget::type_get, nullptr, "type"); - dukglue_register_property(ctx, &ScWidget::x_get, &ScWidget::x_set, "x"); - dukglue_register_property(ctx, &ScWidget::y_get, &ScWidget::y_set, "y"); - dukglue_register_property(ctx, &ScWidget::width_get, &ScWidget::width_set, "width"); - dukglue_register_property(ctx, &ScWidget::height_get, &ScWidget::height_set, "height"); - dukglue_register_property(ctx, &ScWidget::tooltip_get, &ScWidget::tooltip_set, "tooltip"); - dukglue_register_property(ctx, &ScWidget::isDisabled_get, &ScWidget::isDisabled_set, "isDisabled"); - dukglue_register_property(ctx, &ScWidget::isVisible_get, &ScWidget::isVisible_set, "isVisible"); + gScWidget.Unregister(); + + gScTitleSequence.Unregister(); + gScTitleSequenceManager.Unregister(); + gScTitleSequencePark.Unregister(); + gScWindow.Unregister(); } #endif diff --git a/src/openrct2-ui/scripting/UiExtensions.h b/src/openrct2-ui/scripting/UiExtensions.h index 7b0ea4b664..f0f308aa29 100644 --- a/src/openrct2-ui/scripting/UiExtensions.h +++ b/src/openrct2-ui/scripting/UiExtensions.h @@ -19,6 +19,7 @@ namespace OpenRCT2::Scripting { public: static void Extend(ScriptEngine& scriptEngine); + static void Unregister(); }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2-ui/windows/LoadSave.cpp b/src/openrct2-ui/windows/LoadSave.cpp index 4d92d56b9b..424c6a85a9 100644 --- a/src/openrct2-ui/windows/LoadSave.cpp +++ b/src/openrct2-ui/windows/LoadSave.cpp @@ -607,6 +607,8 @@ namespace OpenRCT2::Ui::Windows gGamePaused &= ~GAME_PAUSED_MODAL; Audio::Resume(); } + + UnregisterJSCallback(); } void onResize() override @@ -1159,14 +1161,12 @@ namespace OpenRCT2::Ui::Windows }; WindowBase* LoadsaveOpen( - LoadSaveAction action, LoadSaveType type, std::string_view defaultPath, LoadSaveCallback callback, + LoadSaveAction action, LoadSaveType type, std::string_view defaultPath, LoadSaveCallback callback, bool isJsCallback, TrackDesign* trackDesign) { _trackDesign = trackDesign; _defaultPath = defaultPath; - RegisterCallback(callback); - auto* windowMgr = GetWindowManager(); auto* w = static_cast(windowMgr->BringToFrontByClass(WindowClass::loadsave)); if (w == nullptr) @@ -1182,6 +1182,8 @@ namespace OpenRCT2::Ui::Windows ScreenSize windowSize = { config.fileBrowserWidth, config.fileBrowserHeight }; + RegisterCallback(callback, isJsCallback); + w = windowMgr->Create( WindowClass::loadsave, windowSize, { WindowFlag::stickToFront, WindowFlag::resizable, WindowFlag::autoPosition, WindowFlag::centreScreen }, action, diff --git a/src/openrct2-ui/windows/Windows.h b/src/openrct2-ui/windows/Windows.h index c18023a427..077bc10b5e 100644 --- a/src/openrct2-ui/windows/Windows.h +++ b/src/openrct2-ui/windows/Windows.h @@ -138,7 +138,7 @@ namespace OpenRCT2::Ui::Windows // LoadSave WindowBase* LoadsaveOpen( LoadSaveAction action, LoadSaveType type, std::string_view defaultPath, - std::function callback, TrackDesign* trackDesign); + std::function callback, bool isJsCallback, TrackDesign* trackDesign); void WindowLoadSaveInputKey(WindowBase* w, uint32_t keycode); // Main diff --git a/src/openrct2/CMakeLists.txt b/src/openrct2/CMakeLists.txt index e63299dc73..069a9c7d6b 100644 --- a/src/openrct2/CMakeLists.txt +++ b/src/openrct2/CMakeLists.txt @@ -1,6 +1,6 @@ file(GLOB_RECURSE OPENRCT2_CORE_SOURCES "${CMAKE_CURRENT_LIST_DIR}/*.cpp") file(GLOB_RECURSE OPENRCT2_CORE_HEADERS "${CMAKE_CURRENT_LIST_DIR}/*.h" - "${CMAKE_CURRENT_LIST_DIR}/*.hpp") + "${CMAKE_CURRENT_LIST_DIR}/*.hpp") if (APPLE) file(GLOB_RECURSE OPENRCT2_CORE_MM_SOURCES "${CMAKE_CURRENT_LIST_DIR}/*.mm") @@ -8,14 +8,14 @@ if (APPLE) endif () if (ENABLE_SCRIPTING) - include_directories("${CMAKE_CURRENT_LIST_DIR}/../thirdparty/duktape") + include_directories(SYSTEM "${CMAKE_CURRENT_LIST_DIR}/../thirdparty/quickjs-ng") - # duktape is third party, ignore all warnings - set(OPENRCT2_DUKTAPE_SOURCES "${CMAKE_CURRENT_LIST_DIR}/../thirdparty/duktape/duktape.cpp") + # quickjs-ng is third party, ignore all warnings + set(OPENRCT2_QUICKJS_NG_SOURCES "${CMAKE_CURRENT_LIST_DIR}/../thirdparty/quickjs-ng/quickjs-amalgam.c") if (MSVC) - set_source_files_properties(${OPENRCT2_DUKTAPE_SOURCES} PROPERTIES COMPILE_FLAGS "/w") + set_source_files_properties(${OPENRCT2_QUICKJS_NG_SOURCES} PROPERTIES COMPILE_FLAGS "/w") else () - set_source_files_properties(${OPENRCT2_DUKTAPE_SOURCES} PROPERTIES COMPILE_FLAGS "-w") + set_source_files_properties(${OPENRCT2_QUICKJS_NG_SOURCES} PROPERTIES COMPILE_FLAGS "-w") endif () endif () @@ -26,17 +26,17 @@ endif () # https://github.com/OpenRCT2/OpenRCT2/issues/24936 set(LIBOPENRCT2_LINKAGE) if (APPLE) - set(LIBOPENRCT2_LINKAGE STATIC) + set(LIBOPENRCT2_LINKAGE STATIC) endif() -add_library(libopenrct2 ${LIBOPENRCT2_LINKAGE} ${OPENRCT2_CORE_SOURCES} ${OPENRCT2_CORE_MM_SOURCES} ${OPENRCT2_DUKTAPE_SOURCES}) +add_library(libopenrct2 ${LIBOPENRCT2_LINKAGE} ${OPENRCT2_CORE_SOURCES} ${OPENRCT2_CORE_MM_SOURCES} ${OPENRCT2_QUICKJS_NG_SOURCES}) add_library(OpenRCT2::libopenrct2 ALIAS libopenrct2) if (CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "13") - message(WARNING "Buggy GCC 12 detected! Disabling some warnings") - set_source_files_properties("${CMAKE_CURRENT_LIST_DIR}/localisation/FormatCodes.cpp" PROPERTIES COMPILE_FLAGS "-Wno-restrict") - set_source_files_properties("${CMAKE_CURRENT_LIST_DIR}/platform/Platform.Posix.cpp" PROPERTIES COMPILE_FLAGS "-Wno-restrict") - set_source_files_properties("${CMAKE_CURRENT_LIST_DIR}/ride/Ride.cpp" PROPERTIES COMPILE_FLAGS "-Wno-null-dereference") - set_source_files_properties("${CMAKE_CURRENT_LIST_DIR}/scenario/Scenario.cpp" PROPERTIES COMPILE_FLAGS "-Wno-restrict") + message(WARNING "Buggy GCC 12 detected! Disabling some warnings") + set_source_files_properties("${CMAKE_CURRENT_LIST_DIR}/localisation/FormatCodes.cpp" PROPERTIES COMPILE_FLAGS "-Wno-restrict") + set_source_files_properties("${CMAKE_CURRENT_LIST_DIR}/platform/Platform.Posix.cpp" PROPERTIES COMPILE_FLAGS "-Wno-restrict") + set_source_files_properties("${CMAKE_CURRENT_LIST_DIR}/ride/Ride.cpp" PROPERTIES COMPILE_FLAGS "-Wno-null-dereference") + set_source_files_properties("${CMAKE_CURRENT_LIST_DIR}/scenario/Scenario.cpp" PROPERTIES COMPILE_FLAGS "-Wno-restrict") endif() if (APPLE) target_link_platform_libraries(libopenrct2) @@ -152,23 +152,23 @@ endif () if (STATIC) target_link_libraries(libopenrct2 - ${PNG_STATIC_LIBRARIES} - ${ZLIB_STATIC_LIBRARIES} - ${LIBZIP_STATIC_LIBRARIES} - ${ZSTD_STATIC_LIBRARIES}) + ${PNG_STATIC_LIBRARIES} + ${ZLIB_STATIC_LIBRARIES} + ${LIBZIP_STATIC_LIBRARIES} + ${ZSTD_STATIC_LIBRARIES}) else () if (NOT MSVC AND NOT EMSCRIPTEN) target_link_libraries(libopenrct2 - PkgConfig::PNG - PkgConfig::ZLIB - PkgConfig::LIBZIP - PkgConfig::ZSTD) + PkgConfig::PNG + PkgConfig::ZLIB + PkgConfig::LIBZIP + PkgConfig::ZSTD) else () target_link_libraries(libopenrct2 - ${PNG_LIBRARIES} - ${ZLIB_LIBRARIES} - ${LIBZIP_LIBRARIES} - ${ZSTD_LIBRARIES}) + ${PNG_LIBRARIES} + ${ZLIB_LIBRARIES} + ${LIBZIP_LIBRARIES} + ${ZSTD_LIBRARIES}) endif () endif () @@ -247,7 +247,7 @@ endif() # Includes target_include_directories(libopenrct2 SYSTEM PRIVATE ${LIBZIP_INCLUDE_DIRS}) target_include_directories(libopenrct2 SYSTEM PRIVATE ${PNG_INCLUDE_DIRS} - ${ZLIB_INCLUDE_DIRS}) + ${ZLIB_INCLUDE_DIRS}) target_include_directories(libopenrct2 SYSTEM PRIVATE ${ZSTD_INCLUDE_DIRS}) include_directories(libopenrct2 SYSTEM ${CMAKE_CURRENT_LIST_DIR}/../thirdparty) @@ -256,20 +256,20 @@ include_directories(libopenrct2 SYSTEM ${CMAKE_CURRENT_LIST_DIR}/../thirdparty) # definitions: Version.cpp and Crash/Platform.cpp if (NOT OPENRCT2_VERSION_TAG STREQUAL "") set_property(SOURCE ${CMAKE_CURRENT_LIST_DIR}/Version.cpp ${CMAKE_CURRENT_LIST_DIR}/Crash/Platform.cpp - PROPERTY COMPILE_DEFINITIONS - OPENRCT2_VERSION_TAG="${OPENRCT2_VERSION_TAG}") + PROPERTY COMPILE_DEFINITIONS + OPENRCT2_VERSION_TAG="${OPENRCT2_VERSION_TAG}") endif() if (NOT OPENRCT2_BRANCH STREQUAL "master" AND NOT OPENRCT2_BRANCH STREQUAL "") set_property(SOURCE ${CMAKE_CURRENT_LIST_DIR}/Version.cpp ${CMAKE_CURRENT_LIST_DIR}/Crash/Platform.cpp - APPEND PROPERTY COMPILE_DEFINITIONS - OPENRCT2_BRANCH="${OPENRCT2_BRANCH}") + APPEND PROPERTY COMPILE_DEFINITIONS + OPENRCT2_BRANCH="${OPENRCT2_BRANCH}") endif() if (NOT OPENRCT2_COMMIT_SHA1_SHORT STREQUAL "HEAD" AND NOT OPENRCT2_COMMIT_SHA1_SHORT STREQUAL "") set_property(SOURCE ${CMAKE_CURRENT_LIST_DIR}/Version.cpp ${CMAKE_CURRENT_LIST_DIR}/Crash/Platform.cpp - APPEND PROPERTY COMPILE_DEFINITIONS - OPENRCT2_COMMIT_SHA1_SHORT="${OPENRCT2_COMMIT_SHA1_SHORT}") + APPEND PROPERTY COMPILE_DEFINITIONS + OPENRCT2_COMMIT_SHA1_SHORT="${OPENRCT2_COMMIT_SHA1_SHORT}") endif() if((X86 OR X86_64) AND NOT MSVC AND NOT EMSCRIPTEN) @@ -285,7 +285,7 @@ if (ENABLE_HEADERS_CHECK AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") add_library(libopenrct2-headers-check OBJECT ${OPENRCT2_CORE_HEADERS}) set_target_properties(libopenrct2-headers-check PROPERTIES LINKER_LANGUAGE CXX) set_source_files_properties(${OPENRCT2_CORE_HEADERS} PROPERTIES LANGUAGE CXX) - add_definitions("-x c++ -Wno-pragma-once-outside-header -Wno-unused-const-variable") + target_compile_options(libopenrct2-headers-check PRIVATE -Wno-pragma-once-outside-header -Wno-unused-const-variable) get_target_property(LIBOPENRCT2_INCLUDE_DIRS libopenrct2 INCLUDE_DIRECTORIES) set_target_properties(libopenrct2-headers-check PROPERTIES INCLUDE_DIRECTORIES "${LIBOPENRCT2_INCLUDE_DIRS}") else () @@ -299,12 +299,12 @@ endif () # Defines target_compile_definitions(libopenrct2 PUBLIC - $<$:USE_MMAP> - $<$:DISABLE_NETWORK> - $<$:DISABLE_HTTP> - $<$:DISABLE_TTF> - $<$:DISABLE_VERSION_CHECKER> - $<$:ENABLE_SCRIPTING> + $<$:USE_MMAP> + $<$:DISABLE_NETWORK> + $<$:DISABLE_HTTP> + $<$:DISABLE_TTF> + $<$:DISABLE_VERSION_CHECKER> + $<$:ENABLE_SCRIPTING> ) target_compile_options(libopenrct2 PUBLIC $<$:-fsanitize=address>) diff --git a/src/openrct2/Context.cpp b/src/openrct2/Context.cpp index 5ddc791fa4..7e76c5f0c2 100644 --- a/src/openrct2/Context.cpp +++ b/src/openrct2/Context.cpp @@ -208,7 +208,7 @@ namespace OpenRCT2 #endif auto* windowMgr = GetWindowManager(); - windowMgr->CloseAll(); + windowMgr->Cleanup(); // Unload objects after closing all windows, this is to overcome windows like // the object selection window which loads objects when closed. diff --git a/src/openrct2/actions/GameAction.cpp b/src/openrct2/actions/GameAction.cpp index 3eb5ac5d1c..6d0b5d3a50 100644 --- a/src/openrct2/actions/GameAction.cpp +++ b/src/openrct2/actions/GameAction.cpp @@ -11,7 +11,6 @@ #include "../Context.h" #include "../core/Guard.hpp" -#include "../scripting/Duktape.hpp" #include "../scripting/HookEngine.h" #include "../scripting/ScriptEngine.h" #include "../world/Map.h" @@ -39,21 +38,21 @@ namespace OpenRCT2::GameActions auto ctx = GetContext()->GetScriptEngine().GetContext(); // Create event args object - auto obj = Scripting::DukObject(ctx); - obj.Set("x", coords.x); - obj.Set("y", coords.y); - obj.Set("player", _playerId); - obj.Set("type", EnumValue(_type)); + JSValue obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "x", JS_NewInt32(ctx, coords.x)); + JS_SetPropertyStr(ctx, obj, "y", JS_NewInt32(ctx, coords.y)); + JS_SetPropertyStr(ctx, obj, "player", JS_NewInt32(ctx, _playerId)); + JS_SetPropertyStr(ctx, obj, "type", JS_NewInt32(ctx, EnumValue(_type))); auto flags = GetActionFlags(); - obj.Set("isClientOnly", (flags & Flags::ClientOnly) != 0); - obj.Set("result", true); + JS_SetPropertyStr(ctx, obj, "isClientOnly", JS_NewBool(ctx, (flags & Flags::ClientOnly) != 0)); + JS_SetPropertyStr(ctx, obj, "result", JS_NewBool(ctx, true)); // Call the subscriptions - auto e = obj.Take(); - hookEngine.Call(Scripting::HookType::actionLocation, e, true); + hookEngine.Call(Scripting::HookType::actionLocation, obj, true, true); - auto scriptResult = Scripting::AsOrDefault(e["result"], true); + auto scriptResult = Scripting::AsOrDefault(ctx, obj, "result", true); + JS_FreeValue(ctx, obj); return scriptResult; } diff --git a/src/openrct2/actions/GameActionRunner.cpp b/src/openrct2/actions/GameActionRunner.cpp index 3d73dfea26..1293b25fa2 100644 --- a/src/openrct2/actions/GameActionRunner.cpp +++ b/src/openrct2/actions/GameActionRunner.cpp @@ -23,7 +23,6 @@ #include "../platform/Platform.h" #include "../profiling/Profiling.h" #include "../scenario/Scenario.h" -#include "../scripting/Duktape.hpp" #include "../scripting/HookEngine.h" #include "../scripting/ScriptEngine.h" #include "../ui/WindowManager.h" diff --git a/src/openrct2/actions/general/CustomAction.cpp b/src/openrct2/actions/general/CustomAction.cpp index 82a4c50c3b..d748b991ee 100644 --- a/src/openrct2/actions/general/CustomAction.cpp +++ b/src/openrct2/actions/general/CustomAction.cpp @@ -8,7 +8,6 @@ *****************************************************************************/ #ifdef ENABLE_SCRIPTING - #include "CustomAction.h" #include "../../Context.h" @@ -23,17 +22,17 @@ namespace OpenRCT2::GameActions { } - std::string CustomAction::GetId() const + const std::string& CustomAction::GetId() const { return _id; } - std::string CustomAction::GetJson() const + const std::string& CustomAction::GetJson() const { return _json; } - std::string CustomAction::GetPluginName() const + const std::string& CustomAction::GetPluginName() const { return _pluginName; } diff --git a/src/openrct2/actions/general/CustomAction.h b/src/openrct2/actions/general/CustomAction.h index 9521d4c464..8c548fa109 100644 --- a/src/openrct2/actions/general/CustomAction.h +++ b/src/openrct2/actions/general/CustomAction.h @@ -26,9 +26,9 @@ namespace OpenRCT2::GameActions CustomAction() = default; CustomAction(const std::string& id, const std::string& json, const std::string& pluginName); - std::string GetId() const; - std::string GetJson() const; - std::string GetPluginName() const; + const std::string& GetId() const; + const std::string& GetJson() const; + const std::string& GetPluginName() const; uint16_t GetActionFlags() const override; diff --git a/src/openrct2/core/File.cpp b/src/openrct2/core/File.cpp index 1b752fa2e8..d788bf636c 100644 --- a/src/openrct2/core/File.cpp +++ b/src/openrct2/core/File.cpp @@ -87,6 +87,8 @@ namespace OpenRCT2::File } else { + // Reserve capacity for fsize + 1 to support the caller adding a null terminator if they want (needed for quickjs) + result.reserve(fsize + 1); result.resize(static_cast(fsize)); fstream.Read(result.data(), result.size()); } diff --git a/src/openrct2/entity/Guest.cpp b/src/openrct2/entity/Guest.cpp index a5a18f3369..cc63b5fc7c 100644 --- a/src/openrct2/entity/Guest.cpp +++ b/src/openrct2/entity/Guest.cpp @@ -7416,15 +7416,7 @@ namespace OpenRCT2 auto& hookEngine = GetContext()->GetScriptEngine().GetHookEngine(); if (hookEngine.HasSubscriptions(Scripting::HookType::guestGeneration)) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - - // Create event args object - auto obj = Scripting::DukObject(ctx); - obj.Set("id", peep->Id.ToUnderlying()); - - // Call the subscriptions - auto e = obj.Take(); - hookEngine.Call(Scripting::HookType::guestGeneration, e, true); + hookEngine.Call(Scripting::HookType::guestGeneration, { { "id", peep->Id.ToUnderlying() } }, true); } #endif diff --git a/src/openrct2/interface/Window.cpp b/src/openrct2/interface/Window.cpp index f6d0130935..622aca4dcf 100644 --- a/src/openrct2/interface/Window.cpp +++ b/src/openrct2/interface/Window.cpp @@ -164,17 +164,22 @@ static constexpr float kWindowScrollLocations[][2] = { } } - /** - * - * rct2: 0x006E77A1 - */ - void WindowUpdateAll() + void WindowCullDead() { // Remove all windows in gWindowList that have the WindowFlag::dead flag gWindowList.erase( std::remove_if( gWindowList.begin(), gWindowList.end(), [](auto&& w) -> bool { return w->flags.has(WindowFlag::dead); }), gWindowList.end()); + } + + /** + * + * rct2: 0x006E77A1 + */ + void WindowUpdateAll() + { + WindowCullDead(); // Periodic update happens every second so 40 ticks. if (gCurrentRealTimeTicks >= gWindowUpdateTicks) diff --git a/src/openrct2/interface/Window.h b/src/openrct2/interface/Window.h index 825717be8f..d098cb17af 100644 --- a/src/openrct2/interface/Window.h +++ b/src/openrct2/interface/Window.h @@ -295,6 +295,7 @@ namespace OpenRCT2 void WindowDispatchUpdateAll(); void WindowUpdateAllViewports(); + void WindowCullDead(); void WindowUpdateAll(); void WindowNotifyLanguageChange(); diff --git a/src/openrct2/libopenrct2.vcxproj b/src/openrct2/libopenrct2.vcxproj index f0d3c75903..3e065295d1 100644 --- a/src/openrct2/libopenrct2.vcxproj +++ b/src/openrct2/libopenrct2.vcxproj @@ -658,11 +658,11 @@ - + @@ -1199,6 +1199,7 @@ + @@ -1261,11 +1262,12 @@ - - NotUsing - - + + CompileAsC + stdc11 TurnOffAllWarnings + false + /experimental:c11atomics %(AdditionalOptions) @@ -1274,4 +1276,4 @@ - + \ No newline at end of file diff --git a/src/openrct2/network/NetworkBase.cpp b/src/openrct2/network/NetworkBase.cpp index 2cb7ba5ffb..9daa234965 100644 --- a/src/openrct2/network/NetworkBase.cpp +++ b/src/openrct2/network/NetworkBase.cpp @@ -1900,18 +1900,19 @@ namespace OpenRCT2::Network auto ctx = GetContext()->GetScriptEngine().GetContext(); // Create event args object - DukObject eObj(ctx); - eObj.Set("name", name); - eObj.Set("publicKeyHash", publicKeyHash); - eObj.Set("ipAddress", connection.Socket->GetIpAddress()); - eObj.Set("cancel", false); - auto e = eObj.Take(); + JSValue obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "name", JSFromStdString(ctx, name)); + JS_SetPropertyStr(ctx, obj, "publicKeyHash", JSFromStdString(ctx, publicKeyHash)); + JS_SetPropertyStr(ctx, obj, "ipAddress", JSFromStdString(ctx, connection.Socket->GetIpAddress())); + JS_SetPropertyStr(ctx, obj, "cancel", JS_NewBool(ctx, false)); // Call the subscriptions - hookEngine.Call(HookType::networkAuthenticate, e, false); + hookEngine.Call(HookType::networkAuthenticate, obj, false, true); // Check if any hook has cancelled the join - if (AsOrDefault(e["cancel"], false)) + const bool canceled = AsOrDefault(ctx, obj, "cancel", false); + JS_FreeValue(ctx, obj); + if (canceled) { return false; } @@ -1931,12 +1932,11 @@ namespace OpenRCT2::Network auto ctx = GetContext()->GetScriptEngine().GetContext(); // Create event args object - DukObject eObj(ctx); - eObj.Set("player", playerId); - auto e = eObj.Take(); + JSValue obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "player", JS_NewInt32(ctx, playerId)); // Call the subscriptions - hookEngine.Call(HookType::networkJoin, e, false); + hookEngine.Call(HookType::networkJoin, obj, false); } #endif } @@ -1952,12 +1952,11 @@ namespace OpenRCT2::Network auto ctx = GetContext()->GetScriptEngine().GetContext(); // Create event args object - DukObject eObj(ctx); - eObj.Set("player", playerId); - auto e = eObj.Take(); + JSValue obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "player", JS_NewInt32(ctx, playerId)); // Call the subscriptions - hookEngine.Call(HookType::networkLeave, e, false); + hookEngine.Call(HookType::networkJoin, obj, false); } #endif } @@ -2902,23 +2901,23 @@ namespace OpenRCT2::Network auto ctx = GetContext()->GetScriptEngine().GetContext(); // Create event args object - auto objIdx = duk_push_object(ctx); - duk_push_number(ctx, playerId); - duk_put_prop_string(ctx, objIdx, "player"); - duk_push_string(ctx, text.c_str()); - duk_put_prop_string(ctx, objIdx, "message"); - auto e = DukValue::take_from_stack(ctx); + JSValue obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "player", JS_NewInt32(ctx, playerId)); + JS_SetPropertyStr(ctx, obj, "message", Scripting::JSFromStdString(ctx, text)); // Call the subscriptions - hookEngine.Call(Scripting::HookType::networkChat, e, false); + hookEngine.Call(Scripting::HookType::networkChat, obj, false, true); // Update text from object if subscriptions changed it - if (e["message"].type() != DukValue::Type::STRING) + auto message = Scripting::JSToOptionalStdString(ctx, obj, "message"); + JS_FreeValue(ctx, obj); + + if (!message.has_value()) { // Subscription set text to non-string, do not relay message return false; } - text = e["message"].as_string(); + text = message.value(); if (text.empty()) { // Subscription set text to empty string, do not relay message diff --git a/src/openrct2/park/ParkFile.cpp b/src/openrct2/park/ParkFile.cpp index bb5e1c8bae..34d6de9090 100644 --- a/src/openrct2/park/ParkFile.cpp +++ b/src/openrct2/park/ParkFile.cpp @@ -767,7 +767,7 @@ namespace OpenRCT2 { #ifdef ENABLE_SCRIPTING auto& scriptEngine = GetContext()->GetScriptEngine(); - scriptEngine.SetParkStorageFromJSON(gameState.pluginStorage); + scriptEngine.SetParkStorageFromJSON(gameState.pluginStorage, gameState.scenarioFileName); #endif } } diff --git a/src/openrct2/ride/RideRatings.cpp b/src/openrct2/ride/RideRatings.cpp index 9407db8da3..1f5663637b 100644 --- a/src/openrct2/ride/RideRatings.cpp +++ b/src/openrct2/ride/RideRatings.cpp @@ -1079,19 +1079,19 @@ static void RideRatingsCalculate(RideRating::UpdateState& state, Ride& ride) auto originalRatings = ride.ratings; // Create event args object - auto obj = DukObject(ctx); - obj.Set("rideId", ride.id.ToUnderlying()); - obj.Set("excitement", originalRatings.excitement); - obj.Set("intensity", originalRatings.intensity); - obj.Set("nausea", originalRatings.nausea); + JSValue obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "rideId", JS_NewInt32(ctx, ride.id.ToUnderlying())); + JS_SetPropertyStr(ctx, obj, "excitement", JS_NewInt32(ctx, originalRatings.excitement)); + JS_SetPropertyStr(ctx, obj, "intensity", JS_NewInt32(ctx, originalRatings.intensity)); + JS_SetPropertyStr(ctx, obj, "nausea", JS_NewInt32(ctx, originalRatings.nausea)); // Call the subscriptions - auto e = obj.Take(); - hookEngine.Call(HookType::rideRatingsCalculate, e, true); + hookEngine.Call(HookType::rideRatingsCalculate, obj, true, true); - auto scriptExcitement = AsOrDefault(e["excitement"], static_cast(originalRatings.excitement)); - auto scriptIntensity = AsOrDefault(e["intensity"], static_cast(originalRatings.intensity)); - auto scriptNausea = AsOrDefault(e["nausea"], static_cast(originalRatings.nausea)); + auto scriptExcitement = AsOrDefault(ctx, obj, "excitement", static_cast(originalRatings.excitement)); + auto scriptIntensity = AsOrDefault(ctx, obj, "intensity", static_cast(originalRatings.intensity)); + auto scriptNausea = AsOrDefault(ctx, obj, "nausea", static_cast(originalRatings.nausea)); + JS_FreeValue(ctx, obj); ride.ratings.excitement = std::clamp(scriptExcitement, 0, INT16_MAX); ride.ratings.intensity = std::clamp(scriptIntensity, 0, INT16_MAX); diff --git a/src/openrct2/ride/Vehicle.Crash.cpp b/src/openrct2/ride/Vehicle.Crash.cpp index 257668f3c5..3a94b09746 100644 --- a/src/openrct2/ride/Vehicle.Crash.cpp +++ b/src/openrct2/ride/Vehicle.Crash.cpp @@ -46,16 +46,15 @@ static void InvokeVehicleCrashHook(const EntityId vehicleId, const std::string_v auto& hookEngine = GetContext()->GetScriptEngine().GetHookEngine(); if (hookEngine.HasSubscriptions(Scripting::HookType::vehicleCrash)) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); + JSContext* ctx = GetContext()->GetScriptEngine().GetContext(); // Create event args object - auto obj = Scripting::DukObject(ctx); - obj.Set("id", vehicleId.ToUnderlying()); - obj.Set("crashIntoType", crashId); + JSValue obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "id", JS_NewInt64(ctx, vehicleId.ToUnderlying())); + JS_SetPropertyStr(ctx, obj, "crashIntoType", Scripting::JSFromStdString(ctx, crashId)); // Call the subscriptions - auto e = obj.Take(); - hookEngine.Call(Scripting::HookType::vehicleCrash, e, true); + hookEngine.Call(Scripting::HookType::vehicleCrash, obj, true); } } #endif diff --git a/src/openrct2/scripting/Duktape.hpp b/src/openrct2/scripting/Duktape.hpp deleted file mode 100644 index dccff77800..0000000000 --- a/src/openrct2/scripting/Duktape.hpp +++ /dev/null @@ -1,504 +0,0 @@ -/***************************************************************************** - * Copyright (c) 2014-2026 OpenRCT2 developers - * - * For a complete list of all authors, please refer to contributors.md - * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 - * - * OpenRCT2 is licensed under the GNU General Public License version 3. - *****************************************************************************/ - -#pragma once - -#ifdef ENABLE_SCRIPTING - - #include "../core/Console.hpp" - #include "../core/EnumMap.hpp" - #include "../ride/Vehicle.h" - - #include - #include - #include - #include - #include - -namespace OpenRCT2::Scripting -{ - template - DukValue GetObjectAsDukValue(duk_context* ctx, const std::shared_ptr& value) - { - dukglue::types::DukType>::template push(ctx, value); - return DukValue::take_from_stack(ctx); - } - - template - T AsOrDefault(const DukValue& value, const T& defaultValue = {}) = delete; - - inline std::string AsOrDefault(const DukValue& value, std::string_view defaultValue) - { - return value.type() == DukValue::STRING ? value.as_string() : std::string(defaultValue); - } - - template<> - inline std::string AsOrDefault(const DukValue& value, const std::string& defaultValue) - { - return value.type() == DukValue::STRING ? value.as_string() : defaultValue; - } - - template<> - inline int32_t AsOrDefault(const DukValue& value, const int32_t& defaultValue) - { - return value.type() == DukValue::NUMBER ? value.as_int() : defaultValue; - } - - template<> - inline bool AsOrDefault(const DukValue& value, const bool& defaultValue) - { - return value.type() == DukValue::BOOLEAN ? value.as_bool() : defaultValue; - } - - enum class DukUndefined - { - }; - constexpr DukUndefined undefined{}; - - /** - * Allows creation of an object on the duktape stack and setting properties on it before - * retrieving the DukValue instance of it. - */ - class DukObject - { - private: - duk_context* _ctx{}; - duk_idx_t _idx = DUK_INVALID_INDEX; - - public: - DukObject(duk_context* ctx) - : _ctx(ctx) - { - } - - DukObject(const DukObject&) = delete; - - DukObject(DukObject&& m) noexcept - { - _ctx = m._ctx; - _idx = m._idx; - m._ctx = {}; - m._idx = {}; - } - - ~DukObject() - { - PopObjectIfExists(); - } - - void Set(const char* name, std::nullptr_t) - { - EnsureObjectPushed(); - duk_push_null(_ctx); - duk_put_prop_string(_ctx, _idx, name); - } - - void Set(const char* name, DukUndefined) - { - EnsureObjectPushed(); - duk_push_undefined(_ctx); - duk_put_prop_string(_ctx, _idx, name); - } - - void Set(const char* name, bool value) - { - EnsureObjectPushed(); - duk_push_boolean(_ctx, value); - duk_put_prop_string(_ctx, _idx, name); - } - - void Set(const char* name, int32_t value) - { - EnsureObjectPushed(); - duk_push_int(_ctx, value); - duk_put_prop_string(_ctx, _idx, name); - } - - void Set(const char* name, uint32_t value) - { - EnsureObjectPushed(); - duk_push_uint(_ctx, value); - duk_put_prop_string(_ctx, _idx, name); - } - - void Set(const char* name, int64_t value) - { - EnsureObjectPushed(); - duk_push_number(_ctx, value); - duk_put_prop_string(_ctx, _idx, name); - } - - void Set(const char* name, uint64_t value) - { - EnsureObjectPushed(); - duk_push_number(_ctx, value); - duk_put_prop_string(_ctx, _idx, name); - } - - void Set(const char* name, double value) - { - EnsureObjectPushed(); - duk_push_number(_ctx, value); - duk_put_prop_string(_ctx, _idx, name); - } - - void Set(const char* name, std::string_view value) - { - EnsureObjectPushed(); - duk_push_lstring(_ctx, value.data(), value.size()); - duk_put_prop_string(_ctx, _idx, name); - } - - void Set(const char* name, const char* value) - { - Set(name, std::string_view(value)); - } - - void Set(const char* name, const DukValue& value) - { - EnsureObjectPushed(); - value.push(); - duk_put_prop_string(_ctx, _idx, name); - } - - template - void Set(const char* name, const std::optional& value) - { - if (value) - { - EnsureObjectPushed(); - duk_push_null(_ctx); - duk_put_prop_string(_ctx, _idx, name); - } - else - { - Set(name, *value); - } - } - - DukValue Take() - { - EnsureObjectPushed(); - auto result = DukValue::take_from_stack(_ctx, _idx); - _idx = DUK_INVALID_INDEX; - return result; - } - - private: - void PopObjectIfExists() - { - if (_idx != DUK_INVALID_INDEX) - { - duk_remove(_ctx, _idx); - _idx = DUK_INVALID_INDEX; - } - } - - void EnsureObjectPushed() - { - if (_idx == DUK_INVALID_INDEX) - { - _idx = duk_push_object(_ctx); - } - } - }; - - class DukStackFrame - { - private: - duk_context* _ctx{}; - duk_idx_t _top; - - public: - DukStackFrame(duk_context* ctx) - : _ctx(ctx) - { - _top = duk_get_top(ctx); - } - - ~DukStackFrame() - { - auto top = duk_get_top(_ctx); - if (top != _top) - { - duk_set_top(_ctx, _top); - _ctx = {}; - Console::Error::WriteLine("duktape stack was not returned to original state!"); - } - _ctx = {}; - } - - DukStackFrame(const DukStackFrame&) = delete; - DukStackFrame(DukStackFrame&&) = delete; - }; - - /** - * Bi-directional map for converting between strings and enums / numbers. - */ - template - using DukEnumMap = EnumMap; - - inline duk_ret_t duk_json_decode_wrapper(duk_context* ctx, void*) - { - duk_json_decode(ctx, -1); - return 1; - } - - inline std::optional DuktapeTryParseJson(duk_context* ctx, std::string_view json) - { - duk_push_lstring(ctx, json.data(), json.size()); - if (duk_safe_call(ctx, duk_json_decode_wrapper, nullptr, 1, 1) == DUK_EXEC_SUCCESS) - { - return DukValue::take_from_stack(ctx); - } - - // Pop error off stack - duk_pop(ctx); - return std::nullopt; - } - - std::string ProcessString(const DukValue& value); - - template - DukValue ToDuk(duk_context* ctx, const T& value) = delete; - template - T FromDuk(const DukValue& s) = delete; - - template<> - inline DukValue ToDuk(duk_context* ctx, const std::nullptr_t&) - { - duk_push_null(ctx); - return DukValue::take_from_stack(ctx); - } - - template<> - inline DukValue ToDuk(duk_context* ctx, const DukUndefined&) - { - duk_push_undefined(ctx); - return DukValue::take_from_stack(ctx); - } - - template<> - inline DukValue ToDuk(duk_context* ctx, const bool& value) - { - duk_push_boolean(ctx, value); - return DukValue::take_from_stack(ctx); - } - - template<> - inline DukValue ToDuk(duk_context* ctx, const uint8_t& value) - { - duk_push_int(ctx, value); - return DukValue::take_from_stack(ctx); - } - - template<> - inline DukValue ToDuk(duk_context* ctx, const uint16_t& value) - { - duk_push_int(ctx, value); - return DukValue::take_from_stack(ctx); - } - - template<> - inline DukValue ToDuk(duk_context* ctx, const int32_t& value) - { - duk_push_int(ctx, value); - return DukValue::take_from_stack(ctx); - } - - template<> - inline DukValue ToDuk(duk_context* ctx, const int64_t& value) - { - duk_push_number(ctx, value); - return DukValue::take_from_stack(ctx); - } - - template<> - inline DukValue ToDuk(duk_context* ctx, const std::string_view& value) - { - duk_push_lstring(ctx, value.data(), value.size()); - return DukValue::take_from_stack(ctx); - } - - template<> - inline DukValue ToDuk(duk_context* ctx, const std::string& value) - { - return ToDuk(ctx, std::string_view(value)); - } - - template - inline DukValue ToDuk(duk_context* ctx, const char (&value)[TLen]) - { - duk_push_string(ctx, value); - return DukValue::take_from_stack(ctx); - } - - template - inline DukValue ToDuk(duk_context* ctx, const std::optional& value) - { - return value ? ToDuk(ctx, *value) : ToDuk(ctx, nullptr); - } - - template<> - CoordsXY inline FromDuk(const DukValue& d) - { - CoordsXY result; - result.x = AsOrDefault(d["x"], 0); - result.y = AsOrDefault(d["y"], 0); - return result; - } - - template<> - MapRange inline FromDuk(const DukValue& d) - { - MapRange range; - range.Point1 = FromDuk(d["leftTop"]); - range.Point2 = FromDuk(d["rightBottom"]); - return range.Normalise(); - } - - template<> - DukValue inline ToDuk(duk_context* ctx, const CoordsXY& coords) - { - DukObject dukCoords(ctx); - dukCoords.Set("x", coords.x); - dukCoords.Set("y", coords.y); - return dukCoords.Take(); - } - - template<> - DukValue inline ToDuk(duk_context* ctx, const TileCoordsXY& coords) - { - DukObject dukCoords(ctx); - dukCoords.Set("x", coords.x); - dukCoords.Set("y", coords.y); - return dukCoords.Take(); - } - - template<> - DukValue inline ToDuk(duk_context* ctx, const ScreenCoordsXY& coords) - { - DukObject dukCoords(ctx); - dukCoords.Set("x", coords.x); - dukCoords.Set("y", coords.y); - return dukCoords.Take(); - } - - template<> - inline DukValue ToDuk(duk_context* ctx, const CoordsXYZ& value) - { - if (value.IsNull()) - { - return ToDuk(ctx, nullptr); - } - - DukObject dukCoords(ctx); - dukCoords.Set("x", value.x); - dukCoords.Set("y", value.y); - dukCoords.Set("z", value.z); - return dukCoords.Take(); - } - - template<> - inline CoordsXYZ FromDuk(const DukValue& value) - { - CoordsXYZ result; - if (value.type() == DukValue::Type::OBJECT) - { - result.x = AsOrDefault(value["x"], 0); - result.y = AsOrDefault(value["y"], 0); - result.z = AsOrDefault(value["z"], 0); - } - else - { - result.SetNull(); - } - return result; - } - - template<> - inline DukValue ToDuk(duk_context* ctx, const CoordsXYZD& value) - { - if (value.IsNull()) - { - return ToDuk(ctx, nullptr); - } - - DukObject dukCoords(ctx); - dukCoords.Set("x", value.x); - dukCoords.Set("y", value.y); - dukCoords.Set("z", value.z); - dukCoords.Set("direction", value.direction); - return dukCoords.Take(); - } - - template<> - inline DukValue ToDuk(duk_context* ctx, const GForces& value) - { - DukObject dukGForces(ctx); - dukGForces.Set("lateralG", value.lateralG); - dukGForces.Set("verticalG", value.verticalG); - return dukGForces.Take(); - } - - template<> - inline DukValue ToDuk(duk_context* ctx, const VehicleSpriteGroup& value) - { - DukObject dukSpriteGroup(ctx); - dukSpriteGroup.Set("imageId", value.imageId); - dukSpriteGroup.Set("spriteNumImages", OpenRCT2::Entity::Yaw::NumSpritesPrecision(value.spritePrecision)); - return dukSpriteGroup.Take(); - } - - template<> - inline CoordsXYZD FromDuk(const DukValue& value) - { - CoordsXYZD result; - if (value.type() == DukValue::Type::OBJECT) - { - result.x = AsOrDefault(value["x"], 0); - result.y = AsOrDefault(value["y"], 0); - result.z = AsOrDefault(value["z"], 0); - result.direction = AsOrDefault(value["direction"], 0); - } - else - { - result.SetNull(); - } - return result; - } - - template<> - inline DukValue ToDuk(duk_context* ctx, const ScreenSize& value) - { - DukObject dukCoords(ctx); - dukCoords.Set("width", value.width); - dukCoords.Set("height", value.height); - return dukCoords.Take(); - } - - template<> - ObjectEntryIndex inline FromDuk(const DukValue& d) - { - if (d.type() == DukValue::Type::NUMBER) - { - auto value = d.as_int(); - if (value >= 0 && value <= std::numeric_limits::max()) - { - return static_cast(value); - } - } - return kObjectEntryIndexNull; - } - - uint32_t ImageFromDuk(const DukValue& d); - -} // namespace OpenRCT2::Scripting - -#endif diff --git a/src/openrct2/scripting/HookEngine.cpp b/src/openrct2/scripting/HookEngine.cpp index 16599f9fa2..12632981e6 100644 --- a/src/openrct2/scripting/HookEngine.cpp +++ b/src/openrct2/scripting/HookEngine.cpp @@ -14,8 +14,6 @@ #include "../core/EnumMap.hpp" #include "ScriptEngine.h" - #include - using namespace OpenRCT2::Scripting; static const EnumMap HooksLookupTable( @@ -54,7 +52,7 @@ HookEngine::HookEngine(ScriptEngine& scriptEngine) } } -uint32_t HookEngine::Subscribe(HookType type, std::shared_ptr owner, const DukValue& function) +uint32_t HookEngine::Subscribe(HookType type, const std::shared_ptr& owner, const JSCallback& function) { auto& hookList = GetHookList(type); auto cookie = _nextCookie++; @@ -76,7 +74,7 @@ void HookEngine::Unsubscribe(HookType type, uint32_t cookie) } } -void HookEngine::UnsubscribeAll(std::shared_ptr owner) +void HookEngine::UnsubscribeAll(const std::shared_ptr& owner) { for (auto& hookList : _hookMap) { @@ -115,51 +113,45 @@ void HookEngine::Call(HookType type, bool isGameStateMutable) auto& hookList = GetHookList(type); for (auto& hook : hookList.Hooks) { - _scriptEngine.ExecutePluginCall(hook.Owner, hook.Function, {}, isGameStateMutable); + _scriptEngine.ExecutePluginCall(hook.Owner, hook.Function.callback, {}, isGameStateMutable); } } -void HookEngine::Call(HookType type, const DukValue& arg, bool isGameStateMutable) +void HookEngine::Call(HookType type, const JSValue arg, bool isGameStateMutable, bool keepArgsAlive) { auto& hookList = GetHookList(type); for (auto& hook : hookList.Hooks) { - _scriptEngine.ExecutePluginCall(hook.Owner, hook.Function, { arg }, isGameStateMutable); + _scriptEngine.ExecutePluginCall(hook.Owner, hook.Function.callback, { arg }, isGameStateMutable, keepArgsAlive); } } void HookEngine::Call( - HookType type, const std::initializer_list>& args, bool isGameStateMutable) + HookType type, const std::initializer_list>& args, bool isGameStateMutable) { auto& hookList = GetHookList(type); for (auto& hook : hookList.Hooks) { - auto ctx = _scriptEngine.GetContext(); + JSContext* ctx = hook.Owner.get()->GetContext(); // Convert key/value pairs into an object - auto objIdx = duk_push_object(ctx); + JSValue obj = JS_NewObject(ctx); for (const auto& arg : args) { - if (arg.second.type() == typeid(int32_t)) - { - auto val = std::any_cast(arg.second); - duk_push_int(ctx, val); - } - else if (arg.second.type() == typeid(std::string)) - { - const auto& val = std::any_cast(arg.second); - duk_push_string(ctx, val.c_str()); - } - else - { - throw std::runtime_error("Not implemented"); - } - duk_put_prop_string(ctx, objIdx, arg.first.data()); + JSValue member = std::visit( + HookValuesToJS{ + [ctx](int64_t v) { return JS_NewInt64(ctx, v); }, + [ctx](uint32_t v) { return JS_NewInt64(ctx, v); }, + [ctx](int32_t v) { return JS_NewInt32(ctx, v); }, + [ctx](uint16_t v) { return JS_NewInt32(ctx, v); }, + [ctx](int16_t v) { return JS_NewInt32(ctx, v); }, + [ctx](const std::string& v) { return JSFromStdString(ctx, v); }, + }, + arg.second); + JS_SetPropertyStr(ctx, obj, arg.first.c_str(), member); } - std::vector dukArgs; - dukArgs.push_back(DukValue::take_from_stack(ctx)); - _scriptEngine.ExecutePluginCall(hook.Owner, hook.Function, dukArgs, isGameStateMutable); + _scriptEngine.ExecutePluginCall(hook.Owner, hook.Function.callback, { obj }, isGameStateMutable); } } diff --git a/src/openrct2/scripting/HookEngine.h b/src/openrct2/scripting/HookEngine.h index 072abb1394..13ea6c37b4 100644 --- a/src/openrct2/scripting/HookEngine.h +++ b/src/openrct2/scripting/HookEngine.h @@ -11,12 +11,12 @@ #ifdef ENABLE_SCRIPTING - #include "Duktape.hpp" + #include "ScriptUtil.hpp" #include #include #include - #include + #include #include namespace OpenRCT2::Scripting @@ -49,14 +49,22 @@ namespace OpenRCT2::Scripting constexpr size_t NUM_HookTypeS = static_cast(HookType::count); HookType GetHookType(const std::string& name); + using HookValue = std::variant; + + template + struct HookValuesToJS : Ts... + { + using Ts::operator()...; + }; + struct Hook { uint32_t Cookie; std::shared_ptr Owner; - DukValue Function; + JSCallback Function; Hook() = default; - Hook(uint32_t cookie, std::shared_ptr owner, const DukValue& function) + Hook(uint32_t cookie, const std::shared_ptr& owner, const JSCallback& function) : Cookie(cookie) , Owner(owner) , Function(function) @@ -84,16 +92,15 @@ namespace OpenRCT2::Scripting public: HookEngine(ScriptEngine& scriptEngine); HookEngine(const HookEngine&) = delete; - uint32_t Subscribe(HookType type, std::shared_ptr owner, const DukValue& function); + uint32_t Subscribe(HookType type, const std::shared_ptr& owner, const JSCallback& function); void Unsubscribe(HookType type, uint32_t cookie); - void UnsubscribeAll(std::shared_ptr owner); + void UnsubscribeAll(const std::shared_ptr& owner); void UnsubscribeAll(); bool HasSubscriptions(HookType type) const; bool IsValidHookForPlugin(HookType type, Plugin& plugin) const; void Call(HookType type, bool isGameStateMutable); - void Call(HookType type, const DukValue& arg, bool isGameStateMutable); - void Call( - HookType type, const std::initializer_list>& args, bool isGameStateMutable); + void Call(HookType type, JSValue arg, bool isGameStateMutable, bool keepArgsAlive = false); + void Call(HookType type, const std::initializer_list>& args, bool isGameStateMutable); private: HookList& GetHookList(HookType type); diff --git a/src/openrct2/scripting/Plugin.cpp b/src/openrct2/scripting/Plugin.cpp index 21a8a1a803..b938fd7cbc 100644 --- a/src/openrct2/scripting/Plugin.cpp +++ b/src/openrct2/scripting/Plugin.cpp @@ -11,10 +11,10 @@ #include "Plugin.h" + #include "../Context.h" #include "../Diagnostic.h" #include "../OpenRCT2.h" #include "../core/File.h" - #include "Duktape.hpp" #include "ScriptEngine.h" #include @@ -22,9 +22,8 @@ using namespace OpenRCT2::Scripting; -Plugin::Plugin(duk_context* context, std::string_view path) - : _context(context) - , _path(path) +Plugin::Plugin(std::string_view path) + : _path(path) { } @@ -33,45 +32,46 @@ void Plugin::SetCode(std::string_view code) _code = code; } +static JSValue registerPlugin(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) +{ + Plugin* plugin = static_cast(JS_GetContextOpaque(ctx)); + plugin->SetMetadata(argv[0]); + return JS_UNDEFINED; +} + void Plugin::Load() { + if (_context) + { + JS_FreeContext(_context); + } + if (!_path.empty()) { LoadCodeFromFile(); } - std::string projectedVariables = "console,context,date,map,network,park,profiler"; - if (!gOpenRCT2Headless) + auto& scriptEngine = OpenRCT2::GetContext()->GetScriptEngine(); + _context = scriptEngine.CreateContext(); + JS_SetContextOpaque(_context, this); + + JSValue registerFunc = JS_NewCFunction(_context, registerPlugin, "registerPlugin", 1); + JSValue glb = JS_GetGlobalObject(_context); + JS_SetPropertyStr(_context, glb, "registerPlugin", registerFunc); + JS_FreeValue(_context, glb); + + JSValue res = JS_Eval(_context, _code.c_str(), _code.length(), _path.c_str(), JS_EVAL_TYPE_GLOBAL); + if (JS_IsException(res)) { - projectedVariables += ",ui"; + JSValue exceptionVal = JS_GetException(_context); + std::string details = Stringify(_context, exceptionVal); + JS_FreeValue(_context, exceptionVal); + JS_FreeValue(_context, res); + JS_FreeContext(_context); + throw std::runtime_error("Failed to load plug-in script: " + _path + "\n" + details); } + JS_FreeValue(_context, res); - // Wrap the script in a function and pass the global objects as variables - // so that if the script modifies them, they are not modified for other scripts. - - // clang-format off - auto code = _code; - code = - " (function(" + projectedVariables + ") {" - " var __metadata__ = null;" - " var registerPlugin = function(m) { __metadata__ = m };" - " (function(__metadata__) {" - + code + - " })();" - " return __metadata__;" - " })(" + projectedVariables + ");"; - // clang-format on - - auto flags = DUK_COMPILE_EVAL | DUK_COMPILE_SAFE | DUK_COMPILE_NOSOURCE | DUK_COMPILE_NOFILENAME; - auto result = duk_eval_raw(_context, code.c_str(), code.size(), flags); - if (result != DUK_ERR_NONE) - { - auto val = std::string(duk_safe_to_string(_context, -1)); - duk_pop(_context); - throw std::runtime_error("Failed to load plug-in script: " + val + " at " + _path); - } - - _metadata = GetMetadata(DukValue::take_from_stack(_context)); _hasLoaded = true; } @@ -82,23 +82,24 @@ void Plugin::Start() throw std::runtime_error("Plugin has not been loaded."); } - const auto& mainFunc = _metadata.Main; - if (mainFunc.context() == nullptr) + const JSValue mainFunc = _metadata.Main.callback; + if (!JS_IsFunction(_context, mainFunc)) { throw std::runtime_error("No main function specified."); } _hasStarted = true; - mainFunc.push(); - auto result = duk_pcall(_context, 0); - if (result != DUK_ERR_NONE) + const JSValue res = JS_Call(_context, mainFunc, JS_UNDEFINED, 0, nullptr); + if (JS_IsException(res)) { - auto val = std::string(duk_safe_to_string(_context, -1)); - duk_pop(_context); - throw std::runtime_error("[" + _metadata.Name + "] " + val); + JSValue exceptionVal = JS_GetException(_context); + std::string details = Stringify(_context, exceptionVal); + JS_FreeValue(_context, exceptionVal); + JS_FreeValue(_context, res); + throw std::runtime_error("[" + _metadata.Name + "]\n" + details); } - duk_pop(_context); + JS_FreeValue(_context, res); } void Plugin::StopBegin() @@ -112,16 +113,16 @@ void Plugin::StopEnd() _hasStarted = false; } -void Plugin::ThrowIfStopping() const -{ - if (IsStopping()) - { - duk_error(_context, DUK_ERR_ERROR, "Plugin is stopping."); - } -} - void Plugin::Unload() { + if (!_context) + { + throw std::runtime_error("Plugin is not loaded"); + } + + JS_FreeContext(_context); + _context = nullptr; + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105937, fixed in GCC13 #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push @@ -139,34 +140,51 @@ void Plugin::LoadCodeFromFile() _code = File::ReadAllText(_path); } -static std::string TryGetString(const DukValue& value, const std::string& message) +std::string Plugin::TryGetString(const JSValue value, const char* property, const std::string& message) const { - if (value.type() != DukValue::Type::STRING) + const JSValue res = JS_GetPropertyStr(_context, value, property); + if (!JS_IsString(res)) + { + JS_FreeValue(_context, res); throw std::runtime_error(message); - return value.as_string(); + } + std::string str = JSToStdString(_context, res); + JS_FreeValue(_context, res); + return str; } -PluginMetadata Plugin::GetMetadata(const DukValue& dukMetadata) +void Plugin::SetMetadata(const JSValue obj) { - PluginMetadata metadata; - if (dukMetadata.type() == DukValue::Type::OBJECT) + PluginMetadata metadata{}; + if (JS_IsObject(obj)) { - metadata.Name = TryGetString(dukMetadata["name"], "Plugin name not specified."); - metadata.Version = TryGetString(dukMetadata["version"], "Plugin version not specified."); - metadata.Type = ParsePluginType(TryGetString(dukMetadata["type"], "Plugin type not specified.")); + metadata.Name = TryGetString(obj, "name", "Plugin name not specified."); + metadata.Version = TryGetString(obj, "version", "Plugin version not specified."); + metadata.Type = ParsePluginType(TryGetString(obj, "type", "Plugin type not specified.")); - CheckForLicence(dukMetadata["licence"], metadata.Name); - - auto dukMinApiVersion = dukMetadata["minApiVersion"]; - if (dukMinApiVersion.type() == DukValue::Type::NUMBER) + const JSValue licence = JS_GetPropertyStr(_context, obj, "licence"); + int64_t licenseLen = -1; + if (!JS_IsString(licence) || JS_GetLength(_context, licence, &licenseLen) == -1 || licenseLen == 0) { - metadata.MinApiVersion = dukMinApiVersion.as_uint(); + LOG_ERROR("Plugin %s does not specify a licence", _metadata.Name.c_str()); } + JS_FreeValue(_context, licence); - auto dukTargetApiVersion = dukMetadata["targetApiVersion"]; - if (dukTargetApiVersion.type() == DukValue::Type::NUMBER) + const JSValue minApiVersion = JS_GetPropertyStr(_context, obj, "minApiVersion"); + if (JS_IsNumber(minApiVersion)) { - metadata.TargetApiVersion = dukTargetApiVersion.as_uint(); + int64_t val = -1; + JS_ToInt64(_context, &val, minApiVersion); + metadata.MinApiVersion = val; + } + JS_FreeValue(_context, minApiVersion); + + const JSValue targetApiVersion = JS_GetPropertyStr(_context, obj, "targetApiVersion"); + if (JS_IsNumber(targetApiVersion)) + { + int64_t val = -1; + JS_ToInt64(_context, &val, targetApiVersion); + metadata.TargetApiVersion = val; } else { @@ -174,23 +192,25 @@ PluginMetadata Plugin::GetMetadata(const DukValue& dukMetadata) u8"Plug-in “%s” does not specify a target API version or specifies it incorrectly. Emulating deprecated APIs.", metadata.Name.c_str()); } + JS_FreeValue(_context, targetApiVersion); - auto dukAuthors = dukMetadata["authors"]; - dukAuthors.push(); - if (dukAuthors.is_array()) + const JSValue authors = JS_GetPropertyStr(_context, obj, "authors"); + if (JS_IsArray(authors)) { - auto elements = dukAuthors.as_array(); - std::transform(elements.begin(), elements.end(), std::back_inserter(metadata.Authors), [](const DukValue& v) { - return v.as_string(); + JSIterateArray(_context, authors, [&metadata](JSContext* ctx2, JSValue val) { + if (JS_IsString(val)) + metadata.Authors.emplace_back(JSToStdString(ctx2, val)); }); } - else if (dukAuthors.type() == DukValue::Type::STRING) + else if (JS_IsString(authors)) { - metadata.Authors = { dukAuthors.as_string() }; + metadata.Authors = { JSToStdString(_context, authors) }; } - metadata.Main = dukMetadata["main"]; + JS_FreeValue(_context, authors); + + metadata.Main = JSToCallback(_context, obj, "main"); } - return metadata; + _metadata = metadata; } PluginType Plugin::ParsePluginType(std::string_view type) @@ -204,12 +224,6 @@ PluginType Plugin::ParsePluginType(std::string_view type) throw std::invalid_argument("Unknown plugin type."); } -void Plugin::CheckForLicence(const DukValue& dukLicence, std::string_view pluginName) -{ - if (dukLicence.type() != DukValue::Type::STRING || dukLicence.as_string().empty()) - LOG_ERROR("Plugin %s does not specify a licence", std::string(pluginName).c_str()); -} - int32_t Plugin::GetTargetAPIVersion() const { if (_metadata.TargetApiVersion) diff --git a/src/openrct2/scripting/Plugin.h b/src/openrct2/scripting/Plugin.h index 591db46504..66572873ee 100644 --- a/src/openrct2/scripting/Plugin.h +++ b/src/openrct2/scripting/Plugin.h @@ -11,9 +11,10 @@ #ifdef ENABLE_SCRIPTING - #include "Duktape.hpp" + #include "ScriptUtil.hpp" - #include + #include + #include #include #include #include @@ -49,13 +50,13 @@ namespace OpenRCT2::Scripting PluginType Type{}; int32_t MinApiVersion{}; std::optional TargetApiVersion{}; - DukValue Main; + JSCallback Main; }; class Plugin { private: - duk_context* _context{}; + JSContext* _context = nullptr; std::string _path; PluginMetadata _metadata{}; std::string _code; @@ -63,12 +64,19 @@ namespace OpenRCT2::Scripting bool _hasStarted{}; bool _isStopping{}; + std::string TryGetString(JSValue value, const char* property, const std::string& message) const; + public: std::string_view GetPath() const { return _path; } + JSContext* GetContext() const + { + return _context; + } + bool HasPath() const { return !_path.empty(); @@ -79,6 +87,8 @@ namespace OpenRCT2::Scripting return _metadata; } + void SetMetadata(JSValue obj); + const std::string& GetCode() const { return _code; @@ -102,7 +112,7 @@ namespace OpenRCT2::Scripting int32_t GetTargetAPIVersion() const; Plugin() = default; - Plugin(duk_context* context, std::string_view path); + explicit Plugin(std::string_view path); Plugin(const Plugin&) = delete; Plugin(Plugin&&) = delete; @@ -112,7 +122,6 @@ namespace OpenRCT2::Scripting void StopBegin(); void StopEnd(); - void ThrowIfStopping() const; void Unload(); bool IsTransient() const; @@ -120,9 +129,8 @@ namespace OpenRCT2::Scripting private: void LoadCodeFromFile(); - static PluginMetadata GetMetadata(const DukValue& dukMetadata); static PluginType ParsePluginType(std::string_view type); - static void CheckForLicence(const DukValue& dukLicence, std::string_view pluginName); + static void CheckForLicence(JSValue dukLicence, std::string_view pluginName); }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/ScriptEngine.cpp b/src/openrct2/scripting/ScriptEngine.cpp index 90d48e3b07..f436307697 100644 --- a/src/openrct2/scripting/ScriptEngine.cpp +++ b/src/openrct2/scripting/ScriptEngine.cpp @@ -11,6 +11,7 @@ #include "ScriptEngine.h" + #include "../Context.h" #include "../PlatformEnvironment.h" #include "../actions/GameAction.hpp" #include "../actions/general/CustomAction.h" @@ -20,6 +21,7 @@ #include "../actions/scenery/LargeSceneryPlaceAction.h" #include "../actions/scenery/WallPlaceAction.h" #include "../config/Config.h" + #include "../core/Console.hpp" #include "../core/EnumMap.hpp" #include "../core/File.h" #include "../core/FileScanner.h" @@ -27,7 +29,7 @@ #include "../interface/InteractiveConsole.h" #include "../platform/Platform.h" #include "../ride/ted/PitchAndRoll.h" - #include "Duktape.hpp" + #include "../profiling/Profiling.h" #include "bindings/entity/ScBalloon.hpp" #include "bindings/entity/ScEntity.hpp" #include "bindings/entity/ScGuest.hpp" @@ -79,10 +81,10 @@ struct ExpressionStringifier final { private: std::stringstream _ss; - duk_context* _context{}; + JSContext* _context{}; int32_t _indent{}; - ExpressionStringifier(duk_context* ctx) + ExpressionStringifier(JSContext* ctx) : _context(ctx) { } @@ -102,7 +104,7 @@ private: _ss << "\n" << std::string(_indent, ' '); } - void Stringify(const DukValue& val, bool canStartWithNewLine, int32_t nestLevel) + void Stringify(const JSValue val, bool canStartWithNewLine, int32_t nestLevel) { if (nestLevel >= 8) { @@ -110,73 +112,78 @@ private: return; } - switch (val.type()) + if (JS_IsUndefined(val)) { - case DukValue::Type::UNDEFINED: - _ss << "undefined"; - break; - case DukValue::Type::NULLREF: - _ss << "null"; - break; - case DukValue::Type::BOOLEAN: - StringifyBoolean(val); - break; - case DukValue::Type::NUMBER: - StringifyNumber(val); - break; - case DukValue::Type::STRING: - _ss << "'" << val.as_string() << "'"; - break; - case DukValue::Type::OBJECT: - if (val.is_function()) - { - StringifyFunction(val); - } - else if (val.is_array()) - { - StringifyArray(val, canStartWithNewLine, nestLevel); - } - else - { - StringifyObject(val, canStartWithNewLine, nestLevel); - } - break; - case DukValue::Type::BUFFER: - _ss << "[Buffer]"; - break; - case DukValue::Type::POINTER: - _ss << "[Pointer]"; - break; - case DukValue::Type::LIGHTFUNC: - _ss << "[LightFunc]"; - break; + _ss << "undefined"; + } + else if (JS_IsNull(val)) + { + _ss << "null"; + } + else if (JS_IsUninitialized(val)) + { + _ss << "uninitialized"; + } + else if (JS_IsBool(val)) + { + _ss << (JS_VALUE_GET_BOOL(val) ? "true" : "false"); + } + else if (JS_IsNumber(val) || JS_IsBigInt(val)) + { + StringifyNumber(val); + } + else if (JS_IsString(val)) + { + StringifyString(val); + } + else if (JS_IsObject(val)) + { + if (JS_IsFunction(_context, val)) + { + StringifyFunction(val); + } + else if (JS_IsArray(val)) + { + StringifyArray(val, canStartWithNewLine, nestLevel); + } + else if (JS_IsError(val)) + { + StringifyError(val); + } + else + { + StringifyObject(val, canStartWithNewLine, nestLevel); + } + } + else if (JS_IsArrayBuffer(val)) + { + _ss << "[Buffer]"; + } + else + { + _ss << "[Unknown Value]"; } } - void StringifyArray(const DukValue& val, bool canStartWithNewLine, int32_t nestLevel) + void StringifyArray(const JSValue val, bool canStartWithNewLine, int32_t nestLevel) { - constexpr auto maxItemsToShow = 4; + constexpr int64_t maxItemsToShow = 4; - val.push(); - auto arrayLen = duk_get_length(_context, -1); - if (arrayLen == 0) + int64_t arrayLen; + if (JS_GetLength(_context, val, &arrayLen) == -1) + { + _ss << "[error printing array]"; + } + else if (arrayLen == 0) { _ss << "[]"; } else if (arrayLen == 1) { _ss << "[ "; - for (duk_uarridx_t i = 0; i < arrayLen; i++) - { - if (duk_get_prop_index(_context, -1, i)) - { - if (i != 0) - { - _ss << ", "; - } - Stringify(DukValue::take_from_stack(_context), false, nestLevel + 1); - } - } + JSValue prop = JS_GetPropertyInt64(_context, val, 0); + Stringify(prop, false, nestLevel + 1); + JS_FreeValue(_context, prop); _ss << " ]"; } else @@ -188,7 +195,7 @@ private: } _ss << "[ "; PushIndent(2); - for (duk_uarridx_t i = 0; i < arrayLen; i++) + for (int64_t i = 0; i < arrayLen; i++) { if (i != 0) { @@ -209,10 +216,9 @@ private: break; } - if (duk_get_prop_index(_context, -1, i)) - { - Stringify(DukValue::take_from_stack(_context), false, nestLevel + 1); - } + JSValue prop = JS_GetPropertyInt64(_context, val, i); + Stringify(prop, false, nestLevel + 1); + JS_FreeValue(_context, prop); } _ss << " ]"; PopIndent(2); @@ -221,44 +227,37 @@ private: PopIndent(); } } - duk_pop(_context); } - void StringifyObject(const DukValue& val, bool canStartWithNewLine, int32_t nestLevel) + void StringifyObject(const JSValue val, bool canStartWithNewLine, int32_t nestLevel) { - auto numEnumerables = GetNumEnumerablesOnObject(val); - if (numEnumerables == 0) + JSPropertyEnum* props; + uint32_t propsLen; + JS_GetOwnPropertyNames(_context, &props, &propsLen, val, JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK | JS_GPN_PRIVATE_MASK); + + if (propsLen == 0) { _ss << "{}"; } - else if (numEnumerables == 1) + else if (propsLen == 1) { _ss << "{ "; - val.push(); - duk_enum(_context, -1, 0); - auto index = 0; - while (duk_next(_context, -1, 1)) + const char* key = JS_AtomToCString(_context, props[0].atom); + if (key) { - if (index != 0) - { - _ss << ", "; - } - auto value = DukValue::take_from_stack(_context, -1); - auto key = DukValue::take_from_stack(_context, -1); - if (key.type() == DukValue::Type::STRING) - { - _ss << key.as_string() << ": "; - } - else - { - // For some reason the key was not a string - _ss << "?: "; - } - Stringify(value, true, nestLevel + 1); - index++; + _ss << key << ": "; + JS_FreeCString(_context, key); } - duk_pop_2(_context); + else + { + // For some reason the key was not a string + _ss << "?: "; + } + + JSValue prop = JS_GetProperty(_context, val, props[0].atom); + Stringify(prop, true, nestLevel + 1); + JS_FreeValue(_context, prop); _ss << " }"; } @@ -273,31 +272,29 @@ private: _ss << "{ "; PushIndent(2); - val.push(); - duk_enum(_context, -1, 0); - auto index = 0; - while (duk_next(_context, -1, 1)) + for (uint32_t i = 0; i < propsLen; i++) { - if (index != 0) + if (i != 0) { _ss << ","; LineFeed(); } - auto value = DukValue::take_from_stack(_context, -1); - auto key = DukValue::take_from_stack(_context, -1); - if (key.type() == DukValue::Type::STRING) + const char* key = JS_AtomToCString(_context, props[i].atom); + if (key) { - _ss << key.as_string() << ": "; + _ss << key << ": "; + JS_FreeCString(_context, key); } else { // For some reason the key was not a string _ss << "?: "; } - Stringify(value, true, nestLevel + 1); - index++; + + JSValue prop = JS_GetProperty(_context, val, props[i].atom); + Stringify(prop, true, nestLevel + 1); + JS_FreeValue(_context, prop); } - duk_pop_2(_context); PopIndent(2); _ss << " }"; @@ -307,57 +304,107 @@ private: PopIndent(); } } + + JS_FreePropertyEnum(_context, props, propsLen); } - void StringifyFunction(const DukValue& val) + void StringifyFunction(const JSValue val) { - val.push(); - if (duk_is_c_function(_context, -1)) + if (JS_IsConstructor(_context, val)) { - _ss << "[Native Function]"; - } - else if (duk_is_ecmascript_function(_context, -1)) - { - _ss << "[ECMAScript Function]"; + _ss << "[Constructor]"; } else { _ss << "[Function]"; } - duk_pop(_context); } - void StringifyBoolean(const DukValue& val) + void StringifyString(const JSValue val) { - _ss << (val.as_bool() ? "true" : "false"); - } - - void StringifyNumber(const DukValue& val) - { - const auto d = val.as_double(); - const duk_int_t i = val.as_int(); - if (AlmostEqual(d, i)) + size_t len; + const char* str = JS_ToCStringLen(_context, &len, val); + if (str) { - _ss << std::to_string(i); + _ss << "'" << std::string_view(str, len) << "'"; + JS_FreeCString(_context, str); } else { - _ss << std::to_string(d); + _ss << "[error printing string]"; } } - size_t GetNumEnumerablesOnObject(const DukValue& val) + void StringifyNumber(const JSValue val) { - size_t count = 0; - val.push(); - duk_enum(_context, -1, 0); - while (duk_next(_context, -1, 0)) + if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) { - count++; - duk_pop(_context); + _ss << std::to_string(JS_VALUE_GET_INT(val)); } - duk_pop_2(_context); - return count; + else if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(val))) + { + const double d = JS_VALUE_GET_FLOAT64(val); + const int64_t i = static_cast(d); + if (AlmostEqual(d, i)) + { + _ss << std::to_string(i); + } + else + { + _ss << std::to_string(d); + } + } + else if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) + { + _ss << std::to_string(JS_VALUE_GET_SHORT_BIG_INT(val)); + } + else if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT) + { + const char* str = JS_ToCString(_context, val); + if (str) + { + _ss << str; + JS_FreeCString(_context, str); + } + else + { + _ss << "[BitInt error]"; + } + } + } + + void StringifyError(const JSValue val) + { + const char* str = JS_ToCString(_context, val); + if (str) + { + _ss << str; + JS_FreeCString(_context, str); + } + else + { + _ss << "[error]"; + } + JSValue stackVal = JS_GetPropertyStr(_context, val, "stack"); + if (!JS_IsUndefined(stackVal)) + { + const char* stackStr = JS_ToCString(_context, stackVal); + if (stackStr) + { + LineFeed(); + std::string_view view(stackStr); + if (view.ends_with('\n')) + view = view.substr(0, view.length() - 1); + _ss << view; + JS_FreeCString(_context, stackStr); + } + else + { + LineFeed(); + _ss << "[no stack trace]"; + } + } + JS_FreeValue(_context, stackVal); } // Taken from http://en.cppreference.com/w/cpp/types/numeric_limits/epsilon @@ -373,28 +420,14 @@ private: } public: - static std::string StringifyExpression(const DukValue& val) + static std::string StringifyExpression(JSContext* ctx, const JSValue val) { - ExpressionStringifier instance(val.context()); + ExpressionStringifier instance(ctx); instance.Stringify(val, false, 0); return instance._ss.str(); } }; -DukContext::DukContext() -{ - _context = duk_create_heap_default(); - if (_context == nullptr) - { - throw std::runtime_error("Unable to initialise duktape context."); - } -} - -DukContext::~DukContext() -{ - duk_destroy_heap(_context); -} - ScriptEngine::ScriptEngine(InteractiveConsole& console, IPlatformEnvironment& env) : _console(console) , _env(env) @@ -406,80 +439,26 @@ void ScriptEngine::Initialise() { if (_initialised) throw std::runtime_error("Script engine already initialised."); + if (!_runtime) + { + _runtime = JS_NewRuntime(); + if (!_runtime) + throw std::runtime_error("QuickJS: cannot allocate JS runtime\n"); - auto ctx = static_cast(_context); - ScAward::Register(ctx); - ScCheats::Register(ctx); - ScWeather::Register(ctx); - ScWeatherState::Register(ctx); - ScConfiguration::Register(ctx); - ScConsole::Register(ctx); - ScContext::Register(ctx); - ScDate::Register(ctx); - ScDisposable::Register(ctx); - ScMap::Register(ctx); - ScNetwork::Register(ctx); - ScObjectManager::Register(ctx); - ScInstalledObject::Register(ctx); - ScObject::Register(ctx); - ScSceneryObject::Register(ctx); - ScSmallSceneryObject::Register(ctx); - ScLargeSceneryObject::Register(ctx); - ScLargeSceneryObjectTile::Register(ctx); - ScWallObject::Register(ctx); - ScFootpathAdditionObject::Register(ctx); - ScBannerObject::Register(ctx); - ScSceneryGroupObject::Register(ctx); - ScPark::Register(ctx); - ScParkMessage::Register(ctx); - ScPlayer::Register(ctx); - ScPlayerGroup::Register(ctx); - ScProfiler::Register(ctx); - ScResearch::Register(ctx); - ScRide::Register(ctx); - ScRideStation::Register(ctx); - ScRideObject::Register(ctx); - ScRideObjectVehicle::Register(ctx); - ScTile::Register(ctx); - ScTileElement::Register(ctx); - ScTrackIterator::Register(ctx); - ScTrackSegment::Register(ctx); - ScEntity::Register(ctx); - ScLitter::Register(ctx); - ScBalloon::Register(ctx); - ScMoneyEffect::Register(ctx); - ScVehicle::Register(ctx); - ScCrashedVehicleParticle::Register(ctx); - ScPeep::Register(ctx); - ScGuest::Register(ctx); - ScThought::Register(ctx); - #ifndef DISABLE_NETWORK - ScSocket::Register(ctx); - ScListener::Register(ctx); + #ifndef NDEBUG + // Dump JS engine memory leaks + JS_SetDumpFlags(_runtime, JS_DUMP_LEAKS); #endif - ScScenario::Register(ctx); - ScScenarioObjective::Register(ctx); - ScPatrolArea::Register(ctx); - ScStaff::Register(ctx); - ScHandyman::Register(ctx); - ScMechanic::Register(ctx); - ScSecurity::Register(ctx); - ScPlugin::Register(ctx); + } - dukglue_register_global(ctx, std::make_shared(), "cheats"); - dukglue_register_global(ctx, std::make_shared(), "climate"); - dukglue_register_global(ctx, std::make_shared(_console), "console"); - dukglue_register_global(ctx, std::make_shared(_execInfo, _hookEngine), "context"); - dukglue_register_global(ctx, std::make_shared(), "date"); - dukglue_register_global(ctx, std::make_shared(ctx), "map"); - dukglue_register_global(ctx, std::make_shared(ctx), "network"); - dukglue_register_global(ctx, std::make_shared(ctx), "park"); - dukglue_register_global(ctx, std::make_shared(), "pluginManager"); - dukglue_register_global(ctx, std::make_shared(ctx), "profiler"); - dukglue_register_global(ctx, std::make_shared(), "scenario"); - dukglue_register_global(ctx, std::make_shared(), "objectManager"); + // Disable maximum stack size limit for the JS engine. + JS_SetMaxStackSize(_runtime, 0); - RegisterConstants(); + _replContext = JS_NewContext(_runtime); + if (!_replContext) + throw std::runtime_error("QuickJS: cannot allocate REPL JS context\n"); + RegisterClasses(_replContext); + InitialiseContext(_replContext); _initialised = true; _transientPluginsEnabled = false; @@ -489,57 +468,230 @@ void ScriptEngine::Initialise() ClearParkStorage(); } +JSRuntime* ScriptEngine::_runtime = nullptr; +ScAward Scripting::gScAward; +ScCheats Scripting::gScCheats; +ScWeather Scripting::gScWeather; +ScWeatherState Scripting::gScWeatherState; +ScConfiguration Scripting::gScConfiguration; +ScConsole Scripting::gScConsole; +ScContext Scripting::gScContext; +ScDate Scripting::gScDate; +ScDisposable Scripting::gScDisposable; +ScMap Scripting::gScMap; +ScNetwork Scripting::gScNetwork; +ScObjectManager Scripting::gScObjectManager; +ScInstalledObject Scripting::gScInstalledObject; +ScLargeSceneryObjectTile Scripting::gScLargeSceneryObjectTile; +ScObject Scripting::gScObject; +ScPark Scripting::gScPark; +ScParkMessage Scripting::gScParkMessage; +ScPlayer Scripting::gScPlayer; +ScPlayerGroup Scripting::gScPlayerGroup; +ScProfiler Scripting::gScProfiler; +ScResearch Scripting::gScResearch; +ScRide Scripting::gScRide; +ScRideStation Scripting::gScRideStation; +ScRideObjectVehicle Scripting::gScRideObjectVehicle; +ScTile Scripting::gScTile; +ScTileElement Scripting::gScTileElement; +ScTrackIterator Scripting::gScTrackIterator; +ScTrackSegment Scripting::gScTrackSegment; +ScEntity Scripting::gScEntity; +ScThought Scripting::gScThought; + #ifndef DISABLE_NETWORK +ScSocket Scripting::gScSocket; +ScListener Scripting::gScListener; + #endif +ScScenario Scripting::gScScenario; +ScScenarioObjective Scripting::gScScenarioObjective; +ScPatrolArea Scripting::gScPatrolArea; +ScPlugin Scripting::gScPlugin; + +void ScriptEngine::RegisterClasses(JSContext* ctx) +{ + gScAward.Register(ctx); + gScCheats.Register(ctx); + gScWeather.Register(ctx); + gScWeatherState.Register(ctx); + gScConfiguration.Register(ctx); + gScConsole.Register(ctx); + gScContext.Register(ctx); + gScDate.Register(ctx); + gScDisposable.Register(ctx); + gScMap.Register(ctx); + gScNetwork.Register(ctx); + gScObjectManager.Register(ctx); + gScInstalledObject.Register(ctx); + gScObject.Register(ctx); + gScLargeSceneryObjectTile.Register(ctx); + gScPark.Register(ctx); + gScParkMessage.Register(ctx); + gScPlayer.Register(ctx); + gScPlayerGroup.Register(ctx); + gScProfiler.Register(ctx); + gScResearch.Register(ctx); + gScRide.Register(ctx); + gScRideStation.Register(ctx); + gScRideObjectVehicle.Register(ctx); + gScTile.Register(ctx); + gScTileElement.Register(ctx); + gScTrackIterator.Register(ctx); + gScTrackSegment.Register(ctx); + gScEntity.Register(ctx); + gScThought.Register(ctx); + #ifndef DISABLE_NETWORK + gScSocket.Register(ctx); + gScListener.Register(ctx); + #endif + gScScenario.Register(ctx); + gScScenarioObjective.Register(ctx); + gScPatrolArea.Register(ctx); + gScPlugin.Register(ctx); +} + +void ScriptEngine::UnregisterClasses() +{ + gScAward.Unregister(); + gScCheats.Unregister(); + gScWeather.Unregister(); + gScWeatherState.Unregister(); + gScConfiguration.Unregister(); + gScConsole.Unregister(); + gScContext.Unregister(); + gScDate.Unregister(); + gScDisposable.Unregister(); + gScMap.Unregister(); + gScNetwork.Unregister(); + gScObjectManager.Unregister(); + gScInstalledObject.Unregister(); + gScObject.Unregister(); + gScLargeSceneryObjectTile.Unregister(); + gScPark.Unregister(); + gScParkMessage.Unregister(); + gScPlayer.Unregister(); + gScPlayerGroup.Unregister(); + gScProfiler.Unregister(); + gScResearch.Unregister(); + gScRide.Unregister(); + gScRideStation.Unregister(); + gScRideObjectVehicle.Unregister(); + gScTile.Unregister(); + gScTileElement.Unregister(); + gScTrackIterator.Unregister(); + gScTrackSegment.Unregister(); + gScEntity.Unregister(); + gScThought.Unregister(); + #ifndef DISABLE_NETWORK + gScSocket.Unregister(); + gScListener.Unregister(); + #endif + gScScenario.Unregister(); + gScScenarioObjective.Unregister(); + gScPatrolArea.Unregister(); + gScPlugin.Unregister(); +} + +JSContext* ScriptEngine::CreateContext() const +{ + JSContext* newCtx = JS_NewContext(_runtime); + if (!newCtx) + { + throw std::runtime_error("QuickJS: cannot allocate JS context\n"); + } + InitialiseContext(newCtx); + + for (const auto& ext : _extensions) + { + ext.newContext(newCtx); + } + return newCtx; +} + +void ScriptEngine::InitialiseContext(JSContext* ctx) const +{ + JSValue glb = JS_GetGlobalObject(ctx); + JS_SetPropertyStr(ctx, glb, "cheats", gScCheats.New(ctx)); + JS_SetPropertyStr(ctx, glb, "climate", gScWeather.New(ctx)); + JS_SetPropertyStr(ctx, glb, "console", gScConsole.New(ctx, _console)); + JS_SetPropertyStr(ctx, glb, "context", gScContext.New(ctx)); + JS_SetPropertyStr(ctx, glb, "date", gScDate.New(ctx)); + JS_SetPropertyStr(ctx, glb, "map", gScMap.New(ctx)); + JS_SetPropertyStr(ctx, glb, "network", gScNetwork.New(ctx)); + JS_SetPropertyStr(ctx, glb, "park", gScPark.New(ctx)); + JS_SetPropertyStr(ctx, glb, "pluginManager", gScPlugin.New(ctx)); + JS_SetPropertyStr(ctx, glb, "profiler", gScProfiler.New(ctx)); + JS_SetPropertyStr(ctx, glb, "scenario", gScScenario.New(ctx)); + JS_SetPropertyStr(ctx, glb, "objectManager", gScObjectManager.New(ctx)); + JS_FreeValue(ctx, glb); + + RegisterConstants(ctx); +} + +ScriptEngine::~ScriptEngine() +{ + if (_replContext) + { + JS_FreeValue(_replContext, _sharedStorage); + JS_FreeValue(_replContext, _parkStorage); + JS_FreeContext(_replContext); + _replContext = nullptr; + } + if (_runtime) + { + JS_FreeRuntime(_runtime); + _runtime = nullptr; + } + for (const ExtensionCallbacks& ext : _extensions) + { + ext.unregister(); + } + UnregisterClasses(); +} + class ConstantBuilder { private: - duk_context* _ctx; - DukValue _obj; + JSContext* _ctx; + JSValue _obj; public: - ConstantBuilder(duk_context* ctx) + ConstantBuilder(JSContext* ctx) : _ctx(ctx) { - duk_push_global_object(_ctx); - _obj = DukValue::take_from_stack(_ctx); + _obj = JS_GetGlobalObject(_ctx); } ConstantBuilder& Namespace(std::string_view ns) { - auto flags = DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_HAVE_ENUMERABLE - | DUK_DEFPROP_HAVE_CONFIGURABLE | DUK_DEFPROP_HAVE_VALUE; - // Create a new object for namespace - duk_push_global_object(_ctx); - duk_push_lstring(_ctx, ns.data(), ns.size()); - duk_push_object(_ctx); - // Keep a reference to the namespace object - duk_dup_top(_ctx); - _obj = DukValue::take_from_stack(_ctx); + JS_FreeValue(_ctx, _obj); + _obj = JS_NewObject(_ctx); // Place the namespace object into the global context - duk_def_prop(_ctx, -3, flags); - duk_pop(_ctx); + JSValue global = JS_GetGlobalObject(_ctx); + JS_SetPropertyStr(_ctx, global, std::string(ns).c_str(), JS_DupValue(_ctx, _obj)); + JS_FreeValue(_ctx, global); return *this; } ConstantBuilder& Constant(std::string_view name, int32_t value) { - auto flags = DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_HAVE_ENUMERABLE - | DUK_DEFPROP_HAVE_CONFIGURABLE | DUK_DEFPROP_HAVE_VALUE; - _obj.push(); - duk_push_lstring(_ctx, name.data(), name.size()); - duk_push_int(_ctx, value); - duk_def_prop(_ctx, -3, flags); - duk_pop(_ctx); + JS_SetPropertyStr(_ctx, _obj, std::string(name).c_str(), JS_NewInt32(_ctx, value)); return *this; } + + ~ConstantBuilder() + { + JS_FreeValue(_ctx, _obj); + } }; -void ScriptEngine::RegisterConstants() +void ScriptEngine::RegisterConstants(JSContext* ctx) { - ConstantBuilder builder(_context); + ConstantBuilder builder(ctx); builder.Namespace("TrackSlope") .Constant("None", EnumValue(TrackPitch::none)) .Constant("Up25", EnumValue(TrackPitch::up25)) @@ -634,7 +786,7 @@ void ScriptEngine::UnregisterPlugin(std::string_view path) }); auto& plugin = *pluginIt; - StopPlugin(plugin); + StopPlugin(plugin, true); UnloadPlugin(plugin); LogPluginInfo(plugin, "Unregistered"); @@ -650,7 +802,7 @@ void ScriptEngine::RegisterPlugin(std::string_view path) { try { - auto plugin = std::make_shared(_context, path); + auto plugin = std::make_shared(path); // We must load the plugin to get the metadata for it ScriptExecutionInfo::PluginScope scope(_execInfo, plugin, false); @@ -714,7 +866,7 @@ void ScriptEngine::LoadTransientPlugins() void ScriptEngine::LoadPlugin(const std::string& path) { - auto plugin = std::make_shared(_context, path); + auto plugin = std::make_shared(path); LoadPlugin(plugin); } @@ -749,6 +901,8 @@ void ScriptEngine::UnloadPlugin(std::shared_ptr& plugin) { plugin->Unload(); LogPluginInfo(plugin, "Unloaded"); + // A GC run is required to clean up all JS objects before the game global context is cleaned up on shut down. + JS_RunGC(_runtime); } } @@ -769,9 +923,11 @@ void ScriptEngine::StartPlugin(std::shared_ptr plugin) } } -void ScriptEngine::StopPlugin(std::shared_ptr plugin) +void ScriptEngine::StopPlugin(std::shared_ptr plugin, bool unregistering) { - if (plugin->HasStarted()) + // This is hacky but on the title screen the plugin can register things without having + // been started. Therefore when unregistering we must make sure to clean those things up + if (plugin->HasStarted() || unregistering) { plugin->StopBegin(); @@ -785,7 +941,8 @@ void ScriptEngine::StopPlugin(std::shared_ptr plugin) _hookEngine.UnsubscribeAll(plugin); plugin->StopEnd(); - LogPluginInfo(plugin, "Stopped"); + if (!unregistering) + LogPluginInfo(plugin, "Stopped"); } } @@ -935,6 +1092,13 @@ void ScriptEngine::Tick() PROFILED_FUNCTION(); + JSContext* jobCtx; + while (true) + { + if (JS_ExecutePendingJob(_runtime, &jobCtx) == 0) + break; + } + CheckAndStartPlugins(); UpdateIntervals(); UpdateSockets(); @@ -969,17 +1133,20 @@ void ScriptEngine::ProcessREPL() _evalQueue.pop(); auto promise = std::move(std::get<0>(item)); auto command = std::move(std::get<1>(item)); - if (duk_peval_string(_context, command.c_str()) != 0) + + JSValue res = JS_Eval(_replContext, command.c_str(), command.length(), "", JS_EVAL_TYPE_GLOBAL); + if (JS_IsException(res)) { - std::string result = std::string(duk_safe_to_string(_context, -1)); - _console.WriteLineError(result); + JSValue exceptionVal = JS_GetException(_replContext); + _console.WriteLineError(Stringify(_replContext, exceptionVal)); + JS_FreeValue(_replContext, exceptionVal); } - else if (duk_get_type(_context, -1) != DUK_TYPE_UNDEFINED) + else if (!JS_IsUndefined(res)) { - auto result = Stringify(DukValue::copy_from_stack(_context, -1)); - _console.WriteLine(result); + _console.WriteLine(Stringify(_replContext, res)); } - duk_pop(_context); + JS_FreeValue(_replContext, res); + // Signal the promise so caller can continue promise.set_value(); } @@ -993,40 +1160,52 @@ std::future ScriptEngine::Eval(const std::string& s) return future; } -DukValue ScriptEngine::ExecutePluginCall( - const std::shared_ptr& plugin, const DukValue& func, const std::vector& args, bool isGameStateMutable) +void ScriptEngine::ExecutePluginCall( + const std::shared_ptr& plugin, const JSValue func, const std::vector& args, bool isGameStateMutable, + bool keepArgsAlive) { - duk_push_undefined(_context); - auto dukUndefined = DukValue::take_from_stack(_context); - return ExecutePluginCall(plugin, func, dukUndefined, args, isGameStateMutable); + ExecutePluginCall(plugin, func, JS_UNDEFINED, args, isGameStateMutable, keepArgsAlive); } -// Must pass plugin by-value, a JS function could destroy the original reference -DukValue ScriptEngine::ExecutePluginCall( - std::shared_ptr plugin, const DukValue& func, const DukValue& thisValue, const std::vector& args, - bool isGameStateMutable) +JSValue ScriptEngine::ExecutePluginCall( + const std::shared_ptr& plugin, const JSValue func, const JSValue thisValue, const std::vector& args, + bool isGameStateMutable, bool keepArgsAlive, bool keepRetValueAlive) { - DukStackFrame frame(_context); - if (func.is_function() && plugin->HasStarted()) + JSValue ret = JS_UNDEFINED; + + // Note: the plugin pointer is null when called from the repl, so we assume the repl JSContext in that case. + JSContext* ctx = plugin ? plugin->GetContext() : _replContext; + if (JS_IsFunction(ctx, func) && (!plugin || plugin->HasStarted())) { ScriptExecutionInfo::PluginScope scope(_execInfo, plugin, isGameStateMutable); - func.push(); - thisValue.push(); - for (const auto& arg : args) - { - arg.push(); - } - auto result = duk_pcall_method(_context, static_cast(args.size())); - if (result == DUK_EXEC_SUCCESS) - { - return DukValue::take_from_stack(_context); - } - auto message = duk_safe_to_string(_context, -1); - LogPluginInfo(plugin, message); - duk_pop(_context); + // The call can free itself (by closing windows/clearing timers etc.) so we need to dup the values to keep them alive + JS_DupValue(ctx, thisValue); + JS_DupValue(ctx, func); + ret = JS_Call(ctx, func, thisValue, static_cast(args.size()), const_cast(args.data())); + JS_FreeValue(ctx, thisValue); + JS_FreeValue(ctx, func); + + if (JS_IsException(ret)) + { + JSValue exceptionVal = JS_GetException(ctx); + _console.WriteLineError(Stringify(ctx, exceptionVal)); + JS_FreeValue(ctx, exceptionVal); + } + if (!keepRetValueAlive) + { + JS_FreeValue(ctx, ret); + ret = JS_UNDEFINED; + } } - return DukValue(); + [[likely]] if (!keepArgsAlive) + { + for (const JSValue& arg : args) + { + JS_FreeValue(ctx, arg); + } + } + return ret; } void ScriptEngine::LogPluginInfo(std::string_view message) @@ -1050,7 +1229,7 @@ void ScriptEngine::LogPluginInfo(const std::shared_ptr& plugin, std::str void ScriptEngine::AddNetworkPlugin(std::string_view code) { - auto plugin = std::make_shared(_context, std::string()); + auto plugin = std::make_shared(std::string()); plugin->SetCode(code); _plugins.push_back(plugin); } @@ -1078,6 +1257,7 @@ void ScriptEngine::RemoveNetworkPlugins() GameActions::Result ScriptEngine::QueryOrExecuteCustomGameAction(const GameActions::CustomAction& customAction, bool isExecute) { + JSContext* ctx = _replContext; std::string actionz = customAction.GetId(); auto kvp = _customActions.find(actionz); if (kvp != _customActions.end()) @@ -1085,10 +1265,10 @@ GameActions::Result ScriptEngine::QueryOrExecuteCustomGameAction(const GameActio const auto& customActionInfo = kvp->second; // Deserialise the JSON args - std::string argsz = customAction.GetJson(); + const std::string& argsz = customAction.GetJson(); - auto dukArgs = DuktapeTryParseJson(_context, argsz); - if (!dukArgs) + auto jsArgs = JS_ParseJSON(ctx, argsz.c_str(), argsz.size(), customActionInfo.Name.c_str()); + if (JS_IsException(jsArgs)) { auto res = GameActions::Result(); res.error = GameActions::Status::invalidParameters; @@ -1096,35 +1276,40 @@ GameActions::Result ScriptEngine::QueryOrExecuteCustomGameAction(const GameActio return res; } - std::vector pluginCallArgs; + std::vector pluginCallArgs; if (customActionInfo.Owner->GetTargetAPIVersion() <= kApiVersionCustomActionArgs) { - pluginCallArgs = { *dukArgs }; + pluginCallArgs = { jsArgs }; } else { - DukObject obj(_context); - obj.Set("action", actionz); - obj.Set("args", *dukArgs); - obj.Set("player", customAction.GetPlayer()); - obj.Set("type", EnumValue(customAction.GetType())); + JSValue obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "action", JSFromStdString(ctx, actionz)); + JS_SetPropertyStr(ctx, obj, "args", jsArgs); + JS_SetPropertyStr(ctx, obj, "player", JS_NewInt32(ctx, customAction.GetPlayer())); + JS_SetPropertyStr(ctx, obj, "type", JS_NewInt32(ctx, EnumValue(customAction.GetType()))); auto flags = customAction.GetActionFlags(); - obj.Set("isClientOnly", (flags & GameActions::Flags::ClientOnly) != 0); - pluginCallArgs = { obj.Take() }; + JS_SetPropertyStr(ctx, obj, "isClientOnly", JS_NewBool(ctx, (flags & GameActions::Flags::ClientOnly) != 0)); + pluginCallArgs = { obj }; } // Ready to call plugin handler - DukValue dukResult; + JSValue jsResult; if (!isExecute) { - dukResult = ExecutePluginCall(customActionInfo.Owner, customActionInfo.Query, pluginCallArgs, false); + jsResult = ExecutePluginCall( + customActionInfo.Owner, customActionInfo.Query.callback, JS_UNDEFINED, pluginCallArgs, false, false, true); } else { - dukResult = ExecutePluginCall(customActionInfo.Owner, customActionInfo.Execute, pluginCallArgs, true); + jsResult = ExecutePluginCall( + customActionInfo.Owner, customActionInfo.Execute.callback, JS_UNDEFINED, pluginCallArgs, true, false, true); } - return DukToGameActionResult(dukResult); + + GameActions::Result res = JSToGameActionResult(ctx, jsResult); + JS_FreeValue(ctx, jsResult); + return res; } auto res = GameActions::Result(); @@ -1134,16 +1319,16 @@ GameActions::Result ScriptEngine::QueryOrExecuteCustomGameAction(const GameActio return res; } -GameActions::Result ScriptEngine::DukToGameActionResult(const DukValue& d) +GameActions::Result ScriptEngine::JSToGameActionResult(JSContext* ctx, JSValue d) { auto result = GameActions::Result(); - if (d.type() == DUK_TYPE_OBJECT) + if (JS_IsObject(d)) { - result.error = static_cast(AsOrDefault(d["error"])); - result.errorTitle = AsOrDefault(d["errorTitle"]); - result.errorMessage = AsOrDefault(d["errorMessage"]); - result.cost = AsOrDefault(d["cost"]); - auto expenditureType = AsOrDefault(d["expenditureType"]); + result.error = static_cast(AsOrDefault(ctx, d, "error", int32_t())); + result.errorTitle = AsOrDefault(ctx, d, "errorTitle", ""); + result.errorMessage = AsOrDefault(ctx, d, "errorMessage", ""); + result.cost = AsOrDefault(ctx, d, "cost", int64_t()); + auto expenditureType = AsOrDefault(ctx, d, "expenditureType", ""); if (!expenditureType.empty()) { auto expenditure = StringToExpenditureType(expenditureType); @@ -1199,78 +1384,76 @@ ExpenditureType ScriptEngine::StringToExpenditureType(std::string_view expenditu return ExpenditureType::count; } -DukValue ScriptEngine::GameActionResultToDuk(const GameActions::GameAction& action, const GameActions::Result& result) +JSValue ScriptEngine::GameActionResultToJS( + JSContext* ctx, const GameActions::GameAction& action, const GameActions::Result& result) { - DukStackFrame frame(_context); - DukObject obj(_context); + JSValue obj = JS_NewObject(ctx); - obj.Set("error", static_cast(result.error)); + JS_SetPropertyStr( + ctx, obj, "error", JS_NewInt32(ctx, static_cast>(result.error))); if (result.error != GameActions::Status::ok) { - obj.Set("errorTitle", result.getErrorTitle()); - obj.Set("errorMessage", result.getErrorMessage()); + JS_SetPropertyStr(ctx, obj, "errorTitle", JSFromStdString(ctx, result.getErrorTitle())); + JS_SetPropertyStr(ctx, obj, "errorMessage", JSFromStdString(ctx, result.getErrorMessage())); } if (result.cost != kMoney64Undefined) { - obj.Set("cost", result.cost); + JS_SetPropertyStr(ctx, obj, "cost", JS_NewInt64(ctx, result.cost)); } if (!result.position.IsNull()) { - obj.Set("position", ToDuk(_context, result.position)); + JS_SetPropertyStr(ctx, obj, "position", ToJSValue(ctx, result.position)); } if (result.expenditure != ExpenditureType::count) { - obj.Set("expenditureType", ExpenditureTypeToString(result.expenditure)); + JS_SetPropertyStr(ctx, obj, "expenditureType", JSFromStdString(ctx, ExpenditureTypeToString(result.expenditure))); } - // RideCreateAction only - if (action.GetType() == GameCommand::CreateRide) + if (result.error == GameActions::Status::ok) { - if (result.error == GameActions::Status::ok) + // RideCreateAction only + if (action.GetType() == GameCommand::CreateRide) { const auto rideIndex = result.getData(); - obj.Set("ride", rideIndex.ToUnderlying()); + JS_SetPropertyStr(ctx, obj, "ride", JS_NewInt32(ctx, rideIndex.ToUnderlying())); } - } - // StaffHireNewAction only - else if (action.GetType() == GameCommand::HireNewStaffMember) - { - if (result.error == GameActions::Status::ok) + // StaffHireNewAction only + else if (action.GetType() == GameCommand::HireNewStaffMember) { const auto actionResult = result.getData(); if (!actionResult.StaffEntityId.IsNull()) { - obj.Set("peep", actionResult.StaffEntityId.ToUnderlying()); + JS_SetPropertyStr(ctx, obj, "peep", JS_NewInt32(ctx, actionResult.StaffEntityId.ToUnderlying())); } } - } - // BannerPlaceAction, LargeSceneryPlaceAction, WallPlaceAction - auto bannerId = BannerIndex::GetNull(); - switch (action.GetType()) - { - case GameCommand::PlaceBanner: - bannerId = result.getData().bannerId; - break; - case GameCommand::PlaceLargeScenery: - bannerId = result.getData().bannerId; - break; - case GameCommand::PlaceWall: - bannerId = result.getData().BannerId; - break; - default: - break; - } - if (!bannerId.IsNull()) - { - obj.Set("bannerIndex", bannerId.ToUnderlying()); + // BannerPlaceAction, LargeSceneryPlaceAction, WallPlaceAction + auto bannerId = BannerIndex::GetNull(); + switch (action.GetType()) + { + case GameCommand::PlaceBanner: + bannerId = result.getData().bannerId; + break; + case GameCommand::PlaceLargeScenery: + bannerId = result.getData().bannerId; + break; + case GameCommand::PlaceWall: + bannerId = result.getData().BannerId; + break; + default: + break; + } + if (!bannerId.IsNull()) + { + JS_SetPropertyStr(ctx, obj, "bannerIndex", JS_NewInt32(ctx, bannerId.ToUnderlying())); + } } - return obj.Take(); + return obj; } bool ScriptEngine::RegisterCustomAction( - const std::shared_ptr& plugin, std::string_view action, const DukValue& query, const DukValue& execute) + const std::shared_ptr& plugin, std::string_view action, const JSCallback& query, const JSCallback& execute) { std::string actionz = std::string(action); if (_customActions.find(actionz) != _customActions.end()) @@ -1302,60 +1485,82 @@ void ScriptEngine::RemoveCustomGameActions(const std::shared_ptr& plugin } } -class DukToGameActionParameterVisitor : public GameActions::GameActionParameterVisitor +class JSToGameActionParameterVisitor : public GameActions::GameActionParameterVisitor { private: - DukValue _dukValue; + JSValue _jsValue; + JSContext* _ctx; + bool _error = false; public: - DukToGameActionParameterVisitor(DukValue&& dukValue) - : _dukValue(std::move(dukValue)) + JSToGameActionParameterVisitor(JSContext* ctx, JSValue jsValue) + : _jsValue(jsValue) + , _ctx(ctx) { } void Visit(std::string_view name, bool& param) override { - param = _dukValue[name].as_bool(); + auto val = JSToOptionalBool(_ctx, _jsValue, std::string(name).c_str()); + if (val.has_value()) + param = val.value(); + else + _error = true; } void Visit(std::string_view name, int32_t& param) override { - param = _dukValue[name].as_int(); + auto val = JSToOptionalInt(_ctx, _jsValue, std::string(name).c_str()); + if (val.has_value()) + param = val.value(); + else + _error = true; } void Visit(std::string_view name, std::string& param) override { - param = _dukValue[name].as_string(); + auto val = JSToOptionalStdString(_ctx, _jsValue, std::string(name).c_str()); + if (val.has_value()) + param = val.value(); + else + _error = true; + } + + bool GetErrorFlag() const + { + return _error; } }; -class DukFromGameActionParameterVisitor : public GameActions::GameActionParameterVisitor +class JSFromGameActionParameterVisitor : public GameActions::GameActionParameterVisitor { private: - DukObject& _dukObject; + JSValue _jsObject; + JSContext* _ctx; public: - DukFromGameActionParameterVisitor(DukObject& dukObject) - : _dukObject(dukObject) + JSFromGameActionParameterVisitor(JSContext* ctx, JSValue jsObject) + : _jsObject(jsObject) + , _ctx(ctx) { } void Visit(std::string_view name, bool& param) override { std::string szName(name); - _dukObject.Set(szName.c_str(), param); + JS_SetPropertyStr(_ctx, _jsObject, szName.c_str(), JS_NewBool(_ctx, param)); } void Visit(std::string_view name, int32_t& param) override { std::string szName(name); - _dukObject.Set(szName.c_str(), param); + JS_SetPropertyStr(_ctx, _jsObject, szName.c_str(), JS_NewInt32(_ctx, param)); } void Visit(std::string_view name, std::string& param) override { std::string szName(name); - _dukObject.Set(szName.c_str(), param); + JS_SetPropertyStr(_ctx, _jsObject, szName.c_str(), JSFromStdString(_ctx, param)); } }; @@ -1467,28 +1672,27 @@ static std::unique_ptr CreateGameActionFromActionId(con void ScriptEngine::RunGameActionHooks(const GameActions::GameAction& action, GameActions::Result& result, bool isExecute) { - DukStackFrame frame(_context); - auto hookType = isExecute ? HookType::actionExecute : HookType::actionQuery; if (_hookEngine.HasSubscriptions(hookType)) { - DukObject obj(_context); + JSContext* ctx = _replContext; + JSValue obj = JS_NewObject(ctx); auto actionId = action.GetType(); if (action.GetType() == GameCommand::Custom) { auto customAction = static_cast(action); - obj.Set("action", customAction.GetId()); + JS_SetPropertyStr(ctx, obj, "action", JSFromStdString(ctx, customAction.GetId())); - auto dukArgs = DuktapeTryParseJson(_context, customAction.GetJson()); - if (dukArgs) + JSValue jsArgs = JS_ParseJSON( + ctx, customAction.GetJson().c_str(), customAction.GetJson().length(), customAction.GetName()); + if (!JS_IsException(jsArgs)) { - obj.Set("args", *dukArgs); + JS_SetPropertyStr(ctx, obj, "args", jsArgs); } else { - DukObject args(_context); - obj.Set("args", args.Take()); + JS_SetPropertyStr(ctx, obj, "args", JS_NewObject(ctx)); } } else @@ -1496,86 +1700,98 @@ void ScriptEngine::RunGameActionHooks(const GameActions::GameAction& action, Gam auto actionName = GetActionName(actionId); if (!actionName.empty()) { - obj.Set("action", actionName); + JS_SetPropertyStr(ctx, obj, "action", JSFromStdString(ctx, actionName)); } - DukObject args(_context); - DukFromGameActionParameterVisitor visitor(args); + JSValue args = JS_NewObject(ctx); + JSFromGameActionParameterVisitor visitor(ctx, args); const_cast(action).AcceptParameters(visitor); const_cast(action).AcceptFlags(visitor); - obj.Set("args", args.Take()); + JS_SetPropertyStr(ctx, obj, "args", args); } - obj.Set("player", action.GetPlayer()); - obj.Set("type", EnumValue(actionId)); + JS_SetPropertyStr(ctx, obj, "player", JS_NewInt32(ctx, action.GetPlayer())); + JS_SetPropertyStr(ctx, obj, "type", JS_NewInt32(ctx, EnumValue(actionId))); auto flags = action.GetActionFlags(); - obj.Set("isClientOnly", (flags & GameActions::Flags::ClientOnly) != 0); + JS_SetPropertyStr(ctx, obj, "isClientOnly", JS_NewBool(ctx, (flags & GameActions::Flags::ClientOnly) != 0)); - obj.Set("result", GameActionResultToDuk(action, result)); - auto dukEventArgs = obj.Take(); + JS_SetPropertyStr(ctx, obj, "result", GameActionResultToJS(ctx, action, result)); - _hookEngine.Call(hookType, dukEventArgs, false); + _hookEngine.Call(hookType, obj, false, true); if (!isExecute) { - auto dukResult = dukEventArgs["result"]; - if (dukResult.type() == DukValue::Type::OBJECT) + JSValue jsResult = JS_GetPropertyStr(ctx, obj, "result"); + if (JS_IsObject(jsResult)) { - auto error = AsOrDefault(dukResult["error"]); + int32_t error = AsOrDefault(ctx, jsResult, "error", int32_t()); if (error != 0) { result.error = static_cast(error); - result.errorTitle = AsOrDefault(dukResult["errorTitle"]); - result.errorMessage = AsOrDefault(dukResult["errorMessage"]); + result.errorTitle = AsOrDefault(ctx, jsResult, "errorTitle", ""); + result.errorMessage = AsOrDefault(ctx, jsResult, "errorMessage", ""); } } + JS_FreeValue(ctx, jsResult); } + JS_FreeValue(ctx, obj); } } -std::unique_ptr ScriptEngine::CreateGameAction( - const std::string& actionid, const DukValue& args, const std::string& pluginName) +std::pair, bool> ScriptEngine::CreateGameAction( + JSContext* ctx, const std::string& actionid, JSValue args, const std::string& pluginName) { auto action = CreateGameActionFromActionId(actionid); if (action != nullptr) { - DukValue argsCopy = args; - DukToGameActionParameterVisitor visitor(std::move(argsCopy)); + JSToGameActionParameterVisitor visitor(ctx, args); action->AcceptParameters(visitor); - if (args["flags"].type() == DukValue::Type::NUMBER) + + JSValue flags = JS_GetPropertyStr(ctx, args, "flags"); + if (JS_IsNumber(flags)) { action->AcceptFlags(visitor); } - return action; + JS_FreeValue(ctx, flags); + return { std::move(action), visitor.GetErrorFlag() }; } // Serialise args to json so that it can be sent - auto ctx = args.context(); - if (args.type() == DukValue::Type::OBJECT) + std::string json; + if (JS_IsObject(args)) { - args.push(); + JSValue jsonVal = JS_JSONStringify(ctx, args, JS_UNDEFINED, JS_UNDEFINED); + if (JS_IsString(jsonVal)) + { + json = JSToStdString(ctx, jsonVal); + } + JS_FreeValue(ctx, jsonVal); } - else + if (json.empty()) { - duk_push_object(ctx); + JSValue emptyObj = JS_NewObject(ctx); + JSValue jsonVal = JS_JSONStringify(ctx, args, JS_UNDEFINED, JS_UNDEFINED); + json = JSToStdString(ctx, jsonVal); + JS_FreeValue(ctx, emptyObj); + JS_FreeValue(ctx, jsonVal); } - auto jsonz = duk_json_encode(ctx, -1); - auto json = std::string(jsonz); - duk_pop(ctx); auto customAction = std::make_unique(actionid, json, pluginName); if (customAction->GetPlayer() == -1 && Network::GetMode() != Network::Mode::none) { customAction->SetPlayer(Network::GetCurrentPlayerId()); } - return customAction; + return { std::move(customAction), false }; } void ScriptEngine::InitSharedStorage() { - duk_push_object(_context); - _sharedStorage = std::move(DukValue::take_from_stack(_context)); + if (_replContext) + { + JS_FreeValue(_replContext, _sharedStorage); + _sharedStorage = JS_NewObject(_replContext); + } } void ScriptEngine::LoadSharedStorage() @@ -1588,11 +1804,18 @@ void ScriptEngine::LoadSharedStorage() if (File::Exists(path)) { auto data = File::ReadAllBytes(path); - auto result = DuktapeTryParseJson( - _context, std::string_view(reinterpret_cast(data.data()), data.size())); - if (result) + // quickjs wants a null terminator not counted in the buf_len + data.push_back(0); + JSValue result = JS_ParseJSON( + _replContext, reinterpret_cast(data.data()), data.size() - 1, path.c_str()); + if (JS_IsObject(result)) { - _sharedStorage = std::move(*result); + JS_FreeValue(_replContext, _sharedStorage); + _sharedStorage = result; + } + else + { + Console::Error::WriteLine("Unable to load plugin shared storage"); } } } @@ -1607,11 +1830,17 @@ void ScriptEngine::SaveSharedStorage() auto path = _env.GetFilePath(PathId::pluginStore); try { - _sharedStorage.push(); - auto json = std::string(duk_json_encode(_context, -1)); - duk_pop(_context); - - File::WriteAllBytes(path, json.c_str(), json.size()); + JSValue jsonVal = JS_JSONStringify(_replContext, _sharedStorage, JS_UNDEFINED, JS_UNDEFINED); + if (JS_IsString(jsonVal)) + { + // inefficient to copy the whole string out first, but we have to do this to avoid breaking the exception flow + std::string json = JSToStdString(_replContext, jsonVal); + JS_FreeValue(_replContext, jsonVal); + File::WriteAllBytes(path, json.c_str(), json.size()); + return; + } + JS_FreeValue(_replContext, jsonVal); + Console::Error::WriteLine("Unable to stringify shared storage JSON"); } catch (const std::exception&) { @@ -1621,24 +1850,46 @@ void ScriptEngine::SaveSharedStorage() void ScriptEngine::ClearParkStorage() { - duk_push_object(_context); - _parkStorage = std::move(DukValue::take_from_stack(_context)); + if (_replContext) + { + JS_FreeValue(_replContext, _parkStorage); + _parkStorage = JS_NewObject(_replContext); + } } std::string ScriptEngine::GetParkStorageAsJSON() { - _parkStorage.push(); - auto json = std::string(duk_json_encode(_context, -1)); - duk_pop(_context); - return json; + std::string retStr{}; + JSValue jsonVal = JS_JSONStringify(_replContext, _parkStorage, JS_UNDEFINED, JS_UNDEFINED); + if (JS_IsString(jsonVal)) + { + retStr = JSToStdString(_replContext, jsonVal); + } + else + { + Console::Error::WriteLine("Could not stringify park storage"); + } + JS_FreeValue(_replContext, jsonVal); + return retStr; } -void ScriptEngine::SetParkStorageFromJSON(std::string_view value) +void ScriptEngine::SetParkStorageFromJSON(const std::string& value, const std::string& filename) { - auto result = DuktapeTryParseJson(_context, value); - if (result) + if (value.empty()) { - _parkStorage = std::move(*result); + ClearParkStorage(); + return; + } + JSValue result = JS_ParseJSON(_replContext, value.c_str(), value.size(), filename.c_str()); + if (JS_IsObject(result)) + { + JS_FreeValue(_replContext, _parkStorage); + _parkStorage = result; + } + else + { + ClearParkStorage(); + Console::Error::WriteLine("Could not load park storage"); } } @@ -1652,7 +1903,8 @@ IntervalHandle ScriptEngine::AllocateHandle() return nextHandle; } -IntervalHandle ScriptEngine::AddInterval(const std::shared_ptr& plugin, int32_t delay, bool repeat, DukValue&& callback) +IntervalHandle ScriptEngine::AddInterval( + const std::shared_ptr& plugin, int32_t delay, bool repeat, const JSCallback& callback) { auto handle = AllocateHandle(); assert(handle != 0); @@ -1661,7 +1913,7 @@ IntervalHandle ScriptEngine::AddInterval(const std::shared_ptr& plugin, interval.Owner = plugin; interval.Delay = delay; interval.LastTimestamp = _lastIntervalTimestamp; - interval.Callback = std::move(callback); + interval.Callback = callback; interval.Repeat = repeat; return handle; @@ -1731,7 +1983,7 @@ void ScriptEngine::UpdateIntervals() continue; } - ExecutePluginCall(interval.Owner, interval.Callback, {}, false); + ExecutePluginCall(interval.Owner, interval.Callback.callback, {}, false); interval.LastTimestamp = timestamp; if (!interval.Repeat) @@ -1759,63 +2011,55 @@ void ScriptEngine::RemoveIntervals(const std::shared_ptr& plugin) } #ifndef DISABLE_NETWORK -void ScriptEngine::AddSocket(const std::shared_ptr& socket) +void ScriptEngine::AddSocket(SocketDataBase* data) { - _sockets.push_back(socket); + _sockets.push_back(data); +} + +void ScriptEngine::RemoveSocket(SocketDataBase* data) +{ + // Just remove one + auto it = std::find(_sockets.begin(), _sockets.end(), data); + if (it != _sockets.end()) + *it = nullptr; } #endif void ScriptEngine::UpdateSockets() { #ifndef DISABLE_NETWORK - // Use simple for i loop as Update calls can modify the list - auto it = _sockets.begin(); - while (it != _sockets.end()) + // AddSocket and RemoveSocket can be called as a result of the Update + // Therefore we add to the end and remove by setting to null and cleaning up + // after the update. We also must use [] here and we remember the original + // sockets vector size so that we process new ones in the next tick. + const size_t sz = _sockets.size(); + for (size_t i = 0; i < sz; i++) { - auto& socket = *it; - socket->Update(); - if (socket->IsDisposed()) - { - it = _sockets.erase(it); - } - else - { - it++; - } + if (_sockets[i] != nullptr) + _sockets[i]->Update(); } + std::erase(_sockets, nullptr); #endif } void ScriptEngine::RemoveSockets(const std::shared_ptr& plugin) { #ifndef DISABLE_NETWORK - auto it = _sockets.begin(); - while (it != _sockets.end()) + // The ownership model has the javascript engine owning all the socket data. + // Therefore we rely on the javascript engine to finalise all sockets. + // We just remove the listeners since they can hold references to the javascript socket objects which will + // prevent finalisation. + for (SocketDataBase* socket : _sockets) { - auto socket = it->get(); - if (socket->GetPlugin() == plugin) - { - socket->Dispose(); - it = _sockets.erase(it); - } - else - { - it++; - } + if (socket != nullptr) + socket->_eventList.RemoveAllListeners(); } #endif } -std::string Scripting::Stringify(const DukValue& val) +std::string Scripting::Stringify(JSContext* ctx, const JSValue val) { - return ExpressionStringifier::StringifyExpression(val); -} - -std::string Scripting::ProcessString(const DukValue& value) -{ - if (value.type() == DukValue::Type::STRING) - return value.as_string(); - return {}; + return ExpressionStringifier::StringifyExpression(ctx, val); } bool Scripting::IsGameStateMutable() @@ -1831,21 +2075,6 @@ bool Scripting::IsGameStateMutable() return execInfo.IsGameStateMutable(); } -void Scripting::ThrowIfGameStateNotMutable() -{ - // Allow single player to alter game state anywhere - if (Network::GetMode() != Network::Mode::none) - { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto& execInfo = scriptEngine.GetExecInfo(); - if (!execInfo.IsGameStateMutable()) - { - auto ctx = scriptEngine.GetContext(); - duk_error(ctx, DUK_ERR_ERROR, "Game state is not mutable in this context."); - } - } -} - int32_t Scripting::GetTargetAPIVersion() { auto& scriptEngine = GetContext()->GetScriptEngine(); @@ -1862,9 +2091,4 @@ int32_t Scripting::GetTargetAPIVersion() return plugin->GetTargetAPIVersion(); } -duk_bool_t duk_exec_timeout_check(void*) -{ - return false; -} - #endif diff --git a/src/openrct2/scripting/ScriptEngine.h b/src/openrct2/scripting/ScriptEngine.h index 2157f3ed3a..bee5128035 100644 --- a/src/openrct2/scripting/ScriptEngine.h +++ b/src/openrct2/scripting/ScriptEngine.h @@ -13,8 +13,8 @@ #include "../actions/general/CustomAction.h" #include "../core/FileWatcher.h" + #include "../core/Guard.hpp" #include "../management/Finance.h" - #include "../world/Location.hpp" #include "HookEngine.h" #include "Plugin.h" @@ -23,14 +23,12 @@ #include #include #include + #include #include #include #include #include -struct duk_hthread; -typedef struct duk_hthread duk_context; - namespace OpenRCT2::GameActions { class GameAction; @@ -56,7 +54,7 @@ namespace OpenRCT2::Scripting static constexpr int32_t kApiVersionNetworkIDs = 77; #ifndef DISABLE_NETWORK - class ScSocketBase; + struct SocketDataBase; #endif class ScriptExecutionInfo @@ -105,34 +103,13 @@ namespace OpenRCT2::Scripting } }; - class DukContext - { - private: - duk_context* _context{}; - - public: - DukContext(); - DukContext(DukContext&) = delete; - DukContext(DukContext&& src) noexcept - : _context(std::move(src._context)) - { - src._context = {}; - } - ~DukContext(); - - operator duk_context*() - { - return _context; - } - }; - using IntervalHandle = uint32_t; struct ScriptInterval { std::shared_ptr Owner; uint32_t Delay{}; int64_t LastTimestamp{}; - DukValue Callback; + JSCallback Callback; bool Repeat{}; bool Deleted{}; }; @@ -142,7 +119,8 @@ namespace OpenRCT2::Scripting private: InteractiveConsole& _console; IPlatformEnvironment& _env; - DukContext _context; + static JSRuntime* _runtime; + JSContext* _replContext = nullptr; bool _initialised{}; bool _hotReloadingInitialised{}; bool _transientPluginsEnabled{}; @@ -153,8 +131,8 @@ namespace OpenRCT2::Scripting uint32_t _lastHotReloadCheckTick{}; HookEngine _hookEngine; ScriptExecutionInfo _execInfo; - DukValue _sharedStorage; - DukValue _parkStorage; + JSValue _sharedStorage = JS_UNDEFINED; + JSValue _parkStorage = JS_UNDEFINED; uint32_t _lastIntervalTimestamp{}; std::map _intervals; @@ -165,26 +143,37 @@ namespace OpenRCT2::Scripting std::mutex _changedPluginFilesMutex; std::vector)>> _pluginStoppedSubscriptions; + struct ExtensionCallbacks + { + std::function newContext; + std::function unregister; + }; + + std::vector _extensions; + struct CustomActionInfo { std::shared_ptr Owner; std::string Name; - DukValue Query; - DukValue Execute; + JSCallback Query; + JSCallback Execute; }; std::unordered_map _customActions; #ifndef DISABLE_NETWORK - std::list> _sockets; + std::vector _sockets; #endif + void InitialiseContext(JSContext* ctx) const; + public: ScriptEngine(InteractiveConsole& console, IPlatformEnvironment& env); ScriptEngine(ScriptEngine&) = delete; + ~ScriptEngine(); - duk_context* GetContext() + JSContext* GetContext() { - return _context; + return _replContext; } HookEngine& GetHookEngine() { @@ -194,11 +183,11 @@ namespace OpenRCT2::Scripting { return _execInfo; } - DukValue GetSharedStorage() + JSValue GetSharedStorage() { return _sharedStorage; } - DukValue GetParkStorage() + JSValue GetParkStorage() { return _parkStorage; } @@ -223,20 +212,21 @@ namespace OpenRCT2::Scripting void ClearParkStorage(); std::string GetParkStorageAsJSON(); - void SetParkStorageFromJSON(std::string_view value); + void SetParkStorageFromJSON(const std::string& value, const std::string& filename); void Initialise(); + JSContext* CreateContext() const; void LoadTransientPlugins(); void UnloadTransientPlugins(); void StopUnloadRegisterAllPlugins(); void Tick(); std::future Eval(const std::string& s); - DukValue ExecutePluginCall( - const std::shared_ptr& plugin, const DukValue& func, const std::vector& args, - bool isGameStateMutable); - DukValue ExecutePluginCall( - std::shared_ptr plugin, const DukValue& func, const DukValue& thisValue, const std::vector& args, - bool isGameStateMutable); + void ExecutePluginCall( + const std::shared_ptr& plugin, JSValue func, const std::vector& args, bool isGameStateMutable, + bool keepArgsAlive = false); + JSValue ExecutePluginCall( + const std::shared_ptr& plugin, JSValue func, JSValue thisValue, const std::vector& args, + bool isGameStateMutable, bool keepArgsAlive = false, bool keepRetValueAlive = false); void LogPluginInfo(std::string_view message); void LogPluginInfo(const std::shared_ptr& plugin, std::string_view message); @@ -246,32 +236,43 @@ namespace OpenRCT2::Scripting _pluginStoppedSubscriptions.push_back(callback); } + void RegisterExtension(std::function newContext, std::function unregister) + { + _extensions.emplace_back(newContext, unregister); + newContext(_replContext); + } + void AddNetworkPlugin(std::string_view code); void RemoveNetworkPlugins(); [[nodiscard]] GameActions::Result QueryOrExecuteCustomGameAction( const GameActions::CustomAction& action, bool isExecute); bool RegisterCustomAction( - const std::shared_ptr& plugin, std::string_view action, const DukValue& query, const DukValue& execute); + const std::shared_ptr& plugin, std::string_view action, const JSCallback& query, const JSCallback& execute); void RunGameActionHooks(const GameActions::GameAction& action, GameActions::Result& result, bool isExecute); - [[nodiscard]] std::unique_ptr CreateGameAction( - const std::string& actionid, const DukValue& args, const std::string& pluginName); - [[nodiscard]] DukValue GameActionResultToDuk(const GameActions::GameAction& action, const GameActions::Result& result); + [[nodiscard]] std::pair, bool> CreateGameAction( + JSContext* ctx, const std::string& actionid, JSValue args, const std::string& pluginName); + [[nodiscard]] JSValue GameActionResultToJS( + JSContext* ctx, const GameActions::GameAction& action, const GameActions::Result& result); void SaveSharedStorage(); - IntervalHandle AddInterval(const std::shared_ptr& plugin, int32_t delay, bool repeat, DukValue&& callback); + IntervalHandle AddInterval( + const std::shared_ptr& plugin, int32_t delay, bool repeat, const JSCallback& callback); void RemoveInterval(const std::shared_ptr& plugin, IntervalHandle handle); static std::string_view ExpenditureTypeToString(ExpenditureType expenditureType); static ExpenditureType StringToExpenditureType(std::string_view expenditureType); #ifndef DISABLE_NETWORK - void AddSocket(const std::shared_ptr& socket); + void AddSocket(SocketDataBase* data); + void RemoveSocket(SocketDataBase* data); #endif private: - void RegisterConstants(); + static void RegisterClasses(JSContext* ctx); + static void UnregisterClasses(); + static void RegisterConstants(JSContext* ctx); void RefreshPlugins(); std::vector GetPluginFiles() const; void UnregisterPlugin(std::string_view path); @@ -283,7 +284,7 @@ namespace OpenRCT2::Scripting void LoadPlugin(std::shared_ptr& plugin); void UnloadPlugin(std::shared_ptr& plugin); void StartPlugin(std::shared_ptr plugin); - void StopPlugin(std::shared_ptr plugin); + void StopPlugin(std::shared_ptr plugin, bool unregistering = false); void ReloadPlugin(std::shared_ptr plugin); static bool ShouldLoadScript(std::string_view path); bool ShouldStartPlugin(const std::shared_ptr& plugin); @@ -292,7 +293,7 @@ namespace OpenRCT2::Scripting void AutoReloadPlugins(); void ProcessREPL(); void RemoveCustomGameActions(const std::shared_ptr& plugin); - [[nodiscard]] GameActions::Result DukToGameActionResult(const DukValue& d); + [[nodiscard]] static GameActions::Result JSToGameActionResult(JSContext* ctx, JSValue d); void InitSharedStorage(); void LoadSharedStorage(); @@ -306,10 +307,66 @@ namespace OpenRCT2::Scripting }; bool IsGameStateMutable(); - void ThrowIfGameStateNotMutable(); int32_t GetTargetAPIVersion(); - std::string Stringify(const DukValue& value); + std::string Stringify(JSContext* context, JSValue value); + + class ScBase + { + private: + JSClassID classId = JS_INVALID_CLASS_ID; + + public: + void Unregister() + { + classId = JS_INVALID_CLASS_ID; + } + + protected: + [[nodiscard]] JSValue MakeWithOpaque(JSContext* ctx, std::span classFuncs, void* opaque) + { + JSValue obj = JS_NewObjectClass(ctx, classId); + if (JS_IsException(obj)) + throw std::runtime_error("Failed to create new object for class."); + JS_SetOpaque(obj, opaque); + + // Note: Usually one would set a class prototype rather than setting the functions as properties on every creation. + // However, that causes the attached functions to not be "own properties" which make them a little less + // visible to the user, and also does not match the previous behaviour with the DukTape engine. + JS_SetPropertyFunctionList(ctx, obj, classFuncs.data(), static_cast(classFuncs.size())); + return obj; + } + + template + [[nodiscard]] T GetOpaque(JSValue obj) const + { + return static_cast(JS_GetOpaque(obj, classId)); + } + + void RegisterBase(JSContext* ctx, const JSClassDef& classDef) + { + Guard::Assert(classId == JS_INVALID_CLASS_ID); + // Note: Technically JS_NewClassID is meant to be called once during the lifetime of the program + // whereas JS_NewClass is meant to be called for each runtime. + // If we ever have any more runtimes, this flow would be wrong. + // More runtimes would also require refactoring the way values are passed to the JS code when + // calling callbacks. + JSRuntime* rt = JS_GetRuntime(ctx); + JS_NewClassID(rt, &classId); + JS_NewClass(rt, classId, &classDef); + JS_SetClassProto(ctx, classId, JS_NewObject(ctx)); + } + + void RegisterBaseStr(JSContext* ctx, const char* className) + { + RegisterBase(ctx, { className, nullptr, nullptr, nullptr, nullptr }); + } + + void RegisterBaseStr(JSContext* ctx, const char* className, JSClassFinalizer* finalizer) + { + RegisterBase(ctx, { className, finalizer, nullptr, nullptr, nullptr }); + } + }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/ScriptUtil.hpp b/src/openrct2/scripting/ScriptUtil.hpp new file mode 100644 index 0000000000..c64a61a1fd --- /dev/null +++ b/src/openrct2/scripting/ScriptUtil.hpp @@ -0,0 +1,614 @@ +/***************************************************************************** + * Copyright (c) 2014-2025 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#ifdef ENABLE_SCRIPTING + + #include "../world/Location.hpp" + + #include + #include + #include + #include + +namespace OpenRCT2::Scripting +{ + struct JSCallback + { + JSContext* context = nullptr; + JSValue callback = JS_UNDEFINED; + + JSCallback() {}; + + JSCallback(JSContext* ctx, JSValue func) + : context(ctx) + , callback(ctx && JS_IsFunction(ctx, func) ? JS_DupValue(ctx, func) : JS_UNDEFINED) + { + } + + JSCallback(const JSCallback& other) + : context(other.context) + , callback( + other.context && JS_IsFunction(other.context, other.callback) ? JS_DupValue(other.context, other.callback) + : JS_UNDEFINED) + { + } + + JSCallback(JSCallback&& other) noexcept + : context(other.context) + , callback(other.callback) + { + other.context = nullptr; + other.callback = JS_UNDEFINED; + } + + JSCallback& operator=(const JSCallback& other) + { + if (this != &other) + { + if (context) + { + JS_FreeValue(context, callback); + } + context = other.context; + callback = JS_DupValue(other.context, other.callback); + } + return *this; + } + + JSCallback& operator=(JSCallback&& other) noexcept + { + if (this != &other) + { + if (context) + { + JS_FreeValue(context, callback); + } + context = other.context; + callback = other.callback; + other.context = nullptr; + other.callback = JS_UNDEFINED; + } + return *this; + } + + bool IsValid() const + { + return context && JS_IsFunction(context, callback); + } + + ~JSCallback() noexcept + { + if (context) + { + JS_FreeValue(context, callback); + } + } + }; + + inline JSCallback JSToCallback(JSContext* ctx, JSValue obj, const char* name) + { + JSValue property = JS_GetPropertyStr(ctx, obj, name); + JSCallback out(ctx, property); + JS_FreeValue(ctx, property); + return out; + } + + inline std::string JSToStdString(JSContext* ctx, JSValue obj) + { + size_t len; + if (const char* buf = JS_ToCStringLen(ctx, &len, obj)) + { + std::string str(buf, len); + JS_FreeCString(ctx, buf); + return str; + } + return {}; + } + + inline std::string JSToStdString(JSContext* ctx, JSValue obj, const char* property) + { + JSValue val = JS_GetPropertyStr(ctx, obj, property); + std::string output = JSToStdString(ctx, val); + JS_FreeValue(ctx, val); + return output; + } + + inline JSValue JSFromStdString(JSContext* ctx, const std::string_view str) + { + return JS_NewStringLen(ctx, str.data(), str.length()); + } + + inline std::optional JSToOptionalStdString(JSContext* ctx, JSValue obj, const char* property) + { + JSValue val = JS_GetPropertyStr(ctx, obj, property); + std::optional output = std::nullopt; + if (JS_IsString(val)) + { + output = JSToStdString(ctx, val); + } + JS_FreeValue(ctx, val); + return output; + } + + inline std::optional JSToOptionalInt(JSContext* ctx, JSValue obj, const char* property) + { + JSValue val = JS_GetPropertyStr(ctx, obj, property); + std::optional output = std::nullopt; + if (JS_IsNumber(val)) + { + int32_t intVal = -1; + if (JS_ToInt32(ctx, &intVal, val) >= 0) + { + output = std::make_optional(intVal); + } + } + JS_FreeValue(ctx, val); + return output; + } + + inline std::optional JSToOptionalInt64(JSContext* ctx, JSValue obj, const char* property) + { + JSValue val = JS_GetPropertyStr(ctx, obj, property); + std::optional output = std::nullopt; + if (JS_IsNumber(val)) + { + int64_t intVal = -1; + if (JS_ToInt64(ctx, &intVal, val) >= 0) + { + output = std::make_optional(intVal); + } + } + JS_FreeValue(ctx, val); + return output; + } + + inline std::optional JSToOptionalUint(JSContext* ctx, JSValue obj, const char* property) + { + JSValue val = JS_GetPropertyStr(ctx, obj, property); + std::optional output = std::nullopt; + if (JS_IsNumber(val)) + { + uint32_t uintVal = 0; + if (JS_ToUint32(ctx, &uintVal, val) >= 0) + { + output = std::make_optional(uintVal); + } + } + JS_FreeValue(ctx, val); + return output; + } + + inline std::optional JSToOptionalBool(JSContext* ctx, JSValue obj, const char* property) + { + JSValue val = JS_GetPropertyStr(ctx, obj, property); + std::optional output = std::nullopt; + if (JS_IsBool(val)) + { + const int result = JS_ToBool(ctx, val); + if (result != -1) + { + output = std::make_optional(static_cast(result)); + } + } + JS_FreeValue(ctx, val); + return output; + } + + inline bool AsOrDefault(JSContext* ctx, JSValue obj, const char* property, bool def) + { + JSValue val = JS_GetPropertyStr(ctx, obj, property); + bool output; + if (JS_IsBool(val)) + { + int result = JS_ToBool(ctx, val); + output = result >= 0 ? result : def; + } + else + { + output = def; + } + JS_FreeValue(ctx, val); + return output; + } + + inline int32_t AsOrDefault(JSContext* ctx, JSValue obj, const char* property, int32_t def) + { + JSValue val = JS_GetPropertyStr(ctx, obj, property); + int32_t output; + if (!JS_IsNumber(val)) + { + output = def; + } + else if (JS_ToInt32(ctx, &output, val) < 0) + { + output = def; + } + JS_FreeValue(ctx, val); + return output; + } + + inline uint32_t AsOrDefault(JSContext* ctx, JSValue obj, const char* property, uint32_t def) + { + JSValue val = JS_GetPropertyStr(ctx, obj, property); + uint32_t output; + if (!JS_IsNumber(val)) + { + output = def; + } + else if (JS_ToUint32(ctx, &output, val) < 0) + { + output = def; + } + JS_FreeValue(ctx, val); + return output; + } + + inline int64_t AsOrDefault(JSContext* ctx, JSValue obj, const char* property, int64_t def) + { + JSValue val = JS_GetPropertyStr(ctx, obj, property); + int64_t output; + if (!JS_IsNumber(val)) + { + output = def; + } + else if (JS_ToInt64(ctx, &output, val) < 0) + { + output = def; + } + JS_FreeValue(ctx, val); + return output; + } + + // Note the default parameter needs to be const char* + // If you use a type like string_view, calls can instead resolve to the boolean version of the function. + inline std::string AsOrDefault(JSContext* ctx, JSValue obj, const char* property, const char* def) + { + JSValue val = JS_GetPropertyStr(ctx, obj, property); + std::string output; + if (!JS_IsString(val)) + { + output = std::string(def); + } + else + { + output = JSToStdString(ctx, val); + } + JS_FreeValue(ctx, val); + return output; + } + + inline void JSIterateArray(JSContext* ctx, JSValue val, const std::function& callback) + { + if (JS_IsArray(val)) + { + int64_t arrayLen = -1; + JS_GetLength(ctx, val, &arrayLen); + for (int64_t i = 0; i < arrayLen; i++) + { + JSValue elem = JS_GetPropertyInt64(ctx, val, i); + callback(ctx, elem); + JS_FreeValue(ctx, elem); + } + } + } + + inline void JSIterateArray( + JSContext* ctx, JSValue obj, const char* property, const std::function& callback) + { + JSValue val = JS_GetPropertyStr(ctx, obj, property); + JSIterateArray(ctx, val, callback); + JS_FreeValue(ctx, val); + } + + inline int32_t JSToInt(JSContext* ctx, JSValue val) + { + int32_t output = -1; + if (JS_IsNumber(val)) + { + JS_ToInt32(ctx, &output, val); + } + return output; + } + + inline int32_t JSToInt(JSContext* ctx, JSValue obj, const char* property) + { + JSValue val = JS_GetPropertyStr(ctx, obj, property); + int32_t output = JSToInt(ctx, val); + JS_FreeValue(ctx, val); + return output; + } + + inline int64_t JSToInt64(JSContext* ctx, JSValue val) + { + int64_t output = -1; + if (JS_IsNumber(val)) + { + JS_ToInt64(ctx, &output, val); + } + return output; + } + + inline int64_t JSToInt64(JSContext* ctx, JSValue obj, const char* property) + { + JSValue val = JS_GetPropertyStr(ctx, obj, property); + int64_t output = JSToInt64(ctx, val); + JS_FreeValue(ctx, val); + return output; + } + + inline uint32_t JSToUint(JSContext* ctx, JSValue val) + { + uint32_t output = 0; + if (JS_IsNumber(val)) + { + JS_ToUint32(ctx, &output, val); + } + return output; + } + + inline uint32_t JSToUint(JSContext* ctx, JSValue obj, const char* property) + { + JSValue val = JS_GetPropertyStr(ctx, obj, property); + uint32_t output = JSToUint(ctx, val); + JS_FreeValue(ctx, val); + return output; + } + + inline CoordsXY JSToCoordsXY(JSContext* ctx, JSValue obj) + { + return { AsOrDefault(ctx, obj, "x", 0), AsOrDefault(ctx, obj, "y", 0) }; + } + + inline CoordsXY JSToCoordXY(JSContext* ctx, JSValue obj, const char* property) + { + JSValue val = JS_GetPropertyStr(ctx, obj, property); + CoordsXY output = JSToCoordsXY(ctx, val); + JS_FreeValue(ctx, val); + return output; + } + + inline CoordsXYZ JSToCoordsXYZ(JSContext* ctx, JSValue obj) + { + CoordsXYZ result; + if (JS_IsObject(obj)) + { + result.x = AsOrDefault(ctx, obj, "x", 0); + result.y = AsOrDefault(ctx, obj, "y", 0); + result.z = AsOrDefault(ctx, obj, "z", 0); + } + else + { + result.SetNull(); + } + return result; + } + + inline CoordsXYZD JSToCoordsXYZD(JSContext* ctx, JSValue obj) + { + CoordsXYZD result; + if (JS_IsObject(obj)) + { + result = CoordsXYZD(JSToCoordsXYZ(ctx, obj), AsOrDefault(ctx, obj, "direction", 0)); + } + else + { + result.SetNull(); + } + return result; + } + + inline JSValue ToJSValue(JSContext* ctx, uint8_t val) + { + return JS_NewInt32(ctx, val); + } + + inline JSValue ToJSValue(JSContext* ctx, const CoordsXY& coords) + { + JSValue obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "x", JS_NewInt32(ctx, coords.x)); + JS_SetPropertyStr(ctx, obj, "y", JS_NewInt32(ctx, coords.y)); + return obj; + } + + inline JSValue ToJSValue(JSContext* ctx, const TileCoordsXY& coords) + { + JSValue obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "x", JS_NewInt32(ctx, coords.x)); + JS_SetPropertyStr(ctx, obj, "y", JS_NewInt32(ctx, coords.y)); + return obj; + } + + inline JSValue ToJSValue(JSContext* ctx, const ScreenCoordsXY& coords) + { + JSValue obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "x", JS_NewInt32(ctx, coords.x)); + JS_SetPropertyStr(ctx, obj, "y", JS_NewInt32(ctx, coords.y)); + return obj; + } + + inline JSValue ToJSValue(JSContext* ctx, const ScreenSize& size) + { + JSValue obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "width", JS_NewInt32(ctx, size.width)); + JS_SetPropertyStr(ctx, obj, "height", JS_NewInt32(ctx, size.height)); + return obj; + } + + inline JSValue ToJSValue(JSContext* ctx, const CoordsXYZ& value) + { + if (value.IsNull()) + { + return JS_NULL; + } + + JSValue obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "x", JS_NewInt32(ctx, value.x)); + JS_SetPropertyStr(ctx, obj, "y", JS_NewInt32(ctx, value.y)); + JS_SetPropertyStr(ctx, obj, "z", JS_NewInt32(ctx, value.z)); + return obj; + } + + inline JSValue ToJSValue(JSContext* ctx, const CoordsXYZD& value) + { + if (value.IsNull()) + { + return JS_NULL; + } + + JSValue obj = ToJSValue(ctx, static_cast(value)); + JS_SetPropertyStr(ctx, obj, "direction", JS_NewUint32(ctx, value.direction)); + return obj; + } + + template + JSValue ToJSValue(JSContext* ctx, const std::optional& value) + { + return value ? ToJSValue(ctx, *value) : JS_NULL; + } + + #define JS_THROW_IF_GAME_STATE_NOT_MUTABLE() \ + if (!Scripting::IsGameStateMutable()) \ + { \ + JS_ThrowPlainError(ctx, "Game state is not mutable in this context."); \ + return JS_EXCEPTION; \ + } + + #define JS_UNPACK_INT32(var, ctx, val) \ + int32_t var; \ + if (!JS_IsNumber(val)) \ + { \ + JS_ThrowTypeError(ctx, "Expected number"); \ + return JS_EXCEPTION; \ + } \ + if (JS_ToInt32(ctx, &var, val) < 0) \ + { \ + return JS_EXCEPTION; \ + } + + #define JS_UNPACK_INT64(var, ctx, val) \ + int64_t var; \ + if (!JS_IsNumber(val)) \ + { \ + JS_ThrowTypeError(ctx, "Expected number"); \ + return JS_EXCEPTION; \ + } \ + if (JS_ToInt64(ctx, &var, val) < 0) \ + { \ + return JS_EXCEPTION; \ + } + + #define JS_UNPACK_UINT32(var, ctx, val) \ + uint32_t var; \ + if (!JS_IsNumber(val)) \ + { \ + JS_ThrowTypeError(ctx, "Expected number"); \ + return JS_EXCEPTION; \ + } \ + if (JS_ToUint32(ctx, &var, val) < 0) \ + { \ + return JS_EXCEPTION; \ + } + + #define JS_UNPACK_MONEY64(var, ctx, val) \ + money64 var; \ + if (!JS_IsNumber(val)) \ + { \ + JS_ThrowTypeError(ctx, "Expected number"); \ + return JS_EXCEPTION; \ + } \ + if (JS_ToInt64(ctx, &var, val) < 0) \ + { \ + return JS_EXCEPTION; \ + } + + #define JS_UNPACK_STR(var, ctx, val) \ + std::string var; \ + if (!JS_IsString(val)) \ + { \ + JS_ThrowTypeError(ctx, "Expected string"); \ + return JS_EXCEPTION; \ + } \ + { \ + size_t len; \ + if (const char* buf = JS_ToCStringLen(ctx, &len, val)) \ + { \ + var = std::string(buf, len); \ + JS_FreeCString(ctx, buf); \ + } \ + else \ + { \ + return JS_EXCEPTION; \ + } \ + } + + #define JS_UNPACK_BOOL(var, ctx, val) \ + bool var; \ + if (!JS_IsBool(val)) \ + { \ + JS_ThrowTypeError(ctx, "Expected boolean"); \ + return JS_EXCEPTION; \ + } \ + { \ + const int result = JS_ToBool(ctx, val); \ + if (result == -1) \ + { \ + return JS_EXCEPTION; \ + } \ + var = result; \ + } + + #define JS_UNPACK_ARRAY(var, ctx, val) \ + JSValue var = val; \ + if (!JS_IsArray(val)) \ + { \ + JS_ThrowTypeError(ctx, "Expected array"); \ + return JS_EXCEPTION; \ + } + + #define JS_UNPACK_CALLBACK(var, ctx, val) \ + JSCallback var(ctx, val); \ + if (!var.IsValid()) \ + { \ + JS_ThrowTypeError(ctx, "Expected function"); \ + return JS_EXCEPTION; \ + } + + #define JS_UNPACK_OBJECT(var, ctx, val) \ + JSValue var = val; \ + if (!JS_IsObject(var)) \ + { \ + JS_ThrowTypeError(ctx, "Expected object"); \ + return JS_EXCEPTION; \ + } + + #define JS_UNPACK_COORDSXY(var, ctx, val) \ + CoordsXY var; \ + if (!JS_IsObject(var)) \ + { \ + JS_ThrowTypeError(ctx, "Expected CoordsXY"); \ + return JS_EXCEPTION; \ + } \ + { \ + auto x = JSToOptionalInt(ctx, position, "x"); \ + auto y = JSToOptionalInt(ctx, position, "y"); \ + if (x.has_value() && y.has_value()) \ + { \ + var = CoordsXY(x.value(), y.value()); \ + } \ + else \ + { \ + JS_ThrowTypeError(ctx, "Expected CoordsXY"); \ + return JS_EXCEPTION; \ + } \ + } +} // namespace OpenRCT2::Scripting + +#endif diff --git a/src/openrct2/scripting/bindings/entity/ScBalloon.cpp b/src/openrct2/scripting/bindings/entity/ScBalloon.cpp index 880e0b76e5..886f470fa6 100644 --- a/src/openrct2/scripting/bindings/entity/ScBalloon.cpp +++ b/src/openrct2/scripting/bindings/entity/ScBalloon.cpp @@ -15,40 +15,43 @@ namespace OpenRCT2::Scripting { - ScBalloon::ScBalloon(EntityId Id) - : ScEntity(Id) + JSValue ScBalloon::New(JSContext* ctx, EntityId entityId) { + JSValue obj = gScEntity.New(ctx, entityId); + AddFuncs(ctx, obj); + return obj; } - void ScBalloon::Register(duk_context* ctx) + void ScBalloon::AddFuncs(JSContext* ctx, JSValue obj) { - dukglue_set_base_class(ctx); - dukglue_register_property(ctx, &ScBalloon::colour_get, &ScBalloon::colour_set, "colour"); + static constexpr JSCFunctionListEntry funcs[] = { JS_CGETSET_DEF( + "colour", &ScBalloon::colour_get, &ScBalloon::colour_set) }; + JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs)); } - Balloon* ScBalloon::GetBalloon() const + Balloon* ScBalloon::GetBalloon(JSValue thisVal) { - return getGameState().entities.GetEntity(_id); + auto id = GetEntityId(thisVal); + return getGameState().entities.GetEntity(id); } - uint8_t ScBalloon::colour_get() const + JSValue ScBalloon::colour_get(JSContext* ctx, JSValue thisVal) { - auto balloon = GetBalloon(); - if (balloon != nullptr) - { - return EnumValue(balloon->colour); - } - return 0; + auto balloon = GetBalloon(thisVal); + return JS_NewUint32(ctx, balloon == nullptr ? 0 : EnumValue(balloon->colour)); } - void ScBalloon::colour_set(uint8_t value) + JSValue ScBalloon::colour_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - auto balloon = GetBalloon(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto balloon = GetBalloon(thisVal); if (balloon != nullptr) { balloon->colour = static_cast(value); balloon->Invalidate(); } + return JS_UNDEFINED; } } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/entity/ScBalloon.hpp b/src/openrct2/scripting/bindings/entity/ScBalloon.hpp index aba264aa48..e7c58d3572 100644 --- a/src/openrct2/scripting/bindings/entity/ScBalloon.hpp +++ b/src/openrct2/scripting/bindings/entity/ScBalloon.hpp @@ -20,19 +20,17 @@ namespace OpenRCT2 namespace OpenRCT2::Scripting { - - class ScBalloon : public ScEntity + class ScBalloon final : public ScEntity { public: - ScBalloon(EntityId Id); - - static void Register(duk_context* ctx); + static JSValue New(JSContext* ctx, EntityId entityId); private: - Balloon* GetBalloon() const; + static void AddFuncs(JSContext* ctx, JSValue obj); + static Balloon* GetBalloon(JSValue thisVal); - uint8_t colour_get() const; - void colour_set(uint8_t); + static JSValue colour_get(JSContext* ctx, JSValue thisVal); + static JSValue colour_set(JSContext* ctx, JSValue thisVal, JSValue value); }; } // namespace OpenRCT2::Scripting #endif diff --git a/src/openrct2/scripting/bindings/entity/ScEntity.cpp b/src/openrct2/scripting/bindings/entity/ScEntity.cpp new file mode 100644 index 0000000000..2b90003cc1 --- /dev/null +++ b/src/openrct2/scripting/bindings/entity/ScEntity.cpp @@ -0,0 +1,234 @@ +/***************************************************************************** + * Copyright (c) 2014-2025 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#ifdef ENABLE_SCRIPTING + + #include "ScEntity.hpp" + + #include "../../../entity/Staff.h" + #include "ScBalloon.hpp" + #include "ScGuest.hpp" + #include "ScLitter.hpp" + #include "ScMoneyEffect.hpp" + #include "ScParticle.hpp" + #include "ScPeep.hpp" + #include "ScStaff.hpp" + #include "ScVehicle.hpp" + +namespace OpenRCT2::Scripting +{ + static inline std::string EntityTypeToString(const EntityBase* entity) + { + const auto targetApiVersion = GetTargetAPIVersion(); + + if (entity != nullptr) + { + switch (entity->Type) + { + case EntityType::vehicle: + return "car"; + case EntityType::guest: + if (targetApiVersion <= kApiVersionPeepDeprecation) + return "peep"; + return "guest"; + case EntityType::staff: + if (targetApiVersion <= kApiVersionPeepDeprecation) + return "peep"; + return "staff"; + case EntityType::steamParticle: + return "steam_particle"; + case EntityType::moneyEffect: + return "money_effect"; + case EntityType::crashedVehicleParticle: + return "crashed_vehicle_particle"; + case EntityType::explosionCloud: + return "explosion_cloud"; + case EntityType::crashSplash: + return "crash_splash"; + case EntityType::explosionFlare: + return "explosion_flare"; + case EntityType::balloon: + return "balloon"; + case EntityType::duck: + return "duck"; + case EntityType::jumpingFountain: + return "jumping_fountain"; + case EntityType::litter: + return "litter"; + case EntityType::null: + return "unknown"; + default: + break; + } + } + return "unknown"; + } + + using OpaqueEntityData = struct + { + EntityId id; + }; + + JSValue ScEntity::id_get(JSContext* ctx, JSValue thisVal) + { + auto entity = GetEntity(thisVal); + if (entity == nullptr) + return JS_UNDEFINED; + + return JS_NewInt32(ctx, entity->Id.ToUnderlying()); + } + + JSValue ScEntity::type_get(JSContext* ctx, JSValue thisVal) + { + auto entity = GetEntity(thisVal); + auto type = EntityTypeToString(entity); + return JSFromStdString(ctx, type); + } + + // x getter and setter + JSValue ScEntity::x_get(JSContext* ctx, JSValue thisVal) + { + auto entity = GetEntity(thisVal); + return JS_NewInt32(ctx, entity != nullptr ? entity->x : 0); + } + JSValue ScEntity::x_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) + { + JS_UNPACK_INT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto entity = GetEntity(thisVal); + if (entity != nullptr) + { + entity->MoveTo({ value, entity->y, entity->z }); + } + return JS_UNDEFINED; + } + + // y getter and setter + JSValue ScEntity::y_get(JSContext* ctx, JSValue thisVal) + { + auto entity = GetEntity(thisVal); + return JS_NewInt32(ctx, entity != nullptr ? entity->y : 0); + } + JSValue ScEntity::y_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) + { + JS_UNPACK_INT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto entity = GetEntity(thisVal); + if (entity != nullptr) + { + entity->MoveTo({ entity->x, value, entity->z }); + } + return JS_UNDEFINED; + } + + // z getter and setter + JSValue ScEntity::z_get(JSContext* ctx, JSValue thisVal) + { + auto entity = GetEntity(thisVal); + return JS_NewInt32(ctx, entity != nullptr ? entity->z : 0); + } + JSValue ScEntity::z_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) + { + JS_UNPACK_INT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto entity = GetEntity(thisVal); + if (entity != nullptr) + { + entity->MoveTo({ entity->x, entity->y, value }); + } + return JS_UNDEFINED; + } + + JSValue ScEntity::remove(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) + { + auto entity = GetEntity(thisVal); + if (entity != nullptr) + { + entity->Invalidate(); + switch (entity->Type) + { + case EntityType::vehicle: + JS_ThrowPlainError(ctx, "Removing a vehicle is currently unsupported."); + return JS_EXCEPTION; + case EntityType::guest: + case EntityType::staff: + { + auto peep = entity->As(); + // We can't remove a single peep from a ride at the moment as this can cause complications with the + // vehicle car having an unsupported peep capacity. + if (peep == nullptr || peep->State == PeepState::onRide || peep->State == PeepState::enteringRide) + { + JS_ThrowPlainError(ctx, "Removing a peep that is on a ride is currently unsupported."); + return JS_EXCEPTION; + } + peep->Remove(); + break; + } + case EntityType::steamParticle: + case EntityType::moneyEffect: + case EntityType::crashedVehicleParticle: + case EntityType::explosionCloud: + case EntityType::crashSplash: + case EntityType::explosionFlare: + case EntityType::jumpingFountain: + case EntityType::balloon: + case EntityType::duck: + case EntityType::litter: + getGameState().entities.EntityRemove(entity); + break; + case EntityType::null: + break; + default: + break; + } + } + return JS_UNDEFINED; + } + + EntityId ScEntity::GetEntityId(JSValue thisVal) + { + return gScEntity.GetOpaque(thisVal)->id; + } + + EntityBase* ScEntity::GetEntity(JSValue thisVal) + { + auto id = GetEntityId(thisVal); + return OpenRCT2::getGameState().entities.GetEntity(id); + } + + JSValue ScEntity::NewInstance(JSContext* ctx, EntityId entityId) + { + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("id", &ScEntity::id_get, nullptr), JS_CGETSET_DEF("type", &ScEntity::type_get, nullptr), + JS_CGETSET_DEF("x", &ScEntity::x_get, &ScEntity::x_set), JS_CGETSET_DEF("y", &ScEntity::y_get, &ScEntity::y_set), + JS_CGETSET_DEF("z", &ScEntity::z_get, &ScEntity::z_set), JS_CFUNC_DEF("remove", 0, &ScEntity::remove) + }; + return MakeWithOpaque(ctx, funcs, new OpaqueEntityData{ entityId }); + } + + // this one exists as a hack to make a template work in ScMap + JSValue ScEntity::New(JSContext* ctx, EntityId entityId) + { + return gScEntity.NewInstance(ctx, entityId); + } + + void ScEntity::Register(JSContext* ctx) + { + RegisterBaseStr(ctx, "Entity", Finalize); + } + + void ScEntity::Finalize(JSRuntime* rt, JSValue thisVal) + { + OpaqueEntityData* data = gScEntity.GetOpaque(thisVal); + if (data) + delete data; + } +} // namespace OpenRCT2::Scripting + +#endif diff --git a/src/openrct2/scripting/bindings/entity/ScEntity.hpp b/src/openrct2/scripting/bindings/entity/ScEntity.hpp index ec166ec624..061675c9f6 100644 --- a/src/openrct2/scripting/bindings/entity/ScEntity.hpp +++ b/src/openrct2/scripting/bindings/entity/ScEntity.hpp @@ -15,7 +15,6 @@ #include "../../../GameState.h" #include "../../../entity/EntityRegistry.h" #include "../../../entity/Peep.h" - #include "../../Duktape.hpp" #include "../../ScriptEngine.h" #include @@ -23,188 +22,41 @@ namespace OpenRCT2::Scripting { - class ScEntity + class ScEntity; + extern ScEntity gScEntity; + + class ScEntity : public ScBase { - protected: - EntityId _id{ EntityId::GetNull() }; - - public: - ScEntity(EntityId id) - : _id(id) - { - } - private: - DukValue id_get() const - { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - - auto entity = GetEntity(); - if (entity == nullptr) - return ToDuk(ctx, nullptr); - - return ToDuk(ctx, entity->Id.ToUnderlying()); - } - - std::string type_get() const - { - const auto targetApiVersion = GetTargetAPIVersion(); - - auto entity = GetEntity(); - if (entity != nullptr) - { - switch (entity->Type) - { - case EntityType::vehicle: - return "car"; - case EntityType::guest: - if (targetApiVersion <= kApiVersionPeepDeprecation) - return "peep"; - return "guest"; - case EntityType::staff: - if (targetApiVersion <= kApiVersionPeepDeprecation) - return "peep"; - return "staff"; - case EntityType::steamParticle: - return "steam_particle"; - case EntityType::moneyEffect: - return "money_effect"; - case EntityType::crashedVehicleParticle: - return "crashed_vehicle_particle"; - case EntityType::explosionCloud: - return "explosion_cloud"; - case EntityType::crashSplash: - return "crash_splash"; - case EntityType::explosionFlare: - return "explosion_flare"; - case EntityType::balloon: - return "balloon"; - case EntityType::duck: - return "duck"; - case EntityType::jumpingFountain: - return "jumping_fountain"; - case EntityType::litter: - return "litter"; - case EntityType::null: - return "unknown"; - default: - break; - } - } - return "unknown"; - } + static JSValue id_get(JSContext* ctx, JSValue thisVal); + static JSValue type_get(JSContext* ctx, JSValue thisVal); // x getter and setter - int32_t x_get() const - { - auto entity = GetEntity(); - return entity != nullptr ? entity->x : 0; - } - void x_set(int32_t value) - { - ThrowIfGameStateNotMutable(); - auto entity = GetEntity(); - if (entity != nullptr) - { - entity->MoveTo({ value, entity->y, entity->z }); - } - } + static JSValue x_get(JSContext* ctx, JSValue thisVal); + static JSValue x_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); // y getter and setter - int32_t y_get() const - { - auto entity = GetEntity(); - return entity != nullptr ? entity->y : 0; - } - void y_set(int32_t value) - { - ThrowIfGameStateNotMutable(); - auto entity = GetEntity(); - if (entity != nullptr) - { - entity->MoveTo({ entity->x, value, entity->z }); - } - } + static JSValue y_get(JSContext* ctx, JSValue thisVal); + static JSValue y_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); // z getter and setter - int16_t z_get() const - { - auto entity = GetEntity(); - return entity != nullptr ? entity->z : 0; - } - void z_set(int16_t value) - { - ThrowIfGameStateNotMutable(); - auto entity = GetEntity(); - if (entity != nullptr) - { - entity->MoveTo({ entity->x, entity->y, value }); - } - } + static JSValue z_get(JSContext* ctx, JSValue thisVal); + static JSValue z_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - void remove() - { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - auto entity = GetEntity(); - if (entity != nullptr) - { - entity->Invalidate(); - switch (entity->Type) - { - case EntityType::vehicle: - duk_error(ctx, DUK_ERR_ERROR, "Removing a vehicle is currently unsupported."); - break; - case EntityType::guest: - case EntityType::staff: - { - auto peep = entity->As(); - // We can't remove a single peep from a ride at the moment as this can cause complications with the - // vehicle car having an unsupported peep capacity. - if (peep == nullptr || peep->State == PeepState::onRide || peep->State == PeepState::enteringRide) - { - duk_error(ctx, DUK_ERR_ERROR, "Removing a peep that is on a ride is currently unsupported."); - } - else - { - peep->Remove(); - } - break; - } - case EntityType::steamParticle: - case EntityType::moneyEffect: - case EntityType::crashedVehicleParticle: - case EntityType::explosionCloud: - case EntityType::crashSplash: - case EntityType::explosionFlare: - case EntityType::jumpingFountain: - case EntityType::balloon: - case EntityType::duck: - case EntityType::litter: - getGameState().entities.EntityRemove(entity); - break; - case EntityType::null: - break; - default: - break; - } - } - } + static JSValue remove(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); - EntityBase* GetEntity() const - { - return getGameState().entities.GetEntity(_id); - } + protected: + static EntityId GetEntityId(JSValue thisVal); + static EntityBase* GetEntity(JSValue thisVal); public: - static void Register(duk_context* ctx) - { - dukglue_register_property(ctx, &ScEntity::id_get, nullptr, "id"); - dukglue_register_property(ctx, &ScEntity::type_get, nullptr, "type"); - dukglue_register_property(ctx, &ScEntity::x_get, &ScEntity::x_set, "x"); - dukglue_register_property(ctx, &ScEntity::y_get, &ScEntity::y_set, "y"); - dukglue_register_property(ctx, &ScEntity::z_get, &ScEntity::z_set, "z"); - dukglue_register_method(ctx, &ScEntity::remove, "remove"); - } + JSValue NewInstance(JSContext* ctx, EntityId entityId); + static JSValue New(JSContext* ctx, EntityId entityId); + + void Register(JSContext* ctx); + + private: + static void Finalize(JSRuntime* rt, JSValue thisVal); }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/entity/ScGuest.cpp b/src/openrct2/scripting/bindings/entity/ScGuest.cpp index b28a6f6d65..a16de88e93 100644 --- a/src/openrct2/scripting/bindings/entity/ScGuest.cpp +++ b/src/openrct2/scripting/bindings/entity/ScGuest.cpp @@ -22,7 +22,7 @@ namespace OpenRCT2::Scripting { - static const DukEnumMap ThoughtTypeMap( + static const EnumMap ThoughtTypeMap( { { "cant_afford_ride", PeepThoughtType::CantAffordRide }, { "spent_money", PeepThoughtType::SpentMoney }, @@ -151,419 +151,447 @@ namespace OpenRCT2::Scripting { "here_we_are", PeepThoughtType::HereWeAre }, }); - ScGuest::ScGuest(EntityId id) - : ScPeep(id) + JSValue ScGuest::New(JSContext* ctx, EntityId entityId) { + JSValue obj = ScPeep::New(ctx, entityId); + AddFuncs(ctx, obj); + return obj; } - void ScGuest::Register(duk_context* ctx) + void ScGuest::AddFuncs(JSContext* ctx, JSValue obj) { - dukglue_set_base_class(ctx); - dukglue_register_property(ctx, &ScGuest::tshirtColour_get, &ScGuest::tshirtColour_set, "tshirtColour"); - dukglue_register_property(ctx, &ScGuest::trousersColour_get, &ScGuest::trousersColour_set, "trousersColour"); - dukglue_register_property(ctx, &ScGuest::balloonColour_get, &ScGuest::balloonColour_set, "balloonColour"); - dukglue_register_property(ctx, &ScGuest::hatColour_get, &ScGuest::hatColour_set, "hatColour"); - dukglue_register_property(ctx, &ScGuest::umbrellaColour_get, &ScGuest::umbrellaColour_set, "umbrellaColour"); - dukglue_register_property(ctx, &ScGuest::happiness_get, &ScGuest::happiness_set, "happiness"); - dukglue_register_property(ctx, &ScGuest::happinessTarget_get, &ScGuest::happinessTarget_set, "happinessTarget"); - dukglue_register_property(ctx, &ScGuest::nausea_get, &ScGuest::nausea_set, "nausea"); - dukglue_register_property(ctx, &ScGuest::nauseaTarget_get, &ScGuest::nauseaTarget_set, "nauseaTarget"); - dukglue_register_property(ctx, &ScGuest::hunger_get, &ScGuest::hunger_set, "hunger"); - dukglue_register_property(ctx, &ScGuest::thirst_get, &ScGuest::thirst_set, "thirst"); - dukglue_register_property(ctx, &ScGuest::toilet_get, &ScGuest::toilet_set, "toilet"); - dukglue_register_property(ctx, &ScGuest::mass_get, &ScGuest::mass_set, "mass"); - dukglue_register_property(ctx, &ScGuest::minIntensity_get, &ScGuest::minIntensity_set, "minIntensity"); - dukglue_register_property(ctx, &ScGuest::maxIntensity_get, &ScGuest::maxIntensity_set, "maxIntensity"); - dukglue_register_property(ctx, &ScGuest::nauseaTolerance_get, &ScGuest::nauseaTolerance_set, "nauseaTolerance"); - dukglue_register_property(ctx, &ScGuest::cash_get, &ScGuest::cash_set, "cash"); - dukglue_register_property(ctx, &ScGuest::isInPark_get, nullptr, "isInPark"); - dukglue_register_property(ctx, &ScGuest::isLost_get, nullptr, "isLost"); - dukglue_register_property(ctx, &ScGuest::lostCountdown_get, &ScGuest::lostCountdown_set, "lostCountdown"); - dukglue_register_property(ctx, &ScGuest::favouriteRide_get, &ScGuest::favouriteRide_set, "favouriteRide"); - dukglue_register_property(ctx, &ScGuest::thoughts_get, nullptr, "thoughts"); - dukglue_register_property(ctx, &ScGuest::items_get, nullptr, "items"); - dukglue_register_property(ctx, &ScGuest::availableAnimations_get, nullptr, "availableAnimations"); - dukglue_register_property(ctx, &ScGuest::animation_get, &ScGuest::animation_set, "animation"); - dukglue_register_property(ctx, &ScGuest::animationOffset_get, &ScGuest::animationOffset_set, "animationOffset"); - dukglue_register_property(ctx, &ScGuest::animationLength_get, nullptr, "animationLength"); - dukglue_register_method(ctx, &ScGuest::getAnimationSpriteIds, "getAnimationSpriteIds"); - dukglue_register_method(ctx, &ScGuest::has_item, "hasItem"); - dukglue_register_method(ctx, &ScGuest::give_item, "giveItem"); - dukglue_register_method(ctx, &ScGuest::remove_item, "removeItem"); - dukglue_register_method(ctx, &ScGuest::remove_all_items, "removeAllItems"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("tshirtColour", &ScGuest::tshirtColour_get, &ScGuest::tshirtColour_set), + JS_CGETSET_DEF("trousersColour", &ScGuest::trousersColour_get, &ScGuest::trousersColour_set), + JS_CGETSET_DEF("balloonColour", &ScGuest::balloonColour_get, &ScGuest::balloonColour_set), + JS_CGETSET_DEF("hatColour", &ScGuest::hatColour_get, &ScGuest::hatColour_set), + JS_CGETSET_DEF("umbrellaColour", &ScGuest::umbrellaColour_get, &ScGuest::umbrellaColour_set), + JS_CGETSET_DEF("happiness", &ScGuest::happiness_get, &ScGuest::happiness_set), + JS_CGETSET_DEF("happinessTarget", &ScGuest::happinessTarget_get, &ScGuest::happinessTarget_set), + JS_CGETSET_DEF("nausea", &ScGuest::nausea_get, &ScGuest::nausea_set), + JS_CGETSET_DEF("nauseaTarget", &ScGuest::nauseaTarget_get, &ScGuest::nauseaTarget_set), + JS_CGETSET_DEF("hunger", &ScGuest::hunger_get, &ScGuest::hunger_set), + JS_CGETSET_DEF("thirst", &ScGuest::thirst_get, &ScGuest::thirst_set), + JS_CGETSET_DEF("toilet", &ScGuest::toilet_get, &ScGuest::toilet_set), + JS_CGETSET_DEF("mass", &ScGuest::mass_get, &ScGuest::mass_set), + JS_CGETSET_DEF("minIntensity", &ScGuest::minIntensity_get, &ScGuest::minIntensity_set), + JS_CGETSET_DEF("maxIntensity", &ScGuest::maxIntensity_get, &ScGuest::maxIntensity_set), + JS_CGETSET_DEF("nauseaTolerance", &ScGuest::nauseaTolerance_get, &ScGuest::nauseaTolerance_set), + JS_CGETSET_DEF("cash", &ScGuest::cash_get, &ScGuest::cash_set), + JS_CGETSET_DEF("isInPark", &ScGuest::isInPark_get, nullptr), + JS_CGETSET_DEF("isLost", &ScGuest::isLost_get, nullptr), + JS_CGETSET_DEF("lostCountdown", &ScGuest::lostCountdown_get, &ScGuest::lostCountdown_set), + JS_CGETSET_DEF("favouriteRide", &ScGuest::favouriteRide_get, &ScGuest::favouriteRide_set), + JS_CGETSET_DEF("thoughts", &ScGuest::thoughts_get, nullptr), + JS_CGETSET_DEF("items", &ScGuest::items_get, nullptr), + JS_CGETSET_DEF("availableAnimations", &ScGuest::availableAnimations_get, nullptr), + JS_CGETSET_DEF("animation", &ScGuest::animation_get, &ScGuest::animation_set), + JS_CGETSET_DEF("animationOffset", &ScGuest::animationOffset_get, &ScGuest::animationOffset_set), + JS_CGETSET_DEF("animationLength", &ScGuest::animationLength_get, nullptr), + JS_CFUNC_DEF("getAnimationSpriteIds", 2, &ScGuest::getAnimationSpriteIds), + JS_CFUNC_DEF("hasItem", 1, &ScGuest::has_item), + JS_CFUNC_DEF("giveItem", 1, &ScGuest::give_item), + JS_CFUNC_DEF("removeItem", 1, &ScGuest::remove_item), + JS_CFUNC_DEF("removeAllItems", 0, &ScGuest::remove_all_items), + }; + JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs)); } - Guest* ScGuest::GetGuest() const + Guest* ScGuest::GetGuest(JSValue thisVal) { - return getGameState().entities.GetEntity(_id); + auto id = GetEntityId(thisVal); + return getGameState().entities.GetEntity(id); } - uint8_t ScGuest::tshirtColour_get() const + JSValue ScGuest::tshirtColour_get(JSContext* ctx, JSValue thisVal) { - auto peep = GetGuest(); - return peep != nullptr ? EnumValue(peep->TshirtColour) : 0; + auto peep = GetGuest(thisVal); + return JS_NewUint32(ctx, peep != nullptr ? EnumValue(peep->TshirtColour) : 0); } - void ScGuest::tshirtColour_set(uint8_t value) + JSValue ScGuest::tshirtColour_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto peep = GetGuest(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto peep = GetGuest(thisVal); if (peep != nullptr) { peep->TshirtColour = static_cast(value); peep->Invalidate(); } + return JS_UNDEFINED; } - uint8_t ScGuest::trousersColour_get() const + JSValue ScGuest::trousersColour_get(JSContext* ctx, JSValue thisVal) { - auto peep = GetGuest(); - return peep != nullptr ? EnumValue(peep->TrousersColour) : 0; + auto peep = GetGuest(thisVal); + return JS_NewUint32(ctx, peep != nullptr ? EnumValue(peep->TrousersColour) : 0); } - void ScGuest::trousersColour_set(uint8_t value) + JSValue ScGuest::trousersColour_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto peep = GetGuest(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto peep = GetGuest(thisVal); if (peep != nullptr) { peep->TrousersColour = static_cast(value); peep->Invalidate(); } + return JS_UNDEFINED; } - uint8_t ScGuest::balloonColour_get() const + JSValue ScGuest::balloonColour_get(JSContext* ctx, JSValue thisVal) { - auto peep = GetGuest(); - return peep != nullptr ? EnumValue(peep->BalloonColour) : 0; + auto peep = GetGuest(thisVal); + return JS_NewUint32(ctx, peep != nullptr ? EnumValue(peep->BalloonColour) : 0); } - void ScGuest::balloonColour_set(uint8_t value) + JSValue ScGuest::balloonColour_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto peep = GetGuest(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto peep = GetGuest(thisVal); if (peep != nullptr) { peep->BalloonColour = static_cast(value); peep->Invalidate(); } + return JS_UNDEFINED; } - uint8_t ScGuest::hatColour_get() const + JSValue ScGuest::hatColour_get(JSContext* ctx, JSValue thisVal) { - auto peep = GetGuest(); - return peep != nullptr ? EnumValue(peep->HatColour) : 0; + auto peep = GetGuest(thisVal); + return JS_NewUint32(ctx, peep != nullptr ? EnumValue(peep->HatColour) : 0); } - void ScGuest::hatColour_set(uint8_t value) + JSValue ScGuest::hatColour_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto peep = GetGuest(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto peep = GetGuest(thisVal); if (peep != nullptr) { peep->HatColour = static_cast(value); peep->Invalidate(); } + return JS_UNDEFINED; } - uint8_t ScGuest::umbrellaColour_get() const + JSValue ScGuest::umbrellaColour_get(JSContext* ctx, JSValue thisVal) { - auto peep = GetGuest(); - return peep != nullptr ? EnumValue(peep->UmbrellaColour) : 0; + auto peep = GetGuest(thisVal); + return JS_NewUint32(ctx, peep != nullptr ? EnumValue(peep->UmbrellaColour) : 0); } - void ScGuest::umbrellaColour_set(uint8_t value) + JSValue ScGuest::umbrellaColour_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto peep = GetGuest(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto peep = GetGuest(thisVal); if (peep != nullptr) { peep->UmbrellaColour = static_cast(value); peep->Invalidate(); } + return JS_UNDEFINED; } - uint8_t ScGuest::happiness_get() const + JSValue ScGuest::happiness_get(JSContext* ctx, JSValue thisVal) { - auto peep = GetGuest(); - return peep != nullptr ? peep->Happiness : 0; + auto peep = GetGuest(thisVal); + return JS_NewUint32(ctx, peep != nullptr ? peep->Happiness : 0); } - void ScGuest::happiness_set(uint8_t value) + JSValue ScGuest::happiness_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto peep = GetGuest(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto peep = GetGuest(thisVal); if (peep != nullptr) { peep->Happiness = value; } + return JS_UNDEFINED; } - uint8_t ScGuest::happinessTarget_get() const + JSValue ScGuest::happinessTarget_get(JSContext* ctx, JSValue thisVal) { - auto peep = GetGuest(); - return peep != nullptr ? peep->HappinessTarget : 0; + auto peep = GetGuest(thisVal); + return JS_NewUint32(ctx, peep != nullptr ? peep->HappinessTarget : 0); } - void ScGuest::happinessTarget_set(uint8_t value) + JSValue ScGuest::happinessTarget_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto peep = GetGuest(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto peep = GetGuest(thisVal); if (peep != nullptr) { peep->HappinessTarget = value; } + return JS_UNDEFINED; } - uint8_t ScGuest::nausea_get() const + JSValue ScGuest::nausea_get(JSContext* ctx, JSValue thisVal) { - auto peep = GetGuest(); - return peep != nullptr ? peep->Nausea : 0; + auto peep = GetGuest(thisVal); + return JS_NewUint32(ctx, peep != nullptr ? peep->Nausea : 0); } - void ScGuest::nausea_set(uint8_t value) + JSValue ScGuest::nausea_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto peep = GetGuest(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto peep = GetGuest(thisVal); if (peep != nullptr) { peep->Nausea = value; } + return JS_UNDEFINED; } - uint8_t ScGuest::nauseaTarget_get() const + JSValue ScGuest::nauseaTarget_get(JSContext* ctx, JSValue thisVal) { - auto peep = GetGuest(); - return peep != nullptr ? peep->NauseaTarget : 0; + auto peep = GetGuest(thisVal); + return JS_NewUint32(ctx, peep != nullptr ? peep->NauseaTarget : 0); } - void ScGuest::nauseaTarget_set(uint8_t value) + JSValue ScGuest::nauseaTarget_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto peep = GetGuest(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto peep = GetGuest(thisVal); if (peep != nullptr) { peep->NauseaTarget = value; } + return JS_UNDEFINED; } - uint8_t ScGuest::hunger_get() const + JSValue ScGuest::hunger_get(JSContext* ctx, JSValue thisVal) { - auto peep = GetGuest(); - return peep != nullptr ? peep->Hunger : 0; + auto peep = GetGuest(thisVal); + return JS_NewUint32(ctx, peep != nullptr ? peep->Hunger : 0); } - void ScGuest::hunger_set(uint8_t value) + JSValue ScGuest::hunger_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto peep = GetGuest(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto peep = GetGuest(thisVal); if (peep != nullptr) { peep->Hunger = value; } + return JS_UNDEFINED; } - uint8_t ScGuest::thirst_get() const + JSValue ScGuest::thirst_get(JSContext* ctx, JSValue thisVal) { - auto peep = GetGuest(); - return peep != nullptr ? peep->Thirst : 0; + auto peep = GetGuest(thisVal); + return JS_NewUint32(ctx, peep != nullptr ? peep->Thirst : 0); } - void ScGuest::thirst_set(uint8_t value) + JSValue ScGuest::thirst_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto peep = GetGuest(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto peep = GetGuest(thisVal); if (peep != nullptr) { peep->Thirst = value; } + return JS_UNDEFINED; } - uint8_t ScGuest::toilet_get() const + JSValue ScGuest::toilet_get(JSContext* ctx, JSValue thisVal) { - auto peep = GetGuest(); - return peep != nullptr ? peep->Toilet : 0; + auto peep = GetGuest(thisVal); + return JS_NewUint32(ctx, peep != nullptr ? peep->Toilet : 0); } - void ScGuest::toilet_set(uint8_t value) + JSValue ScGuest::toilet_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto peep = GetGuest(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto peep = GetGuest(thisVal); if (peep != nullptr) { peep->Toilet = value; } + return JS_UNDEFINED; } - uint8_t ScGuest::mass_get() const + JSValue ScGuest::mass_get(JSContext* ctx, JSValue thisVal) { - auto peep = GetGuest(); - return peep != nullptr ? peep->Mass : 0; + auto peep = GetGuest(thisVal); + return JS_NewUint32(ctx, peep != nullptr ? peep->Mass : 0); } - void ScGuest::mass_set(uint8_t value) + JSValue ScGuest::mass_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto peep = GetGuest(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto peep = GetGuest(thisVal); if (peep != nullptr) { peep->Mass = value; } + return JS_UNDEFINED; } - uint8_t ScGuest::minIntensity_get() const + JSValue ScGuest::minIntensity_get(JSContext* ctx, JSValue thisVal) { - auto peep = GetGuest(); - return peep != nullptr ? peep->Intensity.GetMinimum() : 0; + auto peep = GetGuest(thisVal); + return JS_NewUint32(ctx, peep != nullptr ? peep->Intensity.GetMinimum() : 0); } - void ScGuest::minIntensity_set(uint8_t value) + JSValue ScGuest::minIntensity_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto peep = GetGuest(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto peep = GetGuest(thisVal); if (peep != nullptr) { peep->Intensity = peep->Intensity.WithMinimum(value); } + return JS_UNDEFINED; } - uint8_t ScGuest::maxIntensity_get() const + JSValue ScGuest::maxIntensity_get(JSContext* ctx, JSValue thisVal) { - auto peep = GetGuest(); - return peep != nullptr ? peep->Intensity.GetMaximum() : 0; + auto peep = GetGuest(thisVal); + return JS_NewUint32(ctx, peep != nullptr ? peep->Intensity.GetMaximum() : 0); } - void ScGuest::maxIntensity_set(uint8_t value) + JSValue ScGuest::maxIntensity_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto peep = GetGuest(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto peep = GetGuest(thisVal); if (peep != nullptr) { peep->Intensity = peep->Intensity.WithMaximum(value); } + return JS_UNDEFINED; } - uint8_t ScGuest::nauseaTolerance_get() const + JSValue ScGuest::nauseaTolerance_get(JSContext* ctx, JSValue thisVal) { - auto peep = GetGuest(); - return peep != nullptr ? EnumValue(peep->NauseaTolerance) : 0; + auto peep = GetGuest(thisVal); + return JS_NewUint32(ctx, peep != nullptr ? EnumValue(peep->NauseaTolerance) : 0); } - void ScGuest::nauseaTolerance_set(uint8_t value) + JSValue ScGuest::nauseaTolerance_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto peep = GetGuest(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto peep = GetGuest(thisVal); if (peep != nullptr) { peep->NauseaTolerance = static_cast(std::min(value, 3)); } + return JS_UNDEFINED; } - int32_t ScGuest::cash_get() const + JSValue ScGuest::cash_get(JSContext* ctx, JSValue thisVal) { - auto peep = GetGuest(); - return peep != nullptr ? peep->CashInPocket : 0; + auto peep = GetGuest(thisVal); + return JS_NewInt32(ctx, peep != nullptr ? peep->CashInPocket : 0); } - void ScGuest::cash_set(int32_t value) + JSValue ScGuest::cash_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto peep = GetGuest(); + JS_UNPACK_INT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto peep = GetGuest(thisVal); if (peep != nullptr) { peep->CashInPocket = std::max(0, value); } + return JS_UNDEFINED; } - bool ScGuest::isInPark_get() const + JSValue ScGuest::isInPark_get(JSContext* ctx, JSValue thisVal) { - auto peep = GetGuest(); - return (peep != nullptr && !peep->OutsideOfPark); + auto peep = GetGuest(thisVal); + return JS_NewBool(ctx, peep != nullptr && !peep->OutsideOfPark); } - bool ScGuest::isLost_get() const + JSValue ScGuest::isLost_get(JSContext* ctx, JSValue thisVal) { - auto peep = GetGuest(); - return (peep != nullptr && peep->GuestIsLostCountdown < 90); + auto peep = GetGuest(thisVal); + return JS_NewBool(ctx, peep != nullptr && peep->GuestIsLostCountdown < 90); } - uint8_t ScGuest::lostCountdown_get() const + JSValue ScGuest::lostCountdown_get(JSContext* ctx, JSValue thisVal) { - auto peep = GetGuest(); - return peep != nullptr ? peep->GuestIsLostCountdown : 0; + auto peep = GetGuest(thisVal); + return JS_NewUint32(ctx, peep != nullptr ? peep->GuestIsLostCountdown : 0); } - void ScGuest::lostCountdown_set(uint8_t value) + JSValue ScGuest::lostCountdown_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto peep = GetGuest(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto peep = GetGuest(thisVal); if (peep != nullptr) { peep->GuestIsLostCountdown = value; } + return JS_UNDEFINED; } - DukValue ScGuest::favouriteRide_get() const + JSValue ScGuest::favouriteRide_get(JSContext* ctx, JSValue thisVal) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto peep = GetGuest(); + auto peep = GetGuest(thisVal); if (peep != nullptr) { if (peep->FavouriteRide != RideId::GetNull()) { - duk_push_int(ctx, peep->FavouriteRide.ToUnderlying()); - } - else - { - duk_push_null(ctx); + return JS_NewUint32(ctx, peep->FavouriteRide.ToUnderlying()); } } - else - { - duk_push_null(ctx); - } - return DukValue::take_from_stack(ctx); + return JS_NULL; } - void ScGuest::favouriteRide_set(const DukValue& value) + JSValue ScGuest::favouriteRide_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto peep = GetGuest(); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto peep = GetGuest(thisVal); if (peep != nullptr) { auto& gameState = getGameState(); - if (value.type() == DukValue::Type::NUMBER && value.as_uint() < gameState.rides.size() - && gameState.rides[value.as_uint()].type != kRideTypeNull) - { - peep->FavouriteRide = RideId::FromUnderlying(value.as_uint()); - } - else + if (JS_IsNull(jsValue)) { peep->FavouriteRide = RideId::GetNull(); } + else if (JS_IsNumber(jsValue)) + { + JS_UNPACK_UINT32(rideId, ctx, jsValue); + if (rideId < gameState.rides.size() && gameState.rides[rideId].type != kRideTypeNull) + { + peep->FavouriteRide = RideId::FromUnderlying(rideId); + } + } } + return JS_UNDEFINED; } - DukValue ScGuest::thoughts_get() const + JSValue ScGuest::thoughts_get(JSContext* ctx, JSValue thisVal) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); + auto array = JS_NewArray(ctx); - duk_push_array(ctx); - - auto peep = GetGuest(); + auto peep = GetGuest(thisVal); if (peep != nullptr) { - duk_uarridx_t index = 0; + auto index = 0; for (const auto& thought : peep->Thoughts) { if (thought.type == PeepThoughtType::None) break; if (thought.freshness == 0) continue; - auto scThoughtPtr = std::make_shared(thought); - auto dukThought = GetObjectAsDukValue(ctx, scThoughtPtr); - dukThought.push(); - duk_put_prop_index(ctx, -2, index); - index++; + auto scThought = gScThought.New(ctx, thought); + JS_SetPropertyInt64(ctx, array, index++, scThought); } } - return DukValue::take_from_stack(ctx, -1); + return array; } - DukValue ScGuest::items_get() const + JSValue ScGuest::items_get(JSContext* ctx, JSValue thisVal) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); + auto array = JS_NewArray(ctx); - duk_push_array(ctx); - - auto peep = GetGuest(); + auto peep = GetGuest(thisVal); if (peep != nullptr) { - duk_uarridx_t index = 0; + auto index = 0; for (const auto& itemEnumPair : ShopItemMap) { auto shopItem = itemEnumPair.second; @@ -573,72 +601,74 @@ namespace OpenRCT2::Scripting } // GuestItem - auto obj = DukObject(ctx); - obj.Set("type", itemEnumPair.first); + auto obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "type", JSFromStdString(ctx, itemEnumPair.first)); if (shopItem == ShopItem::voucher) { // Voucher - obj.Set("voucherType", VoucherTypeMap[peep->VoucherType]); + JS_SetPropertyStr(ctx, obj, "voucherType", JSFromStdString(ctx, VoucherTypeMap[peep->VoucherType])); if (peep->VoucherType == VOUCHER_TYPE_RIDE_FREE) { // RideVoucher - obj.Set("rideId", peep->VoucherRideId.ToUnderlying()); + JS_SetPropertyStr(ctx, obj, "rideId", JS_NewUint32(ctx, peep->VoucherRideId.ToUnderlying())); } else if (peep->VoucherType == VOUCHER_TYPE_FOOD_OR_DRINK_FREE) { // FoodDrinkVoucher - obj.Set("item", ShopItemMap[peep->VoucherShopItem]); + JS_SetPropertyStr(ctx, obj, "item", JSFromStdString(ctx, ShopItemMap[peep->VoucherShopItem])); } } else if (GetShopItemDescriptor(shopItem).IsPhoto()) { // GuestPhoto + RideId rideId; switch (shopItem) { case ShopItem::photo: - obj.Set("rideId", peep->Photo1RideRef.ToUnderlying()); + rideId = peep->Photo1RideRef; break; case ShopItem::photo2: - obj.Set("rideId", peep->Photo2RideRef.ToUnderlying()); + rideId = peep->Photo2RideRef; break; case ShopItem::photo3: - obj.Set("rideId", peep->Photo3RideRef.ToUnderlying()); + rideId = peep->Photo3RideRef; break; case ShopItem::photo4: - obj.Set("rideId", peep->Photo4RideRef.ToUnderlying()); + rideId = peep->Photo4RideRef; break; default: // This should not be possible - duk_error(ctx, DUK_ERR_TYPE_ERROR, "Item is photo without a ride ref."); + JS_ThrowPlainError(ctx, "Item is photo without a ride ref."); + return JS_EXCEPTION; } + + JS_SetPropertyStr(ctx, obj, "rideId", JS_NewUint32(ctx, rideId.ToUnderlying())); } - auto dukItem = obj.Take(); - dukItem.push(); - duk_put_prop_index(ctx, -2, index); - index++; + JS_SetPropertyInt64(ctx, array, index++, obj); } } - return DukValue::take_from_stack(ctx, -1); + return array; } - bool ScGuest::has_item(const DukValue& item) const + bool ScGuest::has_item(JSContext* ctx, JSValue thisVal, JSValue item) { - auto peep = GetGuest(); + auto peep = GetGuest(thisVal); if (peep == nullptr) { return false; } - if (item["type"].type() != DukValue::Type::STRING) + auto type = JSToOptionalStdString(ctx, item, "type"); + if (!type.has_value()) { return false; } // GuestItem - auto shopItem = ShopItemMap.TryGet(item["type"].as_string()); + auto shopItem = ShopItemMap.TryGet(type.value()); if (!shopItem || !peep->HasItem(*shopItem)) { return false; @@ -646,10 +676,11 @@ namespace OpenRCT2::Scripting if (*shopItem == ShopItem::voucher) { - if (item["voucherType"].type() == DukValue::Type::STRING) + auto voucherType = JSToOptionalStdString(ctx, item, "voucherType"); + if (voucherType.has_value()) { // Voucher - auto voucher = VoucherTypeMap.TryGet(item["voucherType"].as_string()); + auto voucher = VoucherTypeMap.TryGet(voucherType.value()); if (!voucher || *voucher != peep->VoucherType) { return false; @@ -657,10 +688,11 @@ namespace OpenRCT2::Scripting if (*voucher == VOUCHER_TYPE_RIDE_FREE) { - if (item["rideId"].type() == DukValue::Type::NUMBER) + auto rideId = JSToOptionalUint(ctx, item, "rideId"); + if (rideId.has_value()) { // RideVoucher - if (item["rideId"].as_uint() != peep->VoucherRideId.ToUnderlying()) + if (rideId.value() != peep->VoucherRideId.ToUnderlying()) { return false; } @@ -668,10 +700,11 @@ namespace OpenRCT2::Scripting } else if (*voucher == VOUCHER_TYPE_FOOD_OR_DRINK_FREE) { - if (item["item"].type() == DukValue::Type::STRING) + auto foodItem = JSToOptionalStdString(ctx, item, "item"); + if (foodItem.has_value()) { // FoodDrinkVoucher - auto voucherItem = ShopItemMap.TryGet(item["item"].as_string()); + auto voucherItem = ShopItemMap.TryGet(foodItem.value()); if (!voucherItem || *voucherItem != peep->VoucherShopItem) { return false; @@ -683,30 +716,31 @@ namespace OpenRCT2::Scripting else if (GetShopItemDescriptor(*shopItem).IsPhoto()) { // GuestPhoto - if (item["rideId"].type() == DukValue::Type::NUMBER) + auto rideId = JSToOptionalUint(ctx, item, "rideId"); + if (rideId.has_value()) { switch (*shopItem) { case ShopItem::photo: - if (item["rideId"].as_uint() != peep->Photo1RideRef.ToUnderlying()) + if (rideId.value() != peep->Photo1RideRef.ToUnderlying()) { return false; } break; case ShopItem::photo2: - if (item["rideId"].as_uint() != peep->Photo2RideRef.ToUnderlying()) + if (rideId.value() != peep->Photo2RideRef.ToUnderlying()) { return false; } break; case ShopItem::photo3: - if (item["rideId"].as_uint() != peep->Photo3RideRef.ToUnderlying()) + if (rideId.value() != peep->Photo3RideRef.ToUnderlying()) { return false; } break; case ShopItem::photo4: - if (item["rideId"].as_uint() != peep->Photo4RideRef.ToUnderlying()) + if (rideId.value() != peep->Photo4RideRef.ToUnderlying()) { return false; } @@ -716,74 +750,86 @@ namespace OpenRCT2::Scripting } } } - return true; } - void ScGuest::give_item(const DukValue& item) const + JSValue ScGuest::has_item(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - ThrowIfGameStateNotMutable(); - auto peep = GetGuest(); + JS_UNPACK_OBJECT(item, ctx, argv[0]); + auto result = has_item(ctx, thisVal, item); + return JS_NewBool(ctx, result); + } + + JSValue ScGuest::give_item(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) + { + JS_UNPACK_OBJECT(item, ctx, argv[0]); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto peep = GetGuest(thisVal); if (peep == nullptr) { - return; + return JS_UNDEFINED; } // GuestItem - if (item["type"].type() != DukValue::Type::STRING) + auto type = JSToOptionalStdString(ctx, item, "type"); + if (!type.has_value()) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - duk_error(ctx, DUK_ERR_ERROR, "Invalid 'type'."); + JS_ThrowPlainError(ctx, "Invalid 'type'."); + return JS_EXCEPTION; } - auto shopItem = ShopItemMap.TryGet(item["type"].as_string()); + auto shopItem = ShopItemMap.TryGet(type.value()); if (!shopItem) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - duk_error(ctx, DUK_ERR_ERROR, "Invalid 'type'."); + JS_ThrowPlainError(ctx, "Invalid 'type'."); + return JS_EXCEPTION; } if (*shopItem == ShopItem::voucher) { // Voucher - if (item["voucherType"].type() != DukValue::Type::STRING) + auto voucherTypeName = JSToOptionalStdString(ctx, item, "voucherType"); + if (!voucherTypeName.has_value()) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - duk_error(ctx, DUK_ERR_ERROR, "Invalid 'voucherType'."); + JS_ThrowPlainError(ctx, "Invalid 'voucherType'."); + return JS_EXCEPTION; } - auto voucherType = VoucherTypeMap.TryGet(item["voucherType"].as_string()); + auto voucherType = VoucherTypeMap.TryGet(voucherTypeName.value()); if (!voucherType) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - duk_error(ctx, DUK_ERR_ERROR, "Invalid 'voucherType'."); + JS_ThrowPlainError(ctx, "Invalid 'voucherType'."); + return JS_EXCEPTION; } if (*voucherType == VOUCHER_TYPE_RIDE_FREE) { // RideVoucher - if (item["rideId"].type() != DukValue::Type::NUMBER) + auto rideId = JSToOptionalUint(ctx, item, "rideId"); + if (!rideId.has_value()) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - duk_error(ctx, DUK_ERR_ERROR, "Invalid 'rideId'."); + JS_ThrowPlainError(ctx, "Invalid 'rideId'."); + return JS_EXCEPTION; } - peep->VoucherRideId = RideId::FromUnderlying(item["rideId"].as_uint()); + peep->VoucherRideId = RideId::FromUnderlying(rideId.value()); } else if (*voucherType == VOUCHER_TYPE_FOOD_OR_DRINK_FREE) { // FoodDrinkVoucher - if (item["item"].type() != DukValue::Type::STRING) + auto itemName = JSToOptionalStdString(ctx, item, "item"); + if (!itemName.has_value()) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - duk_error(ctx, DUK_ERR_ERROR, "Invalid 'item' (for food/drink voucher)."); + JS_ThrowPlainError(ctx, "Invalid 'item' (for food/drink voucher)."); + return JS_EXCEPTION; } - auto voucherItem = ShopItemMap.TryGet(item["item"].as_string()); + auto voucherItem = ShopItemMap.TryGet(itemName.value()); if (!voucherItem) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - duk_error(ctx, DUK_ERR_ERROR, "Invalid 'item' (for food/drink voucher)."); + JS_ThrowPlainError(ctx, "Invalid 'item' (for food/drink voucher)."); + return JS_EXCEPTION; } peep->VoucherShopItem = *voucherItem; @@ -794,71 +840,81 @@ namespace OpenRCT2::Scripting else if (GetShopItemDescriptor(*shopItem).IsPhoto()) { // GuestPhoto - if (item["rideId"].type() != DukValue::Type::NUMBER) + auto rideId = JSToOptionalUint(ctx, item, "rideId"); + if (!rideId.has_value()) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - duk_error(ctx, DUK_ERR_ERROR, "Invalid 'rideId'."); + JS_ThrowPlainError(ctx, "Invalid 'rideId'."); + return JS_EXCEPTION; } switch (*shopItem) { case ShopItem::photo: - peep->Photo1RideRef = RideId::FromUnderlying(item["rideId"].as_uint()); + peep->Photo1RideRef = RideId::FromUnderlying(rideId.value()); break; case ShopItem::photo2: - peep->Photo2RideRef = RideId::FromUnderlying(item["rideId"].as_uint()); + peep->Photo2RideRef = RideId::FromUnderlying(rideId.value()); break; case ShopItem::photo3: - peep->Photo3RideRef = RideId::FromUnderlying(item["rideId"].as_uint()); + peep->Photo3RideRef = RideId::FromUnderlying(rideId.value()); break; case ShopItem::photo4: - peep->Photo4RideRef = RideId::FromUnderlying(item["rideId"].as_uint()); + peep->Photo4RideRef = RideId::FromUnderlying(rideId.value()); break; default: - return; + return JS_UNDEFINED; } } peep->GiveItem(*shopItem); peep->UpdateAnimationGroup(); + return JS_UNDEFINED; } - void ScGuest::remove_item(const DukValue& item) const + JSValue ScGuest::remove_item(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - ThrowIfGameStateNotMutable(); - if (has_item(item)) + JS_UNPACK_OBJECT(item, ctx, argv[0]); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + if (has_item(ctx, thisVal, item)) { + auto type = JSToStdString(ctx, item, "type"); + // Since guests can only have one item of a type and this item matches, remove it. - auto peep = GetGuest(); - peep->RemoveItem(ShopItemMap[item["type"].as_string()]); + auto peep = GetGuest(thisVal); + peep->RemoveItem(ShopItemMap[type]); peep->UpdateAnimationGroup(); } + return JS_UNDEFINED; } - void ScGuest::remove_all_items() const + JSValue ScGuest::remove_all_items(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - ThrowIfGameStateNotMutable(); - auto peep = GetGuest(); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto peep = GetGuest(thisVal); if (peep != nullptr) { peep->RemoveAllItems(); peep->UpdateAnimationGroup(); } + return JS_UNDEFINED; } - std::vector ScGuest::availableAnimations_get() const + JSValue ScGuest::availableAnimations_get(JSContext* ctx, JSValue thisVal) { - std::vector availableAnimations{}; + auto availableAnimations = JS_NewArray(ctx); + auto index = 0; for (auto& animation : getAnimationsByPeepType(AnimationPeepType::guest)) { - availableAnimations.push_back(std::string(animation.first)); + JS_SetPropertyInt64(ctx, availableAnimations, index++, JSFromStdString(ctx, animation.first)); } return availableAnimations; } - std::vector ScGuest::getAnimationSpriteIds(std::string groupKey, uint8_t rotation) const + JSValue ScGuest::getAnimationSpriteIds(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - std::vector spriteIds{}; + JS_UNPACK_STR(groupKey, ctx, argv[0]); + JS_UNPACK_UINT32(rotation, ctx, argv[1]); + JSValue spriteIds = JS_NewArray(ctx); auto& availableGuestAnimations = getAnimationsByPeepType(AnimationPeepType::guest); auto animationType = availableGuestAnimations.TryGet(groupKey); @@ -867,13 +923,14 @@ namespace OpenRCT2::Scripting return spriteIds; } - auto peep = GetPeep(); + auto peep = GetPeep(thisVal); if (peep != nullptr) { auto& objManager = GetContext()->GetObjectManager(); auto* animObj = objManager.GetLoadedObject(peep->AnimationObjectIndex); const auto& animationGroup = animObj->GetPeepAnimation(peep->AnimationGroup, *animationType); + auto idx = 0; for (auto frameOffset : animationGroup.frameOffsets) { auto imageId = animationGroup.baseImage; @@ -882,18 +939,18 @@ namespace OpenRCT2::Scripting else imageId += frameOffset; - spriteIds.push_back(imageId); + JS_SetPropertyInt64(ctx, spriteIds, idx++, JS_NewUint32(ctx, imageId)); } } return spriteIds; } - std::string ScGuest::animation_get() const + JSValue ScGuest::animation_get(JSContext* ctx, JSValue thisVal) { - auto* peep = GetGuest(); + auto* peep = GetGuest(thisVal); if (peep == nullptr) { - return ""; + return JSFromStdString(ctx, ""); } auto& availableGuestAnimations = getAnimationsByPeepType(AnimationPeepType::guest); @@ -904,21 +961,23 @@ namespace OpenRCT2::Scripting if (peep->AnimationType == PeepAnimationType::walking && peep->State == PeepState::sitting) action = availableGuestAnimations[PeepAnimationType::sittingIdle]; - return std::string(action); + return JSFromStdString(ctx, action); } - void ScGuest::animation_set(std::string groupKey) + JSValue ScGuest::animation_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); + JS_UNPACK_STR(groupKey, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); auto& availableGuestAnimations = getAnimationsByPeepType(AnimationPeepType::guest); auto newType = availableGuestAnimations.TryGet(groupKey); if (newType == std::nullopt) { - throw DukException() << "Invalid animation for this guest (" << groupKey << ")"; + JS_ThrowPlainError(ctx, "Invalid animation for this guest (%s)", groupKey.data()); + return JS_EXCEPTION; } - auto* peep = GetGuest(); + auto* peep = GetGuest(thisVal); peep->AnimationType = peep->NextAnimationType = *newType; auto offset = 0; @@ -935,27 +994,27 @@ namespace OpenRCT2::Scripting peep->Invalidate(); peep->UpdateSpriteBoundingBox(); peep->Invalidate(); + return JS_UNDEFINED; } - uint8_t ScGuest::animationOffset_get() const + JSValue ScGuest::animationOffset_get(JSContext* ctx, JSValue thisVal) { - auto* peep = GetGuest(); + auto* peep = GetGuest(thisVal); if (peep == nullptr) { - return 0; + return JS_NewUint32(ctx, 0); } - if (peep->IsActionWalking()) - return peep->WalkingAnimationFrameNum; - else - return peep->AnimationFrameNum; + auto frame = peep->IsActionWalking() ? peep->WalkingAnimationFrameNum : peep->AnimationFrameNum; + return JS_NewUint32(ctx, frame); } - void ScGuest::animationOffset_set(uint8_t offset) + JSValue ScGuest::animationOffset_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); + JS_UNPACK_UINT32(offset, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); - auto* peep = GetGuest(); + auto* peep = GetGuest(thisVal); auto& objManager = GetContext()->GetObjectManager(); auto* animObj = objManager.GetLoadedObject(peep->AnimationObjectIndex); @@ -971,63 +1030,91 @@ namespace OpenRCT2::Scripting peep->AnimationImageIdOffset = animationGroup.frameOffsets[offset]; peep->UpdateSpriteBoundingBox(); + return JS_UNDEFINED; } - uint8_t ScGuest::animationLength_get() const + JSValue ScGuest::animationLength_get(JSContext* ctx, JSValue thisVal) { - auto* peep = GetGuest(); + auto* peep = GetGuest(thisVal); if (peep == nullptr) { - return 0; + return JS_NewUint32(ctx, 0); } auto& objManager = GetContext()->GetObjectManager(); auto* animObj = objManager.GetLoadedObject(peep->AnimationObjectIndex); const auto& animationGroup = animObj->GetPeepAnimation(peep->AnimationGroup, peep->AnimationType); - return static_cast(animationGroup.frameOffsets.size()); + return JS_NewUint32(ctx, static_cast(animationGroup.frameOffsets.size())); } - ScThought::ScThought(PeepThought backing) - : _backing(backing) + using OpaqueThoughtData = struct { + PeepThought thought; + }; + + JSValue ScThought::New(JSContext* ctx, PeepThought thought) + { + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("type", &ScThought::type_get, nullptr), + JS_CGETSET_DEF("item", &ScThought::item_get, nullptr), + JS_CGETSET_DEF("freshness", &ScThought::freshness_get, nullptr), + JS_CGETSET_DEF("freshTimeout", &ScThought::freshTimeout_get, nullptr), + JS_CFUNC_DEF("toString", 0, &ScThought::toString), + }; + return MakeWithOpaque(ctx, funcs, new OpaqueThoughtData{ thought }); } - void ScThought::Register(duk_context* ctx) + void ScThought::Register(JSContext* ctx) { - dukglue_register_property(ctx, &ScThought::type_get, nullptr, "type"); - dukglue_register_property(ctx, &ScThought::item_get, nullptr, "item"); - dukglue_register_property(ctx, &ScThought::freshness_get, nullptr, "freshness"); - dukglue_register_property(ctx, &ScThought::freshTimeout_get, nullptr, "freshTimeout"); - dukglue_register_method(ctx, &ScThought::toString, "toString"); + RegisterBaseStr(ctx, "Thought", Finalize); } - std::string ScThought::type_get() const + void ScThought::Finalize(JSRuntime* rt, JSValue thisVal) { - return std::string(ThoughtTypeMap[_backing.type]); + OpaqueThoughtData* data = gScThought.GetOpaque(thisVal); + if (data) + delete data; } - uint16_t ScThought::item_get() const + PeepThought ScThought::GetThought(JSValue thisVal) { - return _backing.item; + OpaqueThoughtData* data = gScThought.GetOpaque(thisVal); + return data->thought; } - uint8_t ScThought::freshness_get() const + JSValue ScThought::type_get(JSContext* ctx, JSValue thisVal) { - return _backing.freshness; + auto thought = GetThought(thisVal); + return JSFromStdString(ctx, ThoughtTypeMap[thought.type]); } - uint8_t ScThought::freshTimeout_get() const + JSValue ScThought::item_get(JSContext* ctx, JSValue thisVal) { - return _backing.fresh_timeout; + auto thought = GetThought(thisVal); + return JS_NewUint32(ctx, thought.item); } - std::string ScThought::toString() const + JSValue ScThought::freshness_get(JSContext* ctx, JSValue thisVal) { + auto thought = GetThought(thisVal); + return JS_NewUint32(ctx, thought.freshness); + } + + JSValue ScThought::freshTimeout_get(JSContext* ctx, JSValue thisVal) + { + auto thought = GetThought(thisVal); + return JS_NewUint32(ctx, thought.fresh_timeout); + } + + JSValue ScThought::toString(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) + { + auto thought = GetThought(thisVal); // format string with arguments auto ft = Formatter(); - PeepThoughtSetFormatArgs(&_backing, ft); - return FormatStringIDLegacy(STR_STRINGID, ft.Data()); + PeepThoughtSetFormatArgs(&thought, ft); + auto result = FormatStringIDLegacy(STR_STRINGID, ft.Data()); + return JSFromStdString(ctx, result); } } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/entity/ScGuest.hpp b/src/openrct2/scripting/bindings/entity/ScGuest.hpp index 810110e9a6..9318fed365 100644 --- a/src/openrct2/scripting/bindings/entity/ScGuest.hpp +++ b/src/openrct2/scripting/bindings/entity/ScGuest.hpp @@ -22,7 +22,7 @@ namespace OpenRCT2 namespace OpenRCT2::Scripting { - static const DukEnumMap ShopItemMap( + static const EnumMap ShopItemMap( { { "beef_noodles", ShopItem::beefNoodles }, { "burger", ShopItem::burger }, @@ -79,7 +79,7 @@ namespace OpenRCT2::Scripting // guest cannot carry), 6 is subtracted from the value. static_assert((EnumValue(ShopItem::count) - 6) == 50, "ShopItem::count changed, update scripting binding!"); - static const DukEnumMap VoucherTypeMap( + static const EnumMap VoucherTypeMap( { { "entry_free", VOUCHER_TYPE_PARK_ENTRY_FREE }, { "entry_half_price", VOUCHER_TYPE_PARK_ENTRY_HALF_PRICE }, @@ -87,110 +87,112 @@ namespace OpenRCT2::Scripting { "food_drink_free", VOUCHER_TYPE_FOOD_OR_DRINK_FREE }, }); - class ScThought + class ScThought; + extern ScThought gScThought; + + class ScThought final : public ScBase { - private: - PeepThought _backing; - public: - ScThought(PeepThought backing); - - static void Register(duk_context* ctx); + JSValue New(JSContext* ctx, PeepThought thought); + void Register(JSContext* ctx); private: - std::string type_get() const; - uint16_t item_get() const; - uint8_t freshness_get() const; - uint8_t freshTimeout_get() const; - std::string toString() const; + static void Finalize(JSRuntime* rt, JSValue thisVal); + static PeepThought GetThought(JSValue thisVal); + + static JSValue type_get(JSContext* ctx, JSValue thisVal); + static JSValue item_get(JSContext* ctx, JSValue thisVal); + static JSValue freshness_get(JSContext* ctx, JSValue thisVal); + static JSValue freshTimeout_get(JSContext* ctx, JSValue thisVal); + static JSValue toString(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); }; - class ScGuest : public ScPeep + class ScGuest final : public ScPeep { public: - ScGuest(EntityId id); - - static void Register(duk_context* ctx); + static JSValue New(JSContext* ctx, EntityId entityId); private: - Guest* GetGuest() const; + static void AddFuncs(JSContext* ctx, JSValue obj); + static Guest* GetGuest(JSValue thisVal); - uint8_t tshirtColour_get() const; - void tshirtColour_set(uint8_t value); + static JSValue tshirtColour_get(JSContext* ctx, JSValue thisVal); + static JSValue tshirtColour_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - uint8_t trousersColour_get() const; - void trousersColour_set(uint8_t value); + static JSValue trousersColour_get(JSContext* ctx, JSValue thisVal); + static JSValue trousersColour_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - uint8_t balloonColour_get() const; - void balloonColour_set(uint8_t value); + static JSValue balloonColour_get(JSContext* ctx, JSValue thisVal); + static JSValue balloonColour_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - uint8_t hatColour_get() const; - void hatColour_set(uint8_t value); + static JSValue hatColour_get(JSContext* ctx, JSValue thisVal); + static JSValue hatColour_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - uint8_t umbrellaColour_get() const; - void umbrellaColour_set(uint8_t value); + static JSValue umbrellaColour_get(JSContext* ctx, JSValue thisVal); + static JSValue umbrellaColour_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - uint8_t happiness_get() const; - void happiness_set(uint8_t value); + static JSValue happiness_get(JSContext* ctx, JSValue thisVal); + static JSValue happiness_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - uint8_t happinessTarget_get() const; - void happinessTarget_set(uint8_t value); + static JSValue happinessTarget_get(JSContext* ctx, JSValue thisVal); + static JSValue happinessTarget_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - uint8_t nausea_get() const; - void nausea_set(uint8_t value); + static JSValue nausea_get(JSContext* ctx, JSValue thisVal); + static JSValue nausea_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - uint8_t nauseaTarget_get() const; - void nauseaTarget_set(uint8_t value); + static JSValue nauseaTarget_get(JSContext* ctx, JSValue thisVal); + static JSValue nauseaTarget_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - uint8_t hunger_get() const; - void hunger_set(uint8_t value); + static JSValue hunger_get(JSContext* ctx, JSValue thisVal); + static JSValue hunger_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - uint8_t thirst_get() const; - void thirst_set(uint8_t value); + static JSValue thirst_get(JSContext* ctx, JSValue thisVal); + static JSValue thirst_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - uint8_t toilet_get() const; - void toilet_set(uint8_t value); + static JSValue toilet_get(JSContext* ctx, JSValue thisVal); + static JSValue toilet_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - uint8_t mass_get() const; - void mass_set(uint8_t value); + static JSValue mass_get(JSContext* ctx, JSValue thisVal); + static JSValue mass_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - uint8_t minIntensity_get() const; - void minIntensity_set(uint8_t value); + static JSValue minIntensity_get(JSContext* ctx, JSValue thisVal); + static JSValue minIntensity_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - uint8_t maxIntensity_get() const; - void maxIntensity_set(uint8_t value); + static JSValue maxIntensity_get(JSContext* ctx, JSValue thisVal); + static JSValue maxIntensity_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - uint8_t nauseaTolerance_get() const; - void nauseaTolerance_set(uint8_t value); + static JSValue nauseaTolerance_get(JSContext* ctx, JSValue thisVal); + static JSValue nauseaTolerance_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - int32_t cash_get() const; - void cash_set(int32_t value); + static JSValue cash_get(JSContext* ctx, JSValue thisVal); + static JSValue cash_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - bool isInPark_get() const; + static JSValue isInPark_get(JSContext* ctx, JSValue thisVal); - bool isLost_get() const; + static JSValue isLost_get(JSContext* ctx, JSValue thisVal); - uint8_t lostCountdown_get() const; - void lostCountdown_set(uint8_t value); + static JSValue lostCountdown_get(JSContext* ctx, JSValue thisVal); + static JSValue lostCountdown_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - DukValue favouriteRide_get() const; - void favouriteRide_set(const DukValue& value); + static JSValue favouriteRide_get(JSContext* ctx, JSValue thisVal); + static JSValue favouriteRide_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - DukValue thoughts_get() const; + static JSValue thoughts_get(JSContext* ctx, JSValue thisVal); - DukValue items_get() const; - bool has_item(const DukValue& item) const; - void give_item(const DukValue& item) const; - void remove_item(const DukValue& item) const; - void remove_all_items() const; + static JSValue items_get(JSContext* ctx, JSValue thisVal); + static bool has_item(JSContext* ctx, JSValue thisVal, JSValue jsValue); + static JSValue has_item(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + static JSValue give_item(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + static JSValue remove_item(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + static JSValue remove_all_items(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); - std::vector availableAnimations_get() const; - std::vector getAnimationSpriteIds(std::string groupKey, uint8_t rotation) const; - std::string animation_get() const; - void animation_set(std::string groupKey); - uint8_t animationOffset_get() const; - void animationOffset_set(uint8_t offset); - uint8_t animationLength_get() const; + static JSValue availableAnimations_get(JSContext* ctx, JSValue thisVal); + static JSValue getAnimationSpriteIds(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + static JSValue animation_get(JSContext* ctx, JSValue thisVal); + static JSValue animation_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); + static JSValue animationOffset_get(JSContext* ctx, JSValue thisVal); + static JSValue animationOffset_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); + static JSValue animationLength_get(JSContext* ctx, JSValue thisVal); }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/entity/ScLitter.cpp b/src/openrct2/scripting/bindings/entity/ScLitter.cpp index 2e3ff501b8..1b8c72aba0 100644 --- a/src/openrct2/scripting/bindings/entity/ScLitter.cpp +++ b/src/openrct2/scripting/bindings/entity/ScLitter.cpp @@ -11,11 +11,12 @@ #include "ScLitter.hpp" + #include "../../../core/EnumMap.hpp" #include "../../../entity/Litter.h" namespace OpenRCT2::Scripting { - static const DukEnumMap LitterTypeMap( + static const EnumMap LitterTypeMap( { { "vomit", Litter::Type::vomit }, { "vomit_alt", Litter::Type::vomitAlt }, @@ -31,57 +32,62 @@ namespace OpenRCT2::Scripting { "empty_bowl_blue", Litter::Type::emptyBowlBlue }, }); - ScLitter::ScLitter(EntityId Id) - : ScEntity(Id) + JSValue ScLitter::New(JSContext* ctx, EntityId entityId) { + JSValue obj = gScEntity.New(ctx, entityId); + AddFuncs(ctx, obj); + return obj; } - void ScLitter::Register(duk_context* ctx) + void ScLitter::AddFuncs(JSContext* ctx, JSValue obj) { - dukglue_set_base_class(ctx); - dukglue_register_property(ctx, &ScLitter::litterType_get, &ScLitter::litterType_set, "litterType"); - dukglue_register_property(ctx, &ScLitter::creationTick_get, nullptr, "creationTick"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("litterType", &ScLitter::litterType_get, &ScLitter::litterType_set), + JS_CGETSET_DEF("creationTick", &ScLitter::creationTick_get, nullptr) + }; + JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs)); } - Litter* ScLitter::GetLitter() const + Litter* ScLitter::GetLitter(JSValue thisVal) { - return getGameState().entities.GetEntity(_id); + auto id = GetEntityId(thisVal); + return getGameState().entities.GetEntity(id); } - std::string ScLitter::litterType_get() const + JSValue ScLitter::litterType_get(JSContext* ctx, JSValue thisVal) { - auto* litter = GetLitter(); + auto* litter = GetLitter(thisVal); if (litter != nullptr) { auto it = LitterTypeMap.find(litter->SubType); if (it != LitterTypeMap.end()) { - return std::string{ it->first }; + return JSFromStdString(ctx, it->first); } } - return {}; + return JS_UNDEFINED; } - void ScLitter::litterType_set(const std::string& litterType) + JSValue ScLitter::litterType_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); + JS_UNPACK_STR(litterType, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); auto it = LitterTypeMap.find(litterType); - if (it == LitterTypeMap.end()) - return; - auto* litter = GetLitter(); - litter->SubType = it->second; - litter->Invalidate(); + if (it != LitterTypeMap.end()) + { + auto* litter = GetLitter(thisVal); + litter->SubType = it->second; + litter->Invalidate(); + } + return JS_UNDEFINED; } - uint32_t ScLitter::creationTick_get() const + JSValue ScLitter::creationTick_get(JSContext* ctx, JSValue thisVal) { - auto* litter = GetLitter(); - if (litter == nullptr) - return 0; - return litter->creationTick; + auto* litter = GetLitter(thisVal); + return JS_NewUint32(ctx, litter == nullptr ? 0 : litter->creationTick); } - } // namespace OpenRCT2::Scripting #endif diff --git a/src/openrct2/scripting/bindings/entity/ScLitter.hpp b/src/openrct2/scripting/bindings/entity/ScLitter.hpp index 732edafbea..df38cba3a0 100644 --- a/src/openrct2/scripting/bindings/entity/ScLitter.hpp +++ b/src/openrct2/scripting/bindings/entity/ScLitter.hpp @@ -20,22 +20,20 @@ namespace OpenRCT2 namespace OpenRCT2::Scripting { - class ScLitter : public ScEntity + class ScLitter final : public ScEntity { public: - ScLitter(EntityId Id); - - static void Register(duk_context* ctx); + static JSValue New(JSContext* ctx, EntityId entityId); private: - Litter* GetLitter() const; + static void AddFuncs(JSContext* ctx, JSValue obj); + static Litter* GetLitter(JSValue thisVal); - std::string litterType_get() const; - void litterType_set(const std::string& litterType); + static JSValue litterType_get(JSContext* ctx, JSValue thisVal); + static JSValue litterType_set(JSContext* ctx, JSValue thisVal, JSValue value); - uint32_t creationTick_get() const; + static JSValue creationTick_get(JSContext* ctx, JSValue thisVal); }; - } // namespace OpenRCT2::Scripting #endif diff --git a/src/openrct2/scripting/bindings/entity/ScMoneyEffect.cpp b/src/openrct2/scripting/bindings/entity/ScMoneyEffect.cpp index 2d4b40f59b..0da7e23b8b 100644 --- a/src/openrct2/scripting/bindings/entity/ScMoneyEffect.cpp +++ b/src/openrct2/scripting/bindings/entity/ScMoneyEffect.cpp @@ -16,41 +16,43 @@ namespace OpenRCT2::Scripting { - ScMoneyEffect::ScMoneyEffect(EntityId Id) - : ScEntity(Id) + JSValue ScMoneyEffect::New(JSContext* ctx, EntityId entityId) { + JSValue obj = gScEntity.New(ctx, entityId); + AddFuncs(ctx, obj); + return obj; } - void ScMoneyEffect::Register(duk_context* ctx) + void ScMoneyEffect::AddFuncs(JSContext* ctx, JSValue obj) { - dukglue_set_base_class(ctx); - dukglue_register_property(ctx, &ScMoneyEffect::value_get, &ScMoneyEffect::value_set, "value"); + static constexpr JSCFunctionListEntry funcs[] = { JS_CGETSET_DEF( + "value", &ScMoneyEffect::value_get, &ScMoneyEffect::value_set) }; + JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs)); } - MoneyEffect* ScMoneyEffect::GetMoneyEffect() const + MoneyEffect* ScMoneyEffect::GetMoneyEffect(JSValue thisVal) { - return getGameState().entities.GetEntity(_id); + auto id = GetEntityId(thisVal); + return getGameState().entities.GetEntity(id); } - money64 ScMoneyEffect::value_get() const + JSValue ScMoneyEffect::value_get(JSContext* ctx, JSValue thisVal) { - auto moneyEffect = GetMoneyEffect(); - if (moneyEffect != nullptr) - { - return moneyEffect->Value; - } - return 0; + auto moneyEffect = GetMoneyEffect(thisVal); + return JS_NewUint32(ctx, moneyEffect == nullptr ? 0 : moneyEffect->Value); } - void ScMoneyEffect::value_set(money64 value) + JSValue ScMoneyEffect::value_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - auto moneyEffect = GetMoneyEffect(); + JS_UNPACK_MONEY64(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto moneyEffect = GetMoneyEffect(thisVal); if (moneyEffect != nullptr) { moneyEffect->SetValue(value); } + return JS_UNDEFINED; } - } // namespace OpenRCT2::Scripting #endif diff --git a/src/openrct2/scripting/bindings/entity/ScMoneyEffect.hpp b/src/openrct2/scripting/bindings/entity/ScMoneyEffect.hpp index 4a37138750..18be394440 100644 --- a/src/openrct2/scripting/bindings/entity/ScMoneyEffect.hpp +++ b/src/openrct2/scripting/bindings/entity/ScMoneyEffect.hpp @@ -20,20 +20,17 @@ namespace OpenRCT2 namespace OpenRCT2::Scripting { - - class ScMoneyEffect : public ScEntity + class ScMoneyEffect final : public ScEntity { public: - ScMoneyEffect(EntityId Id); - - static void Register(duk_context* ctx); + static JSValue New(JSContext* ctx, EntityId entityId); private: - MoneyEffect* GetMoneyEffect() const; + static void AddFuncs(JSContext* ctx, JSValue obj); + static MoneyEffect* GetMoneyEffect(JSValue thisVal); - money64 value_get() const; - void value_set(money64); + static JSValue value_get(JSContext* ctx, JSValue thisVal); + static JSValue value_set(JSContext* ctx, JSValue thisVal, JSValue value); }; - } // namespace OpenRCT2::Scripting #endif diff --git a/src/openrct2/scripting/bindings/entity/ScParticle.cpp b/src/openrct2/scripting/bindings/entity/ScParticle.cpp index 45d01fcfe1..8ebfc85e59 100644 --- a/src/openrct2/scripting/bindings/entity/ScParticle.cpp +++ b/src/openrct2/scripting/bindings/entity/ScParticle.cpp @@ -9,13 +9,14 @@ #include "ScParticle.hpp" +#include "../../../core/EnumMap.hpp" #include "../ride/ScRide.hpp" #ifdef ENABLE_SCRIPTING namespace OpenRCT2::Scripting { - static const DukEnumMap CrashParticleTypeMap( + static const EnumMap CrashParticleTypeMap( { { "corner", 0 }, { "rod", 1 }, @@ -24,206 +25,232 @@ namespace OpenRCT2::Scripting { "seat", 4 }, }); - ScCrashedVehicleParticle::ScCrashedVehicleParticle(EntityId id) - : ScEntity(id) + JSValue ScCrashedVehicleParticle::New(JSContext* ctx, EntityId entityId) { + JSValue obj = gScEntity.New(ctx, entityId); + AddFuncs(ctx, obj); + return obj; } - void ScCrashedVehicleParticle::Register(duk_context* ctx) + void ScCrashedVehicleParticle::AddFuncs(JSContext* ctx, JSValue obj) { - dukglue_set_base_class(ctx); - dukglue_register_property( - ctx, &ScCrashedVehicleParticle::acceleration_get, &ScCrashedVehicleParticle::acceleration_set, "acceleration"); - dukglue_register_property( - ctx, &ScCrashedVehicleParticle::velocity_get, &ScCrashedVehicleParticle::velocity_set, "velocity"); - dukglue_register_property( - ctx, &ScCrashedVehicleParticle::colours_get, &ScCrashedVehicleParticle::colours_set, "colours"); - dukglue_register_property( - ctx, &ScCrashedVehicleParticle::timeToLive_get, &ScCrashedVehicleParticle::timeToLive_set, "timeToLive"); - dukglue_register_property( - ctx, &ScCrashedVehicleParticle::crashedSpriteBase_get, &ScCrashedVehicleParticle::crashedSpriteBase_set, - "crashParticleType"); - dukglue_register_property(ctx, &ScCrashedVehicleParticle::frame_get, &ScCrashedVehicleParticle::frame_set, "frame"); - dukglue_register_method(ctx, &ScCrashedVehicleParticle::Launch, "launch"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF( + "acceleration", &ScCrashedVehicleParticle::acceleration_get, &ScCrashedVehicleParticle::acceleration_set), + JS_CGETSET_DEF("velocity", &ScCrashedVehicleParticle::velocity_get, &ScCrashedVehicleParticle::velocity_set), + JS_CGETSET_DEF("colours", &ScCrashedVehicleParticle::colours_get, &ScCrashedVehicleParticle::colours_set), + JS_CGETSET_DEF("timeToLive", &ScCrashedVehicleParticle::timeToLive_get, &ScCrashedVehicleParticle::timeToLive_set), + JS_CGETSET_DEF( + "crashParticleType", &ScCrashedVehicleParticle::crashedSpriteBase_get, + &ScCrashedVehicleParticle::crashedSpriteBase_set), + JS_CGETSET_DEF("frame", &ScCrashedVehicleParticle::frame_get, &ScCrashedVehicleParticle::frame_set), + JS_CFUNC_DEF("launch", 1, &ScCrashedVehicleParticle::Launch), + }; + JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs)); } - VehicleCrashParticle* ScCrashedVehicleParticle::GetCrashedVehicleParticle() const + VehicleCrashParticle* ScCrashedVehicleParticle::GetCrashedVehicleParticle(JSValue thisVal) { - return getGameState().entities.GetEntity(_id); + auto id = GetEntityId(thisVal); + return getGameState().entities.GetEntity(id); } - void ScCrashedVehicleParticle::frame_set(uint8_t value) + JSValue ScCrashedVehicleParticle::frame_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - auto entity = GetCrashedVehicleParticle(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto entity = GetCrashedVehicleParticle(thisVal); if (entity != nullptr) { entity->frame = std::clamp(value, 0, kCrashedVehicleParticleNumberSprites - 1) * kCrashedVehicleParticleFrameToSprite; entity->Invalidate(); } + return JS_UNDEFINED; } - uint8_t ScCrashedVehicleParticle::frame_get() const + JSValue ScCrashedVehicleParticle::frame_get(JSContext* ctx, JSValue thisVal) { - auto entity = GetCrashedVehicleParticle(); - if (entity != nullptr) - { - return entity->frame / kCrashedVehicleParticleFrameToSprite; - } - return 0; + auto entity = GetCrashedVehicleParticle(thisVal); + auto frame = (entity != nullptr) ? entity->frame / kCrashedVehicleParticleFrameToSprite : 0; + return JS_NewUint32(ctx, frame); } - void ScCrashedVehicleParticle::crashedSpriteBase_set(const std::string& value) + JSValue ScCrashedVehicleParticle::crashedSpriteBase_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - auto entity = GetCrashedVehicleParticle(); + JS_UNPACK_STR(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto entity = GetCrashedVehicleParticle(thisVal); if (entity != nullptr) { entity->crashed_sprite_base = CrashParticleTypeMap[value]; entity->Invalidate(); } + return JS_UNDEFINED; } - std::string ScCrashedVehicleParticle::crashedSpriteBase_get() const + JSValue ScCrashedVehicleParticle::crashedSpriteBase_get(JSContext* ctx, JSValue thisVal) { - auto entity = GetCrashedVehicleParticle(); + auto entity = GetCrashedVehicleParticle(thisVal); if (entity != nullptr) { - return std::string(CrashParticleTypeMap[entity->crashed_sprite_base]); + return JSFromStdString(ctx, CrashParticleTypeMap[entity->crashed_sprite_base]); } - return {}; + return JS_UNDEFINED; } - void ScCrashedVehicleParticle::timeToLive_set(uint16_t value) + JSValue ScCrashedVehicleParticle::timeToLive_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - auto entity = GetCrashedVehicleParticle(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto entity = GetCrashedVehicleParticle(thisVal); if (entity != nullptr) { entity->time_to_live = value; } + return JS_UNDEFINED; } - uint16_t ScCrashedVehicleParticle::timeToLive_get() const + JSValue ScCrashedVehicleParticle::timeToLive_get(JSContext* ctx, JSValue thisVal) { - auto entity = GetCrashedVehicleParticle(); - if (entity != nullptr) - { - return entity->time_to_live; - } - return 0; + auto entity = GetCrashedVehicleParticle(thisVal); + return JS_NewUint32(ctx, entity == nullptr ? 0 : entity->time_to_live); } - void ScCrashedVehicleParticle::velocity_set(const DukValue& value) + JSValue ScCrashedVehicleParticle::velocity_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - auto entity = GetCrashedVehicleParticle(); + JS_UNPACK_OBJECT(obj, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto entity = GetCrashedVehicleParticle(thisVal); if (entity != nullptr) { - auto velocity = FromDuk(value); + auto velocity = JSToCoordsXYZ(ctx, obj); entity->velocity_x = velocity.x; entity->velocity_y = velocity.y; entity->velocity_z = velocity.z; } + return JS_UNDEFINED; } - DukValue ScCrashedVehicleParticle::velocity_get() const + JSValue ScCrashedVehicleParticle::velocity_get(JSContext* ctx, JSValue thisVal) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - auto entity = GetCrashedVehicleParticle(); + auto entity = GetCrashedVehicleParticle(thisVal); if (entity != nullptr) { - return ToDuk(ctx, CoordsXYZ(entity->velocity_x, entity->velocity_y, entity->velocity_z)); + return ToJSValue(ctx, CoordsXYZ(entity->velocity_x, entity->velocity_y, entity->velocity_z)); } - return {}; + return JS_UNDEFINED; } - void ScCrashedVehicleParticle::acceleration_set(const DukValue& value) + JSValue ScCrashedVehicleParticle::acceleration_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - auto entity = GetCrashedVehicleParticle(); + JS_UNPACK_OBJECT(obj, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto entity = GetCrashedVehicleParticle(thisVal); if (entity != nullptr) { - auto acceleration = FromDuk(value); + auto acceleration = JSToCoordsXYZ(ctx, obj); entity->acceleration_x = acceleration.x; entity->acceleration_y = acceleration.y; entity->acceleration_z = acceleration.z; } + return JS_UNDEFINED; } - DukValue ScCrashedVehicleParticle::acceleration_get() const + JSValue ScCrashedVehicleParticle::acceleration_get(JSContext* ctx, JSValue thisVal) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - auto entity = GetCrashedVehicleParticle(); + auto entity = GetCrashedVehicleParticle(thisVal); if (entity != nullptr) { - return ToDuk(ctx, CoordsXYZ(entity->acceleration_x, entity->acceleration_y, entity->acceleration_z)); + return ToJSValue(ctx, CoordsXYZ(entity->acceleration_x, entity->acceleration_y, entity->acceleration_z)); } - return {}; + return JS_UNDEFINED; } - void ScCrashedVehicleParticle::Launch(const DukValue& value) + JSValue ScCrashedVehicleParticle::Launch(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto entity = GetCrashedVehicleParticle(); + JS_UNPACK_OBJECT(obj, ctx, argv[0]); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto entity = GetCrashedVehicleParticle(thisVal); if (entity != nullptr) { entity->SetSpriteData(); entity->Launch(); - if (value.type() == DukValue::Type::UNDEFINED) - return; + if (JS_IsUndefined(obj)) + return JS_UNDEFINED; - if (value["colours"].type() == DukValue::Type::OBJECT) + auto colours = JS_GetPropertyStr(ctx, obj, "colours"); + auto acceleration = JS_GetPropertyStr(ctx, obj, "acceleration"); + auto velocity = JS_GetPropertyStr(ctx, obj, "velocity"); + auto timeToLive = JS_GetPropertyStr(ctx, obj, "timeToLive"); + auto frame = JS_GetPropertyStr(ctx, obj, "frame"); + auto crashParticleType = JS_GetPropertyStr(ctx, obj, "crashParticleType"); + + if (JS_IsObject(colours)) { - auto coloursInt = FromDuk(value["colours"]); - entity->colour[0] = coloursInt.Body; - entity->colour[1] = coloursInt.Trim; + entity->colour[0] = static_cast(JSToUint(ctx, colours, "body")); + entity->colour[1] = static_cast(JSToUint(ctx, colours, "trim")); } - if (value["acceleration"].type() == DukValue::Type::OBJECT) + if (JS_IsObject(acceleration)) { - auto accelerationXYZ = FromDuk(value["acceleration"]); + auto accelerationXYZ = JSToCoordsXYZ(ctx, acceleration); entity->acceleration_x = accelerationXYZ.x; entity->acceleration_y = accelerationXYZ.y; entity->acceleration_z = accelerationXYZ.z; } - if (value["velocity"].type() == DukValue::Type::OBJECT) + if (JS_IsObject(velocity)) { - auto velocityXYZ = FromDuk(value["velocity"]); + auto velocityXYZ = JSToCoordsXYZ(ctx, velocity); entity->velocity_x = velocityXYZ.x; entity->velocity_y = velocityXYZ.y; entity->velocity_z = velocityXYZ.z; } - if (value["timeToLive"].type() == DukValue::Type::NUMBER) + if (JS_IsNumber(timeToLive)) { - entity->time_to_live = value["timeToLive"].as_uint(); + entity->time_to_live = JSToUint(ctx, timeToLive); } - if (value["frame"].type() == DukValue::Type::NUMBER) + if (JS_IsNumber(frame)) { - entity->frame = std::clamp(value["frame"].as_uint(), 0, kCrashedVehicleParticleNumberSprites - 1) + entity->frame = std::clamp(JSToUint(ctx, frame), 0, kCrashedVehicleParticleNumberSprites - 1) * kCrashedVehicleParticleFrameToSprite; } - if (value["crashParticleType"].type() == DukValue::Type::STRING) + if (JS_IsString(crashParticleType)) { - entity->crashed_sprite_base = CrashParticleTypeMap[value["crashParticleType"].as_string()]; + auto key = JSToStdString(ctx, crashParticleType); + entity->crashed_sprite_base = CrashParticleTypeMap[key]; } entity->Invalidate(); + + JS_FreeValue(ctx, colours); + JS_FreeValue(ctx, acceleration); + JS_FreeValue(ctx, velocity); + JS_FreeValue(ctx, timeToLive); + JS_FreeValue(ctx, frame); + JS_FreeValue(ctx, crashParticleType); } + return JS_UNDEFINED; } - DukValue ScCrashedVehicleParticle::colours_get() const + JSValue ScCrashedVehicleParticle::colours_get(JSContext* ctx, JSValue thisVal) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - auto entity = GetCrashedVehicleParticle(); + auto entity = GetCrashedVehicleParticle(thisVal); if (entity != nullptr) { - DukObject dukColour(ctx); - dukColour.Set("body", EnumValue(entity->colour[0])); - dukColour.Set("trim", EnumValue(entity->colour[1])); - return dukColour.Take(); + JSValue obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "body", JS_NewInt32(ctx, EnumValue(entity->colour[0]))); + JS_SetPropertyStr(ctx, obj, "trim", JS_NewInt32(ctx, EnumValue(entity->colour[1]))); + return obj; } - return ToDuk(ctx, nullptr); + return JS_NULL; } - void ScCrashedVehicleParticle::colours_set(const DukValue& value) + JSValue ScCrashedVehicleParticle::colours_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - auto entity = GetCrashedVehicleParticle(); + JS_UNPACK_OBJECT(obj, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto entity = GetCrashedVehicleParticle(thisVal); if (entity != nullptr) { - auto colours = FromDuk(value); - entity->colour[0] = colours.Body; - entity->colour[1] = colours.Trim; + entity->colour[0] = static_cast(JSToUint(ctx, obj, "body")); + entity->colour[1] = static_cast(JSToUint(ctx, obj, "trim")); entity->Invalidate(); } + return JS_UNDEFINED; } } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/entity/ScParticle.hpp b/src/openrct2/scripting/bindings/entity/ScParticle.hpp index 95dcf90cda..213a9008cf 100644 --- a/src/openrct2/scripting/bindings/entity/ScParticle.hpp +++ b/src/openrct2/scripting/bindings/entity/ScParticle.hpp @@ -19,35 +19,34 @@ namespace OpenRCT2::Scripting { - class ScCrashedVehicleParticle : public ScEntity + class ScCrashedVehicleParticle final : public ScEntity { public: - ScCrashedVehicleParticle(EntityId id); - - static void Register(duk_context* ctx); + static JSValue New(JSContext* ctx, EntityId entityId); private: - VehicleCrashParticle* GetCrashedVehicleParticle() const; + static void AddFuncs(JSContext* ctx, JSValue obj); + static VehicleCrashParticle* GetCrashedVehicleParticle(JSValue thisVal); - DukValue colours_get() const; - void colours_set(const DukValue& value); + static JSValue colours_get(JSContext* ctx, JSValue thisVal); + static JSValue colours_set(JSContext* ctx, JSValue thisVal, JSValue value); - DukValue acceleration_get() const; - void acceleration_set(const DukValue& value); + static JSValue acceleration_get(JSContext* ctx, JSValue thisVal); + static JSValue acceleration_set(JSContext* ctx, JSValue thisVal, JSValue value); - DukValue velocity_get() const; - void velocity_set(const DukValue& value); + static JSValue velocity_get(JSContext* ctx, JSValue thisVal); + static JSValue velocity_set(JSContext* ctx, JSValue thisVal, JSValue value); - uint8_t frame_get() const; - void frame_set(uint8_t value); + static JSValue frame_get(JSContext* ctx, JSValue thisVal); + static JSValue frame_set(JSContext* ctx, JSValue thisVal, JSValue value); - void crashedSpriteBase_set(const std::string& value); - std::string crashedSpriteBase_get() const; + static JSValue crashedSpriteBase_get(JSContext* ctx, JSValue thisVal); + static JSValue crashedSpriteBase_set(JSContext* ctx, JSValue thisVal, JSValue value); - void timeToLive_set(uint16_t value); - uint16_t timeToLive_get() const; + static JSValue timeToLive_get(JSContext* ctx, JSValue thisVal); + static JSValue timeToLive_set(JSContext* ctx, JSValue thisVal, JSValue value); - void Launch(const DukValue& value); + static JSValue Launch(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/entity/ScPeep.hpp b/src/openrct2/scripting/bindings/entity/ScPeep.hpp index b11da8c1db..ca6305fd2c 100644 --- a/src/openrct2/scripting/bindings/entity/ScPeep.hpp +++ b/src/openrct2/scripting/bindings/entity/ScPeep.hpp @@ -11,11 +11,12 @@ #ifdef ENABLE_SCRIPTING + #include "../../../core/EnumMap.hpp" #include "ScEntity.hpp" namespace OpenRCT2::Scripting { - static const DukEnumMap PeepFlagMap( + static const EnumMap PeepFlagMap( { { "leavingPark", PEEP_FLAGS_LEAVING_PARK }, { "slowWalk", PEEP_FLAGS_SLOW_WALK }, @@ -49,65 +50,74 @@ namespace OpenRCT2::Scripting class ScPeep : public ScEntity { public: - ScPeep(EntityId id) - : ScEntity(id) + static JSValue New(JSContext* ctx, EntityId entityId) { - } - - static void Register(duk_context* ctx) - { - dukglue_set_base_class(ctx); - dukglue_register_property(ctx, &ScPeep::peepType_get, nullptr, "peepType"); - dukglue_register_property(ctx, &ScPeep::name_get, &ScPeep::name_set, "name"); - dukglue_register_property(ctx, &ScPeep::destination_get, &ScPeep::destination_set, "destination"); - dukglue_register_property(ctx, &ScPeep::direction_get, &ScPeep::direction_set, "direction"); - dukglue_register_property(ctx, &ScPeep::energy_get, &ScPeep::energy_set, "energy"); - dukglue_register_property(ctx, &ScPeep::energyTarget_get, &ScPeep::energyTarget_set, "energyTarget"); - dukglue_register_method(ctx, &ScPeep::getFlag, "getFlag"); - dukglue_register_method(ctx, &ScPeep::setFlag, "setFlag"); + JSValue obj = gScEntity.New(ctx, entityId); + AddFuncs(ctx, obj); + return obj; } private: - std::string peepType_get() const + static void AddFuncs(JSContext* ctx, JSValue obj) { - auto peep = GetPeep(); - if (peep != nullptr) - { - return peep->Is() ? "staff" : "guest"; - } - return {}; + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("peepType", &ScPeep::peepType_get, nullptr), + JS_CGETSET_DEF("name", &ScPeep::name_get, &ScPeep::name_set), + JS_CGETSET_DEF("destination", &ScPeep::destination_get, &ScPeep::destination_set), + JS_CGETSET_DEF("direction", &ScPeep::direction_get, &ScPeep::direction_set), + JS_CGETSET_DEF("energy", &ScPeep::energy_get, &ScPeep::energy_set), + JS_CGETSET_DEF("energyTarget", &ScPeep::energyTarget_get, &ScPeep::energyTarget_set), + JS_CFUNC_DEF("getFlag", 1, &ScPeep::getFlag), + JS_CFUNC_DEF("setFlag", 2, &ScPeep::setFlag), + }; + JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs)); } - std::string name_get() const + static JSValue peepType_get(JSContext* ctx, JSValue thisVal) { - auto peep = GetPeep(); - return peep != nullptr ? peep->GetName() : std::string(); + auto peep = GetPeep(thisVal); + if (peep != nullptr) + { + return JSFromStdString(ctx, peep->Is() ? "staff" : "guest"); + } + return JS_UNDEFINED; } - void name_set(const std::string& value) + + static JSValue name_get(JSContext* ctx, JSValue thisVal) { - ThrowIfGameStateNotMutable(); - auto peep = GetPeep(); + auto peep = GetPeep(thisVal); + return JSFromStdString(ctx, peep != nullptr ? peep->GetName() : std::string()); + } + static JSValue name_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) + { + JS_UNPACK_STR(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto peep = GetPeep(thisVal); if (peep != nullptr) { peep->SetName(value); } + return JS_UNDEFINED; } - bool getFlag(const std::string& key) const + static JSValue getFlag(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto peep = GetPeep(); + JS_UNPACK_STR(key, ctx, argv[0]); + auto peep = GetPeep(thisVal); if (peep != nullptr) { auto mask = PeepFlagMap[key]; - return (peep->PeepFlags & mask) != 0; + return JS_NewBool(ctx, (peep->PeepFlags & mask) != 0); } - return false; + return JS_NewBool(ctx, false); } - void setFlag(const std::string& key, bool value) + static JSValue setFlag(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - ThrowIfGameStateNotMutable(); - auto peep = GetPeep(); + JS_UNPACK_STR(key, ctx, argv[0]); + JS_UNPACK_BOOL(value, ctx, argv[1]); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto peep = GetPeep(thisVal); if (peep != nullptr) { auto mask = PeepFlagMap[key]; @@ -117,86 +127,95 @@ namespace OpenRCT2::Scripting peep->PeepFlags &= ~mask; peep->Invalidate(); } + return JS_UNDEFINED; } - DukValue destination_get() const + static JSValue destination_get(JSContext* ctx, JSValue thisVal) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - auto peep = GetPeep(); + auto peep = GetPeep(thisVal); if (peep != nullptr) { - return ToDuk(ctx, peep->GetDestination()); + return ToJSValue(ctx, peep->GetDestination()); } - return ToDuk(ctx, nullptr); + return JS_NULL; } - void destination_set(const DukValue& value) + static JSValue destination_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto peep = GetPeep(); + JS_UNPACK_OBJECT(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto peep = GetPeep(thisVal); if (peep != nullptr) { - auto pos = FromDuk(value); + auto pos = JSToCoordsXY(ctx, value); peep->SetDestination(pos); peep->Invalidate(); } + return JS_UNDEFINED; } - uint8_t direction_get() const + static JSValue direction_get(JSContext* ctx, JSValue thisVal) { - auto peep = GetPeep(); - return peep != nullptr ? peep->PeepDirection : 0; + auto peep = GetPeep(thisVal); + return JS_NewUint32(ctx, peep != nullptr ? peep->PeepDirection : 0); } - void direction_set(const uint8_t value) + static JSValue direction_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto peep = GetPeep(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto peep = GetPeep(thisVal); if (peep != nullptr && value < kNumOrthogonalDirections) { peep->PeepDirection = value; peep->Orientation = value << 3; peep->Invalidate(); } + return JS_UNDEFINED; } - uint8_t energy_get() const + static JSValue energy_get(JSContext* ctx, JSValue thisVal) { - auto peep = GetPeep(); - return peep != nullptr ? peep->Energy : 0; + auto peep = GetPeep(thisVal); + return JS_NewUint32(ctx, peep != nullptr ? peep->Energy : 0); } - void energy_set(uint8_t value) + static JSValue energy_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto peep = GetPeep(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto peep = GetPeep(thisVal); if (peep != nullptr) { - value = std::clamp(value, kPeepMinEnergy, kPeepMaxEnergy); + value = std::clamp(static_cast(value), kPeepMinEnergy, kPeepMaxEnergy); peep->Energy = value; peep->Invalidate(); } + return JS_UNDEFINED; } - uint8_t energyTarget_get() const + static JSValue energyTarget_get(JSContext* ctx, JSValue thisVal) { - auto peep = GetPeep(); - return peep != nullptr ? peep->EnergyTarget : 0; + auto peep = GetPeep(thisVal); + return JS_NewUint32(ctx, peep != nullptr ? peep->EnergyTarget : 0); } - void energyTarget_set(uint8_t value) + static JSValue energyTarget_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto peep = GetPeep(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto peep = GetPeep(thisVal); if (peep != nullptr) { - value = std::clamp(value, kPeepMinEnergy, kPeepMaxEnergyTarget); - peep->EnergyTarget = value; + auto target = std::clamp(static_cast(value), kPeepMinEnergy, kPeepMaxEnergyTarget); + peep->EnergyTarget = target; } + return JS_UNDEFINED; } protected: - Peep* GetPeep() const + static Peep* GetPeep(JSValue thisVal) { - return getGameState().entities.GetEntity(_id); + auto id = GetEntityId(thisVal); + return getGameState().entities.GetEntity(id); } }; diff --git a/src/openrct2/scripting/bindings/entity/ScStaff.cpp b/src/openrct2/scripting/bindings/entity/ScStaff.cpp index 62ab02012f..5f5fdd1331 100644 --- a/src/openrct2/scripting/bindings/entity/ScStaff.cpp +++ b/src/openrct2/scripting/bindings/entity/ScStaff.cpp @@ -21,59 +21,65 @@ namespace OpenRCT2::Scripting { - ScStaff::ScStaff(EntityId Id) - : ScPeep(Id) + JSValue ScStaff::New(JSContext* ctx, EntityId entityId) { + JSValue obj = ScPeep::New(ctx, entityId); + AddFuncs(ctx, obj); + return obj; } - void ScStaff::Register(duk_context* ctx) + void ScStaff::AddFuncs(JSContext* ctx, JSValue obj) { - dukglue_set_base_class(ctx); - dukglue_register_property(ctx, &ScStaff::staffType_get, &ScStaff::staffType_set, "staffType"); - dukglue_register_property(ctx, &ScStaff::colour_get, &ScStaff::colour_set, "colour"); - dukglue_register_property(ctx, &ScStaff::availableCostumes_get, nullptr, "availableCostumes"); - dukglue_register_property(ctx, &ScStaff::costume_get, &ScStaff::costume_set, "costume"); - dukglue_register_property(ctx, &ScStaff::patrolArea_get, nullptr, "patrolArea"); - dukglue_register_property(ctx, &ScStaff::orders_get, &ScStaff::orders_set, "orders"); - dukglue_register_property(ctx, &ScStaff::availableAnimations_get, nullptr, "availableAnimations"); - dukglue_register_property(ctx, &ScStaff::animation_get, &ScStaff::animation_set, "animation"); - dukglue_register_property(ctx, &ScStaff::animationOffset_get, &ScStaff::animationOffset_set, "animationOffset"); - dukglue_register_property(ctx, &ScStaff::animationLength_get, nullptr, "animationLength"); - dukglue_register_method(ctx, &ScStaff::getAnimationSpriteIds, "getAnimationSpriteIds"); - dukglue_register_method(ctx, &ScStaff::getCostumeStrings, "getCostumeStrings"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("staffType", &ScStaff::staffType_get, &ScStaff::staffType_set), + JS_CGETSET_DEF("colour", &ScStaff::colour_get, &ScStaff::colour_set), + JS_CGETSET_DEF("availableCostumes", &ScStaff::availableCostumes_get, nullptr), + JS_CGETSET_DEF("costume", &ScStaff::costume_get, &ScStaff::costume_set), + JS_CGETSET_DEF("patrolArea", &ScStaff::patrolArea_get, nullptr), + JS_CGETSET_DEF("orders", &ScStaff::orders_get, &ScStaff::orders_set), + JS_CGETSET_DEF("availableAnimations", &ScStaff::availableAnimations_get, nullptr), + JS_CGETSET_DEF("animation", &ScStaff::animation_get, &ScStaff::animation_set), + JS_CGETSET_DEF("animationOffset", &ScStaff::animationOffset_get, &ScStaff::animationOffset_set), + JS_CGETSET_DEF("animationLength", &ScStaff::animationLength_get, nullptr), + JS_CFUNC_DEF("getAnimationSpriteIds", 2, &ScStaff::getAnimationSpriteIds), + JS_CFUNC_DEF("getCostumeStrings", 0, &ScStaff::getCostumeStrings) + }; + JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs)); } - Staff* ScStaff::GetStaff() const + Staff* ScStaff::GetStaff(JSValue thisVal) { - return getGameState().entities.GetEntity(_id); + auto id = GetEntityId(thisVal); + return getGameState().entities.GetEntity(id); } - std::string ScStaff::staffType_get() const + JSValue ScStaff::staffType_get(JSContext* ctx, JSValue thisVal) { - auto peep = GetStaff(); + auto peep = GetStaff(thisVal); if (peep != nullptr) { switch (peep->AssignedStaffType) { case StaffType::handyman: - return "handyman"; + return JSFromStdString(ctx, "handyman"); case StaffType::mechanic: - return "mechanic"; + return JSFromStdString(ctx, "mechanic"); case StaffType::security: - return "security"; + return JSFromStdString(ctx, "security"); case StaffType::entertainer: - return "entertainer"; + return JSFromStdString(ctx, "entertainer"); case StaffType::count: break; } } - return {}; + return JS_UNDEFINED; } - void ScStaff::staffType_set(const std::string& value) + JSValue ScStaff::staffType_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto peep = GetStaff(); + JS_UNPACK_STR(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto peep = GetStaff(thisVal); if (peep != nullptr) { if (value == "handyman" && peep->AssignedStaffType != StaffType::handyman) @@ -106,23 +112,26 @@ namespace OpenRCT2::Scripting peep->AnimationType = peep->NextAnimationType = PeepAnimationType::walking; peep->Invalidate(); } + return JS_UNDEFINED; } - uint8_t ScStaff::colour_get() const + JSValue ScStaff::colour_get(JSContext* ctx, JSValue thisVal) { - auto peep = GetStaff(); - return peep != nullptr ? EnumValue(peep->TshirtColour) : 0; + auto peep = GetStaff(thisVal); + return JS_NewUint32(ctx, peep != nullptr ? EnumValue(peep->TshirtColour) : 0); } - void ScStaff::colour_set(uint8_t value) + JSValue ScStaff::colour_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto peep = GetStaff(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto peep = GetStaff(thisVal); if (peep != nullptr) { peep->TshirtColour = static_cast(value); peep->Invalidate(); } + return JS_UNDEFINED; } static const std::vector costumesByStaffType(StaffType staffType) @@ -132,39 +141,42 @@ namespace OpenRCT2::Scripting return getAnimationGroupsByPeepType(animPeepType); } - std::vector ScStaff::availableCostumes_get() const + JSValue ScStaff::availableCostumes_get(JSContext* ctx, JSValue thisVal) { - std::vector availableCostumes{}; - auto peep = GetStaff(); + JSValue availableCostumes = JS_NewArray(ctx); + auto peep = GetStaff(thisVal); if (peep != nullptr) { + auto idx = 0; for (auto& costume : costumesByStaffType(peep->AssignedStaffType)) { - availableCostumes.push_back(std::string(costume.scriptName)); + JS_SetPropertyInt64(ctx, availableCostumes, idx++, JSFromStdString(ctx, costume.scriptName)); } } return availableCostumes; } - std::vector ScStaff::getCostumeStrings() const + JSValue ScStaff::getCostumeStrings(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto peep = GetStaff(); + auto peep = GetStaff(thisVal); auto animPeepType = getAnimationPeepType(peep->AssignedStaffType); - std::vector availableCostumes{}; + JSValue availableCostumes = JS_NewArray(ctx); + auto idx = 0; + for (auto& costume : getAvailableCostumeStrings(animPeepType)) { - availableCostumes.push_back(costume.friendlyName); + JS_SetPropertyInt64(ctx, availableCostumes, idx++, JSFromStdString(ctx, costume.friendlyName)); } return availableCostumes; } - std::string ScStaff::costume_get() const + JSValue ScStaff::costume_get(JSContext* ctx, JSValue thisVal) { - auto peep = GetStaff(); + auto peep = GetStaff(thisVal); if (peep == nullptr) { - return {}; + return JS_UNDEFINED; } auto& costumes = costumesByStaffType(peep->AssignedStaffType); @@ -175,69 +187,78 @@ namespace OpenRCT2::Scripting if (costume != costumes.end()) { - return std::string(costume->scriptName); + return JSFromStdString(ctx, costume->scriptName); } else - return ""; + return JS_UNDEFINED; } - void ScStaff::costume_set(const DukValue& value) + JSValue ScStaff::costume_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); - auto peep = GetStaff(); + auto peep = GetStaff(thisVal); if (peep == nullptr) { - return; + return JS_UNDEFINED; } auto& costumes = costumesByStaffType(peep->AssignedStaffType); auto costume = costumes.end(); // Split by type passed so as to not break old plugins - if (value.type() == DukValue::Type::STRING) + if (JS_IsString(value)) { - costume = std::find_if(costumes.begin(), costumes.end(), [value](auto& candidate) { - return candidate.scriptName == value.as_string(); + JS_UNPACK_STR(valueString, ctx, value); + costume = std::find_if(costumes.begin(), costumes.end(), [valueString](auto& candidate) { + return candidate.scriptName == valueString; }); } - else if (value.type() == DukValue::Type::NUMBER) + else if (JS_IsNumber(value)) { - auto target = RCT12PeepAnimationGroup(value.as_uint() + EnumValue(RCT12PeepAnimationGroup::entertainerPanda)); + JS_UNPACK_UINT32(number, ctx, value); + auto target = RCT12PeepAnimationGroup(number + EnumValue(RCT12PeepAnimationGroup::entertainerPanda)); costume = std::find_if( costumes.begin(), costumes.end(), [target](auto& candidate) { return candidate.legacyPosition == target; }); } if (costume == costumes.end()) - throw DukException() << "Invalid costume for this staff member"; + { + JS_ThrowPlainError(ctx, "Invalid costume for this staff member"); + return JS_EXCEPTION; + } peep->AnimationObjectIndex = costume->objectId; peep->AnimationGroup = costume->group; peep->Invalidate(); + return JS_UNDEFINED; } - std::shared_ptr ScStaff::patrolArea_get() const + JSValue ScStaff::patrolArea_get(JSContext* ctx, JSValue thisVal) { - return std::make_shared(_id); + auto staffId = GetEntityId(thisVal); + return gScPatrolArea.New(ctx, staffId); } - uint8_t ScStaff::orders_get() const + JSValue ScStaff::orders_get(JSContext* ctx, JSValue thisVal) { - auto peep = GetStaff(); - return peep != nullptr ? peep->StaffOrders : 0; + auto peep = GetStaff(thisVal); + return JS_NewUint32(ctx, peep != nullptr ? peep->StaffOrders : 0); } - void ScStaff::orders_set(uint8_t value) + JSValue ScStaff::orders_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto peep = GetStaff(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto peep = GetStaff(thisVal); if (peep != nullptr) { peep->StaffOrders = value; } + return JS_UNDEFINED; } - const DukEnumMap& ScStaff::animationsByStaffType(StaffType staffType) const + EnumMap ScStaff::animationsByStaffType(StaffType staffType) { AnimationPeepType animPeepType{}; switch (staffType) @@ -258,33 +279,36 @@ namespace OpenRCT2::Scripting return getAnimationsByPeepType(animPeepType); } - std::vector ScStaff::availableAnimations_get() const + JSValue ScStaff::availableAnimations_get(JSContext* ctx, JSValue thisVal) { - std::vector availableAnimations{}; + JSValue availableAnimations = JS_NewArray(ctx); - auto* peep = GetStaff(); + auto* peep = GetStaff(thisVal); if (peep != nullptr) { + auto idx = 0; for (auto& animation : animationsByStaffType(peep->AssignedStaffType)) { - availableAnimations.push_back(std::string(animation.first)); + JS_SetPropertyInt64(ctx, availableAnimations, idx++, JSFromStdString(ctx, animation.first)); } } return availableAnimations; } - std::vector ScStaff::getAnimationSpriteIds(std::string groupKey, uint8_t rotation) const + JSValue ScStaff::getAnimationSpriteIds(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - std::vector spriteIds{}; + JS_UNPACK_STR(groupKey, ctx, argv[0]); + JS_UNPACK_UINT32(rotation, ctx, argv[1]); + JSValue spriteIds = JS_NewArray(ctx); - auto* peep = GetStaff(); + auto* peep = GetStaff(thisVal); if (peep == nullptr) { return spriteIds; } - auto& animationGroups = animationsByStaffType(peep->AssignedStaffType); + auto animationGroups = animationsByStaffType(peep->AssignedStaffType); auto animationType = animationGroups.TryGet(groupKey); if (animationType == std::nullopt) { @@ -295,6 +319,7 @@ namespace OpenRCT2::Scripting auto* animObj = objManager.GetLoadedObject(peep->AnimationObjectIndex); const auto& animationGroup = animObj->GetPeepAnimation(peep->AnimationGroup, *animationType); + auto idx = 0; for (auto frameOffset : animationGroup.frameOffsets) { auto imageId = animationGroup.baseImage; @@ -303,35 +328,36 @@ namespace OpenRCT2::Scripting else imageId += frameOffset; - spriteIds.push_back(imageId); + JS_SetPropertyInt64(ctx, spriteIds, idx++, JS_NewUint32(ctx, imageId)); } return spriteIds; } - std::string ScStaff::animation_get() const + JSValue ScStaff::animation_get(JSContext* ctx, JSValue thisVal) { - auto* peep = GetStaff(); + auto* peep = GetStaff(thisVal); if (peep == nullptr) { - return ""; + return JSFromStdString(ctx, ""); } - auto& animationGroups = animationsByStaffType(peep->AssignedStaffType); - std::string_view action = animationGroups[peep->AnimationType]; - return std::string(action); + auto animationGroups = animationsByStaffType(peep->AssignedStaffType); + return JSFromStdString(ctx, animationGroups[peep->AnimationType]); } - void ScStaff::animation_set(std::string groupKey) + JSValue ScStaff::animation_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); + JS_UNPACK_STR(groupKey, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); - auto* peep = GetStaff(); - auto& animationGroups = animationsByStaffType(peep->AssignedStaffType); + auto* peep = GetStaff(thisVal); + auto animationGroups = animationsByStaffType(peep->AssignedStaffType); auto newType = animationGroups.TryGet(groupKey); if (newType == std::nullopt) { - throw DukException() << "Invalid animation for this staff member (" << groupKey << ")"; + JS_ThrowPlainError(ctx, "Invalid animation for this staff member (%s)", groupKey.data()); + return JS_EXCEPTION; } peep->AnimationType = peep->NextAnimationType = *newType; @@ -350,27 +376,27 @@ namespace OpenRCT2::Scripting peep->Invalidate(); peep->UpdateSpriteBoundingBox(); peep->Invalidate(); + return JS_UNDEFINED; } - uint8_t ScStaff::animationOffset_get() const + JSValue ScStaff::animationOffset_get(JSContext* ctx, JSValue thisVal) { - auto* peep = GetStaff(); + auto* peep = GetStaff(thisVal); if (peep == nullptr) { - return 0; + return JS_NewUint32(ctx, 0); } - if (peep->IsActionWalking()) - return peep->WalkingAnimationFrameNum; - else - return peep->AnimationFrameNum; + auto frame = peep->IsActionWalking() ? peep->WalkingAnimationFrameNum : peep->AnimationFrameNum; + return JS_NewUint32(ctx, frame); } - void ScStaff::animationOffset_set(uint8_t offset) + JSValue ScStaff::animationOffset_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); + JS_UNPACK_UINT32(offset, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); - auto* peep = GetStaff(); + auto* peep = GetStaff(thisVal); auto& objManager = GetContext()->GetObjectManager(); auto* animObj = objManager.GetLoadedObject(peep->AnimationObjectIndex); @@ -388,230 +414,223 @@ namespace OpenRCT2::Scripting peep->Invalidate(); peep->UpdateSpriteBoundingBox(); peep->Invalidate(); + return JS_UNDEFINED; } - uint8_t ScStaff::animationLength_get() const + JSValue ScStaff::animationLength_get(JSContext* ctx, JSValue thisVal) { - auto* peep = GetStaff(); + auto* peep = GetStaff(thisVal); if (peep == nullptr) { - return 0; + return JS_NewUint32(ctx, 0); } auto& objManager = GetContext()->GetObjectManager(); auto* animObj = objManager.GetLoadedObject(peep->AnimationObjectIndex); const auto& animationGroup = animObj->GetPeepAnimation(peep->AnimationGroup, peep->AnimationType); - return static_cast(animationGroup.frameOffsets.size()); + auto length = static_cast(animationGroup.frameOffsets.size()); + return JS_NewUint32(ctx, length); } - ScHandyman::ScHandyman(EntityId Id) - : ScStaff(Id) + JSValue ScHandyman::New(JSContext* ctx, EntityId entityId) { + JSValue obj = ScStaff::New(ctx, entityId); + AddFuncs(ctx, obj); + return obj; } - void ScHandyman::Register(duk_context* ctx) + void ScHandyman::AddFuncs(JSContext* ctx, JSValue obj) { - dukglue_set_base_class(ctx); - dukglue_register_property(ctx, &ScHandyman::lawnsMown_get, nullptr, "lawnsMown"); - dukglue_register_property(ctx, &ScHandyman::gardensWatered_get, nullptr, "gardensWatered"); - dukglue_register_property(ctx, &ScHandyman::litterSwept_get, nullptr, "litterSwept"); - dukglue_register_property(ctx, &ScHandyman::binsEmptied_get, nullptr, "binsEmptied"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("lawnsMown", &ScHandyman::lawnsMown_get, nullptr), + JS_CGETSET_DEF("gardensWatered", &ScHandyman::gardensWatered_get, nullptr), + JS_CGETSET_DEF("litterSwept", &ScHandyman::litterSwept_get, nullptr), + JS_CGETSET_DEF("binsEmptied", &ScHandyman::binsEmptied_get, nullptr), + }; + JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs)); } - Staff* ScHandyman::GetHandyman() const + JSValue ScHandyman::lawnsMown_get(JSContext* ctx, JSValue thisVal) { - return getGameState().entities.GetEntity(_id); - } - - DukValue ScHandyman::lawnsMown_get() const - { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto peep = GetHandyman(); + auto peep = GetStaff(thisVal); if (peep != nullptr && peep->AssignedStaffType == StaffType::handyman) { - duk_push_uint(ctx, peep->StaffLawnsMown); + return JS_NewUint32(ctx, peep->StaffLawnsMown); } else { - duk_push_null(ctx); + return JS_NULL; } - return DukValue::take_from_stack(ctx); } - DukValue ScHandyman::gardensWatered_get() const + JSValue ScHandyman::gardensWatered_get(JSContext* ctx, JSValue thisVal) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto peep = GetHandyman(); + auto peep = GetStaff(thisVal); if (peep != nullptr && peep->AssignedStaffType == StaffType::handyman) { - duk_push_uint(ctx, peep->StaffGardensWatered); + return JS_NewUint32(ctx, peep->StaffGardensWatered); } else { - duk_push_null(ctx); + return JS_NULL; } - return DukValue::take_from_stack(ctx); } - DukValue ScHandyman::litterSwept_get() const + JSValue ScHandyman::litterSwept_get(JSContext* ctx, JSValue thisVal) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto peep = GetHandyman(); + auto peep = GetStaff(thisVal); if (peep != nullptr && peep->AssignedStaffType == StaffType::handyman) { - duk_push_uint(ctx, peep->StaffLitterSwept); + return JS_NewUint32(ctx, peep->StaffLitterSwept); } else { - duk_push_null(ctx); + return JS_NULL; } - return DukValue::take_from_stack(ctx); } - DukValue ScHandyman::binsEmptied_get() const + JSValue ScHandyman::binsEmptied_get(JSContext* ctx, JSValue thisVal) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto peep = GetHandyman(); + auto peep = GetStaff(thisVal); if (peep != nullptr && peep->AssignedStaffType == StaffType::handyman) { - duk_push_uint(ctx, peep->StaffBinsEmptied); + return JS_NewUint32(ctx, peep->StaffBinsEmptied); } else { - duk_push_null(ctx); + return JS_NULL; } - return DukValue::take_from_stack(ctx); } - ScMechanic::ScMechanic(EntityId Id) - : ScStaff(Id) + JSValue ScMechanic::New(JSContext* ctx, EntityId entityId) { + JSValue obj = ScStaff::New(ctx, entityId); + AddFuncs(ctx, obj); + return obj; } - void ScMechanic::Register(duk_context* ctx) + void ScMechanic::AddFuncs(JSContext* ctx, JSValue obj) { - dukglue_set_base_class(ctx); - dukglue_register_property(ctx, &ScMechanic::ridesFixed_get, nullptr, "ridesFixed"); - dukglue_register_property(ctx, &ScMechanic::ridesInspected_get, nullptr, "ridesInspected"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("ridesFixed", &ScMechanic::ridesFixed_get, nullptr), + JS_CGETSET_DEF("ridesInspected", &ScMechanic::ridesInspected_get, nullptr), + }; + JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs)); } - Staff* ScMechanic::GetMechanic() const + JSValue ScMechanic::ridesFixed_get(JSContext* ctx, JSValue thisVal) { - return getGameState().entities.GetEntity(_id); - } - - DukValue ScMechanic::ridesFixed_get() const - { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto peep = GetMechanic(); + auto peep = GetStaff(thisVal); if (peep != nullptr && peep->IsMechanic()) { - duk_push_uint(ctx, peep->StaffRidesFixed); + return JS_NewUint32(ctx, peep->StaffRidesFixed); } else { - duk_push_null(ctx); + return JS_NULL; } - return DukValue::take_from_stack(ctx); } - DukValue ScMechanic::ridesInspected_get() const + JSValue ScMechanic::ridesInspected_get(JSContext* ctx, JSValue thisVal) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto peep = GetMechanic(); + auto peep = GetStaff(thisVal); if (peep != nullptr && peep->IsMechanic()) { - duk_push_uint(ctx, peep->StaffRidesInspected); + return JS_NewUint32(ctx, peep->StaffRidesInspected); } else { - duk_push_null(ctx); + return JS_NULL; } - return DukValue::take_from_stack(ctx); } - ScSecurity::ScSecurity(EntityId Id) - : ScStaff(Id) + JSValue ScSecurity::New(JSContext* ctx, EntityId entityId) { + JSValue obj = ScStaff::New(ctx, entityId); + AddFuncs(ctx, obj); + return obj; } - void ScSecurity::Register(duk_context* ctx) + void ScSecurity::AddFuncs(JSContext* ctx, JSValue obj) { - dukglue_set_base_class(ctx); - dukglue_register_property(ctx, &ScSecurity::vandalsStopped_get, nullptr, "vandalsStopped"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("vandalsStopped", &ScSecurity::vandalsStopped_get, nullptr), + }; + JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs)); } - Staff* ScSecurity::GetSecurity() const + JSValue ScSecurity::vandalsStopped_get(JSContext* ctx, JSValue thisVal) { - return getGameState().entities.GetEntity(_id); - } - - DukValue ScSecurity::vandalsStopped_get() const - { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto peep = GetSecurity(); + auto peep = GetStaff(thisVal); if (peep != nullptr && peep->AssignedStaffType == StaffType::security) { - duk_push_uint(ctx, peep->StaffVandalsStopped); + return JS_NewUint32(ctx, peep->StaffVandalsStopped); } else { - duk_push_null(ctx); + return JS_NULL; } - return DukValue::take_from_stack(ctx); } - ScPatrolArea::ScPatrolArea(EntityId id) - : _staffId(id) + using OpaquePatrolAreaData = struct { + EntityId staffId; + }; + + JSValue ScPatrolArea::New(JSContext* ctx, EntityId staffId) + { + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("tiles", &ScPatrolArea::tiles_get, &ScPatrolArea::tiles_set), + JS_CFUNC_DEF("clear", 0, &ScPatrolArea::clear), + JS_CFUNC_DEF("add", 1, &ScPatrolArea::add), + JS_CFUNC_DEF("remove", 1, &ScPatrolArea::remove), + JS_CFUNC_DEF("contains", 1, &ScPatrolArea::contains), + }; + return MakeWithOpaque(ctx, funcs, new OpaquePatrolAreaData{ staffId }); } - void ScPatrolArea::Register(duk_context* ctx) + void ScPatrolArea::Register(JSContext* ctx) { - dukglue_register_property(ctx, &ScPatrolArea::tiles_get, &ScPatrolArea::tiles_set, "tiles"); - dukglue_register_method(ctx, &ScPatrolArea::clear, "clear"); - dukglue_register_method(ctx, &ScPatrolArea::add, "add"); - dukglue_register_method(ctx, &ScPatrolArea::remove, "remove"); - dukglue_register_method(ctx, &ScPatrolArea::contains, "contains"); + RegisterBaseStr(ctx, "PatrolArea", Finalize); } - Staff* ScPatrolArea::GetStaff() const + void ScPatrolArea::Finalize(JSRuntime* rt, JSValue thisVal) { - return getGameState().entities.GetEntity(_staffId); + OpaquePatrolAreaData* data = gScPatrolArea.GetOpaque(thisVal); + if (data) + delete data; } - void ScPatrolArea::ModifyArea(const DukValue& coordsOrRange, bool value) const + Staff* ScPatrolArea::GetStaff(JSValue thisVal) { - auto staff = GetStaff(); + OpaquePatrolAreaData* data = gScPatrolArea.GetOpaque(thisVal); + return getGameState().entities.GetEntity(data->staffId); + } + + void ScPatrolArea::ModifyArea(JSContext* ctx, JSValue thisVal, JSValue coordsOrRange, bool reset) + { + auto staff = GetStaff(thisVal); if (staff != nullptr) { - if (coordsOrRange.is_array()) + if (JS_IsArray(coordsOrRange)) { - auto dukCoords = coordsOrRange.as_array(); - for (const auto& dukCoord : dukCoords) - { - auto coord = FromDuk(dukCoord); - staff->SetPatrolArea(coord, value); + JSIterateArray(ctx, coordsOrRange, [staff, reset](JSContext* ctx2, JSValue v) { + auto coord = JSToCoordsXY(ctx2, v); + staff->SetPatrolArea(coord, reset); MapInvalidateTileFull(coord); - } + }); } else { - auto mapRange = FromDuk(coordsOrRange); + MapRange mapRange = { JSToCoordXY(ctx, coordsOrRange, "leftTop"), + JSToCoordXY(ctx, coordsOrRange, "rightBottom") }; for (int32_t y = mapRange.GetY1(); y <= mapRange.GetY2(); y += kCoordsXYStep) { for (int32_t x = mapRange.GetX1(); x <= mapRange.GetX2(); x += kCoordsXYStep) { CoordsXY coord(x, y); - staff->SetPatrolArea(coord, value); + staff->SetPatrolArea(coord, reset); MapInvalidateTileFull(coord); } } @@ -620,78 +639,82 @@ namespace OpenRCT2::Scripting } } - DukValue ScPatrolArea::tiles_get() const + JSValue ScPatrolArea::tiles_get(JSContext* ctx, JSValue thisVal) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); + auto array = JS_NewArray(ctx); - duk_push_array(ctx); - - auto staff = GetStaff(); + auto staff = GetStaff(thisVal); if (staff != nullptr && staff->PatrolInfo != nullptr) { auto tiles = staff->PatrolInfo->ToVector(); - duk_uarridx_t index = 0; + auto index = 0; for (const auto& tile : tiles) { - auto dukCoord = ToDuk(ctx, tile.ToCoordsXY()); - dukCoord.push(); - duk_put_prop_index(ctx, -2, index); + auto coords = ToJSValue(ctx, tile); + JS_SetPropertyInt64(ctx, array, index, coords); index++; } } - return DukValue::take_from_stack(ctx, -1); + return array; } - void ScPatrolArea::tiles_set(const DukValue& value) + JSValue ScPatrolArea::tiles_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); - auto staff = GetStaff(); + auto staff = GetStaff(thisVal); if (staff != nullptr) { staff->ClearPatrolArea(); - if (value.is_array()) + if (JS_IsArray(value)) { - ModifyArea(value, true); + ModifyArea(ctx, thisVal, value, true); } } + return JS_UNDEFINED; } - void ScPatrolArea::clear() + JSValue ScPatrolArea::clear(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - ThrowIfGameStateNotMutable(); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); - auto staff = GetStaff(); + auto staff = GetStaff(thisVal); if (staff != nullptr) { staff->ClearPatrolArea(); UpdateConsolidatedPatrolAreas(); } + return JS_UNDEFINED; } - void ScPatrolArea::add(const DukValue& coordsOrRange) + JSValue ScPatrolArea::add(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - ThrowIfGameStateNotMutable(); - ModifyArea(coordsOrRange, true); + JS_UNPACK_OBJECT(coordsOrRange, ctx, argv[0]) + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + ModifyArea(ctx, thisVal, coordsOrRange, true); + return JS_UNDEFINED; } - void ScPatrolArea::remove(const DukValue& coordsOrRange) + JSValue ScPatrolArea::remove(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - ThrowIfGameStateNotMutable(); - ModifyArea(coordsOrRange, false); + JS_UNPACK_OBJECT(coordsOrRange, ctx, argv[0]) + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + ModifyArea(ctx, thisVal, coordsOrRange, false); + return JS_UNDEFINED; } - bool ScPatrolArea::contains(const DukValue& coord) const + JSValue ScPatrolArea::contains(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto staff = GetStaff(); + JS_UNPACK_OBJECT(coord, ctx, argv[0]) + auto staff = GetStaff(thisVal); if (staff != nullptr) { - auto pos = FromDuk(coord); - return staff->IsLocationInPatrol(pos); + auto pos = JSToCoordsXY(ctx, coord); + return JS_NewBool(ctx, staff->IsLocationInPatrol(pos)); } - return false; + return JS_NewBool(ctx, false); } } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/entity/ScStaff.hpp b/src/openrct2/scripting/bindings/entity/ScStaff.hpp index 26dd1373c5..2f34e31bec 100644 --- a/src/openrct2/scripting/bindings/entity/ScStaff.hpp +++ b/src/openrct2/scripting/bindings/entity/ScStaff.hpp @@ -23,110 +23,105 @@ namespace OpenRCT2 namespace OpenRCT2::Scripting { - class ScPatrolArea + class ScPatrolArea; + extern ScPatrolArea gScPatrolArea; + + class ScPatrolArea final : public ScBase { - private: - EntityId _staffId; - public: - ScPatrolArea(EntityId id); - - static void Register(duk_context* ctx); + JSValue New(JSContext* ctx, EntityId staffId); + void Register(JSContext* ctx); private: - Staff* GetStaff() const; - void ModifyArea(const DukValue& coordsOrRange, bool value) const; + static void Finalize(JSRuntime* rt, JSValue thisVal); + static Staff* GetStaff(JSValue thisVal); + static void ModifyArea(JSContext* ctx, JSValue thisVal, JSValue coordsOrRange, bool reset); - DukValue tiles_get() const; - void tiles_set(const DukValue& value); + static JSValue tiles_get(JSContext* ctx, JSValue thisVal); + static JSValue tiles_set(JSContext* ctx, JSValue thisVal, JSValue value); - void clear(); - void add(const DukValue& coordsOrRange); - void remove(const DukValue& coordsOrRange); - bool contains(const DukValue& coord) const; + static JSValue clear(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + static JSValue add(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + static JSValue remove(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + static JSValue contains(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); }; class ScStaff : public ScPeep { public: - ScStaff(EntityId Id); + static JSValue New(JSContext* ctx, EntityId entityId); - static void Register(duk_context* ctx); + protected: + static Staff* GetStaff(JSValue thisVal); private: - Staff* GetStaff() const; + static void AddFuncs(JSContext* ctx, JSValue obj); - std::string staffType_get() const; - void staffType_set(const std::string& value); + static JSValue staffType_get(JSContext* ctx, JSValue thisVal); + static JSValue staffType_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - uint8_t colour_get() const; - void colour_set(uint8_t value); + static JSValue colour_get(JSContext* ctx, JSValue thisVal); + static JSValue colour_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - std::vector availableCostumes_get() const; - std::vector getCostumeStrings() const; - std::string costume_get() const; - void costume_set(const DukValue& value); + static JSValue availableCostumes_get(JSContext* ctx, JSValue thisVal); + static JSValue getCostumeStrings(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + static JSValue costume_get(JSContext* ctx, JSValue thisVal); + static JSValue costume_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - std::shared_ptr patrolArea_get() const; + static JSValue patrolArea_get(JSContext* ctx, JSValue thisVal); - uint8_t orders_get() const; - void orders_set(uint8_t value); + static JSValue orders_get(JSContext* ctx, JSValue thisVal); + static JSValue orders_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - const DukEnumMap& animationsByStaffType(StaffType staffType) const; - std::vector getAnimationSpriteIds(std::string groupKey, uint8_t rotation) const; - std::vector availableAnimations_get() const; - std::string animation_get() const; - void animation_set(std::string groupKey); - uint8_t animationOffset_get() const; - void animationOffset_set(uint8_t offset); - uint8_t animationLength_get() const; + static EnumMap animationsByStaffType(StaffType staffType); + static JSValue getAnimationSpriteIds(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + static JSValue availableAnimations_get(JSContext* ctx, JSValue thisVal); + static JSValue animation_get(JSContext* ctx, JSValue thisVal); + static JSValue animation_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); + static JSValue animationOffset_get(JSContext* ctx, JSValue thisVal); + static JSValue animationOffset_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); + static JSValue animationLength_get(JSContext* ctx, JSValue thisVal); }; - class ScHandyman : public ScStaff + class ScHandyman final : public ScStaff { public: - ScHandyman(EntityId Id); - - static void Register(duk_context* ctx); + static JSValue New(JSContext* ctx, EntityId entityId); private: - Staff* GetHandyman() const; + static void AddFuncs(JSContext* ctx, JSValue obj); - DukValue lawnsMown_get() const; + static JSValue lawnsMown_get(JSContext* ctx, JSValue thisVal); - DukValue gardensWatered_get() const; + static JSValue gardensWatered_get(JSContext* ctx, JSValue thisVal); - DukValue litterSwept_get() const; + static JSValue litterSwept_get(JSContext* ctx, JSValue thisVal); - DukValue binsEmptied_get() const; + static JSValue binsEmptied_get(JSContext* ctx, JSValue thisVal); }; - class ScMechanic : public ScStaff + class ScMechanic final : public ScStaff { public: - ScMechanic(EntityId Id); - - static void Register(duk_context* ctx); + static JSValue New(JSContext* ctx, EntityId entityId); private: - Staff* GetMechanic() const; + static void AddFuncs(JSContext* ctx, JSValue obj); - DukValue ridesFixed_get() const; + static JSValue ridesFixed_get(JSContext* ctx, JSValue thisVal); - DukValue ridesInspected_get() const; + static JSValue ridesInspected_get(JSContext* ctx, JSValue thisVal); }; - class ScSecurity : public ScStaff + class ScSecurity final : public ScStaff { public: - ScSecurity(EntityId Id); - - static void Register(duk_context* ctx); + static JSValue New(JSContext* ctx, EntityId entityId); private: - Staff* GetSecurity() const; + static void AddFuncs(JSContext* ctx, JSValue obj); - DukValue vandalsStopped_get() const; + static JSValue vandalsStopped_get(JSContext* ctx, JSValue thisVal); }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/entity/ScVehicle.cpp b/src/openrct2/scripting/bindings/entity/ScVehicle.cpp index e72087df4c..c9ea754edf 100644 --- a/src/openrct2/scripting/bindings/entity/ScVehicle.cpp +++ b/src/openrct2/scripting/bindings/entity/ScVehicle.cpp @@ -9,6 +9,7 @@ #include "ScVehicle.hpp" +#include "../../../core/EnumMap.hpp" #include "../../../ride/Track.h" #include "../../../ride/TrackData.h" #include "../../../ride/ted/TrackElementDescriptor.h" @@ -23,7 +24,7 @@ using namespace OpenRCT2::TrackMetadata; namespace OpenRCT2::Scripting { - static const DukEnumMap VehicleStatusMap( + static const EnumMap VehicleStatusMap( { { "moving_to_end_of_station", Vehicle::Status::movingToEndOfStation }, { "waiting_for_passengers", Vehicle::Status::waitingForPassengers }, @@ -58,312 +59,337 @@ namespace OpenRCT2::Scripting { "stopped_by_block_brake", Vehicle::Status::stoppedByBlockBrakes }, }); - ScVehicle::ScVehicle(EntityId id) - : ScEntity(id) + JSValue ScVehicle::New(JSContext* ctx, EntityId entityId) { + JSValue obj = gScEntity.New(ctx, entityId); + AddFuncs(ctx, obj); + return obj; } - void ScVehicle::Register(duk_context* ctx) + void ScVehicle::AddFuncs(JSContext* ctx, JSValue obj) { - dukglue_set_base_class(ctx); - dukglue_register_property(ctx, &ScVehicle::ride_get, &ScVehicle::ride_set, "ride"); - dukglue_register_property(ctx, &ScVehicle::rideObject_get, &ScVehicle::rideObject_set, "rideObject"); - dukglue_register_property(ctx, &ScVehicle::vehicleObject_get, &ScVehicle::vehicleObject_set, "vehicleObject"); - dukglue_register_property(ctx, &ScVehicle::spriteType_get, &ScVehicle::spriteType_set, "spriteType"); - dukglue_register_property(ctx, &ScVehicle::numSeats_get, &ScVehicle::numSeats_set, "numSeats"); - dukglue_register_property(ctx, &ScVehicle::nextCarOnTrain_get, &ScVehicle::nextCarOnTrain_set, "nextCarOnTrain"); - dukglue_register_property( - ctx, &ScVehicle::previousCarOnRide_get, &ScVehicle::previousCarOnRide_set, "previousCarOnRide"); - dukglue_register_property(ctx, &ScVehicle::nextCarOnRide_get, &ScVehicle::nextCarOnRide_set, "nextCarOnRide"); - dukglue_register_property(ctx, &ScVehicle::currentStation_get, &ScVehicle::currentStation_set, "currentStation"); - dukglue_register_property(ctx, &ScVehicle::mass_get, &ScVehicle::mass_set, "mass"); - dukglue_register_property(ctx, &ScVehicle::acceleration_get, &ScVehicle::acceleration_set, "acceleration"); - dukglue_register_property(ctx, &ScVehicle::velocity_get, &ScVehicle::velocity_set, "velocity"); - dukglue_register_property(ctx, &ScVehicle::bankRotation_get, &ScVehicle::bankRotation_set, "bankRotation"); - dukglue_register_property( - ctx, &ScVehicle::flag_get, &ScVehicle::flag_set, - "isReversed"); - dukglue_register_property( - ctx, &ScVehicle::flag_get, &ScVehicle::flag_set, "isCrashed"); - dukglue_register_property(ctx, &ScVehicle::colours_get, &ScVehicle::colours_set, "colours"); - dukglue_register_property(ctx, &ScVehicle::trackLocation_get, nullptr, "trackLocation"); - dukglue_register_property(ctx, &ScVehicle::trackProgress_get, nullptr, "trackProgress"); - dukglue_register_property(ctx, &ScVehicle::remainingDistance_get, nullptr, "remainingDistance"); - dukglue_register_property(ctx, &ScVehicle::subposition_get, nullptr, "subposition"); - dukglue_register_property( - ctx, &ScVehicle::poweredAcceleration_get, &ScVehicle::poweredAcceleration_set, "poweredAcceleration"); - dukglue_register_property(ctx, &ScVehicle::poweredMaxSpeed_get, &ScVehicle::poweredMaxSpeed_set, "poweredMaxSpeed"); - dukglue_register_property(ctx, &ScVehicle::status_get, &ScVehicle::status_set, "status"); - dukglue_register_property(ctx, &ScVehicle::spin_get, &ScVehicle::spin_set, "spin"); - dukglue_register_property(ctx, &ScVehicle::guests_get, nullptr, "peeps"); - dukglue_register_property(ctx, &ScVehicle::guests_get, nullptr, "guests"); - dukglue_register_property(ctx, &ScVehicle::gForces_get, nullptr, "gForces"); - dukglue_register_method(ctx, &ScVehicle::travelBy, "travelBy"); - dukglue_register_method(ctx, &ScVehicle::moveToTrack, "moveToTrack"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("ride", &ScVehicle::ride_get, &ScVehicle::ride_set), + JS_CGETSET_DEF("rideObject", &ScVehicle::rideObject_get, &ScVehicle::rideObject_set), + JS_CGETSET_DEF("vehicleObject", &ScVehicle::vehicleObject_get, &ScVehicle::vehicleObject_set), + JS_CGETSET_DEF("spriteType", &ScVehicle::spriteType_get, &ScVehicle::spriteType_set), + JS_CGETSET_DEF("numSeats", &ScVehicle::numSeats_get, &ScVehicle::numSeats_set), + JS_CGETSET_DEF("nextCarOnTrain", &ScVehicle::nextCarOnTrain_get, &ScVehicle::nextCarOnTrain_set), + JS_CGETSET_DEF("previousCarOnRide", &ScVehicle::previousCarOnRide_get, &ScVehicle::previousCarOnRide_set), + JS_CGETSET_DEF("nextCarOnRide", &ScVehicle::nextCarOnRide_get, &ScVehicle::nextCarOnRide_set), + JS_CGETSET_DEF("currentStation", &ScVehicle::currentStation_get, &ScVehicle::currentStation_set), + JS_CGETSET_DEF("mass", &ScVehicle::mass_get, &ScVehicle::mass_set), + JS_CGETSET_DEF("acceleration", &ScVehicle::acceleration_get, &ScVehicle::acceleration_set), + JS_CGETSET_DEF("velocity", &ScVehicle::velocity_get, &ScVehicle::velocity_set), + JS_CGETSET_DEF("bankRotation", &ScVehicle::bankRotation_get, &ScVehicle::bankRotation_set), + JS_CGETSET_DEF( + "isReversed", &ScVehicle::flag_get, + &ScVehicle::flag_set), + JS_CGETSET_DEF("isCrashed", &ScVehicle::flag_get, &ScVehicle::flag_set), + JS_CGETSET_DEF("colours", &ScVehicle::colours_get, &ScVehicle::colours_set), + JS_CGETSET_DEF("trackLocation", &ScVehicle::trackLocation_get, nullptr), + JS_CGETSET_DEF("trackProgress", &ScVehicle::trackProgress_get, nullptr), + JS_CGETSET_DEF("remainingDistance", &ScVehicle::remainingDistance_get, nullptr), + JS_CGETSET_DEF("subposition", &ScVehicle::subposition_get, nullptr), + JS_CGETSET_DEF("poweredAcceleration", &ScVehicle::poweredAcceleration_get, &ScVehicle::poweredAcceleration_set), + JS_CGETSET_DEF("poweredMaxSpeed", &ScVehicle::poweredMaxSpeed_get, &ScVehicle::poweredMaxSpeed_set), + JS_CGETSET_DEF("status", &ScVehicle::status_get, &ScVehicle::status_set), + JS_CGETSET_DEF("spin", &ScVehicle::spin_get, &ScVehicle::spin_set), + JS_CGETSET_DEF("peeps", &ScVehicle::guests_get, nullptr), + JS_CGETSET_DEF("guests", &ScVehicle::guests_get, nullptr), + JS_CGETSET_DEF("gForces", &ScVehicle::gForces_get, nullptr), + JS_CFUNC_DEF("travelBy", 1, &ScVehicle::travelBy), + JS_CFUNC_DEF("moveToTrack", 3, &ScVehicle::moveToTrack), + }; + JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs)); } - Vehicle* ScVehicle::GetVehicle() const + Vehicle* ScVehicle::GetVehicle(JSValue thisVal) { - return getGameState().entities.GetEntity(_id); + auto id = GetEntityId(thisVal); + return getGameState().entities.GetEntity(id); } - ObjectEntryIndex ScVehicle::rideObject_get() const + JSValue ScVehicle::rideObject_get(JSContext* ctx, JSValue thisVal) { - auto vehicle = GetVehicle(); - return vehicle != nullptr ? vehicle->ride_subtype : 0; + auto vehicle = GetVehicle(thisVal); + return JS_NewUint32(ctx, vehicle != nullptr ? vehicle->ride_subtype : 0); } - void ScVehicle::rideObject_set(ObjectEntryIndex value) + JSValue ScVehicle::rideObject_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto vehicle = GetVehicle(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto vehicle = GetVehicle(thisVal); if (vehicle != nullptr) { vehicle->ride_subtype = value; vehicle->Invalidate(); } + return JS_UNDEFINED; } - uint8_t ScVehicle::vehicleObject_get() const + JSValue ScVehicle::vehicleObject_get(JSContext* ctx, JSValue thisVal) { - auto vehicle = GetVehicle(); - return vehicle != nullptr ? vehicle->vehicle_type : 0; + auto vehicle = GetVehicle(thisVal); + return JS_NewUint32(ctx, vehicle != nullptr ? vehicle->vehicle_type : 0); } - void ScVehicle::vehicleObject_set(uint8_t value) + JSValue ScVehicle::vehicleObject_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto vehicle = GetVehicle(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto vehicle = GetVehicle(thisVal); if (vehicle != nullptr) { vehicle->vehicle_type = value; vehicle->Invalidate(); } + return JS_UNDEFINED; } - uint8_t ScVehicle::spriteType_get() const + JSValue ScVehicle::spriteType_get(JSContext* ctx, JSValue thisVal) { - auto vehicle = GetVehicle(); - return vehicle != nullptr ? EnumValue(vehicle->pitch) : 0; + auto vehicle = GetVehicle(thisVal); + return JS_NewUint32(ctx, vehicle != nullptr ? EnumValue(vehicle->pitch) : 0); } - void ScVehicle::spriteType_set(uint8_t value) + JSValue ScVehicle::spriteType_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto vehicle = GetVehicle(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto vehicle = GetVehicle(thisVal); if (vehicle != nullptr) { vehicle->pitch = static_cast(value); vehicle->Invalidate(); } + return JS_UNDEFINED; } - int32_t ScVehicle::ride_get() const + JSValue ScVehicle::ride_get(JSContext* ctx, JSValue thisVal) { - auto vehicle = GetVehicle(); - return (vehicle != nullptr ? vehicle->ride : RideId::GetNull()).ToUnderlying(); + auto vehicle = GetVehicle(thisVal); + auto rideId = vehicle != nullptr ? vehicle->ride : RideId::GetNull(); + return JS_NewUint32(ctx, rideId.ToUnderlying()); } - void ScVehicle::ride_set(int32_t value) + JSValue ScVehicle::ride_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto vehicle = GetVehicle(); + JS_UNPACK_INT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto vehicle = GetVehicle(thisVal); if (vehicle != nullptr) { vehicle->ride = RideId::FromUnderlying(value); } + return JS_UNDEFINED; } - uint8_t ScVehicle::numSeats_get() const + JSValue ScVehicle::numSeats_get(JSContext* ctx, JSValue thisVal) { - auto vehicle = GetVehicle(); - return vehicle != nullptr ? vehicle->num_seats & kVehicleSeatNumMask : 0; + auto vehicle = GetVehicle(thisVal); + return JS_NewUint32(ctx, vehicle != nullptr ? vehicle->num_seats & kVehicleSeatNumMask : 0); } - void ScVehicle::numSeats_set(uint8_t value) + JSValue ScVehicle::numSeats_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto vehicle = GetVehicle(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto vehicle = GetVehicle(thisVal); if (vehicle != nullptr) { vehicle->num_seats &= ~kVehicleSeatNumMask; vehicle->num_seats |= value & kVehicleSeatNumMask; } + return JS_UNDEFINED; } - DukValue ScVehicle::nextCarOnTrain_get() const + JSValue ScVehicle::nextCarOnTrain_get(JSContext* ctx, JSValue thisVal) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - auto vehicle = GetVehicle(); + auto vehicle = GetVehicle(thisVal); if (vehicle != nullptr) { if (!vehicle->next_vehicle_on_train.IsNull()) { - return ToDuk(ctx, vehicle->next_vehicle_on_train.ToUnderlying()); + return JS_NewUint32(ctx, vehicle->next_vehicle_on_train.ToUnderlying()); } } - return ToDuk(ctx, nullptr); + return JS_NULL; } - void ScVehicle::nextCarOnTrain_set(DukValue value) + JSValue ScVehicle::nextCarOnTrain_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto vehicle = GetVehicle(); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto vehicle = GetVehicle(thisVal); if (vehicle != nullptr) { - if (value.type() == DukValue::Type::NUMBER) - { - vehicle->next_vehicle_on_train = EntityId::FromUnderlying(value.as_uint()); - } - else + if (JS_IsNull(jsValue)) { vehicle->next_vehicle_on_train = EntityId::GetNull(); } + else + { + JS_UNPACK_UINT32(entityId, ctx, jsValue); + vehicle->next_vehicle_on_train = EntityId::FromUnderlying(entityId); + } } + return JS_UNDEFINED; } - DukValue ScVehicle::previousCarOnRide_get() const + JSValue ScVehicle::previousCarOnRide_get(JSContext* ctx, JSValue thisVal) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - - const auto* vehicle = GetVehicle(); + const auto* vehicle = GetVehicle(thisVal); if (vehicle == nullptr) - return ToDuk(ctx, nullptr); + return JS_NULL; if (vehicle->prev_vehicle_on_ride.IsNull()) - return ToDuk(ctx, nullptr); + return JS_NULL; - return ToDuk(ctx, vehicle->prev_vehicle_on_ride.ToUnderlying()); + return JS_NewUint32(ctx, vehicle->prev_vehicle_on_ride.ToUnderlying()); } - void ScVehicle::previousCarOnRide_set(DukValue value) + JSValue ScVehicle::previousCarOnRide_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto* vehicle = GetVehicle(); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto* vehicle = GetVehicle(thisVal); if (vehicle == nullptr) - return; + return JS_UNDEFINED; - if (value.type() == DukValue::Type::NUMBER) - { - vehicle->prev_vehicle_on_ride = EntityId::FromUnderlying(value.as_uint()); - } - else + if (JS_IsNull(jsValue)) { vehicle->prev_vehicle_on_ride = EntityId::GetNull(); } + else + { + JS_UNPACK_UINT32(entityId, ctx, jsValue); + vehicle->prev_vehicle_on_ride = EntityId::FromUnderlying(entityId); + } + return JS_UNDEFINED; } - DukValue ScVehicle::nextCarOnRide_get() const + JSValue ScVehicle::nextCarOnRide_get(JSContext* ctx, JSValue thisVal) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - - const auto* vehicle = GetVehicle(); + const auto* vehicle = GetVehicle(thisVal); if (vehicle == nullptr) - return ToDuk(ctx, nullptr); + return JS_NULL; if (vehicle->next_vehicle_on_ride.IsNull()) - return ToDuk(ctx, nullptr); + return JS_NULL; - return ToDuk(ctx, vehicle->next_vehicle_on_ride.ToUnderlying()); + return JS_NewUint32(ctx, vehicle->next_vehicle_on_ride.ToUnderlying()); } - void ScVehicle::nextCarOnRide_set(DukValue value) + JSValue ScVehicle::nextCarOnRide_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto* vehicle = GetVehicle(); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto* vehicle = GetVehicle(thisVal); if (vehicle == nullptr) - return; + return JS_UNDEFINED; - if (value.type() == DukValue::Type::NUMBER) - { - vehicle->next_vehicle_on_ride = EntityId::FromUnderlying(value.as_uint()); - } - else + if (JS_IsNull(jsValue)) { vehicle->next_vehicle_on_ride = EntityId::GetNull(); } + else + { + JS_UNPACK_UINT32(entityId, ctx, jsValue); + vehicle->next_vehicle_on_ride = EntityId::FromUnderlying(entityId); + } + return JS_UNDEFINED; } - StationIndex::UnderlyingType ScVehicle::currentStation_get() const + JSValue ScVehicle::currentStation_get(JSContext* ctx, JSValue thisVal) { - auto vehicle = GetVehicle(); - return vehicle != nullptr ? vehicle->current_station.ToUnderlying() : 0; + auto vehicle = GetVehicle(thisVal); + return JS_NewUint32(ctx, vehicle != nullptr ? vehicle->current_station.ToUnderlying() : 0); } - void ScVehicle::currentStation_set(StationIndex::UnderlyingType value) + JSValue ScVehicle::currentStation_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto vehicle = GetVehicle(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto vehicle = GetVehicle(thisVal); if (vehicle != nullptr) { vehicle->current_station = StationIndex::FromUnderlying(value); } + return JS_UNDEFINED; } - uint16_t ScVehicle::mass_get() const + JSValue ScVehicle::mass_get(JSContext* ctx, JSValue thisVal) { - auto vehicle = GetVehicle(); - return vehicle != nullptr ? vehicle->mass : 0; + auto vehicle = GetVehicle(thisVal); + return JS_NewUint32(ctx, vehicle != nullptr ? vehicle->mass : 0); } - void ScVehicle::mass_set(uint16_t value) + JSValue ScVehicle::mass_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto vehicle = GetVehicle(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto vehicle = GetVehicle(thisVal); if (vehicle != nullptr) { vehicle->mass = value; } + return JS_UNDEFINED; } - int32_t ScVehicle::acceleration_get() const + JSValue ScVehicle::acceleration_get(JSContext* ctx, JSValue thisVal) { - auto vehicle = GetVehicle(); - return vehicle != nullptr ? vehicle->acceleration : 0; + auto vehicle = GetVehicle(thisVal); + return JS_NewInt32(ctx, vehicle != nullptr ? vehicle->acceleration : 0); } - void ScVehicle::acceleration_set(int32_t value) + JSValue ScVehicle::acceleration_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto vehicle = GetVehicle(); + JS_UNPACK_INT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto vehicle = GetVehicle(thisVal); if (vehicle != nullptr) { vehicle->acceleration = value; } + return JS_UNDEFINED; } - int32_t ScVehicle::velocity_get() const + JSValue ScVehicle::velocity_get(JSContext* ctx, JSValue thisVal) { - auto vehicle = GetVehicle(); - return vehicle != nullptr ? vehicle->velocity : 0; + auto vehicle = GetVehicle(thisVal); + return JS_NewInt32(ctx, vehicle != nullptr ? vehicle->velocity : 0); } - void ScVehicle::velocity_set(int32_t value) + JSValue ScVehicle::velocity_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto vehicle = GetVehicle(); + JS_UNPACK_INT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto vehicle = GetVehicle(thisVal); if (vehicle != nullptr) { vehicle->velocity = value; } + return JS_UNDEFINED; } - uint8_t ScVehicle::bankRotation_get() const + JSValue ScVehicle::bankRotation_get(JSContext* ctx, JSValue thisVal) { - auto vehicle = GetVehicle(); - return vehicle != nullptr ? EnumValue(vehicle->roll) : 0; + auto vehicle = GetVehicle(thisVal); + return JS_NewUint32(ctx, vehicle != nullptr ? EnumValue(vehicle->roll) : 0); } - void ScVehicle::bankRotation_set(uint8_t value) + JSValue ScVehicle::bankRotation_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto vehicle = GetVehicle(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto vehicle = GetVehicle(thisVal); if (vehicle != nullptr) { vehicle->roll = static_cast(value); vehicle->Invalidate(); } + return JS_UNDEFINED; } template - bool ScVehicle::flag_get() const + JSValue ScVehicle::flag_get(JSContext* ctx, JSValue thisVal) { - auto vehicle = GetVehicle(); - return vehicle != nullptr ? vehicle->flags.has(static_cast(flag)) : false; + auto vehicle = GetVehicle(thisVal); + return JS_NewBool(ctx, vehicle != nullptr ? vehicle->flags.has(static_cast(flag)) : false); } template - void ScVehicle::flag_set(bool value) + JSValue ScVehicle::flag_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto vehicle = GetVehicle(); + JS_UNPACK_BOOL(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto vehicle = GetVehicle(thisVal); if (vehicle != nullptr) { if (value) @@ -376,196 +402,209 @@ namespace OpenRCT2::Scripting } vehicle->Invalidate(); } + return JS_UNDEFINED; } - DukValue ScVehicle::colours_get() const + JSValue ScVehicle::colours_get(JSContext* ctx, JSValue thisVal) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - auto vehicle = GetVehicle(); + auto vehicle = GetVehicle(thisVal); if (vehicle != nullptr) { - return ToDuk(ctx, vehicle->colours); + return ToJSValue(ctx, vehicle->colours); } - return ToDuk(ctx, nullptr); + return JS_NULL; } - void ScVehicle::colours_set(const DukValue& value) + JSValue ScVehicle::colours_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto vehicle = GetVehicle(); + JS_UNPACK_OBJECT(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto vehicle = GetVehicle(thisVal); if (vehicle != nullptr) { - vehicle->colours = FromDuk(value); + vehicle->colours = JSToVehicleColours(ctx, value); vehicle->Invalidate(); } + return JS_UNDEFINED; } - DukValue ScVehicle::trackLocation_get() const + JSValue ScVehicle::trackLocation_get(JSContext* ctx, JSValue thisVal) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - auto vehicle = GetVehicle(); + auto vehicle = GetVehicle(thisVal); if (vehicle != nullptr) { - DukObject dukCoords(ctx); - dukCoords.Set("x", vehicle->TrackLocation.x); - dukCoords.Set("y", vehicle->TrackLocation.y); - dukCoords.Set("z", vehicle->TrackLocation.z); - dukCoords.Set("direction", vehicle->GetTrackDirection()); - dukCoords.Set("trackType", EnumValue(vehicle->GetTrackType())); - return dukCoords.Take(); + JSValue obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "x", JS_NewInt32(ctx, vehicle->TrackLocation.x)); + JS_SetPropertyStr(ctx, obj, "y", JS_NewInt32(ctx, vehicle->TrackLocation.y)); + JS_SetPropertyStr(ctx, obj, "z", JS_NewInt32(ctx, vehicle->TrackLocation.z)); + JS_SetPropertyStr(ctx, obj, "direction", JS_NewUint32(ctx, vehicle->GetTrackDirection())); + JS_SetPropertyStr(ctx, obj, "trackType", JS_NewUint32(ctx, EnumValue(vehicle->GetTrackType()))); + return obj; } - return ToDuk(ctx, nullptr); + return JS_NULL; } - uint16_t ScVehicle::trackProgress_get() const + JSValue ScVehicle::trackProgress_get(JSContext* ctx, JSValue thisVal) { - auto vehicle = GetVehicle(); - return vehicle != nullptr ? vehicle->track_progress : 0; + auto vehicle = GetVehicle(thisVal); + return JS_NewUint32(ctx, vehicle != nullptr ? vehicle->track_progress : 0); } - int32_t ScVehicle::remainingDistance_get() const + JSValue ScVehicle::remainingDistance_get(JSContext* ctx, JSValue thisVal) { - auto vehicle = GetVehicle(); - return vehicle != nullptr ? vehicle->remaining_distance : 0; + auto vehicle = GetVehicle(thisVal); + return JS_NewInt32(ctx, vehicle != nullptr ? vehicle->remaining_distance : 0); } - uint8_t ScVehicle::subposition_get() const + JSValue ScVehicle::subposition_get(JSContext* ctx, JSValue thisVal) { - auto vehicle = GetVehicle(); - return vehicle != nullptr ? static_cast(vehicle->TrackSubposition) : 0; + auto vehicle = GetVehicle(thisVal); + return JS_NewUint32(ctx, vehicle != nullptr ? static_cast(vehicle->TrackSubposition) : 0); } - uint8_t ScVehicle::poweredAcceleration_get() const + JSValue ScVehicle::poweredAcceleration_get(JSContext* ctx, JSValue thisVal) { - auto vehicle = GetVehicle(); - return vehicle != nullptr ? vehicle->powered_acceleration : 0; + auto vehicle = GetVehicle(thisVal); + return JS_NewUint32(ctx, vehicle != nullptr ? vehicle->powered_acceleration : 0); } - void ScVehicle::poweredAcceleration_set(uint8_t value) + JSValue ScVehicle::poweredAcceleration_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto vehicle = GetVehicle(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto vehicle = GetVehicle(thisVal); if (vehicle != nullptr) { vehicle->powered_acceleration = value; } + return JS_UNDEFINED; } - uint8_t ScVehicle::poweredMaxSpeed_get() const + JSValue ScVehicle::poweredMaxSpeed_get(JSContext* ctx, JSValue thisVal) { - auto vehicle = GetVehicle(); - return vehicle != nullptr ? vehicle->speed : 0; + auto vehicle = GetVehicle(thisVal); + return JS_NewUint32(ctx, vehicle != nullptr ? vehicle->speed : 0); } - void ScVehicle::poweredMaxSpeed_set(uint8_t value) + JSValue ScVehicle::poweredMaxSpeed_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto vehicle = GetVehicle(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto vehicle = GetVehicle(thisVal); if (vehicle != nullptr) { vehicle->speed = value; } + return JS_UNDEFINED; } - std::string ScVehicle::status_get() const + JSValue ScVehicle::status_get(JSContext* ctx, JSValue thisVal) { - auto vehicle = GetVehicle(); + auto vehicle = GetVehicle(thisVal); if (vehicle != nullptr) { - return std::string(VehicleStatusMap[vehicle->status]); + return JSFromStdString(ctx, VehicleStatusMap[vehicle->status]); } - return {}; + return JS_UNDEFINED; } - void ScVehicle::status_set(const std::string& value) + JSValue ScVehicle::status_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto vehicle = GetVehicle(); + JS_UNPACK_STR(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto vehicle = GetVehicle(thisVal); if (vehicle != nullptr) { vehicle->status = VehicleStatusMap[value]; } + return JS_UNDEFINED; } - uint8_t ScVehicle::spin_get() const + JSValue ScVehicle::spin_get(JSContext* ctx, JSValue thisVal) { - auto vehicle = GetVehicle(); - if (vehicle != nullptr) - { - return vehicle->spin_sprite; - } - return 0; + auto vehicle = GetVehicle(thisVal); + return JS_NewUint32(ctx, vehicle != nullptr ? vehicle->spin_sprite : 0); } - void ScVehicle::spin_set(const uint8_t value) + JSValue ScVehicle::spin_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto vehicle = GetVehicle(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto vehicle = GetVehicle(thisVal); if (vehicle != nullptr) { vehicle->spin_sprite = value; vehicle->Invalidate(); } + return JS_UNDEFINED; } - std::vector ScVehicle::guests_get() const + JSValue ScVehicle::guests_get(JSContext* ctx, JSValue thisVal) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - std::vector result; - auto vehicle = GetVehicle(); + auto result = JS_NewArray(ctx); + auto vehicle = GetVehicle(thisVal); if (vehicle != nullptr) { size_t len = 0; for (size_t i = 0; i < std::size(vehicle->peep); i++) { auto peep = vehicle->peep[i]; - if (peep.IsNull()) + if (!peep.IsNull()) { - result.push_back(ToDuk(ctx, nullptr)); - } - else - { - result.push_back(ToDuk(ctx, peep.ToUnderlying())); + // Set all peep slots between last valid peep and current to NULL (if there were any null peeps). + for (size_t j = len; j < i; j++) + { + JS_SetPropertyInt64(ctx, result, i, JS_NULL); + } + + JS_SetPropertyInt64(ctx, result, i, JS_NewUint32(ctx, peep.ToUnderlying())); len = i + 1; } } - result.resize(len); } return result; } - DukValue ScVehicle::gForces_get() const + JSValue ScVehicle::gForces_get(JSContext* ctx, JSValue thisVal) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - auto vehicle = GetVehicle(); + auto vehicle = GetVehicle(thisVal); if (vehicle != nullptr) { GForces gForces = vehicle->GetGForces(); - return ToDuk(ctx, gForces); + JSValue obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "lateralG", JS_NewInt32(ctx, gForces.lateralG)); + JS_SetPropertyStr(ctx, obj, "verticalG", JS_NewInt32(ctx, gForces.verticalG)); + return obj; } - return ToDuk(ctx, nullptr); + return JS_NULL; } - void ScVehicle::travelBy(int32_t value) + JSValue ScVehicle::travelBy(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - ThrowIfGameStateNotMutable(); - auto vehicle = GetVehicle(); + JS_UNPACK_INT32(value, ctx, argv[0]); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto vehicle = GetVehicle(thisVal); if (vehicle != nullptr) { vehicle->MoveRelativeDistance(value); EntityTweener::Get().RemoveEntity(vehicle); } + return JS_UNDEFINED; } - void ScVehicle::moveToTrack(int32_t x, int32_t y, int32_t elementIndex) + JSValue ScVehicle::moveToTrack(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto vehicle = GetVehicle(); + JS_UNPACK_INT32(x, ctx, argv[0]); + JS_UNPACK_INT32(y, ctx, argv[1]); + JS_UNPACK_INT32(elementIndex, ctx, argv[2]); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto vehicle = GetVehicle(thisVal); if (vehicle == nullptr) - return; + return JS_UNDEFINED; CoordsXY coords = TileCoordsXY(x, y).ToCoordsXY(); auto el = MapGetNthElementAt(coords, elementIndex); if (el == nullptr) - return; + return JS_UNDEFINED; auto origin = GetTrackSegmentOrigin(CoordsXYE(coords, el)); if (!origin) - return; + return JS_UNDEFINED; const auto& trackType = el->AsTrack()->GetTrackType(); const auto& ted = GetTrackElementDescriptor(trackType); @@ -585,6 +624,7 @@ namespace OpenRCT2::Scripting vehicle->UpdateTrackChange(); EntityTweener::Get().RemoveEntity(vehicle); + return JS_UNDEFINED; } } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/entity/ScVehicle.hpp b/src/openrct2/scripting/bindings/entity/ScVehicle.hpp index 4f34791d5c..281387081d 100644 --- a/src/openrct2/scripting/bindings/entity/ScVehicle.hpp +++ b/src/openrct2/scripting/bindings/entity/ScVehicle.hpp @@ -13,97 +13,96 @@ #include "../../../entity/EntityTweener.h" #include "../../../ride/Ride.h" + #include "../../../ride/Vehicle.h" #include "ScEntity.hpp" #include namespace OpenRCT2::Scripting { - class ScVehicle : public ScEntity + class ScVehicle final : public ScEntity { public: - ScVehicle(EntityId id); - - static void Register(duk_context* ctx); + static JSValue New(JSContext* ctx, EntityId entityId); private: - Vehicle* GetVehicle() const; + static void AddFuncs(JSContext* ctx, JSValue obj); + static Vehicle* GetVehicle(JSValue thisVal); - ObjectEntryIndex rideObject_get() const; - void rideObject_set(ObjectEntryIndex value); + static JSValue rideObject_get(JSContext* ctx, JSValue thisVal); + static JSValue rideObject_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - uint8_t vehicleObject_get() const; - void vehicleObject_set(uint8_t value); + static JSValue vehicleObject_get(JSContext* ctx, JSValue thisVal); + static JSValue vehicleObject_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - uint8_t spriteType_get() const; - void spriteType_set(uint8_t value); + static JSValue spriteType_get(JSContext* ctx, JSValue thisVal); + static JSValue spriteType_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - int32_t ride_get() const; - void ride_set(int32_t value); + static JSValue ride_get(JSContext* ctx, JSValue thisVal); + static JSValue ride_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - uint8_t numSeats_get() const; - void numSeats_set(uint8_t value); + static JSValue numSeats_get(JSContext* ctx, JSValue thisVal); + static JSValue numSeats_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - DukValue nextCarOnTrain_get() const; - void nextCarOnTrain_set(DukValue value); + static JSValue nextCarOnTrain_get(JSContext* ctx, JSValue thisVal); + static JSValue nextCarOnTrain_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - DukValue previousCarOnRide_get() const; - void previousCarOnRide_set(DukValue value); + static JSValue previousCarOnRide_get(JSContext* ctx, JSValue thisVal); + static JSValue previousCarOnRide_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - DukValue nextCarOnRide_get() const; - void nextCarOnRide_set(DukValue value); + static JSValue nextCarOnRide_get(JSContext* ctx, JSValue thisVal); + static JSValue nextCarOnRide_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - StationIndex::UnderlyingType currentStation_get() const; - void currentStation_set(StationIndex::UnderlyingType value); + static JSValue currentStation_get(JSContext* ctx, JSValue thisVal); + static JSValue currentStation_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - uint16_t mass_get() const; - void mass_set(uint16_t value); + static JSValue mass_get(JSContext* ctx, JSValue thisVal); + static JSValue mass_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - int32_t acceleration_get() const; - void acceleration_set(int32_t value); + static JSValue acceleration_get(JSContext* ctx, JSValue thisVal); + static JSValue acceleration_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - int32_t velocity_get() const; - void velocity_set(int32_t value); + static JSValue velocity_get(JSContext* ctx, JSValue thisVal); + static JSValue velocity_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - uint8_t bankRotation_get() const; - void bankRotation_set(uint8_t value); + static JSValue bankRotation_get(JSContext* ctx, JSValue thisVal); + static JSValue bankRotation_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); template - bool flag_get() const; + static JSValue flag_get(JSContext* ctx, JSValue thisVal); template - void flag_set(bool value); + static JSValue flag_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - DukValue colours_get() const; - void colours_set(const DukValue& value); + static JSValue colours_get(JSContext* ctx, JSValue thisVal); + static JSValue colours_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - DukValue trackLocation_get() const; - void trackLocation_set(const DukValue& value); + static JSValue trackLocation_get(JSContext* ctx, JSValue thisVal); - uint16_t trackProgress_get() const; + static JSValue trackProgress_get(JSContext* ctx, JSValue thisVal); - int32_t remainingDistance_get() const; + static JSValue remainingDistance_get(JSContext* ctx, JSValue thisVal); - uint8_t subposition_get() const; + static JSValue subposition_get(JSContext* ctx, JSValue thisVal); - uint8_t poweredAcceleration_get() const; - void poweredAcceleration_set(uint8_t value); + static JSValue poweredAcceleration_get(JSContext* ctx, JSValue thisVal); + static JSValue poweredAcceleration_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - uint8_t poweredMaxSpeed_get() const; - void poweredMaxSpeed_set(uint8_t value); + static JSValue poweredMaxSpeed_get(JSContext* ctx, JSValue thisVal); + static JSValue poweredMaxSpeed_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - std::string status_get() const; - void status_set(const std::string& value); + static JSValue status_get(JSContext* ctx, JSValue thisVal); + static JSValue status_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - uint8_t spin_get() const; - void spin_set(uint8_t value); + static JSValue spin_get(JSContext* ctx, JSValue thisVal); + static JSValue spin_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); - std::vector guests_get() const; + static JSValue guests_get(JSContext* ctx, JSValue thisVal); - DukValue gForces_get() const; + static JSValue gForces_get(JSContext* ctx, JSValue thisVal); - void travelBy(int32_t value); + static JSValue travelBy(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); - void moveToTrack(int32_t x, int32_t y, int32_t elementIndex); + static JSValue moveToTrack(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/game/ScCheats.hpp b/src/openrct2/scripting/bindings/game/ScCheats.hpp index fce32288c1..64bddd6bd1 100644 --- a/src/openrct2/scripting/bindings/game/ScCheats.hpp +++ b/src/openrct2/scripting/bindings/game/ScCheats.hpp @@ -14,372 +14,456 @@ #include "../../../Cheats.h" #include "../../../GameState.h" #include "../../../world/Park.h" - #include "../../Duktape.hpp" #include "../../ScriptEngine.h" namespace OpenRCT2::Scripting { - class ScCheats + class ScCheats; + extern ScCheats gScCheats; + + class ScCheats final : public ScBase { public: - static void Register(duk_context* ctx) + JSValue New(JSContext* ctx) { - dukglue_register_property( - ctx, &ScCheats::allowArbitraryRideTypeChanges_get, &ScCheats::allowArbitraryRideTypeChanges_set, - "allowArbitraryRideTypeChanges"); - dukglue_register_property( - ctx, &ScCheats::allowTrackPlaceInvalidHeights_get, &ScCheats::allowTrackPlaceInvalidHeights_set, - "allowTrackPlaceInvalidHeights"); - dukglue_register_property( - ctx, &ScCheats::buildInPauseMode_get, &ScCheats::buildInPauseMode_set, "buildInPauseMode"); - dukglue_register_property( - ctx, &ScCheats::disableAllBreakdowns_get, &ScCheats::disableAllBreakdowns_set, "disableAllBreakdowns"); - dukglue_register_property( - ctx, &ScCheats::disableBrakesFailure_get, &ScCheats::disableBrakesFailure_set, "disableBrakesFailure"); - dukglue_register_property( - ctx, &ScCheats::disableClearanceChecks_get, &ScCheats::disableClearanceChecks_set, "disableClearanceChecks"); - dukglue_register_property( - ctx, &ScCheats::disableLittering_get, &ScCheats::disableLittering_set, "disableLittering"); - dukglue_register_property( - ctx, &ScCheats::disablePlantAging_get, &ScCheats::disablePlantAging_set, "disablePlantAging"); - dukglue_register_property( - ctx, &ScCheats::allowRegularPathAsQueue_get, &ScCheats::allowRegularPathAsQueue_set, "allowRegularPathAsQueue"); - dukglue_register_property( - ctx, &ScCheats::allowSpecialColourSchemes_get, &ScCheats::allowSpecialColourSchemes_set, - "allowSpecialColourSchemes"); - dukglue_register_property( - ctx, &ScCheats::disableRideValueAging_get, &ScCheats::disableRideValueAging_set, "disableRideValueAging"); - dukglue_register_property( - ctx, &ScCheats::disableSupportLimits_get, &ScCheats::disableSupportLimits_set, "disableSupportLimits"); - dukglue_register_property( - ctx, &ScCheats::disableTrainLengthLimit_get, &ScCheats::disableTrainLengthLimit_set, "disableTrainLengthLimit"); - dukglue_register_property( - ctx, &ScCheats::disableVandalism_get, &ScCheats::disableVandalism_set, "disableVandalism"); - dukglue_register_property( - ctx, &ScCheats::enableAllDrawableTrackPieces_get, &ScCheats::enableAllDrawableTrackPieces_set, - "enableAllDrawableTrackPieces"); - dukglue_register_property( - ctx, &ScCheats::enableChainLiftOnAllTrack_get, &ScCheats::enableChainLiftOnAllTrack_set, - "enableChainLiftOnAllTrack"); - dukglue_register_property(ctx, &ScCheats::fastLiftHill_get, &ScCheats::fastLiftHill_set, "fastLiftHill"); - dukglue_register_property(ctx, &ScCheats::freezeWeather_get, &ScCheats::freezeWeather_set, "freezeWeather"); - dukglue_register_property( - ctx, &ScCheats::ignoreResearchStatus_get, &ScCheats::ignoreResearchStatus_set, "ignoreResearchStatus"); - dukglue_register_property( - ctx, &ScCheats::ignoreRideIntensity_get, &ScCheats::ignoreRideIntensity_set, "ignoreRideIntensity"); - dukglue_register_property(ctx, &ScCheats::ignoreRidePrice_get, &ScCheats::ignoreRidePrice_set, "ignoreRidePrice"); - dukglue_register_property( - ctx, &ScCheats::neverendingMarketing_get, &ScCheats::neverendingMarketing_set, "neverendingMarketing"); - dukglue_register_property( - ctx, &ScCheats::forcedParkRating_get, &ScCheats::forcedParkRating_set, "forcedParkRating"); - dukglue_register_property(ctx, &ScCheats::sandboxMode_get, &ScCheats::sandboxMode_set, "sandboxMode"); - dukglue_register_property( - ctx, &ScCheats::showAllOperatingModes_get, &ScCheats::showAllOperatingModes_set, "showAllOperatingModes"); - dukglue_register_property( - ctx, &ScCheats::showVehiclesFromOtherTrackTypes_get, &ScCheats::showVehiclesFromOtherTrackTypes_set, - "showVehiclesFromOtherTrackTypes"); - dukglue_register_property( - ctx, &ScCheats::makeAllDestructible_get, &ScCheats::makeAllDestructible_set, "makeAllDestructible"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF( + "allowArbitraryRideTypeChanges", &ScCheats::allowArbitraryRideTypeChanges_get, + &ScCheats::allowArbitraryRideTypeChanges_set), + JS_CGETSET_DEF( + "allowTrackPlaceInvalidHeights", &ScCheats::allowTrackPlaceInvalidHeights_get, + &ScCheats::allowTrackPlaceInvalidHeights_set), + JS_CGETSET_DEF("buildInPauseMode", &ScCheats::buildInPauseMode_get, &ScCheats::buildInPauseMode_set), + JS_CGETSET_DEF( + "disableAllBreakdowns", &ScCheats::disableAllBreakdowns_get, &ScCheats::disableAllBreakdowns_set), + JS_CGETSET_DEF( + "disableBrakesFailure", &ScCheats::disableBrakesFailure_get, &ScCheats::disableBrakesFailure_set), + JS_CGETSET_DEF( + "disableClearanceChecks", &ScCheats::disableClearanceChecks_get, &ScCheats::disableClearanceChecks_set), + JS_CGETSET_DEF("disableLittering", &ScCheats::disableLittering_get, &ScCheats::disableLittering_set), + JS_CGETSET_DEF("disablePlantAging", &ScCheats::disablePlantAging_get, &ScCheats::disablePlantAging_set), + JS_CGETSET_DEF( + "allowRegularPathAsQueue", &ScCheats::allowRegularPathAsQueue_get, &ScCheats::allowRegularPathAsQueue_set), + JS_CGETSET_DEF( + "allowSpecialColourSchemes", &ScCheats::allowSpecialColourSchemes_get, + &ScCheats::allowSpecialColourSchemes_set), + JS_CGETSET_DEF( + "disableRideValueAging", &ScCheats::disableRideValueAging_get, &ScCheats::disableRideValueAging_set), + JS_CGETSET_DEF( + "disableSupportLimits", &ScCheats::disableSupportLimits_get, &ScCheats::disableSupportLimits_set), + JS_CGETSET_DEF( + "disableTrainLengthLimit", &ScCheats::disableTrainLengthLimit_get, &ScCheats::disableTrainLengthLimit_set), + JS_CGETSET_DEF("disableVandalism", &ScCheats::disableVandalism_get, &ScCheats::disableVandalism_set), + JS_CGETSET_DEF( + "enableAllDrawableTrackPieces", &ScCheats::enableAllDrawableTrackPieces_get, + &ScCheats::enableAllDrawableTrackPieces_set), + JS_CGETSET_DEF( + "enableChainLiftOnAllTrack", &ScCheats::enableChainLiftOnAllTrack_get, + &ScCheats::enableChainLiftOnAllTrack_set), + JS_CGETSET_DEF("fastLiftHill", &ScCheats::fastLiftHill_get, &ScCheats::fastLiftHill_set), + JS_CGETSET_DEF("freezeWeather", &ScCheats::freezeWeather_get, &ScCheats::freezeWeather_set), + JS_CGETSET_DEF( + "ignoreResearchStatus", &ScCheats::ignoreResearchStatus_get, &ScCheats::ignoreResearchStatus_set), + JS_CGETSET_DEF("ignoreRideIntensity", &ScCheats::ignoreRideIntensity_get, &ScCheats::ignoreRideIntensity_set), + JS_CGETSET_DEF("ignoreRidePrice", &ScCheats::ignoreRidePrice_get, &ScCheats::ignoreRidePrice_set), + JS_CGETSET_DEF( + "neverendingMarketing", &ScCheats::neverendingMarketing_get, &ScCheats::neverendingMarketing_set), + JS_CGETSET_DEF("forcedParkRating", &ScCheats::forcedParkRating_get, &ScCheats::forcedParkRating_set), + JS_CGETSET_DEF("sandboxMode", &ScCheats::sandboxMode_get, &ScCheats::sandboxMode_set), + JS_CGETSET_DEF( + "showAllOperatingModes", &ScCheats::showAllOperatingModes_get, &ScCheats::showAllOperatingModes_set), + JS_CGETSET_DEF( + "showVehiclesFromOtherTrackTypes", &ScCheats::showVehiclesFromOtherTrackTypes_get, + &ScCheats::showVehiclesFromOtherTrackTypes_set), + JS_CGETSET_DEF("makeAllDestructible", &ScCheats::makeAllDestructible_get, &ScCheats::makeAllDestructible_set) + }; + return MakeWithOpaque(ctx, funcs, nullptr); + } + + void Register(JSContext* ctx) + { + RegisterBaseStr(ctx, "Cheats"); } private: - bool allowArbitraryRideTypeChanges_get() + static JSValue allowArbitraryRideTypeChanges_get(JSContext* ctx, JSValue thisVal) { - return getGameState().cheats.allowArbitraryRideTypeChanges; + return JS_NewBool(ctx, getGameState().cheats.allowArbitraryRideTypeChanges); } - void allowArbitraryRideTypeChanges_set(bool value) + static JSValue allowArbitraryRideTypeChanges_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().cheats.allowArbitraryRideTypeChanges = value; + JS_UNPACK_BOOL(valueBool, ctx, value) + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + getGameState().cheats.allowArbitraryRideTypeChanges = valueBool; + + return JS_UNDEFINED; } - bool allowTrackPlaceInvalidHeights_get() + static JSValue allowTrackPlaceInvalidHeights_get(JSContext* ctx, JSValue thisVal) { - return getGameState().cheats.allowTrackPlaceInvalidHeights; + return JS_NewBool(ctx, getGameState().cheats.allowTrackPlaceInvalidHeights); } - void allowTrackPlaceInvalidHeights_set(bool value) + static JSValue allowTrackPlaceInvalidHeights_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().cheats.allowTrackPlaceInvalidHeights = value; + JS_UNPACK_BOOL(valueBool, ctx, value) + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + getGameState().cheats.allowTrackPlaceInvalidHeights = valueBool; + + return JS_UNDEFINED; } - bool buildInPauseMode_get() + static JSValue buildInPauseMode_get(JSContext* ctx, JSValue thisVal) { - return getGameState().cheats.buildInPauseMode; + return JS_NewBool(ctx, getGameState().cheats.buildInPauseMode); } - void buildInPauseMode_set(bool value) + static JSValue buildInPauseMode_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().cheats.buildInPauseMode = value; + JS_UNPACK_BOOL(valueBool, ctx, value) + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + getGameState().cheats.buildInPauseMode = valueBool; + + return JS_UNDEFINED; } - bool disableAllBreakdowns_get() + static JSValue disableAllBreakdowns_get(JSContext* ctx, JSValue thisVal) { - return getGameState().cheats.disableAllBreakdowns; + return JS_NewBool(ctx, getGameState().cheats.disableAllBreakdowns); } - void disableAllBreakdowns_set(bool value) + static JSValue disableAllBreakdowns_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().cheats.disableAllBreakdowns = value; + JS_UNPACK_BOOL(valueBool, ctx, value) + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + getGameState().cheats.disableAllBreakdowns = valueBool; + + return JS_UNDEFINED; } - bool disableBrakesFailure_get() + static JSValue disableBrakesFailure_get(JSContext* ctx, JSValue thisVal) { - return getGameState().cheats.disableBrakesFailure; + return JS_NewBool(ctx, getGameState().cheats.disableBrakesFailure); } - void disableBrakesFailure_set(bool value) + static JSValue disableBrakesFailure_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().cheats.disableBrakesFailure = value; + JS_UNPACK_BOOL(valueBool, ctx, value) + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + getGameState().cheats.disableBrakesFailure = valueBool; + + return JS_UNDEFINED; } - bool disableClearanceChecks_get() + static JSValue disableClearanceChecks_get(JSContext* ctx, JSValue thisVal) { - return getGameState().cheats.disableClearanceChecks; + return JS_NewBool(ctx, getGameState().cheats.disableClearanceChecks); } - void disableClearanceChecks_set(bool value) + static JSValue disableClearanceChecks_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().cheats.disableClearanceChecks = value; + JS_UNPACK_BOOL(valueBool, ctx, value) + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + getGameState().cheats.disableClearanceChecks = valueBool; + + return JS_UNDEFINED; } - bool disableLittering_get() + static JSValue disableLittering_get(JSContext* ctx, JSValue thisVal) { - return getGameState().cheats.disableLittering; + return JS_NewBool(ctx, getGameState().cheats.disableLittering); } - void disableLittering_set(bool value) + static JSValue disableLittering_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().cheats.disableLittering = value; + JS_UNPACK_BOOL(valueBool, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + getGameState().cheats.disableLittering = valueBool; + + return JS_UNDEFINED; } - bool disablePlantAging_get() + static JSValue disablePlantAging_get(JSContext* ctx, JSValue thisVal) { - return getGameState().cheats.disablePlantAging; + return JS_NewBool(ctx, getGameState().cheats.disablePlantAging); } - void disablePlantAging_set(bool value) + static JSValue disablePlantAging_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().cheats.disablePlantAging = value; + JS_UNPACK_BOOL(valueBool, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + getGameState().cheats.disablePlantAging = valueBool; + + return JS_UNDEFINED; } - bool allowRegularPathAsQueue_get() + static JSValue allowRegularPathAsQueue_get(JSContext* ctx, JSValue thisVal) { - return getGameState().cheats.allowRegularPathAsQueue; + return JS_NewBool(ctx, getGameState().cheats.allowRegularPathAsQueue); } - void allowRegularPathAsQueue_set(bool value) + static JSValue allowRegularPathAsQueue_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().cheats.allowRegularPathAsQueue = value; + JS_UNPACK_BOOL(valueBool, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + getGameState().cheats.allowRegularPathAsQueue = valueBool; + + return JS_UNDEFINED; } - bool allowSpecialColourSchemes_get() + static JSValue allowSpecialColourSchemes_get(JSContext* ctx, JSValue thisVal) { - return getGameState().cheats.allowSpecialColourSchemes; + return JS_NewBool(ctx, getGameState().cheats.allowSpecialColourSchemes); } - void allowSpecialColourSchemes_set(bool value) + static JSValue allowSpecialColourSchemes_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().cheats.allowSpecialColourSchemes = value; + JS_UNPACK_BOOL(valueBool, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + getGameState().cheats.allowSpecialColourSchemes = valueBool; + + return JS_UNDEFINED; } - bool disableRideValueAging_get() + static JSValue disableRideValueAging_get(JSContext* ctx, JSValue thisVal) { - return getGameState().cheats.disableRideValueAging; + return JS_NewBool(ctx, getGameState().cheats.disableRideValueAging); } - void disableRideValueAging_set(bool value) + static JSValue disableRideValueAging_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().cheats.disableRideValueAging = value; + JS_UNPACK_BOOL(valueBool, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + getGameState().cheats.disableRideValueAging = valueBool; + + return JS_UNDEFINED; } - bool disableSupportLimits_get() + static JSValue disableSupportLimits_get(JSContext* ctx, JSValue thisVal) { - return getGameState().cheats.disableSupportLimits; + return JS_NewBool(ctx, getGameState().cheats.disableSupportLimits); } - void disableSupportLimits_set(bool value) + static JSValue disableSupportLimits_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().cheats.disableSupportLimits = value; + JS_UNPACK_BOOL(valueBool, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + getGameState().cheats.disableSupportLimits = valueBool; + + return JS_UNDEFINED; } - bool disableTrainLengthLimit_get() + static JSValue disableTrainLengthLimit_get(JSContext* ctx, JSValue thisVal) { - return getGameState().cheats.disableTrainLengthLimit; + return JS_NewBool(ctx, getGameState().cheats.disableTrainLengthLimit); } - void disableTrainLengthLimit_set(bool value) + static JSValue disableTrainLengthLimit_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().cheats.disableTrainLengthLimit = value; + JS_UNPACK_BOOL(valueBool, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + getGameState().cheats.disableTrainLengthLimit = valueBool; + + return JS_UNDEFINED; } - bool disableVandalism_get() + static JSValue disableVandalism_get(JSContext* ctx, JSValue thisVal) { - return getGameState().cheats.disableVandalism; + return JS_NewBool(ctx, getGameState().cheats.disableVandalism); } - void disableVandalism_set(bool value) + static JSValue disableVandalism_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().cheats.disableVandalism = value; + JS_UNPACK_BOOL(valueBool, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + getGameState().cheats.disableVandalism = valueBool; + + return JS_UNDEFINED; } - bool enableAllDrawableTrackPieces_get() + static JSValue enableAllDrawableTrackPieces_get(JSContext* ctx, JSValue thisVal) { - return getGameState().cheats.enableAllDrawableTrackPieces; + return JS_NewBool(ctx, getGameState().cheats.enableAllDrawableTrackPieces); } - void enableAllDrawableTrackPieces_set(bool value) + static JSValue enableAllDrawableTrackPieces_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().cheats.enableAllDrawableTrackPieces = value; + JS_UNPACK_BOOL(valueBool, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + getGameState().cheats.enableAllDrawableTrackPieces = valueBool; + + return JS_UNDEFINED; } - bool enableChainLiftOnAllTrack_get() + static JSValue enableChainLiftOnAllTrack_get(JSContext* ctx, JSValue thisVal) { - return getGameState().cheats.enableChainLiftOnAllTrack; + return JS_NewBool(ctx, getGameState().cheats.enableChainLiftOnAllTrack); } - void enableChainLiftOnAllTrack_set(bool value) + static JSValue enableChainLiftOnAllTrack_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().cheats.enableChainLiftOnAllTrack = value; + JS_UNPACK_BOOL(valueBool, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + getGameState().cheats.enableChainLiftOnAllTrack = valueBool; + + return JS_UNDEFINED; } - bool fastLiftHill_get() + static JSValue fastLiftHill_get(JSContext* ctx, JSValue thisVal) { - return getGameState().cheats.unlockOperatingLimits; + return JS_NewBool(ctx, getGameState().cheats.unlockOperatingLimits); } - void fastLiftHill_set(bool value) + static JSValue fastLiftHill_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().cheats.unlockOperatingLimits = value; + JS_UNPACK_BOOL(valueBool, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + getGameState().cheats.unlockOperatingLimits = valueBool; + + return JS_UNDEFINED; } - bool freezeWeather_get() + static JSValue freezeWeather_get(JSContext* ctx, JSValue thisVal) { - return getGameState().cheats.freezeWeather; + return JS_NewBool(ctx, getGameState().cheats.freezeWeather); } - void freezeWeather_set(bool value) + static JSValue freezeWeather_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().cheats.freezeWeather = value; + JS_UNPACK_BOOL(valueBool, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + getGameState().cheats.freezeWeather = valueBool; + + return JS_UNDEFINED; } - bool ignoreResearchStatus_get() + static JSValue ignoreResearchStatus_get(JSContext* ctx, JSValue thisVal) { - return getGameState().cheats.ignoreResearchStatus; + return JS_NewBool(ctx, getGameState().cheats.ignoreResearchStatus); } - void ignoreResearchStatus_set(bool value) + static JSValue ignoreResearchStatus_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().cheats.ignoreResearchStatus = value; + JS_UNPACK_BOOL(valueBool, ctx, value) + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + getGameState().cheats.ignoreResearchStatus = valueBool; + + return JS_UNDEFINED; } - bool ignoreRideIntensity_get() + static JSValue ignoreRideIntensity_get(JSContext* ctx, JSValue thisVal) { - return getGameState().cheats.ignoreRideIntensity; + return JS_NewBool(ctx, getGameState().cheats.ignoreRideIntensity); } - void ignoreRideIntensity_set(bool value) + static JSValue ignoreRideIntensity_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().cheats.ignoreRideIntensity = value; + JS_UNPACK_BOOL(valueBool, ctx, value) + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + getGameState().cheats.ignoreRideIntensity = valueBool; + + return JS_UNDEFINED; } - bool ignoreRidePrice_get() + static JSValue ignoreRidePrice_get(JSContext* ctx, JSValue thisVal) { - return getGameState().cheats.ignorePrice; + return JS_NewBool(ctx, getGameState().cheats.ignorePrice); } - void ignoreRidePrice_set(bool value) + static JSValue ignoreRidePrice_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().cheats.ignorePrice = value; + JS_UNPACK_BOOL(valueBool, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + getGameState().cheats.ignorePrice = valueBool; + + return JS_UNDEFINED; } - bool neverendingMarketing_get() + static JSValue neverendingMarketing_get(JSContext* ctx, JSValue thisVal) { - return getGameState().cheats.neverendingMarketing; + return JS_NewBool(ctx, getGameState().cheats.neverendingMarketing); } - void neverendingMarketing_set(bool value) + static JSValue neverendingMarketing_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().cheats.neverendingMarketing = value; + JS_UNPACK_BOOL(valueBool, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + getGameState().cheats.neverendingMarketing = valueBool; + + return JS_UNDEFINED; } - int32_t forcedParkRating_get() + static JSValue forcedParkRating_get(JSContext* ctx, JSValue thisVal) { - return getGameState().cheats.forcedParkRating; + return JS_NewInt32(ctx, getGameState().cheats.forcedParkRating); } - void forcedParkRating_set(int32_t value) + static JSValue forcedParkRating_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - int32_t adjusted = std::max(-1, std::min(value, 999)); + JS_UNPACK_INT32(valueInt, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + int32_t adjusted = std::max(-1, std::min(valueInt, 999)); getGameState().cheats.forcedParkRating = adjusted; Park::SetForcedRating(adjusted); + + return JS_UNDEFINED; } - bool sandboxMode_get() + static JSValue sandboxMode_get(JSContext* ctx, JSValue thisVal) { - return getGameState().cheats.sandboxMode; + return JS_NewBool(ctx, getGameState().cheats.sandboxMode); } - void sandboxMode_set(bool value) + static JSValue sandboxMode_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().cheats.sandboxMode = value; + JS_UNPACK_BOOL(valueBool, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + getGameState().cheats.sandboxMode = valueBool; + + return JS_UNDEFINED; } - bool showAllOperatingModes_get() + static JSValue showAllOperatingModes_get(JSContext* ctx, JSValue thisVal) { - return getGameState().cheats.showAllOperatingModes; + return JS_NewBool(ctx, getGameState().cheats.showAllOperatingModes); } - void showAllOperatingModes_set(bool value) + static JSValue showAllOperatingModes_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().cheats.showAllOperatingModes = value; + JS_UNPACK_BOOL(valueBool, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + getGameState().cheats.showAllOperatingModes = valueBool; + + return JS_UNDEFINED; } - bool showVehiclesFromOtherTrackTypes_get() + static JSValue showVehiclesFromOtherTrackTypes_get(JSContext* ctx, JSValue thisVal) { - return getGameState().cheats.showVehiclesFromOtherTrackTypes; + return JS_NewBool(ctx, getGameState().cheats.showVehiclesFromOtherTrackTypes); } - void showVehiclesFromOtherTrackTypes_set(bool value) + static JSValue showVehiclesFromOtherTrackTypes_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().cheats.showVehiclesFromOtherTrackTypes = value; + JS_UNPACK_BOOL(valueBool, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + getGameState().cheats.showVehiclesFromOtherTrackTypes = valueBool; + + return JS_UNDEFINED; } - bool makeAllDestructible_get() + static JSValue makeAllDestructible_get(JSContext* ctx, JSValue thisVal) { - return getGameState().cheats.makeAllDestructible; + return JS_NewBool(ctx, getGameState().cheats.makeAllDestructible); } - void makeAllDestructible_set(bool value) + static JSValue makeAllDestructible_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().cheats.makeAllDestructible = value; + JS_UNPACK_BOOL(valueBool, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + getGameState().cheats.makeAllDestructible = valueBool; + + return JS_UNDEFINED; } }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/game/ScConfiguration.hpp b/src/openrct2/scripting/bindings/game/ScConfiguration.hpp index 544ddc9e5e..c7ac78a4a0 100644 --- a/src/openrct2/scripting/bindings/game/ScConfiguration.hpp +++ b/src/openrct2/scripting/bindings/game/ScConfiguration.hpp @@ -14,11 +14,13 @@ #include "../../../Context.h" #include "../../../config/Config.h" #include "../../../localisation/LocalisationService.h" - #include "../../Duktape.hpp" #include "../../ScriptEngine.h" namespace OpenRCT2::Scripting { + class ScConfiguration; + extern ScConfiguration gScConfiguration; + enum class ScConfigurationKind { User, @@ -26,36 +28,53 @@ namespace OpenRCT2::Scripting Park }; - class ScConfiguration + class ScConfiguration final : public ScBase { private: - ScConfigurationKind _kind; - DukValue _backingObject; - - public: - // context.configuration - ScConfiguration() - : _kind(ScConfigurationKind::User) + struct ConfigurationData { + ScConfigurationKind _kind; + std::string _pluginName; + }; + + static JSValue GetParkStorageForPlugin(JSContext* ctx, const std::string& pluginName) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + JSValue parkStore = scriptEngine.GetParkStorage(); + JSValue pluginStore = JS_GetPropertyStr(ctx, parkStore, pluginName.c_str()); + + // Create if it doesn't exist + if (!JS_IsObject(pluginStore)) + { + JS_FreeValue(ctx, pluginStore); + pluginStore = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, parkStore, pluginName.c_str(), JS_DupValue(ctx, pluginStore)); + } + + return pluginStore; } - // context.sharedStorage / context.getParkStorage - ScConfiguration(ScConfigurationKind kind, const DukValue& backingObject) - : _kind(kind) - , _backingObject(backingObject) + static JSValue GetStoreForConfigType(JSContext* ctx, const ConfigurationData* data) { + switch (data->_kind) + { + case ScConfigurationKind::Park: + { + return GetParkStorageForPlugin(ctx, data->_pluginName); + } + case ScConfigurationKind::Shared: + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + return JS_DupValue(ctx, scriptEngine.GetSharedStorage()); + } + case ScConfigurationKind::User: + default: + Guard::Fail("Invalid ScConfigurationKind"); + return JS_UNDEFINED; + } } - static void Register(duk_context* ctx) - { - dukglue_register_method(ctx, &ScConfiguration::getAll, "getAll"); - dukglue_register_method(ctx, &ScConfiguration::get, "get"); - dukglue_register_method(ctx, &ScConfiguration::set, "set"); - dukglue_register_method(ctx, &ScConfiguration::has, "has"); - } - - private: - std::pair GetNextNamespace(std::string_view input) const + static std::pair GetNextNamespace(std::string_view input) { auto pos = input.find('.'); if (pos == std::string_view::npos) @@ -66,16 +85,16 @@ namespace OpenRCT2::Scripting return std::make_pair(input.substr(0, pos), input.substr(pos + 1)); } - std::pair GetNamespaceAndKey(std::string_view input) const + static std::pair GetNamespaceAndKey(std::string_view input) { auto pos = input.find_last_of('.'); return pos == std::string_view::npos ? std::make_pair(std::string_view(), input) : std::make_pair(input.substr(0, pos), input.substr(pos + 1)); } - std::optional GetNamespaceObject(std::string_view ns) const + static JSValue GetNamespaceObject(JSContext* ctx, const ConfigurationData* data, std::string_view ns) { - auto store = _backingObject; + auto store = GetStoreForConfigType(ctx, data); if (!ns.empty()) { auto k = ns; @@ -83,17 +102,19 @@ namespace OpenRCT2::Scripting do { auto [next, remainder] = GetNextNamespace(k); - store = store[next]; + auto oldStore = store; + store = JS_GetPropertyStr(ctx, store, std::string(next).c_str()); + JS_FreeValue(ctx, oldStore); k = remainder; - end = store.type() == DukValue::Type::UNDEFINED || remainder.empty(); + end = JS_IsUndefined(store) || remainder.empty(); } while (!end); } - return store.type() == DukValue::OBJECT ? std::make_optional(store) : std::nullopt; + return store; } - DukValue GetOrCreateNamespaceObject(duk_context* ctx, std::string_view ns) const + static JSValue GetOrCreateNamespaceObject(JSContext* ctx, const ConfigurationData* data, std::string_view ns) { - auto store = _backingObject; + auto store = GetStoreForConfigType(ctx, data); if (!ns.empty()) { std::string_view k = ns; @@ -101,19 +122,21 @@ namespace OpenRCT2::Scripting do { auto [next, remainder] = GetNextNamespace(k); - auto subStore = store[next]; + std::string nextStr(next); k = remainder; - if (subStore.type() == DukValue::Type::UNDEFINED) + + auto subStore = JS_GetPropertyStr(ctx, store, nextStr.c_str()); + auto oldStore = store; + if (JS_IsUndefined(subStore)) { - store.push(); - duk_push_object(ctx); - store = DukValue::copy_from_stack(ctx); - duk_put_prop_lstring(ctx, -2, next.data(), next.size()); - duk_pop(ctx); + store = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, oldStore, nextStr.c_str(), JS_DupValue(ctx, store)); + JS_FreeValue(ctx, oldStore); } else { - store = std::move(subStore); + store = subStore; + JS_FreeValue(ctx, oldStore); } end = remainder.empty(); } while (!end); @@ -121,14 +144,14 @@ namespace OpenRCT2::Scripting return store; } - bool IsValidNamespace(std::string_view ns) const + static bool IsValidNamespace(std::string_view ns, ScConfigurationKind kind) { if (!ns.empty() && (ns[0] == '.' || ns[ns.size() - 1] == '.')) { return false; } - if (_kind != ScConfigurationKind::Park) + if (kind != ScConfigurationKind::Park) { if (ns.empty()) { @@ -146,154 +169,211 @@ namespace OpenRCT2::Scripting return true; } - bool IsValidKey(std::string_view key) const + static bool IsValidKey(std::string_view key) { return !key.empty() && key.find('.') == std::string_view::npos; } - DukValue getAll(const DukValue& dukNamespace) const + static JSValue getAll(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - DukValue result; - auto ctx = GetContext()->GetScriptEngine().GetContext(); + JSValue jsNamespace = argv[0]; + ConfigurationData* data = gScConfiguration.GetOpaque(thisVal); std::string ns = ""; - if (dukNamespace.type() == DukValue::Type::STRING) + if (JS_IsString(jsNamespace)) { - ns = dukNamespace.as_string(); + ns = JSToStdString(ctx, jsNamespace); } - else if (dukNamespace.type() != DukValue::Type::UNDEFINED) + else if (!JS_IsUndefined(jsNamespace)) { - duk_error(ctx, DUK_ERR_ERROR, "Namespace was invalid."); + JS_ThrowPlainError(ctx, "Namespace was invalid."); + return JS_EXCEPTION; } - if (IsValidNamespace(ns)) + if (IsValidNamespace(ns, data->_kind)) { - if (_kind == ScConfigurationKind::User) + if (data->_kind == ScConfigurationKind::User) { - DukObject obj(ctx); + JSValue obj = JS_NewObject(ctx); if (ns == "general") { - obj.Set("general.language", Config::Get().general.language); - obj.Set("general.showFps", Config::Get().general.showFPS); + auto& localisationService = GetContext()->GetLocalisationService(); + auto locale = localisationService.GetCurrentLanguageLocale(); + JS_SetPropertyStr(ctx, obj, "general.language", JSFromStdString(ctx, locale)); + JS_SetPropertyStr(ctx, obj, "general.showFps", JS_NewBool(ctx, Config::Get().general.showFPS)); } - result = obj.Take(); + return obj; } else { - auto obj = GetNamespaceObject(ns); - result = obj ? *obj : DukObject(ctx).Take(); + auto obj = GetNamespaceObject(ctx, data, ns); + if (!JS_IsObject(obj)) + { + JS_FreeValue(ctx, obj); + return JS_NewObject(ctx); + } + return obj; } } else { - duk_error(ctx, DUK_ERR_ERROR, "Namespace was invalid."); + JS_ThrowPlainError(ctx, "Namespace was invalid."); + return JS_EXCEPTION; } - return result; } - DukValue get(const std::string& key, const DukValue& defaultValue) const + static JSValue get(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - if (_kind == ScConfigurationKind::User) + JS_UNPACK_STR(key, ctx, argv[0]); + JSValue defaultValue = argv[1]; + ConfigurationData* data = gScConfiguration.GetOpaque(thisVal); + + if (data->_kind == ScConfigurationKind::User) { if (key == "general.language") { auto& localisationService = GetContext()->GetLocalisationService(); auto locale = localisationService.GetCurrentLanguageLocale(); - duk_push_lstring(ctx, locale.data(), locale.size()); - return DukValue::take_from_stack(ctx); + return JSFromStdString(ctx, locale); } if (key == "general.showFps") { - duk_push_boolean(ctx, Config::Get().general.showFPS); - return DukValue::take_from_stack(ctx); + return JS_NewBool(ctx, Config::Get().general.showFPS); } } else { auto [ns, n] = GetNamespaceAndKey(key); - if (!IsValidNamespace(ns)) + if (!IsValidNamespace(ns, data->_kind)) { - duk_error(ctx, DUK_ERR_ERROR, "Namespace was invalid."); + JS_ThrowPlainError(ctx, "Namespace was invalid."); + return JS_EXCEPTION; } else if (!IsValidKey(n)) { - duk_error(ctx, DUK_ERR_ERROR, "Key was invalid."); + JS_ThrowPlainError(ctx, "Key was invalid."); + return JS_EXCEPTION; } else { - auto obj = GetNamespaceObject(ns); - if (obj) + auto obj = GetNamespaceObject(ctx, data, ns); + if (JS_IsObject(obj)) { - auto val = (*obj)[n]; - if (val.type() != DukValue::Type::UNDEFINED) + auto val = JS_GetPropertyStr(ctx, obj, std::string(n).c_str()); + JS_FreeValue(ctx, obj); + if (!JS_IsUndefined(val)) { return val; } } + else + { + JS_FreeValue(ctx, obj); + } } } - return defaultValue; + return JS_DupValue(ctx, defaultValue); } - void set(const std::string& key, const DukValue& value) const + static JSValue set(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto ctx = scriptEngine.GetContext(); - if (_kind == ScConfigurationKind::User) + JS_UNPACK_STR(key, ctx, argv[0]); + JSValue value = argv[1]; + ConfigurationData* data = gScConfiguration.GetOpaque(thisVal); + + if (data->_kind == ScConfigurationKind::User) { - try + if (key == "general.showFps") { - if (key == "general.showFps") + if (JS_IsBool(value)) { - Config::Get().general.showFPS = value.as_bool(); + Config::Get().general.showFPS = JS_ToBool(ctx, value) > 0; + return JS_UNDEFINED; } else { - duk_error(ctx, DUK_ERR_ERROR, "Property does not exist."); + JS_ThrowPlainError(ctx, "Invalid value for this property."); + return JS_EXCEPTION; } } - catch (const DukException&) + else { - duk_error(ctx, DUK_ERR_ERROR, "Invalid value for this property."); + JS_ThrowPlainError(ctx, "Property does not exist."); + return JS_EXCEPTION; } } else { auto [ns, n] = GetNamespaceAndKey(key); - if (!IsValidNamespace(ns)) + if (!IsValidNamespace(ns, data->_kind)) { - duk_error(ctx, DUK_ERR_ERROR, "Namespace was invalid."); + JS_ThrowPlainError(ctx, "Namespace was invalid."); + return JS_EXCEPTION; } else if (!IsValidKey(n)) { - duk_error(ctx, DUK_ERR_ERROR, "Key was invalid."); + JS_ThrowPlainError(ctx, "Key was invalid."); + return JS_EXCEPTION; } else { - auto obj = GetOrCreateNamespaceObject(ctx, ns); - obj.push(); - if (value.type() == DukValue::Type::UNDEFINED) - { - duk_del_prop_lstring(ctx, -1, n.data(), n.size()); - } - else - { - value.push(); - duk_put_prop_lstring(ctx, -2, n.data(), n.size()); - } - duk_pop(ctx); + auto obj = GetOrCreateNamespaceObject(ctx, data, ns); + JS_SetPropertyStr(ctx, obj, std::string(n).c_str(), JS_DupValue(ctx, value)); + JS_FreeValue(ctx, obj); + auto& scriptEngine = GetContext()->GetScriptEngine(); scriptEngine.SaveSharedStorage(); + return JS_UNDEFINED; } } } - bool has(const std::string& key) const + static JSValue has(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto val = get(key, DukValue()); - return val.type() != DukValue::Type::UNDEFINED; + std::array newArgs = { argv[0], JS_UNDEFINED }; + auto val = get(ctx, thisVal, 2, newArgs.data()); + if (JS_IsException(val)) + { + return val; + } + bool retval = !JS_IsUndefined(val); + JS_FreeValue(ctx, val); + return JS_NewBool(ctx, retval); } + + static constexpr JSCFunctionListEntry funcs[] = { + JS_CFUNC_DEF("getAll", 1, ScConfiguration::getAll), + JS_CFUNC_DEF("get", 2, ScConfiguration::get), + JS_CFUNC_DEF("set", 2, ScConfiguration::set), + JS_CFUNC_DEF("has", 1, ScConfiguration::has), + }; + + public: + // context.configuration + JSValue New(JSContext* ctx) + { + return MakeWithOpaque(ctx, funcs, new ConfigurationData{ ScConfigurationKind::User, {} }); + } + + // context.sharedStorage / context.getParkStorage + JSValue New(JSContext* ctx, ScConfigurationKind kind, std::string_view pluginName = {}) + { + return MakeWithOpaque(ctx, funcs, new ConfigurationData{ kind, std::string(pluginName) }); + } + + void Register(JSContext* ctx) + { + RegisterBaseStr(ctx, "Configuration", Finalize); + } + + static void Finalize(JSRuntime* rt, JSValue thisVal) + { + ConfigurationData* data = gScConfiguration.GetOpaque(thisVal); + if (data) + delete data; + } + + private: }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/game/ScConsole.hpp b/src/openrct2/scripting/bindings/game/ScConsole.hpp index 18df59ca95..267b0a171e 100644 --- a/src/openrct2/scripting/bindings/game/ScConsole.hpp +++ b/src/openrct2/scripting/bindings/game/ScConsole.hpp @@ -12,55 +12,64 @@ #ifdef ENABLE_SCRIPTING #include "../../../interface/InteractiveConsole.h" - #include "../../Duktape.hpp" #include "../../ScriptEngine.h" namespace OpenRCT2::Scripting { - class ScConsole + class ScConsole; + extern ScConsole gScConsole; + + class ScConsole final : public ScBase { private: - InteractiveConsole& _console; + static JSValue clear(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) + { + if (const auto console = gScConsole.GetOpaque(thisVal)) + console->Clear(); + return JS_UNDEFINED; + } + + static JSValue log(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) + { + if (const auto console = gScConsole.GetOpaque(thisVal)) + { + std::string line; + for (int i = 0; i < argc; i++) + { + if (i != 0) + line.push_back(' '); + line += Stringify(ctx, argv[i]); + } + console->WriteLine(line); + } + return JS_UNDEFINED; + } + + static JSValue executeLegacy(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) + { + if (const auto console = gScConsole.GetOpaque(thisVal)) + { + JS_UNPACK_STR(str, ctx, argv[0]); + console->Execute(str); + } + return JS_UNDEFINED; + } + + static constexpr JSCFunctionListEntry funcs[] = { + JS_CFUNC_DEF("clear", 0, clear), + JS_CFUNC_DEF("log", 0, log), + JS_CFUNC_DEF("executeLegacy", 1, executeLegacy), + }; public: - ScConsole(InteractiveConsole& console) - : _console(console) + JSValue New(JSContext* ctx, InteractiveConsole& console) { + return MakeWithOpaque(ctx, funcs, &console); } - void clear() + void Register(JSContext* ctx) { - _console.Clear(); - } - - duk_ret_t log(duk_context* ctx) - { - std::string line; - auto nargs = duk_get_top(ctx); - for (duk_idx_t i = 0; i < nargs; i++) - { - auto arg = DukValue::copy_from_stack(ctx, i); - auto argsz = Stringify(arg); - if (i != 0) - { - line.push_back(' '); - } - line += argsz; - } - _console.WriteLine(line); - return 0; - } - - void executeLegacy(const std::string& command) - { - _console.Execute(command); - } - - static void Register(duk_context* ctx) - { - dukglue_register_method(ctx, &ScConsole::clear, "clear"); - dukglue_register_method_varargs(ctx, &ScConsole::log, "log"); - dukglue_register_method(ctx, &ScConsole::executeLegacy, "executeLegacy"); + RegisterBaseStr(ctx, "Console"); } }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/game/ScContext.hpp b/src/openrct2/scripting/bindings/game/ScContext.hpp index 7cba5b534c..37f36a80ca 100644 --- a/src/openrct2/scripting/bindings/game/ScContext.hpp +++ b/src/openrct2/scripting/bindings/game/ScContext.hpp @@ -18,7 +18,6 @@ #include "../../../localisation/Formatting.h" #include "../../../object/ObjectManager.h" #include "../../../scenario/Scenario.h" - #include "../../Duktape.hpp" #include "../../HookEngine.h" #include "../../IconNames.hpp" #include "../../ScriptEngine.h" @@ -32,459 +31,428 @@ namespace OpenRCT2::Scripting { - class ScContext + class ScContext; + extern ScContext gScContext; + + class ScContext final : public ScBase { private: - ScriptExecutionInfo& _execInfo; - HookEngine& _hookEngine; - - public: - ScContext(ScriptExecutionInfo& execInfo, HookEngine& hookEngine) - : _execInfo(execInfo) - , _hookEngine(hookEngine) + static JSValue apiVersion_get(JSContext* ctx, JSValue thisVal) { + return JS_NewInt32(ctx, kPluginApiVersion); } - private: - int32_t apiVersion_get() + static JSValue configuration_get(JSContext* ctx, JSValue thisVal) { - return kPluginApiVersion; + return gScConfiguration.New(ctx); } - std::shared_ptr configuration_get() + static JSValue sharedStorage_get(JSContext* ctx, JSValue thisVal) { - return std::make_shared(); + return gScConfiguration.New(ctx, ScConfigurationKind::Shared); } - std::shared_ptr sharedStorage_get() + static JSValue getParkStorage(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { + JSValue jsPluginName = argv[0]; + auto& scriptEngine = GetContext()->GetScriptEngine(); - return std::make_shared(ScConfigurationKind::Shared, scriptEngine.GetSharedStorage()); - } - std::shared_ptr GetParkStorageForPlugin(std::string_view pluginName) - { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto parkStore = scriptEngine.GetParkStorage(); - auto pluginStore = parkStore[pluginName]; - - // Create if it doesn't exist - if (pluginStore.type() != DukValue::Type::OBJECT) + JSValue result = JS_UNDEFINED; + if (JS_IsString(jsPluginName)) { - auto* ctx = scriptEngine.GetContext(); - parkStore.push(); - duk_push_object(ctx); - duk_put_prop_lstring(ctx, -2, pluginName.data(), pluginName.size()); - duk_pop(ctx); - - pluginStore = parkStore[pluginName]; - } - - return std::make_shared(ScConfigurationKind::Park, pluginStore); - } - - std::shared_ptr getParkStorage(const DukValue& dukPluginName) - { - auto& scriptEngine = GetContext()->GetScriptEngine(); - - std::shared_ptr result; - if (dukPluginName.type() == DukValue::Type::STRING) - { - auto& pluginName = dukPluginName.as_string(); + std::string pluginName = JSToStdString(ctx, jsPluginName); if (pluginName.empty()) { - duk_error(scriptEngine.GetContext(), DUK_ERR_ERROR, "Plugin name is empty"); + return JS_ThrowPlainError(ctx, "Plugin name is empty"); } - result = GetParkStorageForPlugin(pluginName); + result = gScConfiguration.New(ctx, ScConfigurationKind::Park, pluginName); } - else if (dukPluginName.type() == DukValue::Type::UNDEFINED) + else if (JS_IsUndefined(jsPluginName)) { - auto plugin = _execInfo.GetCurrentPlugin(); + const auto& plugin = scriptEngine.GetExecInfo().GetCurrentPlugin(); if (plugin == nullptr) { - duk_error( - scriptEngine.GetContext(), DUK_ERR_ERROR, "Plugin name must be specified when used from console."); + return JS_ThrowPlainError(ctx, "Plugin name must be specified when used from console."); } - result = GetParkStorageForPlugin(plugin->GetMetadata().Name); + result = gScConfiguration.New(ctx, ScConfigurationKind::Park, plugin->GetMetadata().Name); } else { - duk_error(scriptEngine.GetContext(), DUK_ERR_ERROR, "Invalid plugin name."); + return JS_ThrowPlainError(ctx, "Invalid plugin name."); } return result; } - std::string mode_get() + static JSValue mode_get(JSContext* ctx, JSValue thisVal) { if (gLegacyScene == LegacyScene::titleSequence) - return "title"; + return JSFromStdString(ctx, "title"); else if (gLegacyScene == LegacyScene::scenarioEditor) - return "scenario_editor"; + return JSFromStdString(ctx, "scenario_editor"); else if (gLegacyScene == LegacyScene::trackDesigner) - return "track_designer"; + return JSFromStdString(ctx, "track_designer"); else if (gLegacyScene == LegacyScene::trackDesignsManager) - return "track_manager"; - return "normal"; + return JSFromStdString(ctx, "track_manager"); + return JSFromStdString(ctx, "normal"); } - bool paused_get() + static JSValue paused_get(JSContext* ctx, JSValue thisVal) { - return GameIsPaused(); + return JS_NewBool(ctx, GameIsPaused()); } - void paused_set(const bool& value) + static JSValue paused_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - if (value != GameIsPaused()) + JS_UNPACK_BOOL(valueBool, ctx, value) + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + if (valueBool != GameIsPaused()) PauseToggle(); + + return JS_UNDEFINED; } - void captureImage(const DukValue& options) + static JSValue captureImage(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); + JSValue options = argv[0]; try { - CaptureOptions captureOptions; - captureOptions.Filename = fs::u8path(AsOrDefault(options["filename"], "")); - captureOptions.Rotation = options["rotation"].as_uint() & 3; - captureOptions.Zoom = ZoomLevel(options["zoom"].as_uint()); - captureOptions.Transparent = AsOrDefault(options["transparent"], false); - - auto dukPosition = options["position"]; - if (dukPosition.type() == DukValue::Type::OBJECT) + auto rotation = JSToOptionalInt(ctx, options, "rotation"); + auto zoom = JSToOptionalInt(ctx, options, "zoom"); + if (!rotation.has_value() || !zoom.has_value()) { + JS_ThrowPlainError(ctx, "Invalid options."); + return JS_EXCEPTION; + } + + CaptureOptions captureOptions; + captureOptions.Filename = fs::u8path(AsOrDefault(ctx, options, "filename", "")); + captureOptions.Rotation = rotation.value() & 3; + captureOptions.Zoom = ZoomLevel(zoom.value()); + captureOptions.Transparent = AsOrDefault(ctx, options, "transparent", false); + + JSValue jsPosition = JS_GetPropertyStr(ctx, options, "position"); + if (JS_IsObject(jsPosition)) + { + auto width = JSToOptionalInt(ctx, options, "width"); + auto height = JSToOptionalInt(ctx, options, "height"); + auto x = JSToOptionalInt(ctx, jsPosition, "x"); + auto y = JSToOptionalInt(ctx, jsPosition, "y"); + + if (!width.has_value() || !height.has_value() || !x.has_value() || !y.has_value()) + { + JS_FreeValue(ctx, jsPosition); + JS_ThrowPlainError(ctx, "Invalid options."); + return JS_EXCEPTION; + } + CaptureView view; - view.Width = options["width"].as_int(); - view.Height = options["height"].as_int(); - view.Position.x = dukPosition["x"].as_int(); - view.Position.y = dukPosition["y"].as_int(); + view.Width = width.value(); + view.Height = height.value(); + view.Position.x = x.value(); + view.Position.y = y.value(); captureOptions.View = view; } + JS_FreeValue(ctx, jsPosition); CaptureImage(captureOptions); } - catch (const DukException&) - { - duk_error(ctx, DUK_ERR_ERROR, "Invalid options."); - } catch (const std::exception& ex) { - duk_error(ctx, DUK_ERR_ERROR, ex.what()); + JS_ThrowPlainError(ctx, "%s", ex.what()); + return JS_EXCEPTION; } + + return JS_UNDEFINED; } - DukValue getObject(const std::string& typez, int32_t index) const + static JSValue getObject(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { // deprecated function, moved to ObjectManager.getObject. - ScObjectManager objectManager; - return objectManager.getObject(typez, index); + return gScObjectManager.getObject(ctx, thisVal, argc, argv); } - std::vector getAllObjects(const std::string& typez) const + static JSValue getAllObjects(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { // deprecated function, moved to ObjectManager.getAllObjects. - ScObjectManager objectManager; - return objectManager.getAllObjects(typez); + return gScObjectManager.getAllObjects(ctx, thisVal, argc, argv); } - DukValue getTrackSegment(uint16_t type) + static JSValue getTrackSegment(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); + JS_UNPACK_INT32(type, ctx, argv[0]) + if (type >= EnumValue(TrackElemType::count)) { - return ToDuk(ctx, nullptr); + return JS_NULL; } else { - return GetObjectAsDukValue(ctx, std::make_shared(static_cast(type))); + return gScTrackSegment.New(ctx, static_cast(type)); } } - std::vector getAllTrackSegments() + static JSValue getAllTrackSegments(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - - std::vector result; + auto result = JS_NewArray(ctx); + int64_t index = 0; for (uint16_t type = 0; type < EnumValue(TrackElemType::count); type++) { - auto obj = std::make_shared(static_cast(type)); - if (obj != nullptr) - { - result.push_back(GetObjectAsDukValue(ctx, obj)); - } + auto obj = gScTrackSegment.New(ctx, static_cast(type)); + JS_SetPropertyInt64(ctx, result, index++, obj); } return result; } - int32_t getRandom(int32_t min, int32_t max) + static JSValue getRandom(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - ThrowIfGameStateNotMutable(); + JS_UNPACK_INT32(min, ctx, argv[0]); + JS_UNPACK_INT32(max, ctx, argv[1]); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + if (min >= max) - return min; + return JS_NewInt32(ctx, min); int32_t range = max - min; - return min + ScenarioRandMax(range); + return JS_NewInt64(ctx, min + ScenarioRandMax(range)); } - duk_ret_t formatString(duk_context* ctx) + static JSValue formatString(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto nargs = duk_get_top(ctx); - if (nargs >= 1) + if (argc >= 1) { - auto dukFmt = DukValue::copy_from_stack(ctx, 0); - if (dukFmt.type() == DukValue::Type::STRING) + const JSValue jsFmt = argv[0]; + if (JS_IsString(jsFmt)) { - FmtString fmt(dukFmt.as_string()); + FmtString fmt(JSToStdString(ctx, jsFmt)); std::vector args; - for (duk_idx_t i = 1; i < nargs; i++) + for (int i = 1; i < argc; i++) { - auto dukArg = DukValue::copy_from_stack(ctx, i); - switch (dukArg.type()) + const JSValue jsArg = argv[i]; + if (JS_IsNumber(jsArg)) { - case DukValue::Type::NUMBER: - args.push_back(dukArg.as_int()); - break; - case DukValue::Type::STRING: - args.push_back(dukArg.as_string()); - break; - default: - duk_error(ctx, DUK_ERR_ERROR, "Invalid format argument."); - break; + args.emplace_back(JSToInt(ctx, jsArg)); + } + else if (JS_IsString(jsArg)) + { + args.emplace_back(JSToStdString(ctx, jsArg)); + } + else + { + JS_ThrowPlainError(ctx, "Invalid format argument."); + return JS_EXCEPTION; } } auto result = FormatStringAny(fmt, args); - duk_push_lstring(ctx, result.c_str(), result.size()); + return JSFromStdString(ctx, result); } else { - duk_error(ctx, DUK_ERR_ERROR, "Invalid format string."); + JS_ThrowPlainError(ctx, "Invalid format string."); + return JS_EXCEPTION; } } - else - { - duk_error(ctx, DUK_ERR_ERROR, "Invalid format string."); - } - return 1; + JS_ThrowPlainError(ctx, "Invalid format string."); + return JS_EXCEPTION; } - #ifdef _MSC_VER - // HACK workaround to resolve issue #14853 - // The exception thrown in duk_error was causing a crash when RAII kicked in for this lambda. - // Only ensuring it was not in the same generated method fixed it. - __declspec(noinline) - #endif - std::shared_ptr - CreateSubscription(HookType hookType, const DukValue& callback) + static JSValue subscribe(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto owner = _execInfo.GetCurrentPlugin(); - auto cookie = _hookEngine.Subscribe(hookType, owner, callback); - return std::make_shared([this, hookType, cookie]() { _hookEngine.Unsubscribe(hookType, cookie); }); - } + JS_UNPACK_STR(hook, ctx, argv[0]); + JS_UNPACK_CALLBACK(callback, ctx, argv[1]); - std::shared_ptr subscribe(const std::string& hook, const DukValue& callback) - { auto& scriptEngine = GetContext()->GetScriptEngine(); - auto ctx = scriptEngine.GetContext(); auto hookType = GetHookType(hook); if (hookType == HookType::notDefined) { - duk_error(ctx, DUK_ERR_ERROR, "Unknown hook type"); + JS_ThrowPlainError(ctx, "Unknown hook type"); + return JS_EXCEPTION; } - if (!callback.is_function()) - { - duk_error(ctx, DUK_ERR_ERROR, "Expected function for callback"); - } - - auto owner = _execInfo.GetCurrentPlugin(); + auto owner = scriptEngine.GetExecInfo().GetCurrentPlugin(); if (owner == nullptr) { - duk_error(ctx, DUK_ERR_ERROR, "Not in a plugin context"); + JS_ThrowPlainError(ctx, "Not in a plugin context"); + return JS_EXCEPTION; } - if (!_hookEngine.IsValidHookForPlugin(hookType, *owner)) + auto& hookEngine = scriptEngine.GetHookEngine(); + if (!hookEngine.IsValidHookForPlugin(hookType, *owner)) { - duk_error(ctx, DUK_ERR_ERROR, "Hook type not available for this plugin type."); + JS_ThrowPlainError(ctx, "Hook type not available for this plugin type."); + return JS_EXCEPTION; } - return CreateSubscription(hookType, callback); + auto cookie = hookEngine.Subscribe(hookType, owner, callback); + return gScDisposable.New(ctx, [&hookEngine, hookType, cookie]() { hookEngine.Unsubscribe(hookType, cookie); }); } - void queryAction(const std::string& action, const DukValue& args, const DukValue& callback) + static JSValue queryAction(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - QueryOrExecuteAction(action, args, callback, false); + JS_UNPACK_STR(action, ctx, argv[0]); + JS_UNPACK_OBJECT(args, ctx, argv[1]); + + return QueryOrExecuteAction(ctx, action, args, JSCallback(ctx, argv[2]), false); } - void executeAction(const std::string& action, const DukValue& args, const DukValue& callback) + static JSValue executeAction(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - QueryOrExecuteAction(action, args, callback, true); + JS_UNPACK_STR(action, ctx, argv[0]); + JS_UNPACK_OBJECT(args, ctx, argv[1]); + + return QueryOrExecuteAction(ctx, action, args, JSCallback(ctx, argv[2]), true); } - void QueryOrExecuteAction(const std::string& actionid, const DukValue& args, const DukValue& callback, bool isExecute) + static JSValue QueryOrExecuteAction( + JSContext* ctx, const std::string& actionid, JSValue args, const JSCallback& callback, bool isExecute) { auto& scriptEngine = GetContext()->GetScriptEngine(); - auto ctx = scriptEngine.GetContext(); - try + auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin(); + auto pair = scriptEngine.CreateGameAction(ctx, actionid, args, plugin->GetMetadata().Name); + + std::unique_ptr action = std::move(pair.first); + + if (pair.second) + return JS_ThrowPlainError(ctx, "Invalid action parameters."); + + if (action != nullptr) { - auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin(); - auto action = scriptEngine.CreateGameAction(actionid, args, plugin->GetMetadata().Name); - if (action != nullptr) + if (isExecute) { - if (isExecute) - { - action->SetCallback( - [this, plugin, - callback](const GameActions::GameAction* act, const GameActions::Result* res) -> void { - HandleGameActionResult(plugin, *act, *res, callback); - }); - GameActions::Execute(action.get(), getGameState()); - } - else - { - auto res = GameActions::Query(action.get(), getGameState()); - HandleGameActionResult(plugin, *action, res, callback); - } + action->SetCallback( + [plugin, callback](const GameActions::GameAction* act, const GameActions::Result* res) -> void { + HandleGameActionResult(plugin, *act, *res, callback); + }); + GameActions::Execute(action.get(), getGameState()); } else { - duk_error(ctx, DUK_ERR_ERROR, "Unknown action."); + auto res = GameActions::Query(action.get(), getGameState()); + HandleGameActionResult(plugin, *action, res, callback); } } - catch (DukException&) - { - duk_error(ctx, DUK_ERR_ERROR, "Invalid action parameters."); - } - } - - void HandleGameActionResult( - const std::shared_ptr& plugin, const GameActions::GameAction& action, const GameActions::Result& res, - const DukValue& callback) - { - if (callback.is_function()) - { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto dukResult = scriptEngine.GameActionResultToDuk(action, res); - // Call the plugin callback and pass the result object - scriptEngine.ExecutePluginCall(plugin, callback, { dukResult }, false); - } - } - - void registerAction(const std::string& action, const DukValue& query, const DukValue& execute) - { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin(); - auto ctx = scriptEngine.GetContext(); - if (!query.is_function()) - { - duk_error(ctx, DUK_ERR_ERROR, "query was not a function."); - } - else if (!execute.is_function()) - { - duk_error(ctx, DUK_ERR_ERROR, "execute was not a function."); - } - else if (!scriptEngine.RegisterCustomAction(plugin, action, query, execute)) - { - duk_error(ctx, DUK_ERR_ERROR, "action has already been registered."); - } - } - - int32_t SetIntervalOrTimeout(DukValue callback, int32_t delay, bool repeat) - { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto ctx = scriptEngine.GetContext(); - auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin(); - - int32_t handle = 0; - if (callback.is_function()) - { - handle = scriptEngine.AddInterval(plugin, delay, repeat, std::move(callback)); - } else { - duk_error(ctx, DUK_ERR_ERROR, "callback was not a function."); + return JS_ThrowPlainError(ctx, "Unknown action."); } - return handle; + return JS_UNDEFINED; } - void ClearIntervalOrTimeout(int32_t handle) + static void HandleGameActionResult( + const std::shared_ptr& plugin, const GameActions::GameAction& action, const GameActions::Result& res, + const JSCallback& callback) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + JSContext* ctx = plugin ? plugin->GetContext() : scriptEngine.GetContext(); + JSValue jsResult = scriptEngine.GameActionResultToJS(ctx, action, res); + // Call the plugin callback and pass the result object + scriptEngine.ExecutePluginCall(plugin, callback.callback, { jsResult }, false); + } + + static JSValue registerAction(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) + { + JS_UNPACK_STR(action, ctx, argv[0]); + JS_UNPACK_CALLBACK(query, ctx, argv[1]); + JS_UNPACK_CALLBACK(execute, ctx, argv[2]); + + auto& scriptEngine = GetContext()->GetScriptEngine(); + auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin(); + if (!scriptEngine.RegisterCustomAction(plugin, action, query, execute)) + { + JS_ThrowPlainError(ctx, "action has already been registered."); + return JS_EXCEPTION; + } + + return JS_UNDEFINED; + } + + static int32_t SetIntervalOrTimeout(const JSCallback& callback, int32_t delay, bool repeat) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin(); + + return scriptEngine.AddInterval(plugin, delay, repeat, callback); + } + + static void ClearIntervalOrTimeout(int32_t handle) { auto& scriptEngine = GetContext()->GetScriptEngine(); auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin(); scriptEngine.RemoveInterval(plugin, handle); } - int32_t setInterval(DukValue callback, int32_t delay) + static JSValue setInterval(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - return SetIntervalOrTimeout(callback, delay, true); + JS_UNPACK_CALLBACK(callback, ctx, argv[0]); + JS_UNPACK_INT32(delay, ctx, argv[1]); + return JS_NewInt32(ctx, SetIntervalOrTimeout(callback, delay, true)); } - int32_t setTimeout(DukValue callback, int32_t delay) + static JSValue setTimeout(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - return SetIntervalOrTimeout(callback, delay, false); + JS_UNPACK_CALLBACK(callback, ctx, argv[0]); + JS_UNPACK_INT32(delay, ctx, argv[1]); + return JS_NewInt32(ctx, SetIntervalOrTimeout(callback, delay, false)); } - void clearInterval(int32_t handle) + static JSValue clearInterval(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { + JS_UNPACK_INT32(handle, ctx, argv[0]); ClearIntervalOrTimeout(handle); + return JS_UNDEFINED; } - void clearTimeout(int32_t handle) + static JSValue clearTimeout(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { + JS_UNPACK_INT32(handle, ctx, argv[0]); ClearIntervalOrTimeout(handle); + return JS_UNDEFINED; } - int32_t getIcon(const std::string& iconName) + static JSValue getIcon(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - return GetIconByName(iconName); + JS_UNPACK_STR(iconName, ctx, argv[0]); + return JS_NewInt64(ctx, GetIconByName(iconName)); } public: - static void Register(duk_context* ctx) + void Register(JSContext* ctx) { - dukglue_register_property(ctx, &ScContext::apiVersion_get, nullptr, "apiVersion"); - dukglue_register_property(ctx, &ScContext::configuration_get, nullptr, "configuration"); - dukglue_register_property(ctx, &ScContext::sharedStorage_get, nullptr, "sharedStorage"); - dukglue_register_method(ctx, &ScContext::getParkStorage, "getParkStorage"); - dukglue_register_property(ctx, &ScContext::mode_get, nullptr, "mode"); - dukglue_register_property(ctx, &ScContext::paused_get, &ScContext::paused_set, "paused"); - dukglue_register_method(ctx, &ScContext::captureImage, "captureImage"); - dukglue_register_method(ctx, &ScContext::getObject, "getObject"); - dukglue_register_method(ctx, &ScContext::getAllObjects, "getAllObjects"); - dukglue_register_method(ctx, &ScContext::getTrackSegment, "getTrackSegment"); - dukglue_register_method(ctx, &ScContext::getAllTrackSegments, "getAllTrackSegments"); - dukglue_register_method(ctx, &ScContext::getRandom, "getRandom"); - dukglue_register_method_varargs(ctx, &ScContext::formatString, "formatString"); - dukglue_register_method(ctx, &ScContext::subscribe, "subscribe"); - dukglue_register_method(ctx, &ScContext::queryAction, "queryAction"); - dukglue_register_method(ctx, &ScContext::executeAction, "executeAction"); - dukglue_register_method(ctx, &ScContext::registerAction, "registerAction"); - dukglue_register_method(ctx, &ScContext::setInterval, "setInterval"); - dukglue_register_method(ctx, &ScContext::setTimeout, "setTimeout"); - dukglue_register_method(ctx, &ScContext::clearInterval, "clearInterval"); - dukglue_register_method(ctx, &ScContext::clearTimeout, "clearTimeout"); - dukglue_register_method(ctx, &ScContext::getIcon, "getIcon"); + RegisterBaseStr(ctx, "Context"); + } + + JSValue New(JSContext* ctx) + { + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("apiVersion", ScContext::apiVersion_get, nullptr), + JS_CGETSET_DEF("configuration", ScContext::configuration_get, nullptr), + JS_CGETSET_DEF("sharedStorage", ScContext::sharedStorage_get, nullptr), + JS_CFUNC_DEF("getParkStorage", 1, ScContext::getParkStorage), + JS_CGETSET_DEF("mode", ScContext::mode_get, nullptr), + JS_CGETSET_DEF("paused", ScContext::paused_get, &ScContext::paused_set), + JS_CFUNC_DEF("captureImage", 1, ScContext::captureImage), + JS_CFUNC_DEF("getObject", 2, ScContext::getObject), + JS_CFUNC_DEF("getAllObjects", 2, ScContext::getAllObjects), + JS_CFUNC_DEF("getTrackSegment", 1, ScContext::getTrackSegment), + JS_CFUNC_DEF("getAllTrackSegments", 0, ScContext::getAllTrackSegments), + JS_CFUNC_DEF("getRandom", 2, ScContext::getRandom), + JS_CFUNC_DEF("formatString", 0, ScContext::formatString), + JS_CFUNC_DEF("subscribe", 2, ScContext::subscribe), + JS_CFUNC_DEF("queryAction", 3, ScContext::queryAction), + JS_CFUNC_DEF("executeAction", 3, ScContext::executeAction), + JS_CFUNC_DEF("registerAction", 3, ScContext::registerAction), + JS_CFUNC_DEF("setInterval", 2, ScContext::setInterval), + JS_CFUNC_DEF("setTimeout", 2, ScContext::setTimeout), + JS_CFUNC_DEF("clearInterval", 1, ScContext::clearInterval), + JS_CFUNC_DEF("clearTimeout", 1, ScContext::clearTimeout), + JS_CFUNC_DEF("getIcon", 1, ScContext::getIcon), + }; + return MakeWithOpaque(ctx, funcs, nullptr); } }; - - uint32_t ImageFromDuk(const DukValue& d) - { - uint32_t img{}; - if (d.type() == DukValue::Type::NUMBER) - { - img = d.as_uint(); - if (GetTargetAPIVersion() <= kApiVersionG2Reorder) - { - img = NewIconIndex(d.as_uint()); - } - } - else if (d.type() == DukValue::Type::STRING) - { - img = GetIconByName(d.as_c_string()); - } - return img; - } } // namespace OpenRCT2::Scripting #endif diff --git a/src/openrct2/scripting/bindings/game/ScDisposable.hpp b/src/openrct2/scripting/bindings/game/ScDisposable.hpp index 35cc0ffa68..ab1cef7b19 100644 --- a/src/openrct2/scripting/bindings/game/ScDisposable.hpp +++ b/src/openrct2/scripting/bindings/game/ScDisposable.hpp @@ -11,34 +11,47 @@ #ifdef ENABLE_SCRIPTING - #include "../../Duktape.hpp" - #include namespace OpenRCT2::Scripting { - class ScDisposable + class ScDisposable; + extern ScDisposable gScDisposable; + + class ScDisposable final : public ScBase { - private: - std::function _onDispose; + using OpaqueType = std::function; public: - ScDisposable(const std::function& onDispose) - : _onDispose(onDispose) + static JSValue dispose(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - } - - void dispose() const - { - if (_onDispose) + OpaqueType* data = gScDisposable.GetOpaque(thisVal); + if (data && *data) { - _onDispose(); + (*data)(); } + return JS_UNDEFINED; } - static void Register(duk_context* ctx) + JSValue New(JSContext* ctx, const std::function& f) { - dukglue_register_method(ctx, &ScDisposable::dispose, "dispose"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CFUNC_DEF("dispose", 0, ScDisposable::dispose), + }; + + return MakeWithOpaque(ctx, funcs, f ? new OpaqueType(f) : nullptr); + } + + void Register(JSContext* ctx) + { + RegisterBaseStr(ctx, "Disposable", Finalize); + } + + static void Finalize(JSRuntime* rt, JSValue thisVal) + { + OpaqueType* data = gScDisposable.GetOpaque(thisVal); + if (data) + delete data; } }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/game/ScPlugin.hpp b/src/openrct2/scripting/bindings/game/ScPlugin.hpp index dec8712530..c05c6e07d3 100644 --- a/src/openrct2/scripting/bindings/game/ScPlugin.hpp +++ b/src/openrct2/scripting/bindings/game/ScPlugin.hpp @@ -11,74 +11,74 @@ #ifdef ENABLE_SCRIPTING - #include "../../Duktape.hpp" #include "../../ScriptEngine.h" #include "../game/ScContext.hpp" namespace OpenRCT2::Scripting { - class ScPlugin + class ScPlugin; + extern ScPlugin gScPlugin; + class ScPlugin final : public ScBase { - public: - static void Register(duk_context* ctx) - { - dukglue_register_property(ctx, &ScPlugin::plugins_get, nullptr, "plugins"); - } - private: - std::vector plugins_get() - { - auto ctx = getContext(); - auto& allPlugins = getallPlugins(); - return formatMetadata(ctx, allPlugins); - } - - duk_context* getContext() - { - // Get the context from the script engine - ScriptEngine& scriptEngine = GetContext()->GetScriptEngine(); - return scriptEngine.GetContext(); - } - - const std::vector> getallPlugins() + static const std::vector> getallPlugins() { // Get all of the plugins from the script engine ScriptEngine& scriptEngine = GetContext()->GetScriptEngine(); return scriptEngine.GetPlugins(); } - const std::vector formatMetadata(duk_context* ctx, const std::vector>& allPlugins) + static JSValue plugins_get(JSContext* ctx, JSValue) { - std::vector formattedMetadata; - duk_idx_t dukIdx = DUK_INVALID_INDEX; - // Iterate through all plugins and and cast their data to Duk objects + auto& allPlugins = getallPlugins(); + return formatMetadata(ctx, allPlugins); + } + + static JSValue formatMetadata( + JSContext* ctx, const std::vector>& allPlugins) + { + JSValue formattedMetadata = JS_NewArray(ctx); + // Iterate through all plugins and and cast their data to JSValue objects + int64_t index = 0; for (const auto& pluginPtr : allPlugins) { // Pull out metadata Plugin& plugin = *pluginPtr; PluginMetadata metadata = plugin.GetMetadata(); - // Create object using Duk stack - dukIdx = duk_push_object(ctx); + // Create object using context + JSValue val = JS_NewObject(ctx); // Name and Version - duk_push_string(ctx, metadata.Name.c_str()); - duk_put_prop_string(ctx, dukIdx, "name"); - duk_push_string(ctx, metadata.Version.c_str()); - duk_put_prop_string(ctx, dukIdx, "version"); + JS_SetPropertyStr(ctx, val, "name", JSFromStdString(ctx, metadata.Name)); + JS_SetPropertyStr(ctx, val, "version", JSFromStdString(ctx, metadata.Version)); // Authors - duk_idx_t arrIdx = duk_push_array(ctx); - for (auto [s, idx] = std::tuple{ metadata.Authors.begin(), 0 }; s != metadata.Authors.end(); s++, idx++) + JSValue authorsArray = JS_NewArray(ctx); + + int64_t idx = 0; + for (auto& str : metadata.Authors) { - auto& str = *s; - duk_push_string(ctx, str.c_str()); - duk_put_prop_index(ctx, arrIdx, idx); + JSValue authorStr = JSFromStdString(ctx, str); + JS_SetPropertyInt64(ctx, authorsArray, idx++, authorStr); } - duk_put_prop_string(ctx, dukIdx, "authors"); - // Take from Duk stack - formattedMetadata.push_back(DukValue::take_from_stack(ctx, dukIdx)); - dukIdx = DUK_INVALID_INDEX; + JS_SetPropertyStr(ctx, val, "authors", authorsArray); + JS_SetPropertyInt64(ctx, formattedMetadata, index++, val); } return formattedMetadata; } + + public: + JSValue New(JSContext* ctx) + { + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("plugins", ScPlugin::plugins_get, nullptr), + }; + + return MakeWithOpaque(ctx, funcs, nullptr); + } + + void Register(JSContext* ctx) + { + RegisterBaseStr(ctx, "PluginManager"); + } }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/game/ScProfiler.hpp b/src/openrct2/scripting/bindings/game/ScProfiler.hpp index a2833ff68d..c4f336fdc8 100644 --- a/src/openrct2/scripting/bindings/game/ScProfiler.hpp +++ b/src/openrct2/scripting/bindings/game/ScProfiler.hpp @@ -12,92 +12,92 @@ #ifdef ENABLE_SCRIPTING #include "../../../profiling/Profiling.h" - #include "../../Duktape.hpp" - + #include "../../ScriptEngine.h" namespace OpenRCT2::Scripting { - class ScProfiler + class ScProfiler; + extern ScProfiler gScProfiler; + class ScProfiler final : public ScBase { private: - duk_context* _ctx{}; - - public: - ScProfiler(duk_context* ctx) - : _ctx(ctx) + static JSValue GetFunctionIndexArray( + JSContext* ctx, const std::vector& all, const std::vector& items) { - } - - private: - DukValue getData() - { - const auto& data = Profiling::getData(); - duk_push_array(_ctx); - duk_uarridx_t index = 0; - for (const auto& f : data) - { - DukObject obj(_ctx); - obj.Set("name", f->getName()); - obj.Set("callCount", f->getCallCount()); - obj.Set("minTime", f->getMinTime()); - obj.Set("maxTime", f->getMaxTime()); - obj.Set("totalTime", f->getTotalTime()); - obj.Set("averageTime", f->getAverageTime()); - obj.Set("parents", GetFunctionIndexArray(data, f->getParents())); - obj.Set("children", GetFunctionIndexArray(data, f->getChildren())); - obj.Take().push(); - duk_put_prop_index(_ctx, /* duk stack index */ -2, index); - index++; - } - return DukValue::take_from_stack(_ctx); - } - - DukValue GetFunctionIndexArray( - const std::vector& all, const std::vector& items) - { - duk_push_array(_ctx); - duk_uarridx_t index = 0; + JSValue functionArray = JS_NewArray(ctx); + int64_t index = 0; for (const auto& item : items) { auto it = std::find(all.begin(), all.end(), item); if (it != all.end()) { - auto value = static_cast(std::distance(all.begin(), it)); - duk_push_int(_ctx, value); - duk_put_prop_index(_ctx, /* duk stack index */ -2, index); - index++; + auto value = static_cast(std::distance(all.begin(), it)); + JS_SetPropertyInt64(ctx, functionArray, index++, JS_NewInt64(ctx, value)); } } - return DukValue::take_from_stack(_ctx); + return functionArray; } - void start() + static JSValue getData(JSContext* ctx, JSValue, int, JSValue*) + { + const auto& data = Profiling::getData(); + JSValue profileData = JS_NewArray(ctx); + int64_t index = 0; + for (const auto& f : data) + { + JSValue val = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, val, "name", JSFromStdString(ctx, f->getName())); + JS_SetPropertyStr(ctx, val, "callCount", JS_NewInt64(ctx, f->getCallCount())); + JS_SetPropertyStr(ctx, val, "minTime", JS_NewFloat64(ctx, f->getMinTime())); + JS_SetPropertyStr(ctx, val, "maxTime", JS_NewFloat64(ctx, f->getMaxTime())); + JS_SetPropertyStr(ctx, val, "totalTime", JS_NewFloat64(ctx, f->getTotalTime())); + JS_SetPropertyStr(ctx, val, "averageTime", JS_NewFloat64(ctx, f->getAverageTime())); + JS_SetPropertyStr(ctx, val, "parents", GetFunctionIndexArray(ctx, data, f->getParents())); + JS_SetPropertyStr(ctx, val, "children", GetFunctionIndexArray(ctx, data, f->getChildren())); + JS_SetPropertyInt64(ctx, profileData, index++, val); + } + return profileData; + } + + static JSValue start(JSContext*, JSValue, int, JSValue*) { Profiling::enable(); + return JS_UNDEFINED; } - void stop() + static JSValue stop(JSContext*, JSValue, int, JSValue*) { Profiling::disable(); + return JS_UNDEFINED; } - void reset() + static JSValue reset(JSContext*, JSValue, int, JSValue*) { Profiling::resetData(); + return JS_UNDEFINED; } - bool enabled_get() const + static JSValue enabled_get(JSContext* ctx, JSValue) { - return Profiling::isEnabled(); + return JS_NewBool(ctx, Profiling::isEnabled()); } public: - static void Register(duk_context* ctx) + JSValue New(JSContext* ctx) { - dukglue_register_method(ctx, &ScProfiler::getData, "getData"); - dukglue_register_method(ctx, &ScProfiler::start, "start"); - dukglue_register_method(ctx, &ScProfiler::stop, "stop"); - dukglue_register_method(ctx, &ScProfiler::reset, "reset"); - dukglue_register_property(ctx, &ScProfiler::enabled_get, nullptr, "enabled"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CFUNC_DEF("getData", 0, ScProfiler::getData), + JS_CFUNC_DEF("start", 0, ScProfiler::start), + JS_CFUNC_DEF("stop", 0, ScProfiler::stop), + JS_CFUNC_DEF("reset", 0, ScProfiler::reset), + JS_CGETSET_DEF("enabled", ScProfiler::enabled_get, nullptr), + }; + + return MakeWithOpaque(ctx, funcs, nullptr); + } + + void Register(JSContext* ctx) + { + RegisterBaseStr(ctx, "Profiler"); } }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/network/ScNetwork.cpp b/src/openrct2/scripting/bindings/network/ScNetwork.cpp index 27e06d808c..c84a88ac72 100644 --- a/src/openrct2/scripting/bindings/network/ScNetwork.cpp +++ b/src/openrct2/scripting/bindings/network/ScNetwork.cpp @@ -21,103 +21,102 @@ namespace OpenRCT2::Scripting { - ScNetwork::ScNetwork(duk_context* ctx) - : _context(ctx) - { - } - - std::string ScNetwork::mode_get() const + JSValue ScNetwork::mode_get(JSContext* ctx, JSValue thisVal) { #ifndef DISABLE_NETWORK switch (Network::GetMode()) { case Network::Mode::server: - return "server"; + return JSFromStdString(ctx, "server"); case Network::Mode::client: - return "client"; + return JSFromStdString(ctx, "client"); default: break; } #endif - return "none"; + return JSFromStdString(ctx, "none"); } - int32_t ScNetwork::numPlayers_get() const + JSValue ScNetwork::numPlayers_get(JSContext* ctx, JSValue thisVal) { #ifndef DISABLE_NETWORK - return Network::GetNumPlayers(); + return JS_NewInt32(ctx, Network::GetNumPlayers()); #else - return 0; + return JS_NewInt32(ctx, 0); #endif } - int32_t ScNetwork::numGroups_get() const + JSValue ScNetwork::numGroups_get(JSContext* ctx, JSValue thisVal) { #ifndef DISABLE_NETWORK - return Network::GetNumGroups(); + return JS_NewInt32(ctx, Network::GetNumGroups()); #else - return 0; + return JS_NewInt32(ctx, 0); #endif } - int32_t ScNetwork::defaultGroup_get() const + JSValue ScNetwork::defaultGroup_get(JSContext* ctx, JSValue thisVal) { #ifndef DISABLE_NETWORK - return Network::GetDefaultGroup(); + return JS_NewInt32(ctx, Network::GetDefaultGroup()); #else - return 0; + return JS_NewInt32(ctx, 0); #endif } - void ScNetwork::defaultGroup_set(int32_t value) + JSValue ScNetwork::defaultGroup_set(JSContext* ctx, JSValue thisVal, JSValue value) { #ifndef DISABLE_NETWORK - auto action = GameActions::NetworkModifyGroupAction(GameActions::ModifyGroupType::SetDefault, value); + JS_UNPACK_INT32(valueInt, ctx, value); + + auto action = GameActions::NetworkModifyGroupAction(GameActions::ModifyGroupType::SetDefault, valueInt); GameActions::Execute(&action, getGameState()); #endif + return JS_UNDEFINED; } - std::vector> ScNetwork::groups_get() const + JSValue ScNetwork::groups_get(JSContext* ctx, JSValue thisVal) { - std::vector> groups; + JSValue groups = JS_NewArray(ctx); #ifndef DISABLE_NETWORK auto numGroups = Network::GetNumGroups(); for (int32_t i = 0; i < numGroups; i++) { auto groupId = Network::GetGroupID(i); - groups.push_back(std::make_shared(groupId)); + JS_SetPropertyInt64(ctx, groups, i, gScPlayerGroup.New(ctx, groupId)); } #endif return groups; } - std::vector> ScNetwork::players_get() const + JSValue ScNetwork::players_get(JSContext* ctx, JSValue thisVal) { - std::vector> players; + JSValue players = JS_NewArray(ctx); #ifndef DISABLE_NETWORK auto numPlayers = Network::GetNumPlayers(); for (int32_t i = 0; i < numPlayers; i++) { auto playerId = Network::GetPlayerID(i); - players.push_back(std::make_shared(playerId)); + JS_SetPropertyInt64(ctx, players, i, gScPlayer.New(ctx, playerId)); } #endif return players; } - std::shared_ptr ScNetwork::currentPlayer_get() const + JSValue ScNetwork::currentPlayer_get(JSContext* ctx, JSValue thisVal) { - std::shared_ptr player; #ifndef DISABLE_NETWORK auto playerId = Network::GetCurrentPlayerId(); - player = std::make_shared(playerId); + return gScPlayer.New(ctx, playerId); + #else + return JS_NULL; #endif - return player; } - std::shared_ptr ScNetwork::getPlayer(int32_t id) const + JSValue ScNetwork::getPlayer(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { #ifndef DISABLE_NETWORK + JS_UNPACK_INT32(id, ctx, argv[0]); if (GetTargetAPIVersion() < kApiVersionNetworkIDs) { auto index = id; @@ -125,7 +124,7 @@ namespace OpenRCT2::Scripting if (index < numPlayers) { auto playerId = Network::GetPlayerID(index); - return std::make_shared(playerId); + return gScPlayer.New(ctx, playerId); } } else @@ -133,50 +132,49 @@ namespace OpenRCT2::Scripting auto index = Network::GetPlayerIndex(id); if (index != -1) { - return std::make_shared(id); + return gScPlayer.New(ctx, id); } } - #endif - return nullptr; + return JS_NULL; } - DukValue ScNetwork::stats_get() const + JSValue ScNetwork::stats_get(JSContext* ctx, JSValue thisVal) { #ifndef DISABLE_NETWORK - auto obj = DukObject(_context); + JSValue obj = JS_NewObject(ctx); auto networkStats = Network::GetStats(); { - duk_push_array(_context); - duk_uarridx_t index = 0; - for (auto v : networkStats.bytesReceived) + JSValue recvStatsArr = JS_NewArray(ctx); + uint32_t index = 0; + for (const auto v : networkStats.bytesReceived) { - duk_push_number(_context, v); - duk_put_prop_index(_context, -2, index); + JS_SetPropertyUint32(ctx, recvStatsArr, index, JS_NewInt64(ctx, static_cast(v))); index++; } - obj.Set("bytesReceived", DukValue::take_from_stack(_context)); + JS_SetPropertyStr(ctx, obj, "bytesReceived", recvStatsArr); } { - duk_push_array(_context); - duk_uarridx_t index = 0; + JSValue sentStatsArr = JS_NewArray(ctx); + uint32_t index = 0; for (auto v : networkStats.bytesSent) { - duk_push_number(_context, v); - duk_put_prop_index(_context, -2, index); + JS_SetPropertyUint32(ctx, sentStatsArr, index, JS_NewInt64(ctx, static_cast(v))); index++; } - obj.Set("bytesSent", DukValue::take_from_stack(_context)); + JS_SetPropertyStr(ctx, obj, "bytesSent", sentStatsArr); } - return obj.Take(); + return obj; #else - return ToDuk(_context, nullptr); + return JS_NULL; #endif } - std::shared_ptr ScNetwork::getGroup(int32_t id) const + JSValue ScNetwork::getGroup(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { #ifndef DISABLE_NETWORK + JS_UNPACK_INT32(id, ctx, argv[0]) + if (GetTargetAPIVersion() < kApiVersionNetworkIDs) { auto index = id; @@ -184,7 +182,7 @@ namespace OpenRCT2::Scripting if (index < numGroups) { auto groupId = Network::GetGroupID(index); - return std::make_shared(groupId); + return gScPlayerGroup.New(ctx, groupId); } } else @@ -192,24 +190,27 @@ namespace OpenRCT2::Scripting auto index = Network::GetGroupIndex(id); if (index != -1) { - return std::make_shared(id); + return gScPlayerGroup.New(ctx, id); } } #endif - return nullptr; + return JS_NULL; } - void ScNetwork::addGroup() + JSValue ScNetwork::addGroup(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { #ifndef DISABLE_NETWORK auto networkModifyGroup = GameActions::NetworkModifyGroupAction(GameActions::ModifyGroupType::AddGroup); GameActions::Execute(&networkModifyGroup, getGameState()); #endif + return JS_UNDEFINED; } - void ScNetwork::removeGroup(int32_t id) + JSValue ScNetwork::removeGroup(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { #ifndef DISABLE_NETWORK + JS_UNPACK_INT32(id, ctx, argv[0]); + if (GetTargetAPIVersion() < kApiVersionNetworkIDs) { auto index = id; @@ -231,11 +232,14 @@ namespace OpenRCT2::Scripting } } #endif + return JS_UNDEFINED; } - void ScNetwork::kickPlayer(int32_t id) + JSValue ScNetwork::kickPlayer(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { #ifndef DISABLE_NETWORK + JS_UNPACK_INT32(id, ctx, argv[0]); + if (GetTargetAPIVersion() < kApiVersionNetworkIDs) { auto index = id; @@ -257,32 +261,32 @@ namespace OpenRCT2::Scripting } } #endif + return JS_UNDEFINED; } - void ScNetwork::sendMessage(std::string message, DukValue players) + JSValue ScNetwork::sendMessage(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { #ifndef DISABLE_NETWORK - if (players.is_array()) + JS_UNPACK_STR(message, ctx, argv[0]); + JSValue players = argv[1]; + + if (JS_IsArray(players)) { if (Network::GetMode() == Network::Mode::server) { std::vector playerIds; - auto playerArray = players.as_array(); - for (const auto& item : playerArray) - { - if (item.type() == DukValue::Type::NUMBER) - { - playerIds.push_back(static_cast(item.as_uint())); - } - } - if (!playerArray.empty()) + JSIterateArray(ctx, players, [&playerIds](JSContext* ctx2, JSValue val) { + playerIds.push_back(static_cast(JSToInt(ctx2, val))); + }); + if (!playerIds.empty()) { Network::SendChat(message.c_str(), playerIds); } } else { - duk_error(players.context(), DUK_ERR_ERROR, "Only servers can send private messages."); + JS_ThrowPlainError(ctx, "Only servers can send private messages."); + return JS_EXCEPTION; } } else @@ -290,59 +294,57 @@ namespace OpenRCT2::Scripting Network::SendChat(message.c_str()); } #endif + return JS_UNDEFINED; } #ifndef DISABLE_NETWORK - std::shared_ptr ScNetwork::createListener() + JSValue ScNetwork::createListener(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin(); - auto socket = std::make_shared(plugin); - scriptEngine.AddSocket(socket); - return socket; + return gScListener.New(ctx, GetContext()->GetScriptEngine().GetExecInfo().GetCurrentPlugin()); } #else - void ScNetwork::createListener() + JSValue ScNetwork::createListener(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - duk_error(_context, DUK_ERR_ERROR, "Networking has been disabled."); + return JS_ThrowPlainError(ctx, "Networking has been disabled."); } #endif #ifndef DISABLE_NETWORK - std::shared_ptr ScNetwork::createSocket() + JSValue ScNetwork::createSocket(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin(); - auto socket = std::make_shared(plugin); - scriptEngine.AddSocket(socket); - return socket; + return gScSocket.New(ctx, GetContext()->GetScriptEngine().GetExecInfo().GetCurrentPlugin()); } #else - void ScNetwork::createSocket() + JSValue ScNetwork::createSocket(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - duk_error(_context, DUK_ERR_ERROR, "Networking has been disabled."); + return JS_ThrowPlainError(ctx, "Networking has been disabled."); } #endif - void ScNetwork::Register(duk_context* ctx) + JSValue ScNetwork::New(JSContext* ctx) { - dukglue_register_property(ctx, &ScNetwork::mode_get, nullptr, "mode"); - dukglue_register_property(ctx, &ScNetwork::numGroups_get, nullptr, "numGroups"); - dukglue_register_property(ctx, &ScNetwork::numPlayers_get, nullptr, "numPlayers"); - dukglue_register_property(ctx, &ScNetwork::groups_get, nullptr, "groups"); - dukglue_register_property(ctx, &ScNetwork::players_get, nullptr, "players"); - dukglue_register_property(ctx, &ScNetwork::currentPlayer_get, nullptr, "currentPlayer"); - dukglue_register_property(ctx, &ScNetwork::defaultGroup_get, &ScNetwork::defaultGroup_set, "defaultGroup"); - dukglue_register_property(ctx, &ScNetwork::stats_get, nullptr, "stats"); - dukglue_register_method(ctx, &ScNetwork::addGroup, "addGroup"); - dukglue_register_method(ctx, &ScNetwork::getGroup, "getGroup"); - dukglue_register_method(ctx, &ScNetwork::removeGroup, "removeGroup"); - dukglue_register_method(ctx, &ScNetwork::getPlayer, "getPlayer"); - dukglue_register_method(ctx, &ScNetwork::kickPlayer, "kickPlayer"); - dukglue_register_method(ctx, &ScNetwork::sendMessage, "sendMessage"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("mode", ScNetwork::mode_get, nullptr), + JS_CGETSET_DEF("numGroups", ScNetwork::numGroups_get, nullptr), + JS_CGETSET_DEF("numPlayers", ScNetwork::numPlayers_get, nullptr), + JS_CGETSET_DEF("groups", ScNetwork::groups_get, nullptr), + JS_CGETSET_DEF("players", ScNetwork::players_get, nullptr), + JS_CGETSET_DEF("currentPlayer", ScNetwork::currentPlayer_get, nullptr), + JS_CGETSET_DEF("defaultGroup", ScNetwork::defaultGroup_get, ScNetwork::defaultGroup_set), + JS_CGETSET_DEF("stats", ScNetwork::stats_get, nullptr), - dukglue_register_method(ctx, &ScNetwork::createListener, "createListener"); - dukglue_register_method(ctx, &ScNetwork::createSocket, "createSocket"); + JS_CFUNC_DEF("addGroup", 0, ScNetwork::addGroup), + JS_CFUNC_DEF("getGroup", 1, ScNetwork::getGroup), + JS_CFUNC_DEF("removeGroup", 1, ScNetwork::removeGroup), + JS_CFUNC_DEF("getPlayer", 1, ScNetwork::getPlayer), + JS_CFUNC_DEF("kickPlayer", 1, ScNetwork::kickPlayer), + JS_CFUNC_DEF("sendMessage", 2, ScNetwork::sendMessage), + + JS_CFUNC_DEF("createListener", 0, ScNetwork::createListener), + JS_CFUNC_DEF("createSocket", 0, ScNetwork::createSocket) + }; + + return MakeWithOpaque(ctx, funcs, nullptr); } } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/network/ScNetwork.hpp b/src/openrct2/scripting/bindings/network/ScNetwork.hpp index b16c45a82e..236ef49684 100644 --- a/src/openrct2/scripting/bindings/network/ScNetwork.hpp +++ b/src/openrct2/scripting/bindings/network/ScNetwork.hpp @@ -11,7 +11,7 @@ #ifdef ENABLE_SCRIPTING - #include "../../Duktape.hpp" + #include "../../ScriptEngine.h" #include "ScPlayer.hpp" #include "ScPlayerGroup.hpp" #include "ScSocket.hpp" @@ -20,57 +20,48 @@ namespace OpenRCT2::Scripting { - class ScNetwork + class ScNetwork final : public ScBase { - private: - #ifdef __clang__ - [[maybe_unused]] - #endif - duk_context* _context; + static JSValue mode_get(JSContext* ctx, JSValue thisVal); + static JSValue numPlayers_get(JSContext* ctx, JSValue thisVal); + static JSValue numGroups_get(JSContext* ctx, JSValue thisVal); + static JSValue defaultGroup_get(JSContext* ctx, JSValue thisVal); + static JSValue defaultGroup_set(JSContext* ctx, JSValue thisVal, JSValue value); + + static JSValue groups_get(JSContext* ctx, JSValue thisVal); + + static JSValue players_get(JSContext* ctx, JSValue thisVal); + + static JSValue currentPlayer_get(JSContext* ctx, JSValue thisVal); + + static JSValue getPlayer(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + + static JSValue stats_get(JSContext* ctx, JSValue thisVal); + + static JSValue getGroup(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + + static JSValue addGroup(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + + static JSValue removeGroup(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + + static JSValue kickPlayer(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + + static JSValue sendMessage(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + + static JSValue createListener(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + + static JSValue createSocket(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); public: - ScNetwork(duk_context* ctx); + JSValue New(JSContext* ctx); - std::string mode_get() const; - int32_t numPlayers_get() const; - int32_t numGroups_get() const; - int32_t defaultGroup_get() const; - void defaultGroup_set(int32_t value); - - std::vector> groups_get() const; - - std::vector> players_get() const; - - std::shared_ptr currentPlayer_get() const; - - std::shared_ptr getPlayer(int32_t id) const; - - DukValue stats_get() const; - - std::shared_ptr getGroup(int32_t id) const; - - void addGroup(); - - void removeGroup(int32_t id); - - void kickPlayer(int32_t id); - - void sendMessage(std::string message, DukValue players); - - #ifndef DISABLE_NETWORK - std::shared_ptr createListener(); - #else - void createListener(); - #endif - - #ifndef DISABLE_NETWORK - std::shared_ptr createSocket(); - #else - void createSocket(); - #endif - - static void Register(duk_context* ctx); + void Register(JSContext* ctx) + { + RegisterBaseStr(ctx, "Network"); + } }; + + extern ScNetwork gScNetwork; } // namespace OpenRCT2::Scripting #endif diff --git a/src/openrct2/scripting/bindings/network/ScPlayer.cpp b/src/openrct2/scripting/bindings/network/ScPlayer.cpp index d74d00006c..b5bc13fd8e 100644 --- a/src/openrct2/scripting/bindings/network/ScPlayer.cpp +++ b/src/openrct2/scripting/bindings/network/ScPlayer.cpp @@ -20,103 +20,128 @@ namespace OpenRCT2::Scripting { - ScPlayer::ScPlayer(int32_t id) - : _id(id) + using OpaquePlayerData = struct { + int32_t id; + }; + + int32_t ScPlayer::GetPlayerId(JSValue thisVal) + { + OpaquePlayerData* data = gScPlayer.GetOpaque(thisVal); + if (data) + return data->id; + return -1; } - int32_t ScPlayer::id_get() const + JSValue ScPlayer::id_get(JSContext* ctx, JSValue thisVal) { - return _id; + return JS_NewInt32(ctx, GetPlayerId(thisVal)); } - std::string ScPlayer::name_get() const + JSValue ScPlayer::name_get(JSContext* ctx, JSValue thisVal) { #ifndef DISABLE_NETWORK - auto index = Network::GetPlayerIndex(_id); + auto index = Network::GetPlayerIndex(GetPlayerId(thisVal)); if (index == -1) - return {}; - return Network::GetPlayerName(index); + return JSFromStdString(ctx, {}); + return JSFromStdString(ctx, Network::GetPlayerName(index)); #else - return {}; + return JSFromStdString(ctx, {}); #endif } - int32_t ScPlayer::group_get() const + JSValue ScPlayer::group_get(JSContext* ctx, JSValue thisVal) { #ifndef DISABLE_NETWORK - auto index = Network::GetPlayerIndex(_id); + auto index = Network::GetPlayerIndex(GetPlayerId(thisVal)); if (index == -1) - return {}; - return Network::GetPlayerGroup(index); + return JS_NewInt32(ctx, {}); + return JS_NewInt32(ctx, Network::GetPlayerGroup(index)); #else - return 0; + return JS_NewInt32(ctx, 0); #endif } - void ScPlayer::group_set(int32_t value) + JSValue ScPlayer::group_set(JSContext* ctx, JSValue thisVal, JSValue value) { #ifndef DISABLE_NETWORK - auto playerSetGroupAction = GameActions::PlayerSetGroupAction(_id, value); + JS_UNPACK_INT32(valueInt, ctx, value) + auto playerSetGroupAction = GameActions::PlayerSetGroupAction(GetPlayerId(thisVal), valueInt); GameActions::Execute(&playerSetGroupAction, getGameState()); #endif + return JS_UNDEFINED; } - int32_t ScPlayer::ping_get() const + JSValue ScPlayer::ping_get(JSContext* ctx, JSValue thisVal) { #ifndef DISABLE_NETWORK - auto index = Network::GetPlayerIndex(_id); + auto index = Network::GetPlayerIndex(GetPlayerId(thisVal)); if (index == -1) - return {}; - return Network::GetPlayerPing(index); + return JS_NewInt32(ctx, {}); + return JS_NewInt32(ctx, Network::GetPlayerPing(index)); #else - return 0; + return JS_NewInt32(ctx, 0); #endif } - int32_t ScPlayer::commandsRan_get() const + JSValue ScPlayer::commandsRan_get(JSContext* ctx, JSValue thisVal) { #ifndef DISABLE_NETWORK - auto index = Network::GetPlayerIndex(_id); + auto index = Network::GetPlayerIndex(GetPlayerId(thisVal)); if (index == -1) - return {}; - return Network::GetPlayerCommandsRan(index); + return JS_NewInt32(ctx, {}); + return JS_NewInt32(ctx, Network::GetPlayerCommandsRan(index)); #else - return 0; + return JS_NewInt32(ctx, 0); #endif } - int32_t ScPlayer::moneySpent_get() const + JSValue ScPlayer::moneySpent_get(JSContext* ctx, JSValue thisVal) { #ifndef DISABLE_NETWORK - auto index = Network::GetPlayerIndex(_id); + auto index = Network::GetPlayerIndex(GetPlayerId(thisVal)); if (index == -1) - return {}; - return Network::GetPlayerMoneySpent(index); + return JS_NewInt64(ctx, {}); + return JS_NewInt64(ctx, Network::GetPlayerMoneySpent(index)); #else - return 0; + return JS_NewInt64(ctx, 0); #endif } - std::string ScPlayer::ipAddress_get() const + JSValue ScPlayer::ipAddress_get(JSContext* ctx, JSValue thisVal) { - return Network::GetPlayerIPAddress(_id); + return JSFromStdString(ctx, Network::GetPlayerIPAddress(GetPlayerId(thisVal))); } - std::string ScPlayer::publicKeyHash_get() const + JSValue ScPlayer::publicKeyHash_get(JSContext* ctx, JSValue thisVal) { - return Network::GetPlayerPublicKeyHash(_id); + return JSFromStdString(ctx, Network::GetPlayerPublicKeyHash(GetPlayerId(thisVal))); } - void ScPlayer::Register(duk_context* ctx) + JSValue ScPlayer::New(JSContext* ctx, int32_t id) { - dukglue_register_property(ctx, &ScPlayer::id_get, nullptr, "id"); - dukglue_register_property(ctx, &ScPlayer::name_get, nullptr, "name"); - dukglue_register_property(ctx, &ScPlayer::group_get, &ScPlayer::group_set, "group"); - dukglue_register_property(ctx, &ScPlayer::ping_get, nullptr, "ping"); - dukglue_register_property(ctx, &ScPlayer::commandsRan_get, nullptr, "commandsRan"); - dukglue_register_property(ctx, &ScPlayer::moneySpent_get, nullptr, "moneySpent"); - dukglue_register_property(ctx, &ScPlayer::ipAddress_get, nullptr, "ipAddress"); - dukglue_register_property(ctx, &ScPlayer::publicKeyHash_get, nullptr, "publicKeyHash"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("id", ScPlayer::id_get, nullptr), + JS_CGETSET_DEF("name", ScPlayer::name_get, nullptr), + JS_CGETSET_DEF("group", ScPlayer::group_get, ScPlayer::group_set), + JS_CGETSET_DEF("ping", ScPlayer::ping_get, nullptr), + JS_CGETSET_DEF("commandsRan", ScPlayer::commandsRan_get, nullptr), + JS_CGETSET_DEF("moneySpent", ScPlayer::moneySpent_get, nullptr), + JS_CGETSET_DEF("ipAddress", ScPlayer::ipAddress_get, nullptr), + JS_CGETSET_DEF("publicKeyHash", ScPlayer::publicKeyHash_get, nullptr), + }; + return MakeWithOpaque(ctx, funcs, new OpaquePlayerData{ id }); + } + + void ScPlayer::Register(JSContext* ctx) + { + RegisterBaseStr(ctx, "Player", Finalize); + } + + void ScPlayer::Finalize(JSRuntime* rt, JSValue thisVal) + { + OpaquePlayerData* data = gScPlayer.GetOpaque(thisVal); + if (data) + delete data; } } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/network/ScPlayer.hpp b/src/openrct2/scripting/bindings/network/ScPlayer.hpp index d9ab47e5b0..a5ff34e2c9 100644 --- a/src/openrct2/scripting/bindings/network/ScPlayer.hpp +++ b/src/openrct2/scripting/bindings/network/ScPlayer.hpp @@ -11,38 +11,43 @@ #ifdef ENABLE_SCRIPTING - #include "../../Duktape.hpp" - - #include + #include "../../ScriptEngine.h" + #include "quickjs.h" namespace OpenRCT2::Scripting { - class ScPlayer + class ScPlayer; + extern ScPlayer gScPlayer; + + class ScPlayer : public ScBase { private: - int32_t _id; + static int32_t GetPlayerId(JSValue thisVal); + + static JSValue id_get(JSContext* ctx, JSValue thisVal); + + static JSValue name_get(JSContext* ctx, JSValue thisVal); + + static JSValue group_get(JSContext* ctx, JSValue thisVal); + static JSValue group_set(JSContext* ctx, JSValue thisVal, JSValue value); + + static JSValue ping_get(JSContext* ctx, JSValue thisVal); + + static JSValue commandsRan_get(JSContext* ctx, JSValue thisVal); + + static JSValue moneySpent_get(JSContext* ctx, JSValue thisVal); + + static JSValue ipAddress_get(JSContext* ctx, JSValue thisVal); + + static JSValue publicKeyHash_get(JSContext* ctx, JSValue thisVal); public: - ScPlayer(int32_t id); + JSValue New(JSContext* ctx, int32_t id); - int32_t id_get() const; + void Register(JSContext* ctx); - std::string name_get() const; - - int32_t group_get() const; - void group_set(int32_t value); - - int32_t ping_get() const; - - int32_t commandsRan_get() const; - - int32_t moneySpent_get() const; - - std::string ipAddress_get() const; - - std::string publicKeyHash_get() const; - - static void Register(duk_context* ctx); + private: + static void Finalize(JSRuntime* rt, JSValue thisVal); }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/network/ScPlayerGroup.cpp b/src/openrct2/scripting/bindings/network/ScPlayerGroup.cpp index 67aa257e0e..9dbf39239d 100644 --- a/src/openrct2/scripting/bindings/network/ScPlayerGroup.cpp +++ b/src/openrct2/scripting/bindings/network/ScPlayerGroup.cpp @@ -19,98 +19,112 @@ #include "../../../core/String.hpp" #include "../../../network/Network.h" #include "../../../network/NetworkAction.h" - #include "../../Duktape.hpp" namespace OpenRCT2::Scripting { - ScPlayerGroup::ScPlayerGroup(int32_t id) - : _id(id) + using OpaquePlayerGroupData = struct { + int32_t id; + }; + + int32_t ScPlayerGroup::GetGroupId(JSValue thisVal) + { + OpaquePlayerGroupData* data = gScPlayerGroup.GetOpaque(thisVal); + if (data) + return data->id; + return -1; } - int32_t ScPlayerGroup::id_get() + JSValue ScPlayerGroup::id_get(JSContext* ctx, JSValue thisVal) { - return _id; + return JS_NewInt32(ctx, GetGroupId(thisVal)); } - std::string ScPlayerGroup::name_get() const + JSValue ScPlayerGroup::name_get(JSContext* ctx, JSValue thisVal) { #ifndef DISABLE_NETWORK - auto index = Network::GetGroupIndex(_id); + auto index = Network::GetGroupIndex(GetGroupId(thisVal)); if (index == -1) - return {}; - return Network::GetGroupName(index); + return JSFromStdString(ctx, {}); + return JSFromStdString(ctx, Network::GetGroupName(index)); #else - return {}; + return JSFromStdString(ctx, {}); #endif } - void ScPlayerGroup::name_set(std::string value) + JSValue ScPlayerGroup::name_set(JSContext* ctx, JSValue thisVal, JSValue value) { #ifndef DISABLE_NETWORK - auto action = GameActions::NetworkModifyGroupAction(GameActions::ModifyGroupType::SetName, _id, value); + JS_UNPACK_STR(valueStr, ctx, value); + auto action = GameActions::NetworkModifyGroupAction( + GameActions::ModifyGroupType::SetName, GetGroupId(thisVal), valueStr); GameActions::Execute(&action, getGameState()); #endif + return JS_UNDEFINED; } #ifndef DISABLE_NETWORK - static std::string TransformPermissionKeyToJS(const std::string& s) + static JSValue TransformPermissionKeyToJS(JSContext* ctx, const std::string& s) { auto result = s.substr(sizeof("PERMISSION_") - 1); for (auto& c : result) { c = std::tolower(static_cast(c)); } - return result; + return JSFromStdString(ctx, result); } - static std::string TransformPermissionKeyToInternal(const std::string& s) + static std::string TransformPermissionKeyToInternal(JSContext* ctx, JSValue s) { - return "PERMISSION_" + String::toUpper(s); + if (JS_IsString(s)) + return "PERMISSION_" + String::toUpper(JSToStdString(ctx, s)); + return std::string(); } #endif - std::vector ScPlayerGroup::permissions_get() const + JSValue ScPlayerGroup::permissions_get(JSContext* ctx, JSValue thisVal) { - #ifndef DISABLE_NETWORK - auto index = Network::GetGroupIndex(_id); - if (index == -1) - return {}; - // Create array of permissions - std::vector result; + JSValue result = JS_NewArray(ctx); + + #ifndef DISABLE_NETWORK + auto index = Network::GetGroupIndex(GetGroupId(thisVal)); + if (index == -1) + return result; + auto permissionIndex = 0; + int64_t resultIdx = 0; for (const auto& action : Network::NetworkActions::Actions) { if (Network::CanPerformAction(index, static_cast(permissionIndex))) { - result.push_back(TransformPermissionKeyToJS(action.PermissionName)); + JS_SetPropertyInt64(ctx, result, resultIdx++, TransformPermissionKeyToJS(ctx, action.PermissionName)); } permissionIndex++; } - return result; - #else - return {}; #endif + return result; } - void ScPlayerGroup::permissions_set(std::vector value) + JSValue ScPlayerGroup::permissions_set(JSContext* ctx, JSValue thisVal, JSValue value) { #ifndef DISABLE_NETWORK - auto groupIndex = Network::GetGroupIndex(_id); + JS_UNPACK_ARRAY(array, ctx, value); + + int32_t id = GetGroupId(thisVal); + auto groupIndex = Network::GetGroupIndex(id); if (groupIndex == -1) - return; + return JS_UNDEFINED; // First clear all permissions auto networkAction = GameActions::NetworkModifyGroupAction( - GameActions::ModifyGroupType::SetPermissions, _id, "", 0, GameActions::PermissionState::ClearAll); + GameActions::ModifyGroupType::SetPermissions, id, "", 0, GameActions::PermissionState::ClearAll); GameActions::Execute(&networkAction, getGameState()); - std::vector enabledPermissions; - enabledPermissions.resize(Network::NetworkActions::Actions.size()); - for (const auto& p : value) - { - auto permissionName = TransformPermissionKeyToInternal(p); + // Don't use vector since the weird bitpacking specialisation does not work with the lambda (on some compilers) + std::vector enabledPermissions(Network::NetworkActions::Actions.size()); + JSIterateArray(ctx, array, [&enabledPermissions](JSContext* ctx2, JSValue x) { + auto permissionName = TransformPermissionKeyToInternal(ctx2, x); auto permissionIndex = 0; for (const auto& action : Network::NetworkActions::Actions) @@ -121,7 +135,7 @@ namespace OpenRCT2::Scripting } permissionIndex++; } - } + }); for (size_t i = 0; i < enabledPermissions.size(); i++) { @@ -130,19 +144,35 @@ namespace OpenRCT2::Scripting if (toggle) { auto networkAction2 = GameActions::NetworkModifyGroupAction( - GameActions::ModifyGroupType::SetPermissions, _id, "", static_cast(i), + GameActions::ModifyGroupType::SetPermissions, id, "", static_cast(i), GameActions::PermissionState::Toggle); GameActions::Execute(&networkAction2, getGameState()); } } #endif + return JS_UNDEFINED; } - void ScPlayerGroup::Register(duk_context* ctx) + JSValue ScPlayerGroup::New(JSContext* ctx, int32_t id) { - dukglue_register_property(ctx, &ScPlayerGroup::id_get, nullptr, "id"); - dukglue_register_property(ctx, &ScPlayerGroup::name_get, &ScPlayerGroup::name_set, "name"); - dukglue_register_property(ctx, &ScPlayerGroup::permissions_get, &ScPlayerGroup::permissions_set, "permissions"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("id", ScPlayerGroup::id_get, nullptr), + JS_CGETSET_DEF("name", ScPlayerGroup::name_get, ScPlayerGroup::name_set), + JS_CGETSET_DEF("permissions", ScPlayerGroup::permissions_get, ScPlayerGroup::permissions_set), + }; + return MakeWithOpaque(ctx, funcs, new OpaquePlayerGroupData{ id }); + } + + void ScPlayerGroup::Register(JSContext* ctx) + { + RegisterBaseStr(ctx, "PlayerGroup", Finalize); + } + + void ScPlayerGroup::Finalize(JSRuntime* rt, JSValue thisVal) + { + OpaquePlayerGroupData* data = gScPlayerGroup.GetOpaque(thisVal); + if (data) + delete data; } } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/network/ScPlayerGroup.hpp b/src/openrct2/scripting/bindings/network/ScPlayerGroup.hpp index ea1ac89580..12d4e362d6 100644 --- a/src/openrct2/scripting/bindings/network/ScPlayerGroup.hpp +++ b/src/openrct2/scripting/bindings/network/ScPlayerGroup.hpp @@ -11,29 +11,33 @@ #ifdef ENABLE_SCRIPTING - #include "../../Duktape.hpp" - - #include - #include + #include "../../ScriptEngine.h" namespace OpenRCT2::Scripting { - class ScPlayerGroup + class ScPlayerGroup; + extern ScPlayerGroup gScPlayerGroup; + + class ScPlayerGroup : public ScBase { - int32_t _id; + private: + static int32_t GetGroupId(JSValue thisVal); + + static JSValue id_get(JSContext* ctx, JSValue thisVal); + + static JSValue name_get(JSContext* ctx, JSValue thisVal); + static JSValue name_set(JSContext* ctx, JSValue thisVal, JSValue value); + + static JSValue permissions_get(JSContext* ctx, JSValue thisVal); + static JSValue permissions_set(JSContext* ctx, JSValue thisVal, JSValue value); public: - ScPlayerGroup(int32_t id); + JSValue New(JSContext* ctx, int32_t id); - int32_t id_get(); + void Register(JSContext* ctx); - std::string name_get() const; - void name_set(std::string value); - - std::vector permissions_get() const; - void permissions_set(std::vector value); - - static void Register(duk_context* ctx); + private: + static void Finalize(JSRuntime* rt, JSValue thisVal); }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/network/ScSocket.hpp b/src/openrct2/scripting/bindings/network/ScSocket.hpp index a1066966c1..17f65014ef 100644 --- a/src/openrct2/scripting/bindings/network/ScSocket.hpp +++ b/src/openrct2/scripting/bindings/network/ScSocket.hpp @@ -15,7 +15,6 @@ #include "../../../Context.h" #include "../../../config/Config.h" #include "../../../network/Socket.h" - #include "../../Duktape.hpp" #include "../../ScriptEngine.h" #include @@ -26,9 +25,9 @@ namespace OpenRCT2::Scripting class EventList { private: - std::vector> _listeners; + std::vector> _listeners; - std::vector& GetListenerList(uint32_t id) + std::vector& GetListenerList(uint32_t id) { if (_listeners.size() <= id) { @@ -39,7 +38,7 @@ namespace OpenRCT2::Scripting public: void Raise( - uint32_t id, const std::shared_ptr& plugin, const std::vector& args, bool isGameStateMutable) + uint32_t id, const std::shared_ptr& plugin, const std::vector& args, bool isGameStateMutable) { auto& scriptEngine = GetContext()->GetScriptEngine(); @@ -47,23 +46,26 @@ namespace OpenRCT2::Scripting auto listeners = GetListenerList(id); for (size_t i = 0; i < listeners.size(); i++) { - scriptEngine.ExecutePluginCall(plugin, listeners[i], args, isGameStateMutable); + scriptEngine.ExecutePluginCall(plugin, listeners[i].callback, args, isGameStateMutable); // Safety, listeners might get reallocated listeners = GetListenerList(id); } } - void AddListener(uint32_t id, const DukValue& listener) + void AddListener(uint32_t id, const JSCallback& listener) { auto& listeners = GetListenerList(id); listeners.push_back(listener); } - void RemoveListener(uint32_t id, const DukValue& value) + void RemoveListener(uint32_t id, const JSCallback& value) { auto& listeners = GetListenerList(id); - listeners.erase(std::remove(listeners.begin(), listeners.end(), value), listeners.end()); + std::erase_if(listeners, [&value](const JSCallback& x) { + // Note: this might be hacky because I am not sure if/when quickjs will keep these function ptrs stable. + return JS_VALUE_GET_PTR(value.callback) == JS_VALUE_GET_PTR(x.callback); + }); } void RemoveAllListeners(uint32_t id) @@ -71,204 +73,61 @@ namespace OpenRCT2::Scripting auto& listeners = GetListenerList(id); listeners.clear(); } + + void RemoveAllListeners() + { + _listeners.clear(); + } }; - class ScSocketBase + inline bool IsLocalhostAddress(std::string_view s) { - private: - std::shared_ptr _plugin; + return s == "localhost" || s == "127.0.0.1" || s == "::"; + } - protected: - static bool IsLocalhostAddress(std::string_view s) + inline bool IsOnWhiteList(std::string_view host) + { + constexpr char delimiter = ','; + size_t start_pos = 0; + size_t end_pos = 0; + while ((end_pos = Config::Get().plugin.allowedHosts.find(delimiter, start_pos)) != std::string::npos) { - return s == "localhost" || s == "127.0.0.1" || s == "::"; - } - - static bool IsOnWhiteList(std::string_view host) - { - constexpr char delimiter = ','; - size_t start_pos = 0; - size_t end_pos = 0; - while ((end_pos = Config::Get().plugin.allowedHosts.find(delimiter, start_pos)) != std::string::npos) + if (host == Config::Get().plugin.allowedHosts.substr(start_pos, end_pos - start_pos)) { - if (host == Config::Get().plugin.allowedHosts.substr(start_pos, end_pos - start_pos)) - { - return true; - } - start_pos = end_pos + 1; + return true; } - return host - == Config::Get().plugin.allowedHosts.substr(start_pos, Config::Get().plugin.allowedHosts.length() - start_pos); + start_pos = end_pos + 1; } + return host + == Config::Get().plugin.allowedHosts.substr(start_pos, Config::Get().plugin.allowedHosts.length() - start_pos); + } - public: - ScSocketBase(const std::shared_ptr& plugin) - : _plugin(plugin) - { - } - - virtual ~ScSocketBase() - { - Dispose(); - } - - const std::shared_ptr& GetPlugin() const - { - return _plugin; - } + struct SocketDataBase + { + EventList _eventList; + std::unique_ptr _socket; + std::shared_ptr _plugin; + bool _disposed{}; + virtual ~SocketDataBase() = default; virtual void Update() = 0; - - virtual void Dispose() - { - } - - virtual bool IsDisposed() const = 0; + virtual void Dispose() = 0; }; - class ScSocket final : public ScSocketBase + struct SocketData final : SocketDataBase { - private: static constexpr uint32_t EVENT_NONE = std::numeric_limits::max(); static constexpr uint32_t EVENT_CLOSE = 0; static constexpr uint32_t EVENT_DATA = 1; static constexpr uint32_t EVENT_CONNECT_ONCE = 2; static constexpr uint32_t EVENT_ERROR = 3; - EventList _eventList; - std::unique_ptr _socket; - bool _disposed{}; bool _connecting{}; bool _wasConnected{}; - public: - ScSocket(const std::shared_ptr& plugin) - : ScSocketBase(plugin) + ~SocketData() override { - } - - ScSocket(const std::shared_ptr& plugin, std::unique_ptr&& socket) - : ScSocketBase(plugin) - , _socket(std::move(socket)) - { - } - - private: - ScSocket* destroy(const DukValue& error) - { - CloseSocket(); - return this; - } - - ScSocket* setNoDelay(bool noDelay) - { - if (_socket != nullptr) - { - _socket->SetNoDelay(noDelay); - } - return this; - } - - ScSocket* connect(uint16_t port, const std::string& host, const DukValue& callback) - { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - if (_socket != nullptr) - { - duk_error(ctx, DUK_ERR_ERROR, "Socket has already been created."); - } - else if (_disposed) - { - duk_error(ctx, DUK_ERR_ERROR, "Socket is disposed."); - } - else if (_connecting) - { - duk_error(ctx, DUK_ERR_ERROR, "Socket is already connecting."); - } - else if (!IsLocalhostAddress(host) && !IsOnWhiteList(host)) - { - duk_error(ctx, DUK_ERR_ERROR, "For security reasons, only connecting to localhost is allowed."); - } - else - { - _socket = Network::CreateTcpSocket(); - try - { - _socket->ConnectAsync(host, port); - _eventList.AddListener(EVENT_CONNECT_ONCE, callback); - _connecting = true; - } - catch (const std::exception& e) - { - duk_error(ctx, DUK_ERR_ERROR, e.what()); - } - } - return this; - } - - ScSocket* end(const DukValue& data) - { - if (_disposed) - { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - duk_error(ctx, DUK_ERR_ERROR, "Socket is disposed."); - } - else if (_socket != nullptr) - { - if (data.type() == DukValue::Type::STRING) - { - write(data.as_string()); - _socket->Finish(); - } - else - { - _socket->Finish(); - auto ctx = GetContext()->GetScriptEngine().GetContext(); - duk_error(ctx, DUK_ERR_ERROR, "Only sending strings is currently supported."); - } - } - return this; - } - - bool write(const std::string& data) - { - if (_disposed) - { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - duk_error(ctx, DUK_ERR_ERROR, "Socket is disposed."); - } - else if (_socket != nullptr) - { - try - { - auto sentBytes = _socket->SendData(data.c_str(), data.size()); - return sentBytes != data.size(); - } - catch (const std::exception&) - { - return false; - } - } - return false; - } - - ScSocket* on(const std::string& eventType, const DukValue& callback) - { - auto eventId = GetEventType(eventType); - if (eventId != EVENT_NONE) - { - _eventList.AddListener(eventId, callback); - } - return this; - } - - ScSocket* off(const std::string& eventType, const DukValue& callback) - { - auto eventId = GetEventType(eventType); - if (eventId != EVENT_NONE) - { - _eventList.RemoveListener(eventId, callback); - } - return this; + SocketData::Dispose(); } void CloseSocket() @@ -283,22 +142,22 @@ namespace OpenRCT2::Scripting RaiseOnClose(false); } } + _eventList.RemoveAllListeners(); } void RaiseOnClose(bool hadError) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - _eventList.Raise(EVENT_CLOSE, GetPlugin(), { ToDuk(ctx, hadError) }, false); + JSContext* ctx = GetContext()->GetScriptEngine().GetContext(); + _eventList.Raise(EVENT_CLOSE, _plugin, { JS_NewBool(ctx, hadError) }, false); } - void RaiseOnData(const std::string& data) + void RaiseOnData(std::string_view data) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto ctx = scriptEngine.GetContext(); - _eventList.Raise(EVENT_DATA, GetPlugin(), { ToDuk(ctx, data) }, false); + JSContext* ctx = GetContext()->GetScriptEngine().GetContext(); + _eventList.Raise(EVENT_DATA, _plugin, { JSFromStdString(ctx, data) }, false); } - uint32_t GetEventType(std::string_view name) + static uint32_t GetEventType(std::string_view name) { if (name == "close") return EVENT_CLOSE; @@ -309,7 +168,6 @@ namespace OpenRCT2::Scripting return EVENT_NONE; } - public: void Update() override { if (_disposed) @@ -324,22 +182,21 @@ namespace OpenRCT2::Scripting { _connecting = false; _wasConnected = true; - _eventList.Raise(EVENT_CONNECT_ONCE, GetPlugin(), {}, false); + _eventList.Raise(EVENT_CONNECT_ONCE, _plugin, {}, false); _eventList.RemoveAllListeners(EVENT_CONNECT_ONCE); } else if (status == Network::SocketStatus::closed) { _connecting = false; - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto ctx = scriptEngine.GetContext(); + JSContext* ctx = GetContext()->GetScriptEngine().GetContext(); auto err = _socket->GetError(); if (err == nullptr) { err = ""; } - auto dukErr = ToDuk(ctx, std::string_view(err)); - _eventList.Raise(EVENT_ERROR, GetPlugin(), { dukErr }, true); + auto jsErr = JSFromStdString(ctx, std::string_view(err)); + _eventList.Raise(EVENT_ERROR, _plugin, { jsErr }, true); } } else if (status == Network::SocketStatus::connected) @@ -350,7 +207,7 @@ namespace OpenRCT2::Scripting switch (result) { case Network::ReadPacket::success: - RaiseOnData(std::string(buffer, bytesRead)); + RaiseOnData(std::string_view(buffer, bytesRead)); break; case Network::ReadPacket::noData: break; @@ -373,129 +230,235 @@ namespace OpenRCT2::Scripting CloseSocket(); _disposed = true; } - - bool IsDisposed() const override - { - return _disposed; - } - - static void Register(duk_context* ctx) - { - dukglue_register_method(ctx, &ScSocket::destroy, "destroy"); - dukglue_register_method(ctx, &ScSocket::setNoDelay, "setNoDelay"); - dukglue_register_method(ctx, &ScSocket::connect, "connect"); - dukglue_register_method(ctx, &ScSocket::end, "end"); - dukglue_register_method(ctx, &ScSocket::write, "write"); - dukglue_register_method(ctx, &ScSocket::on, "on"); - dukglue_register_method(ctx, &ScSocket::off, "off"); - } }; - class ScListener final : public ScSocketBase + class ScSocket; + extern ScSocket gScSocket; + class ScSocket final : public ScBase { private: - static constexpr uint32_t EVENT_NONE = std::numeric_limits::max(); - static constexpr uint32_t EVENT_CONNECTION = 0; - - EventList _eventList; - std::unique_ptr _socket; - std::vector> _scClientSockets; - bool _disposed{}; - - bool listening_get() + static SocketData* GetData(JSValue thisVal) { - if (_socket != nullptr) + return gScSocket.GetOpaque(thisVal); + } + + static JSValue destroy(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) + { + auto data = GetData(thisVal); + if (!data) + return JS_ThrowInternalError(ctx, "Invalid socket"); + + data->CloseSocket(); + return JS_DupValue(ctx, thisVal); + } + + static JSValue setNoDelay(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) + { + JS_UNPACK_BOOL(noDelay, ctx, argv[0]); + + auto data = GetData(thisVal); + if (!data) + return JS_ThrowInternalError(ctx, "Invalid socket"); + + if (data->_socket != nullptr) { - return _socket->GetStatus() == Network::SocketStatus::listening; + data->_socket->SetNoDelay(noDelay); } - return false; + return JS_DupValue(ctx, thisVal); } - ScListener* close() + static JSValue connect(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - CloseSocket(); - return this; - } + JS_UNPACK_UINT32(port, ctx, argv[0]); + if (port & 0xFFFF0000) + return JS_ThrowRangeError(ctx, "Invalid port."); + JS_UNPACK_STR(host, ctx, argv[1]); - ScListener* listen(int32_t port, const DukValue& dukHost) - { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - if (_disposed) + auto data = GetData(thisVal); + if (!data) + return JS_ThrowInternalError(ctx, "Invalid socket"); + + if (data->_socket != nullptr) { - duk_error(ctx, DUK_ERR_ERROR, "Socket is disposed."); + JS_ThrowPlainError(ctx, "Socket has already been created."); + } + else if (data->_disposed) + { + JS_ThrowPlainError(ctx, "Socket is disposed."); + } + else if (data->_connecting) + { + JS_ThrowPlainError(ctx, "Socket is already connecting."); + } + else if (!IsLocalhostAddress(host) && !IsOnWhiteList(host)) + { + JS_ThrowPlainError(ctx, "For security reasons, only connecting to localhost is allowed."); } else { - if (_socket == nullptr) + data->_socket = Network::CreateTcpSocket(); + try { - _socket = Network::CreateTcpSocket(); + data->_socket->ConnectAsync(host, port); + if (JS_IsFunction(ctx, argv[2])) + { + data->_eventList.AddListener(SocketData::EVENT_CONNECT_ONCE, JSCallback(ctx, argv[2])); + } + data->_connecting = true; } - - if (_socket->GetStatus() == Network::SocketStatus::listening) + catch (const std::exception& e) { - duk_error(ctx, DUK_ERR_ERROR, "Server is already listening."); + JS_ThrowInternalError(ctx, "%s", e.what()); + } + } + return JS_DupValue(ctx, thisVal); + } + + static JSValue end(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) + { + auto data = GetData(thisVal); + if (!data) + return JS_ThrowInternalError(ctx, "Invalid socket"); + + if (data->_disposed) + { + JS_ThrowPlainError(ctx, "Socket is disposed."); + } + else if (data->_socket != nullptr) + { + if (JS_IsString(argv[0])) + { + if (JSValue res = write(ctx, thisVal, argc, argv); JS_IsException(res)) + { + return res; + } + data->_socket->Finish(); } else { - if (dukHost.type() == DukValue::Type::STRING) - { - auto host = dukHost.as_string(); - if (IsLocalhostAddress(host) || IsOnWhiteList(host)) - { - try - { - _socket->Listen(host, port); - } - catch (const std::exception& e) - { - duk_error(ctx, DUK_ERR_ERROR, e.what()); - } - } - else - { - duk_error(ctx, DUK_ERR_ERROR, "For security reasons, only binding to localhost is allowed."); - } - } - else - { - _socket->Listen("127.0.0.1", port); - } + data->_socket->Finish(); + JS_ThrowPlainError(ctx, "Only sending strings is currently supported."); } } - return this; + return JS_DupValue(ctx, thisVal); } - ScListener* on(const std::string& eventType, const DukValue& callback) + static JSValue write(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto eventId = GetEventType(eventType); - if (eventId != EVENT_NONE) + JS_UNPACK_STR(str, ctx, argv[0]); + + auto data = GetData(thisVal); + if (!data) + return JS_ThrowInternalError(ctx, "Invalid socket"); + + if (data->_disposed) { - _eventList.AddListener(eventId, callback); + JS_ThrowPlainError(ctx, "Socket is disposed."); } - return this; - } - - ScListener* off(const std::string& eventType, const DukValue& callback) - { - auto eventId = GetEventType(eventType); - if (eventId != EVENT_NONE) + else if (data->_socket != nullptr) { - _eventList.RemoveListener(eventId, callback); + try + { + auto sentBytes = data->_socket->SendData(str.c_str(), str.size()); + return JS_NewBool(ctx, sentBytes != str.size()); + } + catch (const std::exception&) + { + return JS_NewBool(ctx, false); + } } - return this; + return JS_NewBool(ctx, false); } - uint32_t GetEventType(std::string_view name) + static JSValue on(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - if (name == "connection") - return EVENT_CONNECTION; - return EVENT_NONE; + JS_UNPACK_STR(eventType, ctx, argv[0]); + JS_UNPACK_CALLBACK(callback, ctx, argv[1]); + + auto data = GetData(thisVal); + if (!data) + return JS_ThrowInternalError(ctx, "Invalid socket"); + + auto eventId = SocketData::GetEventType(eventType); + if (eventId != SocketData::EVENT_NONE) + { + data->_eventList.AddListener(eventId, callback); + } + return JS_DupValue(ctx, thisVal); + } + + static JSValue off(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) + { + JS_UNPACK_STR(eventType, ctx, argv[0]); + JS_UNPACK_CALLBACK(callback, ctx, argv[1]); + + auto data = GetData(thisVal); + if (!data) + return JS_ThrowInternalError(ctx, "Invalid socket"); + + auto eventId = SocketData::GetEventType(eventType); + if (eventId != SocketData::EVENT_NONE) + { + data->_eventList.RemoveListener(eventId, callback); + } + return JS_DupValue(ctx, thisVal); } public: - ScListener(const std::shared_ptr& plugin) - : ScSocketBase(plugin) + static constexpr JSCFunctionListEntry funcs[] = { + JS_CFUNC_DEF("destroy", 1, ScSocket::destroy), JS_CFUNC_DEF("setNoDelay", 1, ScSocket::setNoDelay), + JS_CFUNC_DEF("connect", 3, ScSocket::connect), JS_CFUNC_DEF("end", 1, ScSocket::end), + JS_CFUNC_DEF("write", 1, ScSocket::write), JS_CFUNC_DEF("on", 2, ScSocket::on), + JS_CFUNC_DEF("off", 2, ScSocket::off), + }; + + JSValue New(JSContext* ctx, const std::shared_ptr& plugin) { + // unique ptr is used to avoid static analyzer false positives. + auto data = std::make_unique(); + data->_plugin = plugin; + GetContext()->GetScriptEngine().AddSocket(data.get()); + return MakeWithOpaque(ctx, funcs, data.release()); + } + + JSValue New(JSContext* ctx, const std::shared_ptr& plugin, std::unique_ptr&& socket) + { + auto data = std::make_unique(); + data->_plugin = plugin; + data->_socket = std::move(socket); + GetContext()->GetScriptEngine().AddSocket(data.get()); + return MakeWithOpaque(ctx, funcs, data.release()); + } + + void Register(JSContext* ctx) + { + RegisterBaseStr(ctx, "Socket", Finalize); + } + + static void Finalize(JSRuntime* rt, JSValue thisVal) + { + SocketData* data = gScSocket.GetOpaque(thisVal); + if (data) + { + // Note the game context pointer can be null if the game is shutting down, + // but all sockets should have been cleaned up by then. + IContext* gameContext = GetContext(); + assert(gameContext != nullptr); + gameContext->GetScriptEngine().RemoveSocket(data); + data->Dispose(); + delete data; + } + } + }; + + struct ListenerData final : SocketDataBase + { + static constexpr uint32_t EVENT_NONE = std::numeric_limits::max(); + static constexpr uint32_t EVENT_CONNECTION = 0; + + ~ListenerData() override + { + ListenerData::Dispose(); } void Update() override @@ -514,13 +477,9 @@ namespace OpenRCT2::Scripting // Default to using Nagle's algorithm like node.js does client->SetNoDelay(false); - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto clientSocket = std::make_shared(GetPlugin(), std::move(client)); - scriptEngine.AddSocket(clientSocket); - - auto ctx = scriptEngine.GetContext(); - auto dukClientSocket = GetObjectAsDukValue(ctx, clientSocket); - _eventList.Raise(EVENT_CONNECTION, GetPlugin(), { dukClientSocket }, false); + JSContext* ctx = GetContext()->GetScriptEngine().GetContext(); + auto clientSocket = gScSocket.New(ctx, _plugin, std::move(client)); + _eventList.Raise(EVENT_CONNECTION, _plugin, { clientSocket }, false); } } } @@ -532,6 +491,7 @@ namespace OpenRCT2::Scripting _socket->Close(); _socket = nullptr; } + _eventList.RemoveAllListeners(); } void Dispose() override @@ -539,19 +499,172 @@ namespace OpenRCT2::Scripting CloseSocket(); _disposed = true; } + }; - bool IsDisposed() const override + class ScListener; + extern ScListener gScListener; + class ScListener final : public ScBase + { + static ListenerData* GetData(JSValue thisVal) { - return _disposed; + return gScListener.GetOpaque(thisVal); } - static void Register(duk_context* ctx) + static JSValue listening_get(JSContext* ctx, JSValue thisVal) { - dukglue_register_property(ctx, &ScListener::listening_get, nullptr, "listening"); - dukglue_register_method(ctx, &ScListener::close, "close"); - dukglue_register_method(ctx, &ScListener::listen, "listen"); - dukglue_register_method(ctx, &ScListener::on, "on"); - dukglue_register_method(ctx, &ScListener::off, "off"); + auto data = GetData(thisVal); + if (!data) + return JS_ThrowInternalError(ctx, "Invalid listener"); + + if (data->_socket != nullptr) + { + return JS_NewBool(ctx, data->_socket->GetStatus() == Network::SocketStatus::listening); + } + return JS_NewBool(ctx, false); + } + + static JSValue close(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) + { + auto data = GetData(thisVal); + if (!data) + return JS_ThrowInternalError(ctx, "Invalid listener"); + + data->CloseSocket(); + return JS_DupValue(ctx, thisVal); + } + + static JSValue listen(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) + { + JS_UNPACK_UINT32(port, ctx, argv[0]); + if (port & 0xFFFF0000) + return JS_ThrowRangeError(ctx, "Invalid port."); + + auto data = GetData(thisVal); + if (!data) + return JS_ThrowInternalError(ctx, "Invalid listener"); + + if (data->_disposed) + { + JS_ThrowPlainError(ctx, "Socket is disposed."); + } + else + { + if (data->_socket == nullptr) + { + data->_socket = Network::CreateTcpSocket(); + } + + if (data->_socket->GetStatus() == Network::SocketStatus::listening) + { + JS_ThrowPlainError(ctx, "Server is already listening."); + } + else + { + if (JS_IsString(argv[1])) + { + std::string host = JSToStdString(ctx, argv[1]); + if (IsLocalhostAddress(host) || IsOnWhiteList(host)) + { + try + { + data->_socket->Listen(host, port); + } + catch (const std::exception& e) + { + JS_ThrowPlainError(ctx, "%s", e.what()); + } + } + else + { + JS_ThrowPlainError(ctx, "For security reasons, only binding to localhost is allowed."); + } + } + else + { + data->_socket->Listen("127.0.0.1", port); + } + } + } + return JS_DupValue(ctx, thisVal); + } + + static JSValue on(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) + { + JS_UNPACK_STR(eventType, ctx, argv[0]); + JS_UNPACK_CALLBACK(callback, ctx, argv[1]); + + auto data = GetData(thisVal); + if (!data) + return JS_ThrowInternalError(ctx, "Invalid listener"); + + auto eventId = GetEventType(eventType); + if (eventId != ListenerData::EVENT_NONE) + { + data->_eventList.AddListener(eventId, callback); + } + return JS_DupValue(ctx, thisVal); + } + + static JSValue off(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) + { + JS_UNPACK_STR(eventType, ctx, argv[0]); + JS_UNPACK_CALLBACK(callback, ctx, argv[1]); + + auto data = GetData(thisVal); + if (!data) + return JS_ThrowInternalError(ctx, "Invalid listener"); + + auto eventId = GetEventType(eventType); + if (eventId != ListenerData::EVENT_NONE) + { + data->_eventList.RemoveListener(eventId, callback); + } + return JS_DupValue(ctx, thisVal); + } + + static uint32_t GetEventType(std::string_view name) + { + if (name == "connection") + return ListenerData::EVENT_CONNECTION; + return ListenerData::EVENT_NONE; + } + + public: + JSValue New(JSContext* ctx, const std::shared_ptr& plugin) + { + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("listening", ScListener::listening_get, nullptr), + JS_CFUNC_DEF("close", 0, ScListener::close), + JS_CFUNC_DEF("listen", 2, ScListener::listen), + JS_CFUNC_DEF("on", 2, ScListener::on), + JS_CFUNC_DEF("off", 2, ScListener::off), + }; + + // unique ptr is used to avoid static analyzer false positives. + auto data = std::make_unique(); + data->_plugin = plugin; + GetContext()->GetScriptEngine().AddSocket(data.get()); + return MakeWithOpaque(ctx, funcs, data.release()); + } + + void Register(JSContext* ctx) + { + RegisterBaseStr(ctx, "Listener", Finalize); + } + + static void Finalize(JSRuntime* rt, JSValue thisVal) + { + ListenerData* data = gScListener.GetOpaque(thisVal); + if (data) + { + // Note the game context pointer can be null if the game is shutting down, + // but all sockets should have been cleaned up by then. + IContext* gameContext = GetContext(); + assert(gameContext != nullptr); + gameContext->GetScriptEngine().RemoveSocket(data); + data->Dispose(); + delete data; + } } }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/object/ScInstalledObject.cpp b/src/openrct2/scripting/bindings/object/ScInstalledObject.cpp index 68fe9030a9..5ee5c2423b 100644 --- a/src/openrct2/scripting/bindings/object/ScInstalledObject.cpp +++ b/src/openrct2/scripting/bindings/object/ScInstalledObject.cpp @@ -9,6 +9,8 @@ #ifdef ENABLE_SCRIPTING + #include "../../../core/EnumMap.hpp" + #include "../../../object/ObjectTypes.h" #include "ScObject.hpp" namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/object/ScInstalledObject.hpp b/src/openrct2/scripting/bindings/object/ScInstalledObject.hpp index fe8dd51f70..8f560af3aa 100644 --- a/src/openrct2/scripting/bindings/object/ScInstalledObject.hpp +++ b/src/openrct2/scripting/bindings/object/ScInstalledObject.hpp @@ -13,7 +13,6 @@ #include "../../../Context.h" #include "../../../object/ObjectRepository.h" - #include "../../Duktape.hpp" #include "../../ScriptEngine.h" #include @@ -34,137 +33,166 @@ namespace OpenRCT2::Scripting return values[EnumValue(sourceGame)]; } - class ScInstalledObject + class ScInstalledObject; + extern ScInstalledObject gScInstalledObject; + + class ScInstalledObject final : public ScBase { - protected: - size_t _index{}; + private: + struct InstalledObjectData + { + size_t _index{}; + }; public: - ScInstalledObject(size_t index) - : _index(index) + void Register(JSContext* ctx) { + RegisterBaseStr(ctx, "InstalledObject", Finalize); } - static void Register(duk_context* ctx) + JSValue New(JSContext* ctx, size_t index) { - dukglue_register_property(ctx, &ScInstalledObject::path_get, nullptr, "path"); - dukglue_register_property(ctx, &ScInstalledObject::generation_get, nullptr, "generation"); - dukglue_register_property(ctx, &ScInstalledObject::identifier_get, nullptr, "identifier"); - dukglue_register_property(ctx, &ScInstalledObject::type_get, nullptr, "type"); - dukglue_register_property(ctx, &ScInstalledObject::sourceGames_get, nullptr, "sourceGames"); - dukglue_register_property(ctx, &ScInstalledObject::legacyIdentifier_get, nullptr, "legacyIdentifier"); - dukglue_register_property(ctx, &ScInstalledObject::authors_get, nullptr, "authors"); - dukglue_register_property(ctx, &ScInstalledObject::name_get, nullptr, "name"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("path", ScInstalledObject::path_get, nullptr), + JS_CGETSET_DEF("generation", ScInstalledObject::generation_get, nullptr), + JS_CGETSET_DEF("identifier", ScInstalledObject::identifier_get, nullptr), + JS_CGETSET_DEF("type", ScInstalledObject::type_get, nullptr), + JS_CGETSET_DEF("sourceGames", ScInstalledObject::sourceGames_get, nullptr), + JS_CGETSET_DEF("legacyIdentifier", ScInstalledObject::legacyIdentifier_get, nullptr), + JS_CGETSET_DEF("authors", ScInstalledObject::authors_get, nullptr), + JS_CGETSET_DEF("name", ScInstalledObject::name_get, nullptr), + }; + return MakeWithOpaque(ctx, funcs, new InstalledObjectData{ index }); } private: - std::string path_get() const + static void Finalize(JSRuntime* rt, JSValue thisVal) { - auto installedObject = GetInstalledObject(); - if (installedObject != nullptr) - { - return installedObject->Path; - } - return {}; + InstalledObjectData* data = gScInstalledObject.GetOpaque(thisVal); + if (data) + delete data; } - std::string generation_get() const + static JSValue path_get(JSContext* ctx, JSValue thisVal) { - auto installedObject = GetInstalledObject(); + auto installedObject = GetInstalledObject(thisVal); + if (installedObject != nullptr) + { + return JSFromStdString(ctx, installedObject->Path); + } + return JSFromStdString(ctx, ""); + } + + static JSValue generation_get(JSContext* ctx, JSValue thisVal) + { + auto installedObject = GetInstalledObject(thisVal); if (installedObject != nullptr) { if (installedObject->Generation == ObjectGeneration::DAT) - return "dat"; + return JSFromStdString(ctx, "dat"); else - return "json"; + return JSFromStdString(ctx, "json"); } - return {}; + return JSFromStdString(ctx, ""); } - std::vector sourceGames_get() const + static JSValue sourceGames_get(JSContext* ctx, JSValue thisVal) { - std::vector result; - auto installedObject = GetInstalledObject(); + auto installedObject = GetInstalledObject(thisVal); + + JSValue array = JS_NewArray(ctx); if (installedObject != nullptr) { + int64_t index = 0; for (const auto& sourceGame : installedObject->Sources) { - result.push_back(std::string(ObjectSourceGameToString(sourceGame))); + JSValue sourceGameStr = JSFromStdString(ctx, std::string(ObjectSourceGameToString(sourceGame))); + JS_SetPropertyInt64(ctx, array, index++, sourceGameStr); } } - return result; + return array; } - std::string type_get() const + static JSValue type_get(JSContext* ctx, JSValue thisVal) { - auto installedObject = GetInstalledObject(); + auto installedObject = GetInstalledObject(thisVal); if (installedObject != nullptr) { - return std::string(objectTypeToString(installedObject->Type)); + return JSFromStdString(ctx, std::string(objectTypeToString(installedObject->Type))); } - return {}; + return JSFromStdString(ctx, ""); } - std::string identifier_get() const + static JSValue identifier_get(JSContext* ctx, JSValue thisVal) { - auto installedObject = GetInstalledObject(); + auto installedObject = GetInstalledObject(thisVal); if (installedObject != nullptr) { if (installedObject->Generation == ObjectGeneration::DAT) { - return ObjectEntryDescriptor(installedObject->ObjectEntry).ToString(); + return JSFromStdString(ctx, ObjectEntryDescriptor(installedObject->ObjectEntry).ToString()); } else { - return installedObject->Identifier; + return JSFromStdString(ctx, installedObject->Identifier); } } - return {}; + return JSFromStdString(ctx, ""); } - DukValue legacyIdentifier_get() const + static JSValue legacyIdentifier_get(JSContext* ctx, JSValue thisVal) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - auto installedObject = GetInstalledObject(); + auto installedObject = GetInstalledObject(thisVal); if (installedObject != nullptr) { if (!installedObject->ObjectEntry.IsEmpty()) { - return ToDuk(ctx, installedObject->ObjectEntry.GetName()); + auto str = installedObject->ObjectEntry.GetName(); + if (str.find('\0') != std::string::npos) + str = {}; + return JSFromStdString(ctx, str); } } - return ToDuk(ctx, nullptr); + return JS_NULL; } - std::vector authors_get() const + static JSValue authors_get(JSContext* ctx, JSValue thisVal) { - auto installedObject = GetInstalledObject(); + auto installedObject = GetInstalledObject(thisVal); + + JSValue array = JS_NewArray(ctx); if (installedObject != nullptr) { - return installedObject->Authors; + uint32_t index = 0; + for (const auto& author : installedObject->Authors) + { + JSValue authorStr = JSFromStdString(ctx, author); + JS_SetPropertyInt64(ctx, array, index++, authorStr); + } } - return {}; + return array; } - std::string name_get() const + static JSValue name_get(JSContext* ctx, JSValue thisVal) { - auto installedObject = GetInstalledObject(); + auto installedObject = GetInstalledObject(thisVal); if (installedObject != nullptr) { - return installedObject->Name; + return JSFromStdString(ctx, installedObject->Name); } - return {}; + return JSFromStdString(ctx, ""); } - const ObjectRepositoryItem* GetInstalledObject() const + static const ObjectRepositoryItem* GetInstalledObject(JSValue thisVal) { + size_t index = gScInstalledObject.GetOpaque(thisVal)->_index; auto context = GetContext(); auto& objectRepository = context->GetObjectRepository(); auto numObjects = objectRepository.GetNumObjects(); - if (_index < numObjects) + if (index < numObjects) { auto* objects = objectRepository.GetObjects(); - return &objects[_index]; + return &objects[index]; } return nullptr; } diff --git a/src/openrct2/scripting/bindings/object/ScObject.hpp b/src/openrct2/scripting/bindings/object/ScObject.hpp index a4cb211044..29b0f424bc 100644 --- a/src/openrct2/scripting/bindings/object/ScObject.hpp +++ b/src/openrct2/scripting/bindings/object/ScObject.hpp @@ -17,7 +17,6 @@ #include "../../../object/RideObject.h" #include "../../../object/SceneryGroupObject.h" #include "../../../object/SmallSceneryObject.h" - #include "../../Duktape.hpp" #include "../../ScriptEngine.h" #include "ScInstalledObject.hpp" @@ -26,747 +25,611 @@ namespace OpenRCT2::Scripting { - class ScObject + class ScObject; + extern ScObject gScObject; + + class ScObject : public ScBase { protected: - ObjectType _type{}; - int32_t _index{}; + struct ObjectData + { + ObjectType _type{}; + int32_t _index{}; + }; public: - ScObject(ObjectType type, int32_t index) - : _type(type) - , _index(index) + void Register(JSContext* ctx) { + RegisterBaseStr(ctx, "Object", Finalize); } - static void Register(duk_context* ctx) + JSValue NewInstance(JSContext* ctx, ObjectType type, int32_t index) { - dukglue_register_property(ctx, &ScObject::installedObject_get, nullptr, "installedObject"); - dukglue_register_property(ctx, &ScObject::type_get, nullptr, "type"); - dukglue_register_property(ctx, &ScObject::index_get, nullptr, "index"); - dukglue_register_property(ctx, &ScObject::identifier_get, nullptr, "identifier"); - dukglue_register_property(ctx, &ScObject::legacyIdentifier_get, nullptr, "legacyIdentifier"); - dukglue_register_property(ctx, &ScObject::name_get, nullptr, "name"); - dukglue_register_property(ctx, &ScObject::baseImageId_get, nullptr, "baseImageId"); - dukglue_register_property(ctx, &ScObject::numImages_get, nullptr, "numImages"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("installedObject", ScObject::installedObject_get, nullptr), + JS_CGETSET_DEF("type", ScObject::type_get, nullptr), + JS_CGETSET_DEF("index", ScObject::index_get, nullptr), + JS_CGETSET_DEF("identifier", ScObject::identifier_get, nullptr), + JS_CGETSET_DEF("legacyIdentifier", ScObject::legacyIdentifier_get, nullptr), + JS_CGETSET_DEF("name", ScObject::name_get, nullptr), + JS_CGETSET_DEF("baseImageId", ScObject::baseImageId_get, nullptr), + JS_CGETSET_DEF("numImages", ScObject::numImages_get, nullptr), + }; + return MakeWithOpaque(ctx, funcs, new ObjectData{ type, index }); + } + + static JSValue New(JSContext* ctx, ObjectType type, int32_t index) + { + return gScObject.NewInstance(ctx, type, index); } private: - std::shared_ptr installedObject_get() const + static void Finalize(JSRuntime* rt, JSValue thisVal) { - auto obj = GetObject(); + ObjectData* data = GetObjectData(thisVal); + if (data) + delete data; + } + + static JSValue installedObject_get(JSContext* ctx, JSValue thisVal) + { + auto obj = GetObject(thisVal); if (obj != nullptr) { auto& objectRepository = GetContext()->GetObjectRepository(); auto installedObject = objectRepository.FindObject(obj->GetDescriptor()); if (installedObject != nullptr) { - return std::make_shared(installedObject->Id); + return gScInstalledObject.New(ctx, installedObject->Id); } } - return {}; + return JS_NULL; } - std::string type_get() const + static JSValue type_get(JSContext* ctx, JSValue thisVal) { - return std::string(objectTypeToString(_type)); + auto* data = GetObjectData(thisVal); + return JSFromStdString(ctx, std::string(objectTypeToString(data->_type))); } - int32_t index_get() const + static JSValue index_get(JSContext* ctx, JSValue thisVal) { - return _index; + auto* data = GetObjectData(thisVal); + return JS_NewInt32(ctx, data->_index); } - std::string identifier_get() const + static JSValue identifier_get(JSContext* ctx, JSValue thisVal) { - auto obj = GetObject(); + auto obj = GetObject(thisVal); + std::string str; if (obj != nullptr) { if (obj->GetGeneration() == ObjectGeneration::DAT) { - return obj->GetDescriptor().ToString(); + str = obj->GetDescriptor().ToString(); } else { - return std::string(obj->GetIdentifier()); + str = obj->GetIdentifier(); } } - return {}; + else + { + str = ""; + } + return JSFromStdString(ctx, str); } - std::string legacyIdentifier_get() const + static JSValue legacyIdentifier_get(JSContext* ctx, JSValue thisVal) { - auto obj = GetObject(); - if (obj != nullptr) + auto obj = GetObject(thisVal); + if (obj != nullptr && obj->GetLegacyIdentifier().find('\0') == std::string::npos) { - return std::string(obj->GetLegacyIdentifier()); + return JSFromStdString(ctx, obj->GetLegacyIdentifier()); } - return {}; + return JSFromStdString(ctx, {}); } - std::string name_get() const + static JSValue name_get(JSContext* ctx, JSValue thisVal) { - auto obj = GetObject(); - if (obj != nullptr) - { - return obj->GetName(); - } - return {}; + auto obj = GetObject(thisVal); + return JSFromStdString(ctx, obj != nullptr ? obj->GetName() : ""); } - uint32_t baseImageId_get() const + static JSValue baseImageId_get(JSContext* ctx, JSValue thisVal) { - auto obj = GetObject(); - if (obj != nullptr) - { - return obj->GetBaseImageId(); - } - return 0; + auto obj = GetObject(thisVal); + return JS_NewUint32(ctx, obj != nullptr ? obj->GetBaseImageId() : 0); } - uint32_t numImages_get() const + static JSValue numImages_get(JSContext* ctx, JSValue thisVal) { - auto obj = GetObject(); - if (obj != nullptr) - { - return obj->GetNumImages(); - } - return 0; + auto obj = GetObject(thisVal); + return JS_NewUint32(ctx, obj != nullptr ? obj->GetNumImages() : 0); } protected: - Object* GetObject() const + static ObjectData* GetObjectData(JSValue thisVal) { + return gScObject.GetOpaque(thisVal); + } + + static Object* GetObject(JSValue thisVal) + { + ObjectData* data = GetObjectData(thisVal); auto& objManager = GetContext()->GetObjectManager(); - return objManager.GetLoadedObject(_type, _index); + return objManager.GetLoadedObject(data->_type, data->_index); } }; - class ScRideObjectVehicle + class ScRideObjectVehicle; + extern ScRideObjectVehicle gScRideObjectVehicle; + + class ScRideObjectVehicle final : public ScBase { private: - ObjectEntryIndex _objectIndex{}; - size_t _vehicleIndex{}; + struct VehicleData + { + ObjectEntryIndex _objectIndex{}; + size_t _vehicleIndex{}; + }; public: - ScRideObjectVehicle(ObjectEntryIndex objectIndex, size_t vehicleIndex) - : _objectIndex(objectIndex) - , _vehicleIndex(vehicleIndex) + void Register(JSContext* ctx) { + RegisterBaseStr(ctx, "RideObjectVehicle", Finalize); } - static void Register(duk_context* ctx) + JSValue New(JSContext* ctx, ObjectEntryIndex objectIndex, size_t vehicleIndex) { - dukglue_register_property(ctx, &ScRideObjectVehicle::rotationFrameMask_get, nullptr, "rotationFrameMask"); - dukglue_register_property(ctx, &ScRideObjectVehicle::spacing_get, nullptr, "spacing"); - dukglue_register_property(ctx, &ScRideObjectVehicle::carMass_get, nullptr, "carMass"); - dukglue_register_property(ctx, &ScRideObjectVehicle::tabHeight_get, nullptr, "tabHeight"); - dukglue_register_property(ctx, &ScRideObjectVehicle::numSeats_get, nullptr, "numSeats"); - dukglue_register_property(ctx, &ScRideObjectVehicle::spriteFlags_get, nullptr, "spriteFlags"); - dukglue_register_property(ctx, &ScRideObjectVehicle::spriteWidth_get, nullptr, "spriteWidth"); - dukglue_register_property(ctx, &ScRideObjectVehicle::spriteHeightNegative_get, nullptr, "spriteHeightNegative"); - dukglue_register_property(ctx, &ScRideObjectVehicle::spriteHeightPositive_get, nullptr, "spriteHeightPositive"); - dukglue_register_property(ctx, &ScRideObjectVehicle::animation_get, nullptr, "animation"); - dukglue_register_property(ctx, &ScRideObjectVehicle::flags_get, nullptr, "flags"); - dukglue_register_property(ctx, &ScRideObjectVehicle::baseNumFrames_get, nullptr, "baseNumFrames"); - dukglue_register_property(ctx, &ScRideObjectVehicle::baseImageId_get, nullptr, "baseImageId"); - dukglue_register_property(ctx, &ScRideObjectVehicle::spriteGroups_get, nullptr, "spriteGroups"); - dukglue_register_property(ctx, &ScRideObjectVehicle::noVehicleImages_get, nullptr, "noVehicleImages"); - dukglue_register_property(ctx, &ScRideObjectVehicle::noSeatingRows_get, nullptr, "noSeatingRows"); - dukglue_register_property(ctx, &ScRideObjectVehicle::spinningInertia_get, nullptr, "spinningInertia"); - dukglue_register_property(ctx, &ScRideObjectVehicle::spinningFriction_get, nullptr, "spinningFriction"); - dukglue_register_property(ctx, &ScRideObjectVehicle::frictionSoundId_get, nullptr, "frictionSoundId"); - dukglue_register_property( - ctx, &ScRideObjectVehicle::logFlumeReverserVehicleType_get, nullptr, "logFlumeReverserVehicleType"); - dukglue_register_property(ctx, &ScRideObjectVehicle::soundRange_get, nullptr, "soundRange"); - dukglue_register_property(ctx, &ScRideObjectVehicle::doubleSoundFrequency_get, nullptr, "doubleSoundFrequency"); - dukglue_register_property(ctx, &ScRideObjectVehicle::poweredAcceleration_get, nullptr, "poweredAcceleration"); - dukglue_register_property(ctx, &ScRideObjectVehicle::poweredMaxSpeed_get, nullptr, "poweredMaxSpeed"); - dukglue_register_property(ctx, &ScRideObjectVehicle::carVisual_get, nullptr, "carVisual"); - dukglue_register_property(ctx, &ScRideObjectVehicle::effectVisual_get, nullptr, "effectVisual"); - dukglue_register_property(ctx, &ScRideObjectVehicle::drawOrder_get, nullptr, "drawOrder"); - dukglue_register_property( - ctx, &ScRideObjectVehicle::numVerticalFramesOverride_get, nullptr, "numVerticalFramesOverride"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("rotationFrameMask", ScRideObjectVehicle::rotationFrameMask_get, nullptr), + JS_CGETSET_DEF("spacing", ScRideObjectVehicle::spacing_get, nullptr), + JS_CGETSET_DEF("carMass", ScRideObjectVehicle::carMass_get, nullptr), + JS_CGETSET_DEF("tabHeight", ScRideObjectVehicle::tabHeight_get, nullptr), + JS_CGETSET_DEF("numSeats", ScRideObjectVehicle::numSeats_get, nullptr), + JS_CGETSET_DEF("spriteFlags", ScRideObjectVehicle::spriteFlags_get, nullptr), + JS_CGETSET_DEF("spriteWidth", ScRideObjectVehicle::spriteWidth_get, nullptr), + JS_CGETSET_DEF("spriteHeightNegative", ScRideObjectVehicle::spriteHeightNegative_get, nullptr), + JS_CGETSET_DEF("spriteHeightPositive", ScRideObjectVehicle::spriteHeightPositive_get, nullptr), + JS_CGETSET_DEF("animation", ScRideObjectVehicle::animation_get, nullptr), + JS_CGETSET_DEF("flags", ScRideObjectVehicle::flags_get, nullptr), + JS_CGETSET_DEF("baseNumFrames", ScRideObjectVehicle::baseNumFrames_get, nullptr), + JS_CGETSET_DEF("baseImageId", ScRideObjectVehicle::baseImageId_get, nullptr), + JS_CGETSET_DEF("spriteGroups", ScRideObjectVehicle::spriteGroups_get, nullptr), + JS_CGETSET_DEF("noVehicleImages", ScRideObjectVehicle::noVehicleImages_get, nullptr), + JS_CGETSET_DEF("noSeatingRows", ScRideObjectVehicle::noSeatingRows_get, nullptr), + JS_CGETSET_DEF("spinningInertia", ScRideObjectVehicle::spinningInertia_get, nullptr), + JS_CGETSET_DEF("spinningFriction", ScRideObjectVehicle::spinningFriction_get, nullptr), + JS_CGETSET_DEF("frictionSoundId", ScRideObjectVehicle::frictionSoundId_get, nullptr), + JS_CGETSET_DEF("logFlumeReverserVehicleType", ScRideObjectVehicle::logFlumeReverserVehicleType_get, nullptr), + JS_CGETSET_DEF("soundRange", ScRideObjectVehicle::soundRange_get, nullptr), + JS_CGETSET_DEF("doubleSoundFrequency", ScRideObjectVehicle::doubleSoundFrequency_get, nullptr), + JS_CGETSET_DEF("poweredAcceleration", ScRideObjectVehicle::poweredAcceleration_get, nullptr), + JS_CGETSET_DEF("poweredMaxSpeed", ScRideObjectVehicle::poweredMaxSpeed_get, nullptr), + JS_CGETSET_DEF("carVisual", ScRideObjectVehicle::carVisual_get, nullptr), + JS_CGETSET_DEF("effectVisual", ScRideObjectVehicle::effectVisual_get, nullptr), + JS_CGETSET_DEF("drawOrder", ScRideObjectVehicle::drawOrder_get, nullptr), + JS_CGETSET_DEF("numVerticalFramesOverride", ScRideObjectVehicle::numVerticalFramesOverride_get, nullptr), + }; + return MakeWithOpaque(ctx, funcs, new VehicleData{ objectIndex, vehicleIndex }); } private: - uint16_t rotationFrameMask_get() const + static void Finalize(JSRuntime* rt, JSValue thisVal) { - auto carEntry = GetEntry(); - if (carEntry != nullptr) - { - return carEntry->TabRotationMask; - } - return 0; + VehicleData* data = gScRideObjectVehicle.GetOpaque(thisVal); + if (data) + delete data; } - uint32_t spacing_get() const + static JSValue rotationFrameMask_get(JSContext* ctx, JSValue thisVal) { - auto carEntry = GetEntry(); - if (carEntry != nullptr) - { - return carEntry->spacing; - } - return 0; + auto carEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, carEntry != nullptr ? carEntry->TabRotationMask : 0); } - uint16_t carMass_get() const + static JSValue spacing_get(JSContext* ctx, JSValue thisVal) { - auto carEntry = GetEntry(); - if (carEntry != nullptr) - { - return carEntry->car_mass; - } - return 0; + auto carEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, carEntry != nullptr ? carEntry->spacing : 0); } - int8_t tabHeight_get() const + static JSValue carMass_get(JSContext* ctx, JSValue thisVal) { - auto carEntry = GetEntry(); - if (carEntry != nullptr) - { - return carEntry->tab_height; - } - return 0; + auto carEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, carEntry != nullptr ? carEntry->car_mass : 0); } - uint8_t numSeats_get() const + static JSValue tabHeight_get(JSContext* ctx, JSValue thisVal) { - auto carEntry = GetEntry(); - if (carEntry != nullptr) - { - return carEntry->num_seats; - } - return 0; + auto carEntry = GetEntry(thisVal); + return JS_NewInt32(ctx, carEntry != nullptr ? carEntry->tab_height : 0); } - uint16_t spriteFlags_get() const + static JSValue numSeats_get(JSContext* ctx, JSValue thisVal) { - return 0; + auto carEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, carEntry != nullptr ? carEntry->num_seats : 0); } - uint8_t spriteWidth_get() const + static JSValue spriteFlags_get(JSContext* ctx, JSValue thisVal) { - auto carEntry = GetEntry(); - if (carEntry != nullptr) - { - return carEntry->spriteWidth; - } - return 0; + return JS_NewUint32(ctx, 0); } - uint8_t spriteHeightNegative_get() const + static JSValue spriteWidth_get(JSContext* ctx, JSValue thisVal) { - auto carEntry = GetEntry(); - if (carEntry != nullptr) - { - return carEntry->spriteHeightNegative; - } - return 0; + auto carEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, carEntry != nullptr ? carEntry->spriteWidth : 0); } - uint8_t spriteHeightPositive_get() const + static JSValue spriteHeightNegative_get(JSContext* ctx, JSValue thisVal) { - auto carEntry = GetEntry(); - if (carEntry != nullptr) - { - return carEntry->spriteHeightPositive; - } - return 0; + auto carEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, carEntry != nullptr ? carEntry->spriteHeightNegative : 0); } - uint8_t animation_get() const + static JSValue spriteHeightPositive_get(JSContext* ctx, JSValue thisVal) { - auto carEntry = GetEntry(); - if (carEntry != nullptr) - { - return EnumValue(carEntry->animation); - } - return 0; + auto carEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, carEntry != nullptr ? carEntry->spriteHeightPositive : 0); } - uint64_t flags_get() const + static JSValue animation_get(JSContext* ctx, JSValue thisVal) { - auto carEntry = GetEntry(); - if (carEntry != nullptr) - { - return carEntry->flags.holder; - } - return 0; + auto carEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, carEntry != nullptr ? EnumValue(carEntry->animation) : 0); } - uint16_t baseNumFrames_get() const + static JSValue flags_get(JSContext* ctx, JSValue thisVal) { - auto carEntry = GetEntry(); - if (carEntry != nullptr) - { - return carEntry->base_num_frames; - } - return 0; + auto carEntry = GetEntry(thisVal); + return JS_NewInt64(ctx, carEntry != nullptr ? static_cast(carEntry->flags.holder) : 0); } - uint32_t baseImageId_get() const + static JSValue baseNumFrames_get(JSContext* ctx, JSValue thisVal) { - auto carEntry = GetEntry(); - if (carEntry != nullptr) - { - return carEntry->base_image_id; - } - return 0; + auto carEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, carEntry != nullptr ? carEntry->base_num_frames : 0); } - DukValue spriteGroups_get() const + static JSValue baseImageId_get(JSContext* ctx, JSValue thisVal) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - DukObject groups(ctx); - auto carEntry = GetEntry(); + auto carEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, carEntry != nullptr ? carEntry->base_image_id : 0); + } + + static JSValue spriteGroups_get(JSContext* ctx, JSValue thisVal) + { + JSValue groups = JS_NewObject(ctx); + auto carEntry = GetEntry(thisVal); if (carEntry != nullptr) { - for (uint8_t g = 0; g < EnumValue(SpriteGroupType::Count); g++) + for (std::underlying_type_t g = 0; g < EnumValue(SpriteGroupType::Count); g++) { auto group = carEntry->SpriteGroups[g]; if (group.Enabled()) - groups.Set(SpriteGroupNames[g], ToDuk(ctx, group)); + { + JSValue groupObj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, groupObj, "imageId", JS_NewUint32(ctx, group.imageId)); + JS_SetPropertyStr( + ctx, groupObj, "spriteNumImages", + JS_NewUint32(ctx, OpenRCT2::Entity::Yaw::NumSpritesPrecision(group.spritePrecision))); + JS_SetPropertyStr(ctx, groups, SpriteGroupNames[g], groupObj); + } } } - return groups.Take(); + return groups; } - uint32_t noVehicleImages_get() const + static JSValue noVehicleImages_get(JSContext* ctx, JSValue thisVal) { - auto carEntry = GetEntry(); - if (carEntry != nullptr) - { - return carEntry->NumCarImages; - } - return 0; + auto carEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, carEntry != nullptr ? carEntry->NumCarImages : 0); } - uint8_t noSeatingRows_get() const + static JSValue noSeatingRows_get(JSContext* ctx, JSValue thisVal) { - auto carEntry = GetEntry(); - if (carEntry != nullptr) - { - return carEntry->no_seating_rows; - } - return 0; + auto carEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, carEntry != nullptr ? carEntry->no_seating_rows : 0); } - uint8_t spinningInertia_get() const + static JSValue spinningInertia_get(JSContext* ctx, JSValue thisVal) { - auto carEntry = GetEntry(); - if (carEntry != nullptr) - { - return carEntry->spinning_inertia; - } - return 0; + auto carEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, carEntry != nullptr ? carEntry->spinning_inertia : 0); } - uint8_t spinningFriction_get() const + static JSValue spinningFriction_get(JSContext* ctx, JSValue thisVal) { - auto carEntry = GetEntry(); - if (carEntry != nullptr) - { - return carEntry->spinning_friction; - } - return 0; + auto carEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, carEntry != nullptr ? carEntry->spinning_friction : 0); } - int32_t frictionSoundId_get() const + static JSValue frictionSoundId_get(JSContext* ctx, JSValue thisVal) { - auto carEntry = GetEntry(); - if (carEntry != nullptr) - { - return static_cast(carEntry->friction_sound_id); - } - return 0; + auto carEntry = GetEntry(thisVal); + return JS_NewInt32(ctx, carEntry != nullptr ? static_cast(carEntry->friction_sound_id) : 0); } - uint8_t logFlumeReverserVehicleType_get() const + static JSValue logFlumeReverserVehicleType_get(JSContext* ctx, JSValue thisVal) { - auto carEntry = GetEntry(); - if (carEntry != nullptr) - { - return carEntry->ReversedCarIndex; - } - return 0; + auto carEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, carEntry != nullptr ? carEntry->ReversedCarIndex : 0); } - uint8_t soundRange_get() const + static JSValue soundRange_get(JSContext* ctx, JSValue thisVal) { - auto carEntry = GetEntry(); - if (carEntry != nullptr) - { - return EnumValue(carEntry->soundRange); - } - return 0; + auto carEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, carEntry != nullptr ? EnumValue(carEntry->soundRange) : 0); } - uint8_t doubleSoundFrequency_get() const + static JSValue doubleSoundFrequency_get(JSContext* ctx, JSValue thisVal) { - auto carEntry = GetEntry(); - if (carEntry != nullptr) - { - return carEntry->double_sound_frequency; - } - return 0; + auto carEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, carEntry != nullptr ? carEntry->double_sound_frequency : 0); } - uint8_t poweredAcceleration_get() const + static JSValue poweredAcceleration_get(JSContext* ctx, JSValue thisVal) { - auto carEntry = GetEntry(); - if (carEntry != nullptr) - { - return carEntry->powered_acceleration; - } - return 0; + auto carEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, carEntry != nullptr ? carEntry->powered_acceleration : 0); } - uint8_t poweredMaxSpeed_get() const + static JSValue poweredMaxSpeed_get(JSContext* ctx, JSValue thisVal) { - auto carEntry = GetEntry(); - if (carEntry != nullptr) - { - return carEntry->powered_max_speed; - } - return 0; + auto carEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, carEntry != nullptr ? carEntry->powered_max_speed : 0); } - uint8_t carVisual_get() const + static JSValue carVisual_get(JSContext* ctx, JSValue thisVal) { - auto carEntry = GetEntry(); - if (carEntry != nullptr) - { - return carEntry->PaintStyle; - } - return 0; + auto carEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, carEntry != nullptr ? carEntry->PaintStyle : 0); } - uint8_t effectVisual_get() const + static JSValue effectVisual_get(JSContext* ctx, JSValue thisVal) { - auto carEntry = GetEntry(); - if (carEntry != nullptr) - { - return carEntry->effect_visual; - } - return 0; + auto carEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, carEntry != nullptr ? carEntry->effect_visual : 0); } - uint8_t drawOrder_get() const + static JSValue drawOrder_get(JSContext* ctx, JSValue thisVal) { - auto carEntry = GetEntry(); - if (carEntry != nullptr) - { - return carEntry->draw_order; - } - return 0; + auto carEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, carEntry != nullptr ? carEntry->draw_order : 0); } - uint8_t numVerticalFramesOverride_get() const + static JSValue numVerticalFramesOverride_get(JSContext* ctx, JSValue thisVal) { - auto carEntry = GetEntry(); - if (carEntry != nullptr) - { - return carEntry->num_vertical_frames_override; - } - return 0; + auto carEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, carEntry != nullptr ? carEntry->num_vertical_frames_override : 0); } - const RideObject* GetObject() const + static const RideObject* GetObject(JSValue thisVal) { + VehicleData* data = gScRideObjectVehicle.GetOpaque(thisVal); auto& objManager = GetContext()->GetObjectManager(); - return objManager.GetLoadedObject(_objectIndex); + return objManager.GetLoadedObject(data->_objectIndex); } - const CarEntry* GetEntry() const + static const CarEntry* GetEntry(JSValue thisVal) { - auto obj = GetObject(); + auto obj = GetObject(thisVal); if (obj != nullptr) { + VehicleData* data = gScRideObjectVehicle.GetOpaque(thisVal); auto rideEntry = &obj->GetEntry(); - if (rideEntry != nullptr && _vehicleIndex < std::size(rideEntry->Cars)) + if (rideEntry != nullptr && data->_vehicleIndex < std::size(rideEntry->Cars)) { - return rideEntry->GetCar(_vehicleIndex); + return rideEntry->GetCar(data->_vehicleIndex); } } return nullptr; } }; - class ScRideObject : public ScObject + class ScRideObject final : public ScObject { public: - ScRideObject(ObjectType type, int32_t index) - : ScObject(type, index) + static JSValue New(JSContext* ctx, ObjectType type, int32_t index) { - } - - static void Register(duk_context* ctx) - { - dukglue_set_base_class(ctx); - dukglue_register_property(ctx, &ScRideObject::description_get, nullptr, "description"); - dukglue_register_property(ctx, &ScRideObject::capacity_get, nullptr, "capacity"); - dukglue_register_property(ctx, &ScRideObject::firstImageId_get, nullptr, "firstImageId"); - dukglue_register_property(ctx, &ScRideObject::flags_get, nullptr, "flags"); - dukglue_register_property(ctx, &ScRideObject::rideType_get, nullptr, "rideType"); - dukglue_register_property(ctx, &ScRideObject::minCarsInTrain_get, nullptr, "minCarsInTrain"); - dukglue_register_property(ctx, &ScRideObject::maxCarsInTrain_get, nullptr, "maxCarsInTrain"); - dukglue_register_property(ctx, &ScRideObject::carsPerFlatRide_get, nullptr, "carsPerFlatRide"); - dukglue_register_property(ctx, &ScRideObject::zeroCars_get, nullptr, "zeroCars"); - dukglue_register_property(ctx, &ScRideObject::tabVehicle_get, nullptr, "tabVehicle"); - dukglue_register_property(ctx, &ScRideObject::defaultVehicle_get, nullptr, "defaultVehicle"); - dukglue_register_property(ctx, &ScRideObject::frontVehicle_get, nullptr, "frontVehicle"); - dukglue_register_property(ctx, &ScRideObject::secondVehicle_get, nullptr, "secondVehicle"); - dukglue_register_property(ctx, &ScRideObject::rearVehicle_get, nullptr, "rearVehicle"); - dukglue_register_property(ctx, &ScRideObject::thirdVehicle_get, nullptr, "thirdVehicle"); - dukglue_register_property(ctx, &ScRideObject::vehicles_get, nullptr, "vehicles"); - dukglue_register_property(ctx, &ScRideObject::excitementMultiplier_get, nullptr, "excitementMultiplier"); - dukglue_register_property(ctx, &ScRideObject::intensityMultiplier_get, nullptr, "intensityMultiplier"); - dukglue_register_property(ctx, &ScRideObject::nauseaMultiplier_get, nullptr, "nauseaMultiplier"); - dukglue_register_property(ctx, &ScRideObject::maxHeight_get, nullptr, "maxHeight"); - dukglue_register_property(ctx, &ScRideObject::shopItem_get, nullptr, "shopItem"); - dukglue_register_property(ctx, &ScRideObject::shopItemSecondary_get, nullptr, "shopItemSecondary"); + JSValue obj = ScObject::New(ctx, type, index); + AddFuncs(ctx, obj); + return obj; } private: - std::string description_get() const + static void AddFuncs(JSContext* ctx, JSValue obj) { - auto obj = GetObject(); - if (obj != nullptr) - { - return obj->GetDescription(); - } - return {}; + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("description", ScRideObject::description_get, nullptr), + JS_CGETSET_DEF("capacity", ScRideObject::capacity_get, nullptr), + JS_CGETSET_DEF("firstImageId", ScRideObject::firstImageId_get, nullptr), + JS_CGETSET_DEF("flags", ScRideObject::flags_get, nullptr), + JS_CGETSET_DEF("rideType", ScRideObject::rideType_get, nullptr), + JS_CGETSET_DEF("minCarsInTrain", ScRideObject::minCarsInTrain_get, nullptr), + JS_CGETSET_DEF("maxCarsInTrain", ScRideObject::maxCarsInTrain_get, nullptr), + JS_CGETSET_DEF("carsPerFlatRide", ScRideObject::carsPerFlatRide_get, nullptr), + JS_CGETSET_DEF("zeroCars", ScRideObject::zeroCars_get, nullptr), + JS_CGETSET_DEF("tabVehicle", ScRideObject::tabVehicle_get, nullptr), + JS_CGETSET_DEF("defaultVehicle", ScRideObject::defaultVehicle_get, nullptr), + JS_CGETSET_DEF("frontVehicle", ScRideObject::frontVehicle_get, nullptr), + JS_CGETSET_DEF("secondVehicle", ScRideObject::secondVehicle_get, nullptr), + JS_CGETSET_DEF("rearVehicle", ScRideObject::rearVehicle_get, nullptr), + JS_CGETSET_DEF("thirdVehicle", ScRideObject::thirdVehicle_get, nullptr), + JS_CGETSET_DEF("vehicles", ScRideObject::vehicles_get, nullptr), + JS_CGETSET_DEF("excitementMultiplier", ScRideObject::excitementMultiplier_get, nullptr), + JS_CGETSET_DEF("intensityMultiplier", ScRideObject::intensityMultiplier_get, nullptr), + JS_CGETSET_DEF("nauseaMultiplier", ScRideObject::nauseaMultiplier_get, nullptr), + JS_CGETSET_DEF("maxHeight", ScRideObject::maxHeight_get, nullptr), + JS_CGETSET_DEF("shopItem", ScRideObject::shopItem_get, nullptr), + JS_CGETSET_DEF("shopItemSecondary", ScRideObject::shopItemSecondary_get, nullptr), + }; + JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs)); } - std::string capacity_get() const + static JSValue description_get(JSContext* ctx, JSValue thisVal) { - auto obj = GetObject(); - if (obj != nullptr) - { - return obj->GetCapacity(); - } - return {}; + auto obj = GetRideObject(thisVal); + return JSFromStdString(ctx, obj != nullptr ? obj->GetDescription() : ""); } - uint32_t firstImageId_get() const + static JSValue capacity_get(JSContext* ctx, JSValue thisVal) { - auto rideEntry = GetEntry(); - if (rideEntry != nullptr) - { - return rideEntry->images_offset; - } - return 0; - } - - uint32_t flags_get() const - { - auto rideEntry = GetEntry(); - if (rideEntry != nullptr) - { - return rideEntry->flags.holder; - } - return 0; - } - - std::vector rideType_get() const - { - std::vector result; - auto rideEntry = GetEntry(); + auto obj = GetRideObject(thisVal); + return JSFromStdString(ctx, obj != nullptr ? obj->GetCapacity() : ""); + } + + static JSValue firstImageId_get(JSContext* ctx, JSValue thisVal) + { + auto rideEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, rideEntry != nullptr ? rideEntry->images_offset : 0); + } + + static JSValue flags_get(JSContext* ctx, JSValue thisVal) + { + auto rideEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, rideEntry != nullptr ? rideEntry->flags.holder : 0); + } + + static JSValue rideType_get(JSContext* ctx, JSValue thisVal) + { + JSValue result = JS_NewArray(ctx); + auto rideEntry = GetEntry(thisVal); if (rideEntry != nullptr) { + int64_t index = 0; for (auto rideType : rideEntry->ride_type) { - result.push_back(rideType); + JS_SetPropertyInt64(ctx, result, index++, JS_NewUint32(ctx, rideType)); } } return result; } - uint8_t minCarsInTrain_get() const + static JSValue minCarsInTrain_get(JSContext* ctx, JSValue thisVal) { - auto rideEntry = GetEntry(); - if (rideEntry != nullptr) - { - return rideEntry->min_cars_in_train; - } - return 0; + auto rideEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, rideEntry != nullptr ? rideEntry->min_cars_in_train : 0); } - uint8_t maxCarsInTrain_get() const + static JSValue maxCarsInTrain_get(JSContext* ctx, JSValue thisVal) { - auto rideEntry = GetEntry(); - if (rideEntry != nullptr) - { - return rideEntry->max_cars_in_train; - } - return 0; + auto rideEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, rideEntry != nullptr ? rideEntry->max_cars_in_train : 0); } - uint8_t carsPerFlatRide_get() const + static JSValue carsPerFlatRide_get(JSContext* ctx, JSValue thisVal) { - auto rideEntry = GetEntry(); - if (rideEntry != nullptr) - { - return rideEntry->cars_per_flat_ride; - } - return 0; + auto rideEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, rideEntry != nullptr ? rideEntry->cars_per_flat_ride : 0); } - uint8_t zeroCars_get() const + static JSValue zeroCars_get(JSContext* ctx, JSValue thisVal) { - auto rideEntry = GetEntry(); - if (rideEntry != nullptr) - { - return rideEntry->zero_cars; - } - return 0; + auto rideEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, rideEntry != nullptr ? rideEntry->zero_cars : 0); } - uint8_t tabVehicle_get() const + static JSValue tabVehicle_get(JSContext* ctx, JSValue thisVal) { - auto rideEntry = GetEntry(); - if (rideEntry != nullptr) - { - return rideEntry->TabCar; - } - return 0; + auto rideEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, rideEntry != nullptr ? rideEntry->TabCar : 0); } - uint8_t defaultVehicle_get() const + static JSValue defaultVehicle_get(JSContext* ctx, JSValue thisVal) { - auto rideEntry = GetEntry(); - if (rideEntry != nullptr) - { - return rideEntry->DefaultCar; - } - return 0; + auto rideEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, rideEntry != nullptr ? rideEntry->DefaultCar : 0); } - uint8_t frontVehicle_get() const + static JSValue frontVehicle_get(JSContext* ctx, JSValue thisVal) { - auto rideEntry = GetEntry(); - if (rideEntry != nullptr) - { - return rideEntry->FrontCar; - } - return 0; + auto rideEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, rideEntry != nullptr ? rideEntry->FrontCar : 0); } - uint8_t secondVehicle_get() const + static JSValue secondVehicle_get(JSContext* ctx, JSValue thisVal) { - auto rideEntry = GetEntry(); - if (rideEntry != nullptr) - { - return rideEntry->SecondCar; - } - return 0; + auto rideEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, rideEntry != nullptr ? rideEntry->SecondCar : 0); } - uint8_t rearVehicle_get() const + static JSValue rearVehicle_get(JSContext* ctx, JSValue thisVal) { - auto rideEntry = GetEntry(); - if (rideEntry != nullptr) - { - return rideEntry->RearCar; - } - return 0; + auto rideEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, rideEntry != nullptr ? rideEntry->RearCar : 0); } - uint8_t thirdVehicle_get() const + static JSValue thirdVehicle_get(JSContext* ctx, JSValue thisVal) { - auto rideEntry = GetEntry(); - if (rideEntry != nullptr) - { - return rideEntry->ThirdCar; - } - return 0; + auto rideEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, rideEntry != nullptr ? rideEntry->ThirdCar : 0); } - std::vector> vehicles_get() const + static JSValue vehicles_get(JSContext* ctx, JSValue thisVal) { - std::vector> result; - auto rideEntry = GetEntry(); + JSValue result = JS_NewArray(ctx); + auto rideEntry = GetEntry(thisVal); if (rideEntry != nullptr) { + ObjectData* data = GetObjectData(thisVal); + int64_t index = 0; for (size_t i = 0; i < std::size(rideEntry->Cars); i++) { - result.push_back(std::make_shared(_index, i)); + JS_SetPropertyInt64(ctx, result, index++, gScRideObjectVehicle.New(ctx, data->_index, i)); } } return result; } - int8_t excitementMultiplier_get() const + static JSValue excitementMultiplier_get(JSContext* ctx, JSValue thisVal) { - auto rideEntry = GetEntry(); - if (rideEntry != nullptr) - { - return rideEntry->excitement_multiplier; - } - return 0; + auto rideEntry = GetEntry(thisVal); + return JS_NewInt32(ctx, rideEntry != nullptr ? rideEntry->excitement_multiplier : 0); } - int8_t intensityMultiplier_get() const + static JSValue intensityMultiplier_get(JSContext* ctx, JSValue thisVal) { - auto rideEntry = GetEntry(); - if (rideEntry != nullptr) - { - return rideEntry->intensity_multiplier; - } - return 0; + auto rideEntry = GetEntry(thisVal); + return JS_NewInt32(ctx, rideEntry != nullptr ? rideEntry->intensity_multiplier : 0); } - int8_t nauseaMultiplier_get() const + static JSValue nauseaMultiplier_get(JSContext* ctx, JSValue thisVal) { - auto rideEntry = GetEntry(); - if (rideEntry != nullptr) - { - return rideEntry->nausea_multiplier; - } - return 0; + auto rideEntry = GetEntry(thisVal); + return JS_NewInt32(ctx, rideEntry != nullptr ? rideEntry->nausea_multiplier : 0); } - uint8_t maxHeight_get() const + static JSValue maxHeight_get(JSContext* ctx, JSValue thisVal) { - auto rideEntry = GetEntry(); - if (rideEntry != nullptr) - { - return rideEntry->maxHeight; - } - return 0; + auto rideEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, rideEntry != nullptr ? rideEntry->maxHeight : 0); } - uint8_t shopItem_get() const + static JSValue shopItem_get(JSContext* ctx, JSValue thisVal) { - auto rideEntry = GetEntry(); - if (rideEntry != nullptr) - { - return EnumValue(rideEntry->shop_item[0]); - } - return 0; + auto rideEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, rideEntry != nullptr ? EnumValue(rideEntry->shop_item[0]) : 0); } - uint8_t shopItemSecondary_get() const + static JSValue shopItemSecondary_get(JSContext* ctx, JSValue thisVal) { - auto rideEntry = GetEntry(); - if (rideEntry != nullptr) - { - return EnumValue(rideEntry->shop_item[1]); - } - return 0; + auto rideEntry = GetEntry(thisVal); + return JS_NewUint32(ctx, rideEntry != nullptr ? EnumValue(rideEntry->shop_item[1]) : 0); } protected: - RideObject* GetObject() const + static RideObject* GetRideObject(JSValue thisVal) { - return static_cast(ScObject::GetObject()); + return static_cast(GetObject(thisVal)); } - const RideObjectEntry* GetEntry() const + static const RideObjectEntry* GetEntry(JSValue thisVal) { - auto obj = GetObject(); + auto obj = GetRideObject(thisVal); if (obj != nullptr) { return &obj->GetEntry(); @@ -778,101 +641,94 @@ namespace OpenRCT2::Scripting class ScSceneryObject : public ScObject { public: - ScSceneryObject(ObjectType type, int32_t index) - : ScObject(type, index) + static JSValue New(JSContext* ctx, ObjectType type, int32_t index) { - } - - static void Register(duk_context* ctx) - { - dukglue_set_base_class(ctx); - dukglue_register_property(ctx, &ScSceneryObject::sceneryGroups_get, nullptr, "sceneryGroups"); + JSValue obj = ScObject::New(ctx, type, index); + AddFuncs(ctx, obj); + return obj; } private: - std::vector sceneryGroups_get() const + static void AddFuncs(JSContext* ctx, JSValue obj) { - std::vector result; - auto obj = GetObject(); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("sceneryGroups", ScSceneryObject::sceneryGroups_get, nullptr), + }; + JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs)); + } + + static JSValue sceneryGroups_get(JSContext* ctx, JSValue thisVal) + { + JSValue result = JS_NewArray(ctx); + auto obj = GetSceneryObject(thisVal); if (obj != nullptr) { auto& scgDescriptor = obj->GetPrimarySceneryGroup(); if (scgDescriptor.HasValue()) { - result.push_back(scgDescriptor.ToString()); + JS_SetPropertyInt64(ctx, result, 0, JSFromStdString(ctx, scgDescriptor.ToString())); } } return result; } - SceneryObject* GetObject() const + protected: + static SceneryObject* GetSceneryObject(JSValue thisVal) { - return static_cast(ScObject::GetObject()); + return static_cast(GetObject(thisVal)); } }; - class ScSmallSceneryObject : public ScSceneryObject + class ScSmallSceneryObject final : public ScSceneryObject { public: - ScSmallSceneryObject(ObjectType type, int32_t index) - : ScSceneryObject(type, index) + static JSValue New(JSContext* ctx, ObjectType type, int32_t index) { - } - - static void Register(duk_context* ctx) - { - dukglue_set_base_class(ctx); - dukglue_register_property(ctx, &ScSmallSceneryObject::flags_get, nullptr, "flags"); - dukglue_register_property(ctx, &ScSmallSceneryObject::height_get, nullptr, "height"); - dukglue_register_property(ctx, &ScSmallSceneryObject::price_get, nullptr, "price"); - dukglue_register_property(ctx, &ScSmallSceneryObject::removalPrice_get, nullptr, "removalPrice"); + JSValue obj = ScSceneryObject::New(ctx, type, index); + AddFuncs(ctx, obj); + return obj; } private: - uint32_t flags_get() const + static void AddFuncs(JSContext* ctx, JSValue obj) { - auto sceneryEntry = GetLegacyData(); - if (sceneryEntry != nullptr) - { - return sceneryEntry->flags.holder; - } - return 0; + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("flags", ScSmallSceneryObject::flags_get, nullptr), + JS_CGETSET_DEF("height", ScSmallSceneryObject::height_get, nullptr), + JS_CGETSET_DEF("price", ScSmallSceneryObject::price_get, nullptr), + JS_CGETSET_DEF("removalPrice", ScSmallSceneryObject::removalPrice_get, nullptr), + }; + JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs)); } - uint8_t height_get() const + static JSValue flags_get(JSContext* ctx, JSValue thisVal) { - auto sceneryEntry = GetLegacyData(); - if (sceneryEntry != nullptr) - { - return sceneryEntry->height; - } - return 0; + auto sceneryEntry = GetLegacyData(thisVal); + return JS_NewUint32(ctx, sceneryEntry != nullptr ? sceneryEntry->flags.holder : 0); } - money64 price_get() const + static JSValue height_get(JSContext* ctx, JSValue thisVal) { - auto sceneryEntry = GetLegacyData(); - if (sceneryEntry != nullptr) - { - return sceneryEntry->price; - } - return 0; + auto sceneryEntry = GetLegacyData(thisVal); + return JS_NewUint32(ctx, sceneryEntry != nullptr ? sceneryEntry->height : 0); } - money64 removalPrice_get() const + static JSValue price_get(JSContext* ctx, JSValue thisVal) { - auto sceneryEntry = GetLegacyData(); - if (sceneryEntry != nullptr) - { - return sceneryEntry->removal_price; - } - return 0; + auto sceneryEntry = GetLegacyData(thisVal); + return JS_NewInt64(ctx, sceneryEntry != nullptr ? sceneryEntry->price : 0); + } + + static JSValue removalPrice_get(JSContext* ctx, JSValue thisVal) + { + auto sceneryEntry = GetLegacyData(thisVal); + return JS_NewInt64(ctx, sceneryEntry != nullptr ? sceneryEntry->removal_price : 0); } protected: - SmallSceneryEntry* GetLegacyData() const + static SmallSceneryEntry* GetLegacyData(JSValue thisVal) { - auto obj = GetObject(); + auto obj = GetSmallSceneryObject(thisVal); if (obj != nullptr) { return static_cast(obj->GetLegacyData()); @@ -880,106 +736,134 @@ namespace OpenRCT2::Scripting return nullptr; } - SmallSceneryObject* GetObject() const + static SmallSceneryObject* GetSmallSceneryObject(JSValue thisVal) { - return static_cast(ScObject::GetObject()); + return static_cast(GetObject(thisVal)); } }; - class ScLargeSceneryObjectTile + class ScLargeSceneryObjectTile; + extern ScLargeSceneryObjectTile gScLargeSceneryObjectTile; + + class ScLargeSceneryObjectTile final : public ScBase { private: - LargeSceneryTile _tile{}; + struct TileData + { + LargeSceneryTile _tile{}; + }; public: - ScLargeSceneryObjectTile(const LargeSceneryTile& tile) - : _tile(tile) + void Register(JSContext* ctx) { + RegisterBaseStr(ctx, "LargeSceneryObjectTile", Finalize); } - static void Register(duk_context* ctx) + JSValue New(JSContext* ctx, const LargeSceneryTile& tile) { - dukglue_register_property(ctx, &ScLargeSceneryObjectTile::offset_get, nullptr, "offset"); - dukglue_register_property(ctx, &ScLargeSceneryObjectTile::zClearance_get, nullptr, "zClearance"); - dukglue_register_property(ctx, &ScLargeSceneryObjectTile::hasSupports_get, nullptr, "hasSupports"); - dukglue_register_property(ctx, &ScLargeSceneryObjectTile::allowSupportsAbove_get, nullptr, "allowSupportsAbove"); - dukglue_register_property(ctx, &ScLargeSceneryObjectTile::corners_get, nullptr, "corners"); - dukglue_register_property(ctx, &ScLargeSceneryObjectTile::walls_get, nullptr, "walls"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("offset", ScLargeSceneryObjectTile::offset_get, nullptr), + JS_CGETSET_DEF("zClearance", ScLargeSceneryObjectTile::zClearance_get, nullptr), + JS_CGETSET_DEF("hasSupports", ScLargeSceneryObjectTile::hasSupports_get, nullptr), + JS_CGETSET_DEF("allowSupportsAbove", ScLargeSceneryObjectTile::allowSupportsAbove_get, nullptr), + JS_CGETSET_DEF("corners", ScLargeSceneryObjectTile::corners_get, nullptr), + JS_CGETSET_DEF("walls", ScLargeSceneryObjectTile::walls_get, nullptr), + }; + return MakeWithOpaque(ctx, funcs, new TileData{ tile }); } private: - DukValue offset_get() const + static void Finalize(JSRuntime* rt, JSValue thisVal) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - - auto start = _tile.offset; - return ToDuk(ctx, start); + TileData* data = GetObjectData(thisVal); + if (data) + delete data; } - int32_t zClearance_get() const + static JSValue offset_get(JSContext* ctx, JSValue thisVal) { - return _tile.zClearance; + TileData* data = GetObjectData(thisVal); + return ToJSValue(ctx, data->_tile.offset); } - bool hasSupports_get() const + static JSValue zClearance_get(JSContext* ctx, JSValue thisVal) { - return _tile.hasSupports; + TileData* data = GetObjectData(thisVal); + return JS_NewInt32(ctx, data->_tile.zClearance); } - bool allowSupportsAbove_get() const + static JSValue hasSupports_get(JSContext* ctx, JSValue thisVal) { - return _tile.allowSupportsAbove; + TileData* data = GetObjectData(thisVal); + return JS_NewBool(ctx, data->_tile.hasSupports); } - uint8_t corners_get() const + static JSValue allowSupportsAbove_get(JSContext* ctx, JSValue thisVal) { - return _tile.corners; + TileData* data = GetObjectData(thisVal); + return JS_NewBool(ctx, data->_tile.allowSupportsAbove); } - uint8_t walls_get() const + static JSValue corners_get(JSContext* ctx, JSValue thisVal) { - return _tile.walls; + TileData* data = GetObjectData(thisVal); + return JS_NewUint32(ctx, data->_tile.corners); + } + + static JSValue walls_get(JSContext* ctx, JSValue thisVal) + { + TileData* data = GetObjectData(thisVal); + return JS_NewUint32(ctx, data->_tile.walls); + } + + static TileData* GetObjectData(JSValue thisVal) + { + return gScLargeSceneryObjectTile.GetOpaque(thisVal); } }; - class ScLargeSceneryObject : public ScSceneryObject + class ScLargeSceneryObject final : public ScSceneryObject { public: - ScLargeSceneryObject(ObjectType type, int32_t index) - : ScSceneryObject(type, index) + static JSValue New(JSContext* ctx, ObjectType type, int32_t index) { - } - - static void Register(duk_context* ctx) - { - dukglue_set_base_class(ctx); - - dukglue_register_property(ctx, &ScLargeSceneryObject::tiles_get, nullptr, "tiles"); + JSValue obj = ScSceneryObject::New(ctx, type, index); + AddFuncs(ctx, obj); + return obj; } private: - std::vector> tiles_get() const + static void AddFuncs(JSContext* ctx, JSValue obj) { - std::vector> result; - auto entry = GetEntry(); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("tiles", ScLargeSceneryObject::tiles_get, nullptr), + }; + JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs)); + } + + static JSValue tiles_get(JSContext* ctx, JSValue thisVal) + { + JSValue result = JS_NewArray(ctx); + auto entry = GetEntry(thisVal); if (entry != nullptr) { + int64_t index = 0; for (auto& tile : entry->tiles) { - result.push_back(std::make_shared(tile)); + JS_SetPropertyInt64(ctx, result, index++, gScLargeSceneryObjectTile.New(ctx, tile)); } } return result; } - LargeSceneryObject* GetObject() const + static LargeSceneryObject* GetLargeSceneryObject(JSValue thisVal) { - return static_cast(ScObject::GetObject()); + return static_cast(GetObject(thisVal)); } - const LargeSceneryEntry* GetEntry() const + static const LargeSceneryEntry* GetEntry(JSValue thisVal) { - auto obj = GetObject(); + auto obj = GetLargeSceneryObject(thisVal); if (obj != nullptr) { return static_cast(obj->GetLegacyData()); @@ -988,82 +872,71 @@ namespace OpenRCT2::Scripting } }; - class ScWallObject : public ScSceneryObject + class ScWallObject final : public ScSceneryObject { public: - ScWallObject(ObjectType type, int32_t index) - : ScSceneryObject(type, index) + static JSValue New(JSContext* ctx, ObjectType type, int32_t index) { - } - - static void Register(duk_context* ctx) - { - dukglue_set_base_class(ctx); + return ScSceneryObject::New(ctx, type, index); } }; - class ScFootpathAdditionObject : public ScSceneryObject + class ScFootpathAdditionObject final : public ScSceneryObject { public: - ScFootpathAdditionObject(ObjectType type, int32_t index) - : ScSceneryObject(type, index) + static JSValue New(JSContext* ctx, ObjectType type, int32_t index) { - } - - static void Register(duk_context* ctx) - { - dukglue_set_base_class(ctx); + return ScSceneryObject::New(ctx, type, index); } }; - class ScBannerObject : public ScSceneryObject + class ScBannerObject final : public ScSceneryObject { public: - ScBannerObject(ObjectType type, int32_t index) - : ScSceneryObject(type, index) + static JSValue New(JSContext* ctx, ObjectType type, int32_t index) { - } - - static void Register(duk_context* ctx) - { - dukglue_set_base_class(ctx); + return ScSceneryObject::New(ctx, type, index); } }; - class ScSceneryGroupObject : public ScObject + class ScSceneryGroupObject final : public ScObject { public: - ScSceneryGroupObject(ObjectType type, int32_t index) - : ScObject(type, index) + static JSValue New(JSContext* ctx, ObjectType type, int32_t index) { - } - - static void Register(duk_context* ctx) - { - dukglue_set_base_class(ctx); - dukglue_register_property(ctx, &ScSceneryGroupObject::items_get, nullptr, "items"); + JSValue obj = ScObject::New(ctx, type, index); + AddFuncs(ctx, obj); + return obj; } private: - std::vector items_get() const + static void AddFuncs(JSContext* ctx, JSValue obj) { - std::vector result; - auto obj = GetObject(); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("items", ScSceneryGroupObject::items_get, nullptr), + }; + JS_SetPropertyFunctionList(ctx, obj, funcs, std::size(funcs)); + } + + static JSValue items_get(JSContext* ctx, JSValue thisVal) + { + JSValue result = JS_NewArray(ctx); + auto obj = GetSceneryGroupObject(thisVal); if (obj != nullptr) { auto& items = obj->GetItems(); + int64_t index = 0; for (const auto& item : items) { - result.push_back(item.ToString()); + JS_SetPropertyInt64(ctx, result, index++, JSFromStdString(ctx, item.ToString())); } } return result; } - protected: - SceneryGroupObject* GetObject() const + static SceneryGroupObject* GetSceneryGroupObject(JSValue thisVal) { - return static_cast(ScObject::GetObject()); + return static_cast(GetObject(thisVal)); } }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/object/ScObjectManager.cpp b/src/openrct2/scripting/bindings/object/ScObjectManager.cpp index c817e999eb..3f64ef7696 100644 --- a/src/openrct2/scripting/bindings/object/ScObjectManager.cpp +++ b/src/openrct2/scripting/bindings/object/ScObjectManager.cpp @@ -15,69 +15,78 @@ #include "../../../object/ObjectList.h" #include "../../../ride/RideData.h" #include "../../../windows/Intent.h" - #include "../../Duktape.hpp" #include "../../ScriptEngine.h" using namespace OpenRCT2; using namespace OpenRCT2::Scripting; -void ScObjectManager::Register(duk_context* ctx) +void ScObjectManager::Register(JSContext* ctx) { - dukglue_register_property(ctx, &ScObjectManager::installedObjects_get, nullptr, "installedObjects"); - dukglue_register_method(ctx, &ScObjectManager::installedObject_get, "getInstalledObject"); - dukglue_register_method(ctx, &ScObjectManager::load, "load"); - dukglue_register_method(ctx, &ScObjectManager::unload, "unload"); - dukglue_register_method(ctx, &ScObjectManager::getObject, "getObject"); - dukglue_register_method(ctx, &ScObjectManager::getAllObjects, "getAllObjects"); + RegisterBaseStr(ctx, "ObjectManager"); } -std::vector> ScObjectManager::installedObjects_get() const +JSValue ScObjectManager::New(JSContext* ctx) { - std::vector> result; + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("installedObjects", ScObjectManager::installedObjects_get, nullptr), + JS_CFUNC_DEF("getInstalledObject", 1, ScObjectManager::getInstalledObject), + JS_CFUNC_DEF("load", 2, ScObjectManager::load), + JS_CFUNC_DEF("unload", 1, ScObjectManager::unload), + JS_CFUNC_DEF("getObject", 2, ScObjectManager::getObject), + JS_CFUNC_DEF("getAllObjects", 1, ScObjectManager::getAllObjects), + }; + return MakeWithOpaque(ctx, funcs, nullptr); +} + +JSValue ScObjectManager::installedObjects_get(JSContext* ctx, JSValue thisVal) +{ + JSValue result = JS_NewArray(ctx); auto context = GetContext(); auto& objectManager = context->GetObjectRepository(); - auto count = objectManager.GetNumObjects(); - for (size_t i = 0; i < count; i++) + auto count = static_cast(objectManager.GetNumObjects()); + for (int64_t i = 0; i < count; i++) { - auto installedObject = std::make_shared(i); - result.push_back(installedObject); + JSValue installedObject = gScInstalledObject.New(ctx, i); + JS_SetPropertyInt64(ctx, result, i, installedObject); } return result; } -std::shared_ptr ScObjectManager::installedObject_get(const std::string& identifier) const +JSValue ScObjectManager::getInstalledObject(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { + JS_UNPACK_STR(identifier, ctx, argv[0]); + auto context = GetContext(); auto& objectRepository = context->GetObjectRepository(); auto object = objectRepository.FindObject(identifier); - return object != nullptr ? std::make_shared(object->Id) : nullptr; + return object != nullptr ? gScInstalledObject.New(ctx, object->Id) : JS_NULL; } -DukValue ScObjectManager::load(const DukValue& p1, const DukValue& p2) +JSValue ScObjectManager::load(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { auto context = GetContext(); - auto& scriptEngine = context->GetScriptEngine(); auto& objectRepository = context->GetObjectRepository(); auto& objectManager = context->GetObjectManager(); - auto ctx = scriptEngine.GetContext(); - if (p1.is_array()) + if (JS_IsArray(argv[0])) { // load(identifiers) + JS_UNPACK_ARRAY(array, ctx, argv[0]); std::vector descriptors; - for (const auto& item : p1.as_array()) - { - if (item.type() != DukValue::STRING) - throw DukException() << "Expected string for 'identifier'."; + int64_t length; + JS_GetLength(ctx, array, &length); - const auto& identifier = item.as_string(); + for (int64_t i = 0; i < length; i++) + { + JSValue item = JS_GetPropertyInt64(ctx, array, i); + JS_UNPACK_STR(identifier, ctx, item); descriptors.push_back(ObjectEntryDescriptor::Parse(identifier)); } - duk_push_array(ctx); - duk_uarridx_t index = 0; + JSValue result = JS_NewArray(ctx); + int64_t index = 0; for (const auto& descriptor : descriptors) { auto obj = objectManager.LoadObject(descriptor); @@ -85,38 +94,31 @@ DukValue ScObjectManager::load(const DukValue& p1, const DukValue& p2) { MarkAsResearched(obj); auto objIndex = objectManager.GetLoadedObjectEntryIndex(obj); - auto scLoadedObject = CreateScObject(scriptEngine.GetContext(), obj->GetObjectType(), objIndex); - scLoadedObject.push(); - duk_put_prop_index(ctx, -2, index); + auto scLoadedObject = CreateScObject(ctx, obj->GetObjectType(), objIndex); + JS_SetPropertyInt64(ctx, result, index, scLoadedObject); } else { - duk_push_null(ctx); - duk_put_prop_index(ctx, -2, index); + JS_SetPropertyInt64(ctx, result, index, JS_NULL); } index++; } RefreshResearchedItems(); - return DukValue::take_from_stack(ctx); + return result; } else { // load(identifier, index?) - if (p1.type() != DukValue::STRING) - throw DukException() << "Expected string for 'identifier'."; - - const auto& identifier = p1.as_string(); + JS_UNPACK_STR(identifier, ctx, argv[0]); auto descriptor = ObjectEntryDescriptor::Parse(identifier); auto installedObject = objectRepository.FindObject(descriptor); if (installedObject != nullptr) { - if (p2.type() != DukValue::UNDEFINED) + if (argc > 1 && !JS_IsUndefined(argv[1])) { - if (p2.type() != DukValue::NUMBER) - throw DukException() << "Expected number for 'index'."; + JS_UNPACK_UINT32(index, ctx, argv[1]); - auto index = static_cast(p2.as_uint()); auto limit = getObjectTypeLimit(installedObject->Type); if (index < limit) { @@ -131,7 +133,7 @@ DukValue ScObjectManager::load(const DukValue& p1, const DukValue& p2) MarkAsResearched(obj); RefreshResearchedItems(); auto objIndex = objectManager.GetLoadedObjectEntryIndex(obj); - return CreateScObject(scriptEngine.GetContext(), obj->GetObjectType(), objIndex); + return CreateScObject(ctx, obj->GetObjectType(), objIndex); } } } @@ -143,30 +145,28 @@ DukValue ScObjectManager::load(const DukValue& p1, const DukValue& p2) MarkAsResearched(obj); RefreshResearchedItems(); auto objIndex = objectManager.GetLoadedObjectEntryIndex(obj); - return CreateScObject(scriptEngine.GetContext(), obj->GetObjectType(), objIndex); + return CreateScObject(ctx, obj->GetObjectType(), objIndex); } } } } - return ToDuk(ctx, nullptr); + return JS_NULL; } -void ScObjectManager::unload(const DukValue& p1, const DukValue& p2) +JSValue ScObjectManager::unload(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { + // TODO: shouldn't this throw JS_THROW_IF_GAME_STATE_NOT_MUTABLE()? auto context = GetContext(); auto& objectManager = context->GetObjectManager(); - if (p1.type() == DukValue::STRING) + if (JS_IsString(argv[0])) { - const auto& szP1 = p1.as_string(); + JS_UNPACK_STR(szP1, ctx, argv[0]); auto objType = objectTypeFromString(szP1); if (objType != ObjectType::none) { // unload(type, index) - if (p2.type() != DukValue::NUMBER) - throw DukException() << "'index' is invalid."; - - auto objIndex = p2.as_uint(); + JS_UNPACK_UINT32(objIndex, ctx, argv[1]); auto obj = objectManager.GetLoadedObject(objType, objIndex); if (obj != nullptr) { @@ -179,27 +179,35 @@ void ScObjectManager::unload(const DukValue& p1, const DukValue& p2) objectManager.UnloadObjects({ ObjectEntryDescriptor::Parse(szP1) }); } } - else if (p1.is_array()) + else if (JS_IsArray(argv[0])) { // unload(identifiers) - auto identifiers = p1.as_array(); + JS_UNPACK_ARRAY(array, ctx, argv[0]); + int64_t length; + JS_GetLength(ctx, array, &length); + std::vector descriptors; - for (const auto& identifier : identifiers) + for (int64_t i = 0; i < length; i++) { - if (identifier.type() == DukValue::STRING) + JSValue item = JS_GetPropertyInt64(ctx, array, i); + if (JS_IsString(item)) { - descriptors.push_back(ObjectEntryDescriptor::Parse(identifier.as_string())); + JS_UNPACK_STR(identifier, ctx, item); + descriptors.push_back(ObjectEntryDescriptor::Parse(identifier)); } } objectManager.UnloadObjects(descriptors); } auto intent = Intent(INTENT_ACTION_REFRESH_SCENERY); ContextBroadcastIntent(&intent); + + return JS_UNDEFINED; } -DukValue ScObjectManager::getObject(const std::string& typez, int32_t index) const +JSValue ScObjectManager::getObject(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); + JS_UNPACK_STR(typez, ctx, argv[0]); + JS_UNPACK_INT32(index, ctx, argv[1]); auto& objManager = GetContext()->GetObjectManager(); auto type = objectTypeFromString(typez); @@ -213,35 +221,35 @@ DukValue ScObjectManager::getObject(const std::string& typez, int32_t index) con } else { - duk_error(ctx, DUK_ERR_ERROR, "Invalid object type."); + return JS_ThrowPlainError(ctx, "Invalid object type."); } - return ToDuk(ctx, nullptr); + return JS_NULL; } -std::vector ScObjectManager::getAllObjects(const std::string& typez) const +JSValue ScObjectManager::getAllObjects(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - auto& objManager = GetContext()->GetObjectManager(); + JS_UNPACK_STR(typez, ctx, argv[0]); - std::vector result; + auto& objManager = GetContext()->GetObjectManager(); auto type = objectTypeFromString(typez); + if (type != ObjectType::none) { - auto count = getObjectEntryGroupCount(type); - for (auto i = 0u; i < count; i++) + JSValue result = JS_NewArray(ctx); + size_t count = getObjectEntryGroupCount(type); + int64_t resultIndex = 0; + for (size_t i = 0; i < count; i++) { auto obj = objManager.GetLoadedObject(type, i); if (obj != nullptr) { - result.push_back(CreateScObject(ctx, type, i)); + JSValue scObj = CreateScObject(ctx, type, static_cast(i)); + JS_SetPropertyInt64(ctx, result, resultIndex++, scObj); } } + return result; } - else - { - duk_error(ctx, DUK_ERR_ERROR, "Invalid object type."); - } - return result; + return JS_ThrowPlainError(ctx, "Invalid object type."); } void ScObjectManager::MarkAsResearched(const Object* object) @@ -270,26 +278,42 @@ void ScObjectManager::RefreshResearchedItems() gSilentResearch = false; } -DukValue ScObjectManager::CreateScObject(duk_context* ctx, ObjectType type, int32_t index) +JSValue ScObjectManager::CreateScObject(JSContext* ctx, ObjectType type, int32_t index) { switch (type) { case ObjectType::ride: - return GetObjectAsDukValue(ctx, std::make_shared(type, index)); + { + return ScRideObject::New(ctx, type, index); + } case ObjectType::smallScenery: - return GetObjectAsDukValue(ctx, std::make_shared(type, index)); + { + return ScSmallSceneryObject::New(ctx, type, index); + } case ObjectType::largeScenery: - return GetObjectAsDukValue(ctx, std::make_shared(type, index)); + { + return ScLargeSceneryObject::New(ctx, type, index); + } case ObjectType::walls: - return GetObjectAsDukValue(ctx, std::make_shared(type, index)); + { + return ScWallObject::New(ctx, type, index); + } case ObjectType::pathAdditions: - return GetObjectAsDukValue(ctx, std::make_shared(type, index)); + { + return ScFootpathAdditionObject::New(ctx, type, index); + } case ObjectType::banners: - return GetObjectAsDukValue(ctx, std::make_shared(type, index)); + { + return ScBannerObject::New(ctx, type, index); + } case ObjectType::sceneryGroup: - return GetObjectAsDukValue(ctx, std::make_shared(type, index)); + { + return ScSceneryGroupObject::New(ctx, type, index); + } default: - return GetObjectAsDukValue(ctx, std::make_shared(type, index)); + { + return ScObject::New(ctx, type, index); + } } } diff --git a/src/openrct2/scripting/bindings/object/ScObjectManager.h b/src/openrct2/scripting/bindings/object/ScObjectManager.h index 16bdda6837..e82ea695b3 100644 --- a/src/openrct2/scripting/bindings/object/ScObjectManager.h +++ b/src/openrct2/scripting/bindings/object/ScObjectManager.h @@ -11,7 +11,6 @@ #ifdef ENABLE_SCRIPTING - #include "../../Duktape.hpp" #include "../../ScriptEngine.h" #include "ScInstalledObject.hpp" #include "ScObject.hpp" @@ -20,24 +19,28 @@ namespace OpenRCT2::Scripting { - class ScObjectManager + class ScObjectManager; + extern ScObjectManager gScObjectManager; + + class ScObjectManager final : public ScBase { public: - static void Register(duk_context* ctx); + void Register(JSContext* ctx); + JSValue New(JSContext* ctx); - std::vector> installedObjects_get() const; - std::shared_ptr installedObject_get(const std::string& identifier) const; - - DukValue load(const DukValue& p1, const DukValue& p2); - void unload(const DukValue& p1, const DukValue& p2); - - DukValue getObject(const std::string& typez, int32_t index) const; - std::vector getAllObjects(const std::string& typez) const; + static JSValue getObject(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + static JSValue getAllObjects(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); private: + static JSValue installedObjects_get(JSContext* ctx, JSValue thisVal); + static JSValue getInstalledObject(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + + static JSValue load(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + static JSValue unload(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + static void MarkAsResearched(const Object* object); static void RefreshResearchedItems(); - static DukValue CreateScObject(duk_context* ctx, ObjectType type, int32_t index); + static JSValue CreateScObject(JSContext* ctx, ObjectType type, int32_t index); }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/ride/ScRide.cpp b/src/openrct2/scripting/bindings/ride/ScRide.cpp index 59a9a9a874..9d3e630d59 100644 --- a/src/openrct2/scripting/bindings/ride/ScRide.cpp +++ b/src/openrct2/scripting/bindings/ride/ScRide.cpp @@ -12,692 +12,815 @@ #include "ScRide.hpp" #include "../../../Context.h" + #include "../../../core/EnumMap.hpp" #include "../../../core/UnitConversion.h" #include "../../../ride/Ride.h" #include "../../../ride/RideData.h" - #include "../../Duktape.hpp" #include "../../ScriptEngine.h" #include "../object/ScObject.hpp" namespace OpenRCT2::Scripting { - static const DukEnumMap BreakdownMap // The types of breakdowns. - ({ { "safety_cut_out", Breakdown::safetyCutOut }, - { "restraints_stuck_closed", Breakdown::restraintsStuckClosed }, - { "restraints_stuck_open", Breakdown::restraintsStuckOpen }, - { "doors_stuck_closed", Breakdown::doorsStuckClosed }, - { "doors_stuck_open", Breakdown::doorsStuckOpen }, - { "vehicle_malfunction", Breakdown::vehicleMalfunction }, - { "brakes_failure", Breakdown::brakesFailure }, - { "control_failure", Breakdown::controlFailure } }); + static const EnumMap BreakdownMap( + { + { "safety_cut_out", Breakdown::safetyCutOut }, + { "restraints_stuck_closed", Breakdown::restraintsStuckClosed }, + { "restraints_stuck_open", Breakdown::restraintsStuckOpen }, + { "doors_stuck_closed", Breakdown::doorsStuckClosed }, + { "doors_stuck_open", Breakdown::doorsStuckOpen }, + { "vehicle_malfunction", Breakdown::vehicleMalfunction }, + { "brakes_failure", Breakdown::brakesFailure }, + { "control_failure", Breakdown::controlFailure }, + }); - ScRide::ScRide(RideId rideId) - : _rideId(rideId) + void ScRide::Register(JSContext* ctx) { + RegisterBaseStr(ctx, "Ride", Finalize); } - int32_t ScRide::id_get() const + JSValue ScRide::New(JSContext* ctx, RideId rideId) { - return _rideId.ToUnderlying(); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("id", ScRide::id_get, nullptr), + JS_CGETSET_DEF("object", ScRide::object_get, nullptr), + JS_CGETSET_DEF("type", ScRide::type_get, nullptr), + JS_CGETSET_DEF("classification", ScRide::classification_get, nullptr), + JS_CGETSET_DEF("name", ScRide::name_get, ScRide::name_set), + JS_CGETSET_DEF("status", ScRide::status_get, nullptr), + JS_CGETSET_DEF("lifecycleFlags", ScRide::flags_get, ScRide::flags_set), + JS_CGETSET_DEF("flags", ScRide::flags_get, ScRide::flags_set), + JS_CGETSET_DEF("mode", ScRide::mode_get, ScRide::mode_set), + JS_CGETSET_DEF("departFlags", ScRide::departFlags_get, ScRide::departFlags_set), + JS_CGETSET_DEF("minimumWaitingTime", ScRide::minimumWaitingTime_get, ScRide::minimumWaitingTime_set), + JS_CGETSET_DEF("maximumWaitingTime", ScRide::maximumWaitingTime_get, ScRide::maximumWaitingTime_set), + JS_CGETSET_DEF("vehicles", ScRide::vehicles_get, nullptr), + JS_CGETSET_DEF("vehicleColours", ScRide::vehicleColours_get, ScRide::vehicleColours_set), + JS_CGETSET_DEF("colourSchemes", ScRide::colourSchemes_get, ScRide::colourSchemes_set), + JS_CGETSET_DEF("stationStyle", ScRide::stationStyle_get, ScRide::stationStyle_set), + JS_CGETSET_DEF("music", ScRide::music_get, ScRide::music_set), + JS_CGETSET_DEF("stations", ScRide::stations_get, nullptr), + JS_CGETSET_DEF("price", ScRide::price_get, ScRide::price_set), + JS_CGETSET_DEF("excitement", ScRide::excitement_get, ScRide::excitement_set), + JS_CGETSET_DEF("intensity", ScRide::intensity_get, ScRide::intensity_set), + JS_CGETSET_DEF("nausea", ScRide::nausea_get, ScRide::nausea_set), + JS_CGETSET_DEF("totalCustomers", ScRide::totalCustomers_get, ScRide::totalCustomers_set), + JS_CGETSET_DEF("buildDate", ScRide::buildDate_get, ScRide::buildDate_set), + JS_CGETSET_DEF("age", ScRide::age_get, nullptr), + JS_CGETSET_DEF("runningCost", ScRide::runningCost_get, ScRide::runningCost_set), + JS_CGETSET_DEF("totalProfit", ScRide::totalProfit_get, ScRide::totalProfit_set), + JS_CGETSET_DEF("inspectionInterval", ScRide::inspectionInterval_get, ScRide::inspectionInterval_set), + JS_CGETSET_DEF("value", ScRide::value_get, ScRide::value_set), + JS_CGETSET_DEF("downtime", ScRide::downtime_get, nullptr), + JS_CGETSET_DEF("liftHillSpeed", ScRide::liftHillSpeed_get, ScRide::liftHillSpeed_set), + JS_CGETSET_DEF("maxLiftHillSpeed", ScRide::maxLiftHillSpeed_get, nullptr), + JS_CGETSET_DEF("minLiftHillSpeed", ScRide::minLiftHillSpeed_get, nullptr), + JS_CGETSET_DEF("satisfaction", ScRide::satisfaction_get, nullptr), + JS_CGETSET_DEF("maxSpeed", ScRide::maxSpeed_get, nullptr), + JS_CGETSET_DEF("averageSpeed", ScRide::averageSpeed_get, nullptr), + JS_CGETSET_DEF("rideTime", ScRide::rideTime_get, nullptr), + JS_CGETSET_DEF("rideLength", ScRide::rideLength_get, nullptr), + JS_CGETSET_DEF("maxPositiveVerticalGs", ScRide::maxPositiveVerticalGs_get, nullptr), + JS_CGETSET_DEF("maxNegativeVerticalGs", ScRide::maxNegativeVerticalGs_get, nullptr), + JS_CGETSET_DEF("maxLateralGs", ScRide::maxLateralGs_get, nullptr), + JS_CGETSET_DEF("totalAirTime", ScRide::totalAirTime_get, nullptr), + JS_CGETSET_DEF("numDrops", ScRide::numDrops_get, nullptr), + JS_CGETSET_DEF("numLiftHills", ScRide::numLiftHills_get, nullptr), + JS_CGETSET_DEF("highestDropHeight", ScRide::highestDropHeight_get, nullptr), + JS_CGETSET_DEF("breakdown", ScRide::breakdown_get, nullptr), + JS_CFUNC_DEF("setBreakdown", 1, ScRide::setBreakdown), + JS_CFUNC_DEF("fixBreakdown", 0, ScRide::fixBreakdown), + }; + return MakeWithOpaque(ctx, funcs, new RideData{ rideId }); } - std::shared_ptr ScRide::object_get() + void ScRide::Finalize(JSRuntime* rt, JSValue thisVal) { - auto ride = GetRide(); + RideData* data = GetRideData(thisVal); + if (data) + delete data; + } + + ScRide::RideData* ScRide::GetRideData(JSValue thisVal) + { + return gScRide.GetOpaque(thisVal); + } + + Ride* ScRide::GetRide(JSValue thisVal) + { + RideData* data = GetRideData(thisVal); + return ::GetRide(data->_rideId); + } + + JSValue ScRide::id_get(JSContext* ctx, JSValue thisVal) + { + RideData* data = GetRideData(thisVal); + return JS_NewInt32(ctx, data->_rideId.ToUnderlying()); + } + + JSValue ScRide::object_get(JSContext* ctx, JSValue thisVal) + { + auto ride = GetRide(thisVal); if (ride != nullptr) { auto rideObject = GetContext()->GetObjectManager().GetLoadedObject(ride->subtype); if (rideObject != nullptr) { - return std::make_shared(ObjectType::ride, ride->subtype); + return ScRideObject::New(ctx, ObjectType::ride, ride->subtype); } } - return nullptr; + return JS_NULL; } - int32_t ScRide::type_get() const + JSValue ScRide::type_get(JSContext* ctx, JSValue thisVal) { - auto ride = GetRide(); - return ride != nullptr ? ride->type : 0; + auto ride = GetRide(thisVal); + return JS_NewInt32(ctx, ride != nullptr ? ride->type : 0); } - std::string ScRide::classification_get() const + JSValue ScRide::classification_get(JSContext* ctx, JSValue thisVal) { - auto ride = GetRide(); + auto ride = GetRide(thisVal); + std::string str = ""; if (ride != nullptr) { switch (ride->getClassification()) { case RideClassification::ride: - return "ride"; + str = "ride"; + break; case RideClassification::shopOrStall: - return "stall"; + str = "stall"; + break; case RideClassification::kioskOrFacility: - return "facility"; + str = "facility"; + break; } } - return ""; + return JSFromStdString(ctx, str); } - std::string ScRide::name_get() const + JSValue ScRide::name_get(JSContext* ctx, JSValue thisVal) { - auto ride = GetRide(); - return ride != nullptr ? ride->getName() : std::string(); + auto ride = GetRide(thisVal); + return JSFromStdString(ctx, ride != nullptr ? ride->getName() : std::string()); } - void ScRide::name_set(std::string value) + + JSValue ScRide::name_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - auto ride = GetRide(); + JS_UNPACK_STR(valueStr, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto ride = GetRide(thisVal); if (ride != nullptr) { - ride->customName = std::move(value); + ride->customName = std::move(valueStr); } + return JS_UNDEFINED; } - std::string ScRide::status_get() const + JSValue ScRide::status_get(JSContext* ctx, JSValue thisVal) { - auto ride = GetRide(); + auto ride = GetRide(thisVal); + std::string str = ""; + if (ride != nullptr) { switch (ride->status) { case RideStatus::closed: - return "closed"; + str = "closed"; + break; case RideStatus::open: - return "open"; + str = "open"; + break; case RideStatus::testing: - return "testing"; + str = "testing"; + break; case RideStatus::simulating: - return "simulating"; - case RideStatus::count: // Meaningless but necessary to satisfy -Wswitch - return "count"; + str = "simulating"; + break; + case RideStatus::count: + str = "count"; + break; } } - return ""; + return JSFromStdString(ctx, str); } - uint32_t ScRide::flags_get() const + JSValue ScRide::flags_get(JSContext* ctx, JSValue thisVal) { - auto ride = GetRide(); - return ride != nullptr ? ride->flags.holder : 0; + auto ride = GetRide(thisVal); + return JS_NewUint32(ctx, ride != nullptr ? ride->flags.holder : 0); } - void ScRide::flags_set(uint32_t value) + JSValue ScRide::flags_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - auto ride = GetRide(); + JS_UNPACK_UINT32(valueInt, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto ride = GetRide(thisVal); if (ride != nullptr) { - ride->flags.holder = value; + ride->flags.holder = valueInt; } + return JS_UNDEFINED; } - uint8_t ScRide::mode_get() const + JSValue ScRide::mode_get(JSContext* ctx, JSValue thisVal) { - auto ride = GetRide(); - return ride != nullptr ? static_cast(ride->mode) : 0; + auto ride = GetRide(thisVal); + return JS_NewUint32(ctx, ride != nullptr ? static_cast(ride->mode) : 0); } - void ScRide::mode_set(uint8_t value) + JSValue ScRide::mode_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - auto ride = GetRide(); + JS_UNPACK_UINT32(valueInt, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto ride = GetRide(thisVal); if (ride != nullptr) { - ride->mode = static_cast(value); + ride->mode = static_cast(valueInt); } + return JS_UNDEFINED; } - uint8_t ScRide::departFlags_get() const + JSValue ScRide::departFlags_get(JSContext* ctx, JSValue thisVal) { - auto ride = GetRide(); - return ride != nullptr ? ride->departFlags : 0; + auto ride = GetRide(thisVal); + return JS_NewUint32(ctx, ride != nullptr ? ride->departFlags : 0); } - void ScRide::departFlags_set(uint8_t value) + JSValue ScRide::departFlags_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - auto ride = GetRide(); + JS_UNPACK_UINT32(valueInt, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto ride = GetRide(thisVal); if (ride != nullptr) { - ride->departFlags = value; + ride->departFlags = static_cast(valueInt); } + return JS_UNDEFINED; } - uint8_t ScRide::minimumWaitingTime_get() const + JSValue ScRide::minimumWaitingTime_get(JSContext* ctx, JSValue thisVal) { - auto ride = GetRide(); - return ride != nullptr ? ride->minWaitingTime : 0; + auto ride = GetRide(thisVal); + return JS_NewUint32(ctx, ride != nullptr ? ride->minWaitingTime : 0); } - void ScRide::minimumWaitingTime_set(uint8_t value) + JSValue ScRide::minimumWaitingTime_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - auto ride = GetRide(); + JS_UNPACK_UINT32(valueInt, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto ride = GetRide(thisVal); if (ride != nullptr) { - ride->minWaitingTime = value; + ride->minWaitingTime = static_cast(valueInt); } + return JS_UNDEFINED; } - uint8_t ScRide::maximumWaitingTime_get() const + JSValue ScRide::maximumWaitingTime_get(JSContext* ctx, JSValue thisVal) { - auto ride = GetRide(); - return ride != nullptr ? ride->maxWaitingTime : 0; + auto ride = GetRide(thisVal); + return JS_NewUint32(ctx, ride != nullptr ? ride->maxWaitingTime : 0); } - void ScRide::maximumWaitingTime_set(uint8_t value) + JSValue ScRide::maximumWaitingTime_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - auto ride = GetRide(); + JS_UNPACK_UINT32(valueInt, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto ride = GetRide(thisVal); if (ride != nullptr) { - ride->maxWaitingTime = value; + ride->maxWaitingTime = static_cast(valueInt); } + return JS_UNDEFINED; } - std::vector ScRide::vehicles_get() const + JSValue ScRide::vehicles_get(JSContext* ctx, JSValue thisVal) { - std::vector result; - auto ride = GetRide(); + JSValue result = JS_NewArray(ctx); + auto ride = GetRide(thisVal); if (ride != nullptr) { + int64_t index = 0; std::for_each(std::begin(ride->vehicles), std::begin(ride->vehicles) + ride->numTrains, [&](auto& veh) { - result.push_back(veh.ToUnderlying()); + JS_SetPropertyInt64(ctx, result, index++, JS_NewUint32(ctx, veh.ToUnderlying())); }); } return result; } - std::vector ScRide::vehicleColours_get() const + JSValue ScRide::vehicleColours_get(JSContext* ctx, JSValue thisVal) { - std::vector result; - auto ride = GetRide(); + JSValue result = JS_NewArray(ctx); + auto ride = GetRide(thisVal); if (ride != nullptr) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); + int64_t index = 0; for (const auto& vehicleColour : ride->vehicleColours) { - result.push_back(ToDuk(ctx, vehicleColour)); + JS_SetPropertyInt64(ctx, result, index++, ToJSValue(ctx, vehicleColour)); } } return result; } - void ScRide::vehicleColours_set(const std::vector& value) + JSValue ScRide::vehicleColours_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto ride = GetRide(); - if (ride != nullptr) + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto ride = GetRide(thisVal); + if (ride != nullptr && JS_IsArray(value)) { - auto count = std::min(value.size(), std::size(ride->vehicleColours)); + int64_t length; + JS_GetLength(ctx, value, &length); + + auto count = std::min(static_cast(length), std::size(ride->vehicleColours)); for (size_t i = 0; i < count; i++) { - ride->vehicleColours[i] = FromDuk(value[i]); + JSValue item = JS_GetPropertyInt64(ctx, value, static_cast(i)); + ride->vehicleColours[i] = JSToVehicleColours(ctx, item); + JS_FreeValue(ctx, item); } } + return JS_UNDEFINED; } - std::vector ScRide::colourSchemes_get() const + JSValue ScRide::colourSchemes_get(JSContext* ctx, JSValue thisVal) { - std::vector result; - auto ride = GetRide(); + JSValue result = JS_NewArray(ctx); + auto ride = GetRide(thisVal); if (ride != nullptr) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); + int64_t index = 0; for (const auto& trackColour : ride->trackColours) { - result.push_back(ToDuk(ctx, trackColour)); + JS_SetPropertyInt64(ctx, result, index++, ToJSValue(ctx, trackColour)); } } return result; } - void ScRide::colourSchemes_set(const std::vector& value) + JSValue ScRide::colourSchemes_set(JSContext* ctx, JSValue thisVal, JSValue value) { - auto ride = GetRide(); - if (ride != nullptr) + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto ride = GetRide(thisVal); + if (ride != nullptr && JS_IsArray(value)) { - auto count = std::min(value.size(), std::size(ride->trackColours)); + int64_t length; + JS_GetLength(ctx, value, &length); + + auto count = std::min(static_cast(length), std::size(ride->trackColours)); for (size_t i = 0; i < count; i++) { - ride->trackColours[i] = FromDuk(value[i]); + JSValue item = JS_GetPropertyInt64(ctx, value, static_cast(i)); + ride->trackColours[i] = JSToTrackColour(ctx, item); + JS_FreeValue(ctx, item); } } + return JS_UNDEFINED; } - ObjectEntryIndex ScRide::stationStyle_get() const + JSValue ScRide::stationStyle_get(JSContext* ctx, JSValue thisVal) { - auto ride = GetRide(); - return ride != nullptr ? ride->entranceStyle : 0; + auto ride = GetRide(thisVal); + return JS_NewUint32(ctx, ride != nullptr ? ride->entranceStyle : 0); } - void ScRide::stationStyle_set(ObjectEntryIndex value) + JSValue ScRide::stationStyle_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - auto ride = GetRide(); + JS_UNPACK_UINT32(valueInt, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto ride = GetRide(thisVal); if (ride != nullptr) { - ride->entranceStyle = value; + ride->entranceStyle = static_cast(valueInt); } + return JS_UNDEFINED; } - ObjectEntryIndex ScRide::music_get() const + JSValue ScRide::music_get(JSContext* ctx, JSValue thisVal) { - auto ride = GetRide(); - return ride != nullptr ? ride->music : 0; + auto ride = GetRide(thisVal); + return JS_NewUint32(ctx, ride != nullptr ? ride->music : 0); } - void ScRide::music_set(ObjectEntryIndex value) + JSValue ScRide::music_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - auto ride = GetRide(); + JS_UNPACK_UINT32(valueInt, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto ride = GetRide(thisVal); if (ride != nullptr) { - ride->music = value; + ride->music = static_cast(valueInt); } + return JS_UNDEFINED; } - std::vector> ScRide::stations_get() const + JSValue ScRide::stations_get(JSContext* ctx, JSValue thisVal) { - std::vector> result; - auto ride = GetRide(); + JSValue result = JS_NewArray(ctx); + auto ride = GetRide(thisVal); if (ride != nullptr) { + int64_t index = 0; for (const auto& station : ride->getStations()) { - result.push_back(std::make_shared(ride->id, ride->getStationIndex(&station))); + JS_SetPropertyInt64(ctx, result, index++, gScRideStation.New(ctx, ride->id, ride->getStationIndex(&station))); } } return result; } - std::vector ScRide::price_get() const + JSValue ScRide::price_get(JSContext* ctx, JSValue thisVal) { - std::vector result; - auto ride = GetRide(); + JSValue result = JS_NewArray(ctx); + auto ride = GetRide(thisVal); if (ride != nullptr) { auto numPrices = ride->getNumPrices(); for (size_t i = 0; i < numPrices; i++) { - result.push_back(ride->price[i]); - }; + JS_SetPropertyInt64(ctx, result, static_cast(i), JS_NewInt64(ctx, ride->price[i])); + } } return result; } - void ScRide::price_set(const std::vector& value) + JSValue ScRide::price_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - auto ride = GetRide(); - if (ride != nullptr) + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto ride = GetRide(thisVal); + if (ride != nullptr && JS_IsArray(value)) { - auto numPrices = std::min(value.size(), ride->getNumPrices()); + int64_t length; + JS_GetLength(ctx, value, &length); + + auto numPrices = std::min(static_cast(length), ride->getNumPrices()); for (size_t i = 0; i < numPrices; i++) { - ride->price[i] = std::clamp(value[i], kRideMinPrice, kRideMaxPrice); + JSValue item = JS_GetPropertyInt64(ctx, value, static_cast(i)); + JS_UNPACK_MONEY64(money, ctx, item); + ride->price[i] = std::clamp(money, kRideMinPrice, kRideMaxPrice); } } + return JS_UNDEFINED; } - int32_t ScRide::excitement_get() const + JSValue ScRide::excitement_get(JSContext* ctx, JSValue thisVal) { - auto ride = GetRide(); - return ride != nullptr ? ride->ratings.excitement : 0; + auto ride = GetRide(thisVal); + return JS_NewInt32(ctx, ride != nullptr ? ride->ratings.excitement : 0); } - void ScRide::excitement_set(int32_t value) + + JSValue ScRide::excitement_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - auto ride = GetRide(); + JS_UNPACK_INT32(valueInt, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto ride = GetRide(thisVal); if (ride != nullptr) { - ride->ratings.excitement = value; + ride->ratings.excitement = valueInt; } + return JS_UNDEFINED; } - int32_t ScRide::intensity_get() const + JSValue ScRide::intensity_get(JSContext* ctx, JSValue thisVal) { - auto ride = GetRide(); - return ride != nullptr ? ride->ratings.intensity : 0; + auto ride = GetRide(thisVal); + return JS_NewInt32(ctx, ride != nullptr ? ride->ratings.intensity : 0); } - void ScRide::intensity_set(int32_t value) + + JSValue ScRide::intensity_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - auto ride = GetRide(); + JS_UNPACK_INT32(valueInt, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto ride = GetRide(thisVal); if (ride != nullptr) { - ride->ratings.intensity = value; + ride->ratings.intensity = valueInt; } + return JS_UNDEFINED; } - int32_t ScRide::nausea_get() const + JSValue ScRide::nausea_get(JSContext* ctx, JSValue thisVal) { - auto ride = GetRide(); - return ride != nullptr ? ride->ratings.nausea : 0; + auto ride = GetRide(thisVal); + return JS_NewInt32(ctx, ride != nullptr ? ride->ratings.nausea : 0); } - void ScRide::nausea_set(int32_t value) + + JSValue ScRide::nausea_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - auto ride = GetRide(); + JS_UNPACK_INT32(valueInt, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto ride = GetRide(thisVal); if (ride != nullptr) { - ride->ratings.nausea = value; + ride->ratings.nausea = valueInt; } + return JS_UNDEFINED; } - int32_t ScRide::totalCustomers_get() const + JSValue ScRide::totalCustomers_get(JSContext* ctx, JSValue thisVal) { - auto ride = GetRide(); - return ride != nullptr ? ride->totalCustomers : 0; + auto ride = GetRide(thisVal); + return JS_NewInt32(ctx, ride != nullptr ? ride->totalCustomers : 0); } - void ScRide::totalCustomers_set(int32_t value) + + JSValue ScRide::totalCustomers_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - auto ride = GetRide(); + JS_UNPACK_INT32(valueInt, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto ride = GetRide(thisVal); if (ride != nullptr) { - ride->totalCustomers = value; + ride->totalCustomers = valueInt; } + return JS_UNDEFINED; } - int32_t ScRide::buildDate_get() const + JSValue ScRide::buildDate_get(JSContext* ctx, JSValue thisVal) { - auto ride = GetRide(); - return ride != nullptr ? ride->buildDate : 0; + auto ride = GetRide(thisVal); + return JS_NewInt32(ctx, ride != nullptr ? ride->buildDate : 0); } - void ScRide::buildDate_set(int32_t value) + + JSValue ScRide::buildDate_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - auto ride = GetRide(); + JS_UNPACK_INT32(valueInt, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto ride = GetRide(thisVal); if (ride != nullptr) { - ride->buildDate = value; + ride->buildDate = valueInt; } + return JS_UNDEFINED; } - int32_t ScRide::age_get() const + JSValue ScRide::age_get(JSContext* ctx, JSValue thisVal) { - auto ride = GetRide(); - return ride != nullptr ? ride->getAge() : 0; + auto ride = GetRide(thisVal); + return JS_NewInt32(ctx, ride != nullptr ? ride->getAge() : 0); } - money64 ScRide::runningCost_get() const + JSValue ScRide::runningCost_get(JSContext* ctx, JSValue thisVal) { - auto ride = GetRide(); - return ride != nullptr ? ride->upkeepCost : 0; + auto ride = GetRide(thisVal); + return JS_NewInt64(ctx, ride != nullptr ? ride->upkeepCost : 0); } - void ScRide::runningCost_set(money64 value) + + JSValue ScRide::runningCost_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - auto ride = GetRide(); + JS_UNPACK_MONEY64(valueInt, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto ride = GetRide(thisVal); if (ride != nullptr) { - ride->upkeepCost = value; + ride->upkeepCost = valueInt; } + return JS_UNDEFINED; } - int32_t ScRide::totalProfit_get() const + JSValue ScRide::totalProfit_get(JSContext* ctx, JSValue thisVal) { - auto ride = GetRide(); - return ride != nullptr ? ride->totalProfit : 0; + auto ride = GetRide(thisVal); + return JS_NewInt32(ctx, ride != nullptr ? ride->totalProfit : 0); } - void ScRide::totalProfit_set(int32_t value) + + JSValue ScRide::totalProfit_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - auto ride = GetRide(); + JS_UNPACK_MONEY64(valueInt, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto ride = GetRide(thisVal); if (ride != nullptr) { - ride->totalProfit = value; + ride->totalProfit = valueInt; } + return JS_UNDEFINED; } - uint8_t ScRide::inspectionInterval_get() const + JSValue ScRide::inspectionInterval_get(JSContext* ctx, JSValue thisVal) { - auto ride = GetRide(); - return ride != nullptr ? EnumValue(ride->inspectionInterval) : 0; + auto ride = GetRide(thisVal); + return JS_NewUint32(ctx, ride != nullptr ? EnumValue(ride->inspectionInterval) : 0); } - void ScRide::inspectionInterval_set(uint8_t value) + + JSValue ScRide::inspectionInterval_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - auto ride = GetRide(); + JS_UNPACK_UINT32(valueInt, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto ride = GetRide(thisVal); if (ride != nullptr) { auto clamped = std::clamp( - value, EnumValue(RideInspection::every10Minutes), EnumValue(RideInspection::never)); + static_cast(valueInt), EnumValue(RideInspection::every10Minutes), EnumValue(RideInspection::never)); ride->inspectionInterval = static_cast(clamped); } + return JS_UNDEFINED; } - DukValue ScRide::value_get() const + JSValue ScRide::value_get(JSContext* ctx, JSValue thisVal) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - auto ride = GetRide(); + auto ride = GetRide(thisVal); if (ride != nullptr && ride->value != kRideValueUndefined) { - return ToDuk(ctx, ride->value); + return JS_NewInt32(ctx, ride->value); } - return ToDuk(ctx, nullptr); + return JS_NULL; } - void ScRide::value_set(const DukValue& value) + JSValue ScRide::value_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - auto ride = GetRide(); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto ride = GetRide(thisVal); if (ride != nullptr) { - if (value.type() == DukValue::Type::NUMBER) + if (JS_IsNumber(value)) { - ride->value = value.as_int(); + JS_UNPACK_MONEY64(valueInt, ctx, value); + ride->value = valueInt; } else { ride->value = kRideValueUndefined; } } + return JS_UNDEFINED; } - Ride* ScRide::GetRide() const + JSValue ScRide::downtime_get(JSContext* ctx, JSValue thisVal) { - return ::GetRide(_rideId); + auto ride = GetRide(thisVal); + return JS_NewUint32(ctx, ride != nullptr ? ride->downtime : 0); } - void ScRide::SetBreakdown(const std::string& breakDown) + JSValue ScRide::liftHillSpeed_get(JSContext* ctx, JSValue thisVal) { - ThrowIfGameStateNotMutable(); - auto ride = GetRide(); - if (ride != nullptr && ride->canBreakDown() && ride->status == RideStatus::open) + auto ride = GetRide(thisVal); + return JS_NewUint32(ctx, ride != nullptr ? ride->liftHillSpeed : 0); + } + + JSValue ScRide::liftHillSpeed_set(JSContext* ctx, JSValue thisVal, JSValue value) + { + JS_UNPACK_UINT32(valueInt, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto ride = GetRide(thisVal); + if (ride != nullptr) { - auto it = BreakdownMap.find(breakDown); - if (it == BreakdownMap.end()) - return; - RidePrepareBreakdown(*ride, it->second); + ride->liftHillSpeed = static_cast(valueInt); } + return JS_UNDEFINED; } - void ScRide::FixBreakdown() + JSValue ScRide::maxLiftHillSpeed_get(JSContext* ctx, JSValue thisVal) { - ThrowIfGameStateNotMutable(); - auto ride = GetRide(); - if (ride != nullptr && ride->canBreakDown()) - { - RideFixBreakdown(*ride, 0); - } + auto ride = GetRide(thisVal); + auto maximum = ride != nullptr ? ride->getRideTypeDescriptor().LiftData.maximum_speed : 0; + return JS_NewUint32(ctx, maximum); } - std::string ScRide::getBreakdown() const + JSValue ScRide::minLiftHillSpeed_get(JSContext* ctx, JSValue thisVal) { - auto ride = GetRide(); + auto ride = GetRide(thisVal); + auto minimum = ride != nullptr ? ride->getRideTypeDescriptor().LiftData.minimum_speed : 0; + return JS_NewUint32(ctx, minimum); + } + JSValue ScRide::satisfaction_get(JSContext* ctx, JSValue thisVal) + { + auto ride = GetRide(thisVal); + return JS_NewUint32(ctx, ride != nullptr ? ride->satisfaction * 5 : 0); + } + + JSValue ScRide::maxSpeed_get(JSContext* ctx, JSValue thisVal) + { + auto ride = GetRide(thisVal); + return JS_NewFloat64(ctx, ride != nullptr ? ToHumanReadableSpeed(ride->maxSpeed) : 0); + } + + JSValue ScRide::averageSpeed_get(JSContext* ctx, JSValue thisVal) + { + auto ride = GetRide(thisVal); + return JS_NewFloat64(ctx, ride != nullptr ? ToHumanReadableSpeed(ride->averageSpeed) : 0); + } + + JSValue ScRide::rideTime_get(JSContext* ctx, JSValue thisVal) + { + auto ride = GetRide(thisVal); + return JS_NewInt32(ctx, ride != nullptr ? ride->getTotalTime() : 0); + } + + JSValue ScRide::rideLength_get(JSContext* ctx, JSValue thisVal) + { + auto ride = GetRide(thisVal); + return JS_NewFloat64(ctx, ride != nullptr ? ToHumanReadableRideLength(ride->getTotalLength()) : 0); + } + + JSValue ScRide::maxPositiveVerticalGs_get(JSContext* ctx, JSValue thisVal) + { + auto ride = GetRide(thisVal); + return JS_NewFloat64(ctx, ride != nullptr ? ride->maxPositiveVerticalG / 100.0 : 0); + } + + JSValue ScRide::maxNegativeVerticalGs_get(JSContext* ctx, JSValue thisVal) + { + auto ride = GetRide(thisVal); + return JS_NewFloat64(ctx, ride != nullptr ? ride->maxNegativeVerticalG / 100.0 : 0); + } + + JSValue ScRide::maxLateralGs_get(JSContext* ctx, JSValue thisVal) + { + auto ride = GetRide(thisVal); + return JS_NewFloat64(ctx, ride != nullptr ? ride->maxLateralG / 100.0 : 0); + } + + JSValue ScRide::totalAirTime_get(JSContext* ctx, JSValue thisVal) + { + auto ride = GetRide(thisVal); + return JS_NewFloat64(ctx, ride != nullptr ? ToHumanReadableAirTime(ride->totalAirTime) / 100.0 : 0); + } + + JSValue ScRide::numDrops_get(JSContext* ctx, JSValue thisVal) + { + auto ride = GetRide(thisVal); + return JS_NewUint32(ctx, ride != nullptr ? ride->numDrops : 0); + } + + JSValue ScRide::numLiftHills_get(JSContext* ctx, JSValue thisVal) + { + auto ride = GetRide(thisVal); + return JS_NewUint32(ctx, ride != nullptr ? ride->numPoweredLifts : 0); + } + + JSValue ScRide::highestDropHeight_get(JSContext* ctx, JSValue thisVal) + { + auto ride = GetRide(thisVal); + return JS_NewFloat64(ctx, ride != nullptr ? ride->highestDropHeight : 0); + } + + JSValue ScRide::breakdown_get(JSContext* ctx, JSValue thisVal) + { + auto ride = GetRide(thisVal); if (ride != nullptr) { if (!ride->flags.has(RideFlag::brokenDown)) { - return "none"; + return JSFromStdString(ctx, "none"); } auto it = BreakdownMap.find(ride->breakdownReason); if (it != BreakdownMap.end()) - return std::string(it->first); + return JSFromStdString(ctx, std::string(it->first)); } - return {}; + return JSFromStdString(ctx, ""); } - uint8_t ScRide::downtime_get() const + JSValue ScRide::setBreakdown(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto ride = GetRide(); - return ride != nullptr ? ride->downtime : 0; - } + JS_UNPACK_STR(breakDown, ctx, argv[0]); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); - uint8_t ScRide::liftHillSpeed_get() const - { - auto ride = GetRide(); - return ride != nullptr ? ride->liftHillSpeed : 0; - } - - void ScRide::lifthillSpeed_set(uint8_t value) - { - ThrowIfGameStateNotMutable(); - - auto ride = GetRide(); - if (ride) + auto ride = GetRide(thisVal); + if (ride != nullptr && ride->canBreakDown() && ride->status == RideStatus::open) { - ride->liftHillSpeed = value; + auto it = BreakdownMap.find(breakDown); + if (it != BreakdownMap.end()) + { + RidePrepareBreakdown(*ride, it->second); + } } + return JS_UNDEFINED; } - uint8_t ScRide::maxLiftHillSpeed_get() const + JSValue ScRide::fixBreakdown(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto ride = GetRide(); - return ride != nullptr ? ride->getRideTypeDescriptor().LiftData.maximum_speed : 0; - } + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); - uint8_t ScRide::minLiftHillSpeed_get() const - { - auto ride = GetRide(); - return ride != nullptr ? ride->getRideTypeDescriptor().LiftData.minimum_speed : 0; - } - - uint8_t ScRide::satisfaction_get() const - { - auto ride = GetRide(); - return ride != nullptr ? ride->satisfaction * 5 : 0; - } - - double ScRide::maxSpeed_get() const - { - auto ride = GetRide(); - return ride != nullptr ? ToHumanReadableSpeed(ride->maxSpeed) : 0; - } - - double ScRide::averageSpeed_get() const - { - auto ride = GetRide(); - return ride != nullptr ? ToHumanReadableSpeed(ride->averageSpeed) : 0; - } - - int32_t ScRide::rideTime_get() const - { - auto ride = GetRide(); - return ride != nullptr ? ride->getTotalTime() : 0; - } - - double ScRide::rideLength_get() const - { - auto ride = GetRide(); - return ride != nullptr ? ToHumanReadableRideLength(ride->getTotalLength()) : 0; - } - - double ScRide::maxPositiveVerticalGs_get() const - { - auto ride = GetRide(); - return ride != nullptr ? ride->maxPositiveVerticalG / 100.0 : 0; - } - - double ScRide::maxNegativeVerticalGs_get() const - { - auto ride = GetRide(); - return ride != nullptr ? ride->maxNegativeVerticalG / 100.0 : 0; - } - - double ScRide::maxLateralGs_get() const - { - auto ride = GetRide(); - return ride != nullptr ? ride->maxLateralG / 100.0 : 0; - } - - double ScRide::totalAirTime_get() const - { - auto ride = GetRide(); - return ride != nullptr ? ToHumanReadableAirTime(ride->totalAirTime) / 100.0 : 0; - } - - uint8_t ScRide::numDrops_get() const - { - auto ride = GetRide(); - return ride != nullptr ? ride->numDrops : 0; - } - - uint8_t ScRide::numLiftHills_get() const - { - auto ride = GetRide(); - return ride != nullptr ? ride->numPoweredLifts : 0; - } - - double ScRide::highestDropHeight_get() const - { - auto ride = GetRide(); - return ride != nullptr ? ride->highestDropHeight : 0; - } - - void ScRide::Register(duk_context* ctx) - { - dukglue_register_property(ctx, &ScRide::id_get, nullptr, "id"); - dukglue_register_property(ctx, &ScRide::object_get, nullptr, "object"); - dukglue_register_property(ctx, &ScRide::type_get, nullptr, "type"); - dukglue_register_property(ctx, &ScRide::classification_get, nullptr, "classification"); - dukglue_register_property(ctx, &ScRide::name_get, &ScRide::name_set, "name"); - dukglue_register_property(ctx, &ScRide::status_get, nullptr, "status"); - dukglue_register_property(ctx, &ScRide::flags_get, &ScRide::flags_set, "lifecycleFlags"); - dukglue_register_property(ctx, &ScRide::flags_get, &ScRide::flags_set, "flags"); - dukglue_register_property(ctx, &ScRide::mode_get, &ScRide::mode_set, "mode"); - dukglue_register_property(ctx, &ScRide::departFlags_get, &ScRide::departFlags_set, "departFlags"); - dukglue_register_property(ctx, &ScRide::minimumWaitingTime_get, &ScRide::minimumWaitingTime_set, "minimumWaitingTime"); - dukglue_register_property(ctx, &ScRide::maximumWaitingTime_get, &ScRide::maximumWaitingTime_set, "maximumWaitingTime"); - dukglue_register_property(ctx, &ScRide::vehicles_get, nullptr, "vehicles"); - dukglue_register_property(ctx, &ScRide::vehicleColours_get, &ScRide::vehicleColours_set, "vehicleColours"); - dukglue_register_property(ctx, &ScRide::colourSchemes_get, &ScRide::colourSchemes_set, "colourSchemes"); - dukglue_register_property(ctx, &ScRide::stationStyle_get, &ScRide::stationStyle_set, "stationStyle"); - dukglue_register_property(ctx, &ScRide::music_get, &ScRide::music_set, "music"); - dukglue_register_property(ctx, &ScRide::stations_get, nullptr, "stations"); - dukglue_register_property(ctx, &ScRide::price_get, &ScRide::price_set, "price"); - dukglue_register_property(ctx, &ScRide::excitement_get, &ScRide::excitement_set, "excitement"); - dukglue_register_property(ctx, &ScRide::intensity_get, &ScRide::intensity_set, "intensity"); - dukglue_register_property(ctx, &ScRide::nausea_get, &ScRide::nausea_set, "nausea"); - dukglue_register_property(ctx, &ScRide::totalCustomers_get, &ScRide::totalCustomers_set, "totalCustomers"); - dukglue_register_property(ctx, &ScRide::buildDate_get, &ScRide::buildDate_set, "buildDate"); - dukglue_register_property(ctx, &ScRide::age_get, nullptr, "age"); - dukglue_register_property(ctx, &ScRide::runningCost_get, &ScRide::runningCost_set, "runningCost"); - dukglue_register_property(ctx, &ScRide::totalProfit_get, &ScRide::totalProfit_set, "totalProfit"); - dukglue_register_property(ctx, &ScRide::inspectionInterval_get, &ScRide::inspectionInterval_set, "inspectionInterval"); - dukglue_register_property(ctx, &ScRide::value_get, &ScRide::value_set, "value"); - dukglue_register_property(ctx, &ScRide::downtime_get, nullptr, "downtime"); - dukglue_register_property(ctx, &ScRide::liftHillSpeed_get, &ScRide::lifthillSpeed_set, "liftHillSpeed"); - dukglue_register_property(ctx, &ScRide::maxLiftHillSpeed_get, nullptr, "maxLiftHillSpeed"); - dukglue_register_property(ctx, &ScRide::minLiftHillSpeed_get, nullptr, "minLiftHillSpeed"); - dukglue_register_property(ctx, &ScRide::satisfaction_get, nullptr, "satisfaction"); - dukglue_register_property(ctx, &ScRide::maxSpeed_get, nullptr, "maxSpeed"); - dukglue_register_property(ctx, &ScRide::averageSpeed_get, nullptr, "averageSpeed"); - dukglue_register_property(ctx, &ScRide::rideTime_get, nullptr, "rideTime"); - dukglue_register_property(ctx, &ScRide::rideLength_get, nullptr, "rideLength"); - dukglue_register_property(ctx, &ScRide::maxPositiveVerticalGs_get, nullptr, "maxPositiveVerticalGs"); - dukglue_register_property(ctx, &ScRide::maxNegativeVerticalGs_get, nullptr, "maxNegativeVerticalGs"); - dukglue_register_property(ctx, &ScRide::maxLateralGs_get, nullptr, "maxLateralGs"); - dukglue_register_property(ctx, &ScRide::totalAirTime_get, nullptr, "totalAirTime"); - dukglue_register_property(ctx, &ScRide::numDrops_get, nullptr, "numDrops"); - dukglue_register_property(ctx, &ScRide::numLiftHills_get, nullptr, "numLiftHills"); - dukglue_register_property(ctx, &ScRide::highestDropHeight_get, nullptr, "highestDropHeight"); - dukglue_register_property(ctx, &ScRide::getBreakdown, nullptr, "breakdown"); - dukglue_register_method(ctx, &ScRide::SetBreakdown, "setBreakdown"); - dukglue_register_method(ctx, &ScRide::FixBreakdown, "fixBreakdown"); + auto ride = GetRide(thisVal); + if (ride != nullptr && ride->canBreakDown()) + { + RideFixBreakdown(*ride, 0); + } + return JS_UNDEFINED; } } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/ride/ScRide.hpp b/src/openrct2/scripting/bindings/ride/ScRide.hpp index f6fb42474a..fa9565d55c 100644 --- a/src/openrct2/scripting/bindings/ride/ScRide.hpp +++ b/src/openrct2/scripting/bindings/ride/ScRide.hpp @@ -9,197 +9,144 @@ #pragma once -#ifdef ENABLE_SCRIPTING +#include "../../../Context.h" +#include "../../../ride/Ride.h" +#include "../../ScriptEngine.h" +#include "../object/ScObject.hpp" +#include "ScRideStation.hpp" - #include "../../../Context.h" - #include "../../../ride/Ride.h" - #include "../../Duktape.hpp" - #include "../../ScriptEngine.h" - #include "../object/ScObject.hpp" - #include "ScRideStation.hpp" +#ifdef ENABLE_SCRIPTING namespace OpenRCT2::Scripting { - template<> - inline DukValue ToDuk(duk_context* ctx, const TrackColour& value) + + inline JSValue ToJSValue(JSContext* ctx, const VehicleColour& value) { - DukObject obj(ctx); - obj.Set("main", EnumValue(value.main)); - obj.Set("additional", EnumValue(value.additional)); - obj.Set("supports", EnumValue(value.supports)); - return obj.Take(); + JSValue obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "body", JS_NewInt32(ctx, EnumValue(value.Body))); + JS_SetPropertyStr(ctx, obj, "trim", JS_NewInt32(ctx, EnumValue(value.Trim))); + JS_SetPropertyStr(ctx, obj, "ternary", JS_NewInt32(ctx, EnumValue(value.Tertiary))); + JS_SetPropertyStr(ctx, obj, "tertiary", JS_NewInt32(ctx, EnumValue(value.Tertiary))); + return obj; } - template<> - inline TrackColour FromDuk(const DukValue& s) - { - TrackColour result{}; - result.main = static_cast(AsOrDefault(s["main"], 0)); - result.additional = static_cast(AsOrDefault(s["additional"], 0)); - result.supports = static_cast(AsOrDefault(s["supports"], 0)); - return result; - } - - template<> - inline DukValue ToDuk(duk_context* ctx, const VehicleColour& value) - { - DukObject obj(ctx); - obj.Set("body", EnumValue(value.Body)); - obj.Set("trim", EnumValue(value.Trim)); - obj.Set("ternary", EnumValue(value.Tertiary)); - obj.Set("tertiary", EnumValue(value.Tertiary)); - return obj.Take(); - } - - template<> - inline VehicleColour FromDuk(const DukValue& s) + inline VehicleColour JSToVehicleColours(JSContext* ctx, JSValue val) { VehicleColour result{}; - result.Body = static_cast(AsOrDefault(s["body"], 0)); - result.Trim = static_cast(AsOrDefault(s["trim"], 0)); - result.Tertiary = static_cast(AsOrDefault(s["ternary"], 0)); - result.Tertiary = static_cast(AsOrDefault(s["tertiary"], EnumValue(result.Tertiary))); + result.Body = static_cast(AsOrDefault(ctx, val, "body", 0)); + result.Trim = static_cast(AsOrDefault(ctx, val, "trim", 0)); + result.Tertiary = static_cast(AsOrDefault(ctx, val, "ternary", 0)); + result.Tertiary = static_cast( + AsOrDefault(ctx, val, "tertiary", static_cast(result.Tertiary))); return result; } - class ScRide + inline JSValue ToJSValue(JSContext* ctx, const TrackColour& value) + { + JSValue obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "main", JS_NewInt32(ctx, EnumValue(value.main))); + JS_SetPropertyStr(ctx, obj, "additional", JS_NewInt32(ctx, EnumValue(value.additional))); + JS_SetPropertyStr(ctx, obj, "supports", JS_NewInt32(ctx, EnumValue(value.supports))); + return obj; + } + + inline TrackColour JSToTrackColour(JSContext* ctx, JSValue val) + { + TrackColour result{}; + result.main = static_cast(AsOrDefault(ctx, val, "main", 0)); + result.additional = static_cast(AsOrDefault(ctx, val, "additional", 0)); + result.supports = static_cast(AsOrDefault(ctx, val, "supports", 0)); + return result; + } + + class ScRide; + extern ScRide gScRide; + + class ScRide final : public ScBase { private: - RideId _rideId = RideId::GetNull(); + struct RideData + { + RideId _rideId = RideId::GetNull(); + }; public: - ScRide(RideId rideId); + void Register(JSContext* ctx); + JSValue New(JSContext* ctx, RideId rideId); private: - int32_t id_get() const; + static void Finalize(JSRuntime* rt, JSValue thisVal); + static RideData* GetRideData(JSValue thisVal); + static Ride* GetRide(JSValue thisVal); - std::shared_ptr object_get(); - - int32_t type_get() const; - - std::string classification_get() const; - - std::string name_get() const; - void name_set(std::string value); - - std::string status_get() const; - - uint32_t flags_get() const; - - void flags_set(uint32_t value); - - uint8_t mode_get() const; - - void mode_set(uint8_t value); - - uint8_t departFlags_get() const; - - void departFlags_set(uint8_t value); - - uint8_t minimumWaitingTime_get() const; - - void minimumWaitingTime_set(uint8_t value); - - uint8_t maximumWaitingTime_get() const; - - void maximumWaitingTime_set(uint8_t value); - - std::vector vehicles_get() const; - - std::vector vehicleColours_get() const; - - void vehicleColours_set(const std::vector& value); - - std::vector colourSchemes_get() const; - - void colourSchemes_set(const std::vector& value); - - ObjectEntryIndex stationStyle_get() const; - - void stationStyle_set(ObjectEntryIndex value); - - ObjectEntryIndex music_get() const; - - void music_set(ObjectEntryIndex value); - - std::vector> stations_get() const; - - std::vector price_get() const; - - void price_set(const std::vector& value); - - int32_t excitement_get() const; - void excitement_set(int32_t value); - - int32_t intensity_get() const; - void intensity_set(int32_t value); - - int32_t nausea_get() const; - void nausea_set(int32_t value); - - int32_t totalCustomers_get() const; - void totalCustomers_set(int32_t value); - - int32_t buildDate_get() const; - void buildDate_set(int32_t value); - - int32_t age_get() const; - - money64 runningCost_get() const; - void runningCost_set(money64 value); - - int32_t totalProfit_get() const; - void totalProfit_set(int32_t value); - - uint8_t inspectionInterval_get() const; - void inspectionInterval_set(uint8_t value); - - DukValue value_get() const; - - void value_set(const DukValue& value); - - uint8_t downtime_get() const; - - uint8_t liftHillSpeed_get() const; - void lifthillSpeed_set(uint8_t value); - - uint8_t maxLiftHillSpeed_get() const; - uint8_t minLiftHillSpeed_get() const; - - uint8_t satisfaction_get() const; - - double maxSpeed_get() const; - - double averageSpeed_get() const; - - int32_t rideTime_get() const; - - double rideLength_get() const; - - double maxPositiveVerticalGs_get() const; - - double maxNegativeVerticalGs_get() const; - - double maxLateralGs_get() const; - - double totalAirTime_get() const; - - uint8_t numDrops_get() const; - - uint8_t numLiftHills_get() const; - - double highestDropHeight_get() const; - - Ride* GetRide() const; - - void SetBreakdown(const std::string& breakDown); - - void FixBreakdown(); - - std::string getBreakdown() const; - - public: - static void Register(duk_context* ctx); + static JSValue id_get(JSContext* ctx, JSValue thisVal); + static JSValue object_get(JSContext* ctx, JSValue thisVal); + static JSValue type_get(JSContext* ctx, JSValue thisVal); + static JSValue classification_get(JSContext* ctx, JSValue thisVal); + static JSValue name_get(JSContext* ctx, JSValue thisVal); + static JSValue name_set(JSContext* ctx, JSValue thisVal, JSValue value); + static JSValue status_get(JSContext* ctx, JSValue thisVal); + static JSValue flags_get(JSContext* ctx, JSValue thisVal); + static JSValue flags_set(JSContext* ctx, JSValue thisVal, JSValue value); + static JSValue mode_get(JSContext* ctx, JSValue thisVal); + static JSValue mode_set(JSContext* ctx, JSValue thisVal, JSValue value); + static JSValue departFlags_get(JSContext* ctx, JSValue thisVal); + static JSValue departFlags_set(JSContext* ctx, JSValue thisVal, JSValue value); + static JSValue minimumWaitingTime_get(JSContext* ctx, JSValue thisVal); + static JSValue minimumWaitingTime_set(JSContext* ctx, JSValue thisVal, JSValue value); + static JSValue maximumWaitingTime_get(JSContext* ctx, JSValue thisVal); + static JSValue maximumWaitingTime_set(JSContext* ctx, JSValue thisVal, JSValue value); + static JSValue vehicles_get(JSContext* ctx, JSValue thisVal); + static JSValue vehicleColours_get(JSContext* ctx, JSValue thisVal); + static JSValue vehicleColours_set(JSContext* ctx, JSValue thisVal, JSValue value); + static JSValue colourSchemes_get(JSContext* ctx, JSValue thisVal); + static JSValue colourSchemes_set(JSContext* ctx, JSValue thisVal, JSValue value); + static JSValue stationStyle_get(JSContext* ctx, JSValue thisVal); + static JSValue stationStyle_set(JSContext* ctx, JSValue thisVal, JSValue value); + static JSValue music_get(JSContext* ctx, JSValue thisVal); + static JSValue music_set(JSContext* ctx, JSValue thisVal, JSValue value); + static JSValue stations_get(JSContext* ctx, JSValue thisVal); + static JSValue price_get(JSContext* ctx, JSValue thisVal); + static JSValue price_set(JSContext* ctx, JSValue thisVal, JSValue value); + static JSValue excitement_get(JSContext* ctx, JSValue thisVal); + static JSValue excitement_set(JSContext* ctx, JSValue thisVal, JSValue value); + static JSValue intensity_get(JSContext* ctx, JSValue thisVal); + static JSValue intensity_set(JSContext* ctx, JSValue thisVal, JSValue value); + static JSValue nausea_get(JSContext* ctx, JSValue thisVal); + static JSValue nausea_set(JSContext* ctx, JSValue thisVal, JSValue value); + static JSValue totalCustomers_get(JSContext* ctx, JSValue thisVal); + static JSValue totalCustomers_set(JSContext* ctx, JSValue thisVal, JSValue value); + static JSValue buildDate_get(JSContext* ctx, JSValue thisVal); + static JSValue buildDate_set(JSContext* ctx, JSValue thisVal, JSValue value); + static JSValue age_get(JSContext* ctx, JSValue thisVal); + static JSValue runningCost_get(JSContext* ctx, JSValue thisVal); + static JSValue runningCost_set(JSContext* ctx, JSValue thisVal, JSValue value); + static JSValue totalProfit_get(JSContext* ctx, JSValue thisVal); + static JSValue totalProfit_set(JSContext* ctx, JSValue thisVal, JSValue value); + static JSValue inspectionInterval_get(JSContext* ctx, JSValue thisVal); + static JSValue inspectionInterval_set(JSContext* ctx, JSValue thisVal, JSValue value); + static JSValue value_get(JSContext* ctx, JSValue thisVal); + static JSValue value_set(JSContext* ctx, JSValue thisVal, JSValue value); + static JSValue downtime_get(JSContext* ctx, JSValue thisVal); + static JSValue liftHillSpeed_get(JSContext* ctx, JSValue thisVal); + static JSValue liftHillSpeed_set(JSContext* ctx, JSValue thisVal, JSValue value); + static JSValue maxLiftHillSpeed_get(JSContext* ctx, JSValue thisVal); + static JSValue minLiftHillSpeed_get(JSContext* ctx, JSValue thisVal); + static JSValue satisfaction_get(JSContext* ctx, JSValue thisVal); + static JSValue maxSpeed_get(JSContext* ctx, JSValue thisVal); + static JSValue averageSpeed_get(JSContext* ctx, JSValue thisVal); + static JSValue rideTime_get(JSContext* ctx, JSValue thisVal); + static JSValue rideLength_get(JSContext* ctx, JSValue thisVal); + static JSValue maxPositiveVerticalGs_get(JSContext* ctx, JSValue thisVal); + static JSValue maxNegativeVerticalGs_get(JSContext* ctx, JSValue thisVal); + static JSValue maxLateralGs_get(JSContext* ctx, JSValue thisVal); + static JSValue totalAirTime_get(JSContext* ctx, JSValue thisVal); + static JSValue numDrops_get(JSContext* ctx, JSValue thisVal); + static JSValue numLiftHills_get(JSContext* ctx, JSValue thisVal); + static JSValue highestDropHeight_get(JSContext* ctx, JSValue thisVal); + static JSValue breakdown_get(JSContext* ctx, JSValue thisVal); + static JSValue setBreakdown(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + static JSValue fixBreakdown(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/ride/ScRideStation.cpp b/src/openrct2/scripting/bindings/ride/ScRideStation.cpp index 9b48bf7cf7..7212f3f3b2 100644 --- a/src/openrct2/scripting/bindings/ride/ScRideStation.cpp +++ b/src/openrct2/scripting/bindings/ride/ScRideStation.cpp @@ -13,121 +13,140 @@ #include "../../../Context.h" #include "../../../ride/Ride.h" - #include "../../Duktape.hpp" #include "../../ScriptEngine.h" - #include "../object/ScObject.hpp" namespace OpenRCT2::Scripting { - ScRideStation::ScRideStation(RideId rideId, StationIndex stationIndex) - : _rideId(rideId) - , _stationIndex(stationIndex) + void ScRideStation::Register(JSContext* ctx) { + RegisterBaseStr(ctx, "RideStation", Finalize); } - void ScRideStation::Register(duk_context* ctx) + JSValue ScRideStation::New(JSContext* ctx, RideId rideId, StationIndex stationIndex) { - dukglue_register_property(ctx, &ScRideStation::start_get, &ScRideStation::start_set, "start"); - dukglue_register_property(ctx, &ScRideStation::length_get, &ScRideStation::length_set, "length"); - dukglue_register_property(ctx, &ScRideStation::entrance_get, &ScRideStation::entrance_set, "entrance"); - dukglue_register_property(ctx, &ScRideStation::exit_get, &ScRideStation::exit_set, "exit"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("start", ScRideStation::start_get, ScRideStation::start_set), + JS_CGETSET_DEF("length", ScRideStation::length_get, ScRideStation::length_set), + JS_CGETSET_DEF("entrance", ScRideStation::entrance_get, ScRideStation::entrance_set), + JS_CGETSET_DEF("exit", ScRideStation::exit_get, ScRideStation::exit_set), + }; + return MakeWithOpaque(ctx, funcs, new RideStationData{ rideId, stationIndex }); } - DukValue ScRideStation::start_get() const + void ScRideStation::Finalize(JSRuntime* rt, JSValue thisVal) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - auto station = GetRideStation(); - if (station != nullptr) - { - auto start = CoordsXYZ(station->Start, station->GetBaseZ()); - return ToDuk(ctx, start); - } - return ToDuk(ctx, nullptr); + RideStationData* data = GetRideStationData(thisVal); + if (data) + delete data; } - void ScRideStation::start_set(const DukValue& value) + ScRideStation::RideStationData* ScRideStation::GetRideStationData(JSValue thisVal) { - auto station = GetRideStation(); - if (station != nullptr) - { - auto start = FromDuk(value); - station->Start = { start.x, start.y }; - station->SetBaseZ(start.z); - } + return gScRideStation.GetOpaque(thisVal); } - int32_t ScRideStation::length_get() const + RideStation* ScRideStation::GetRideStation(JSValue thisVal) { - auto station = GetRideStation(); - if (station != nullptr) - { - return station->Length; - } - return 0; - } - - void ScRideStation::length_set(int32_t value) - { - auto station = GetRideStation(); - if (station != nullptr) - { - station->Length = value; - } - } - - DukValue ScRideStation::entrance_get() const - { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - auto station = GetRideStation(); - if (station != nullptr) - { - return ToDuk(ctx, station->Entrance.ToCoordsXYZD()); - } - return ToDuk(ctx, nullptr); - } - - void ScRideStation::entrance_set(const DukValue& value) - { - auto station = GetRideStation(); - if (station != nullptr) - { - station->Entrance = FromDuk(value); - } - } - - DukValue ScRideStation::exit_get() const - { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - auto station = GetRideStation(); - if (station != nullptr) - { - return ToDuk(ctx, station->Exit.ToCoordsXYZD()); - } - return ToDuk(ctx, nullptr); - } - - void ScRideStation::exit_set(const DukValue& value) - { - auto station = GetRideStation(); - if (station != nullptr) - { - station->Exit = FromDuk(value); - } - } - - RideStation* ScRideStation::GetRideStation() const - { - auto ride = GetRide(_rideId); + RideStationData* data = GetRideStationData(thisVal); + auto ride = ::GetRide(data->_rideId); if (ride != nullptr) { - if (_stationIndex.ToUnderlying() < std::size(ride->getStations())) + if (data->_stationIndex.ToUnderlying() < std::size(ride->getStations())) { - return &ride->getStation(_stationIndex); + return &ride->getStation(data->_stationIndex); } } return nullptr; } + JSValue ScRideStation::start_get(JSContext* ctx, JSValue thisVal) + { + auto station = GetRideStation(thisVal); + if (station != nullptr) + { + auto start = CoordsXYZ(station->Start, station->GetBaseZ()); + return ToJSValue(ctx, start); + } + return JS_NULL; + } + + JSValue ScRideStation::start_set(JSContext* ctx, JSValue thisVal, JSValue value) + { + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto station = GetRideStation(thisVal); + if (station != nullptr) + { + auto start = JSToCoordsXYZ(ctx, value); + station->Start = { start.x, start.y }; + station->SetBaseZ(start.z); + } + return JS_UNDEFINED; + } + + JSValue ScRideStation::length_get(JSContext* ctx, JSValue thisVal) + { + auto station = GetRideStation(thisVal); + return JS_NewInt32(ctx, station != nullptr ? station->Length : 0); + } + + JSValue ScRideStation::length_set(JSContext* ctx, JSValue thisVal, JSValue value) + { + JS_UNPACK_INT32(valueInt, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto station = GetRideStation(thisVal); + if (station != nullptr) + { + station->Length = valueInt; + } + return JS_UNDEFINED; + } + + JSValue ScRideStation::entrance_get(JSContext* ctx, JSValue thisVal) + { + auto station = GetRideStation(thisVal); + if (station != nullptr) + { + return ToJSValue(ctx, station->Entrance.ToCoordsXYZD()); + } + return JS_NULL; + } + + JSValue ScRideStation::entrance_set(JSContext* ctx, JSValue thisVal, JSValue value) + { + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto station = GetRideStation(thisVal); + if (station != nullptr) + { + station->Entrance = JSToCoordsXYZD(ctx, value); + } + return JS_UNDEFINED; + } + + JSValue ScRideStation::exit_get(JSContext* ctx, JSValue thisVal) + { + auto station = GetRideStation(thisVal); + if (station != nullptr) + { + return ToJSValue(ctx, station->Exit.ToCoordsXYZD()); + } + return JS_NULL; + } + + JSValue ScRideStation::exit_set(JSContext* ctx, JSValue thisVal, JSValue value) + { + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto station = GetRideStation(thisVal); + if (station != nullptr) + { + station->Exit = JSToCoordsXYZD(ctx, value); + } + return JS_UNDEFINED; + } + } // namespace OpenRCT2::Scripting #endif diff --git a/src/openrct2/scripting/bindings/ride/ScRideStation.hpp b/src/openrct2/scripting/bindings/ride/ScRideStation.hpp index 34c0207da9..d545492df9 100644 --- a/src/openrct2/scripting/bindings/ride/ScRideStation.hpp +++ b/src/openrct2/scripting/bindings/ride/ScRideStation.hpp @@ -13,39 +13,39 @@ #include "../../../Context.h" #include "../../../ride/Ride.h" - #include "../../Duktape.hpp" + #include "../../ScriptEngine.h" namespace OpenRCT2::Scripting { - class ScRideStation + class ScRideStation; + extern ScRideStation gScRideStation; + + class ScRideStation final : public ScBase { private: - RideId _rideId = RideId::GetNull(); - StationIndex _stationIndex{}; + struct RideStationData + { + RideId _rideId = RideId::GetNull(); + StationIndex _stationIndex{}; + }; public: - ScRideStation(RideId rideId, StationIndex stationIndex); - - static void Register(duk_context* ctx); + void Register(JSContext* ctx); + JSValue New(JSContext* ctx, RideId rideId, StationIndex stationIndex); private: - DukValue start_get() const; + static void Finalize(JSRuntime* rt, JSValue thisVal); + static RideStationData* GetRideStationData(JSValue thisVal); + static RideStation* GetRideStation(JSValue thisVal); - void start_set(const DukValue& value); - - int32_t length_get() const; - - void length_set(int32_t value); - - DukValue entrance_get() const; - - void entrance_set(const DukValue& value); - - DukValue exit_get() const; - - void exit_set(const DukValue& value); - - RideStation* GetRideStation() const; + static JSValue start_get(JSContext* ctx, JSValue thisVal); + static JSValue start_set(JSContext* ctx, JSValue thisVal, JSValue value); + static JSValue length_get(JSContext* ctx, JSValue thisVal); + static JSValue length_set(JSContext* ctx, JSValue thisVal, JSValue value); + static JSValue entrance_get(JSContext* ctx, JSValue thisVal); + static JSValue entrance_set(JSContext* ctx, JSValue thisVal, JSValue value); + static JSValue exit_get(JSContext* ctx, JSValue thisVal); + static JSValue exit_set(JSContext* ctx, JSValue thisVal, JSValue value); }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/ride/ScTrackIterator.cpp b/src/openrct2/scripting/bindings/ride/ScTrackIterator.cpp index a9a1f8250b..40eaceab96 100644 --- a/src/openrct2/scripting/bindings/ride/ScTrackIterator.cpp +++ b/src/openrct2/scripting/bindings/ride/ScTrackIterator.cpp @@ -25,105 +25,116 @@ using namespace OpenRCT2::Scripting; using namespace OpenRCT2::TrackMetadata; -std::shared_ptr ScTrackIterator::FromElement(const CoordsXY& position, int32_t elementIndex) +JSValue ScTrackIterator::FromElement(JSContext* ctx, const CoordsXY& position, int32_t elementIndex) { auto el = MapGetNthElementAt(position, elementIndex); if (el == nullptr) - return nullptr; + return JS_NULL; auto origin = GetTrackSegmentOrigin(CoordsXYE(position, el)); if (!origin) - return nullptr; + return JS_NULL; auto trackEl = el->AsTrack(); - return std::make_shared(*origin, trackEl->GetTrackType(), trackEl->GetRideIndex()); + return gScTrackIterator.New(ctx, *origin, trackEl->GetTrackType()); } -ScTrackIterator::ScTrackIterator(const CoordsXYZD& position, OpenRCT2::TrackElemType type, RideId ride) - : _position(position) - , _type(type) - , _ride(ride) +void ScTrackIterator::Register(JSContext* ctx) { + RegisterBaseStr(ctx, "TrackIterator", Finalize); } -void ScTrackIterator::Register(duk_context* ctx) +JSValue ScTrackIterator::New(JSContext* ctx, const CoordsXYZD& position, OpenRCT2::TrackElemType type) { - dukglue_register_property(ctx, &ScTrackIterator::position_get, nullptr, "position"); - dukglue_register_property(ctx, &ScTrackIterator::segment_get, nullptr, "segment"); - dukglue_register_property(ctx, &ScTrackIterator::previousPosition_get, nullptr, "previousPosition"); - dukglue_register_property(ctx, &ScTrackIterator::nextPosition_get, nullptr, "nextPosition"); - dukglue_register_method(ctx, &ScTrackIterator::previous, "previous"); - dukglue_register_method(ctx, &ScTrackIterator::next, "next"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("position", ScTrackIterator::position_get, nullptr), + JS_CGETSET_DEF("segment", ScTrackIterator::segment_get, nullptr), + JS_CGETSET_DEF("previousPosition", ScTrackIterator::previousPosition_get, nullptr), + JS_CGETSET_DEF("nextPosition", ScTrackIterator::nextPosition_get, nullptr), + JS_CFUNC_DEF("previous", 0, ScTrackIterator::previous), + JS_CFUNC_DEF("next", 0, ScTrackIterator::next), + }; + return MakeWithOpaque(ctx, funcs, new TrackIteratorData{ position, type }); } -DukValue ScTrackIterator::position_get() const +void ScTrackIterator::Finalize(JSRuntime* rt, JSValue thisVal) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto ctx = scriptEngine.GetContext(); - return ToDuk(ctx, _position); + TrackIteratorData* data = GetTrackIteratorData(thisVal); + if (data) + delete data; } -DukValue ScTrackIterator::segment_get() const +ScTrackIterator::TrackIteratorData* ScTrackIterator::GetTrackIteratorData(JSValue thisVal) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto ctx = scriptEngine.GetContext(); - - if (_type >= TrackElemType::count) - return ToDuk(ctx, nullptr); - - return GetObjectAsDukValue(ctx, std::make_shared(_type)); + return gScTrackIterator.GetOpaque(thisVal); } -DukValue ScTrackIterator::previousPosition_get() const +JSValue ScTrackIterator::position_get(JSContext* ctx, JSValue thisVal) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto ctx = scriptEngine.GetContext(); + auto* data = GetTrackIteratorData(thisVal); + return ToJSValue(ctx, data->_position); +} - auto& ted = GetTrackElementDescriptor(_type); +JSValue ScTrackIterator::segment_get(JSContext* ctx, JSValue thisVal) +{ + auto* data = GetTrackIteratorData(thisVal); + + if (data->_type >= TrackElemType::count) + return JS_NULL; + + return gScTrackSegment.New(ctx, data->_type); +} + +JSValue ScTrackIterator::previousPosition_get(JSContext* ctx, JSValue thisVal) +{ + auto* data = GetTrackIteratorData(thisVal); + + auto& ted = GetTrackElementDescriptor(data->_type); const auto& seq0 = ted.sequenceData.sequences[0].clearance; - auto pos = _position + CoordsXYZ(seq0.x, seq0.y, seq0.z); + auto pos = data->_position + CoordsXYZ(seq0.x, seq0.y, seq0.z); - auto el = MapGetTrackElementAtOfTypeSeq(pos, _type, 0); + auto el = MapGetTrackElementAtOfTypeSeq(pos, data->_type, 0); if (el == nullptr) - return ToDuk(ctx, nullptr); + return JS_NULL; auto posEl = CoordsXYE(pos.x, pos.y, reinterpret_cast(el)); TrackBeginEnd tbe{}; trackBlockGetPrevious(posEl, &tbe); CoordsXYZD result(tbe.end_x, tbe.end_y, tbe.begin_z, tbe.begin_direction); - return ToDuk(ctx, result); + return ToJSValue(ctx, result); } -DukValue ScTrackIterator::nextPosition_get() const +JSValue ScTrackIterator::nextPosition_get(JSContext* ctx, JSValue thisVal) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto ctx = scriptEngine.GetContext(); + auto* data = GetTrackIteratorData(thisVal); - auto& ted = GetTrackElementDescriptor(_type); + auto& ted = GetTrackElementDescriptor(data->_type); const auto& seq0 = ted.sequenceData.sequences[0].clearance; - auto pos = _position + CoordsXYZ(seq0.x, seq0.y, seq0.z); + auto pos = data->_position + CoordsXYZ(seq0.x, seq0.y, seq0.z); - auto el = MapGetTrackElementAtOfTypeSeq(pos, _type, 0); + auto el = MapGetTrackElementAtOfTypeSeq(pos, data->_type, 0); if (el == nullptr) - return ToDuk(ctx, nullptr); + return JS_NULL; - auto posEl = CoordsXYE(_position.x, _position.y, reinterpret_cast(el)); + auto posEl = CoordsXYE(data->_position.x, data->_position.y, reinterpret_cast(el)); CoordsXYE next; int32_t z{}; int32_t direction{}; trackBlockGetNext(&posEl, &next, &z, &direction); CoordsXYZD result(next.x, next.y, z, direction); - return ToDuk(ctx, result); + return ToJSValue(ctx, result); } -bool ScTrackIterator::previous() +JSValue ScTrackIterator::previous(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto& ted = GetTrackElementDescriptor(_type); - const auto& seq0 = ted.sequenceData.sequences[0].clearance; - auto pos = _position + CoordsXYZ(seq0.x, seq0.y, seq0.z); + auto* data = GetTrackIteratorData(thisVal); - auto el = MapGetTrackElementAtOfTypeSeq(pos, _type, 0); + auto& ted = GetTrackElementDescriptor(data->_type); + const auto& seq0 = ted.sequenceData.sequences[0].clearance; + auto pos = data->_position + CoordsXYZ(seq0.x, seq0.y, seq0.z); + + auto el = MapGetTrackElementAtOfTypeSeq(pos, data->_type, 0); if (el == nullptr) - return false; + return JS_NewBool(ctx, false); auto posEl = CoordsXYE(pos.x, pos.y, reinterpret_cast(el)); TrackBeginEnd tbe{}; @@ -133,25 +144,27 @@ bool ScTrackIterator::previous() auto origin = GetTrackSegmentOrigin(prev); if (origin) { - _position = *origin; - _type = prev.element->AsTrack()->GetTrackType(); - return true; + data->_position = *origin; + data->_type = prev.element->AsTrack()->GetTrackType(); + return JS_NewBool(ctx, true); } } - return false; + return JS_NewBool(ctx, false); } -bool ScTrackIterator::next() +JSValue ScTrackIterator::next(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto& ted = GetTrackElementDescriptor(_type); + auto* data = GetTrackIteratorData(thisVal); + + auto& ted = GetTrackElementDescriptor(data->_type); const auto& seq0 = ted.sequenceData.sequences[0].clearance; - auto pos = _position + CoordsXYZ(seq0.x, seq0.y, seq0.z); + auto pos = data->_position + CoordsXYZ(seq0.x, seq0.y, seq0.z); - auto el = MapGetTrackElementAtOfTypeSeq(pos, _type, 0); + auto el = MapGetTrackElementAtOfTypeSeq(pos, data->_type, 0); if (el == nullptr) - return false; + return JS_NewBool(ctx, false); - auto posEl = CoordsXYE(_position.x, _position.y, reinterpret_cast(el)); + auto posEl = CoordsXYE(data->_position.x, data->_position.y, reinterpret_cast(el)); CoordsXYE next; int32_t z{}; int32_t direction{}; @@ -160,12 +173,12 @@ bool ScTrackIterator::next() auto origin = GetTrackSegmentOrigin(next); if (origin) { - _position = *origin; - _type = next.element->AsTrack()->GetTrackType(); - return true; + data->_position = *origin; + data->_type = next.element->AsTrack()->GetTrackType(); + return JS_NewBool(ctx, true); } } - return false; + return JS_NewBool(ctx, false); } #endif diff --git a/src/openrct2/scripting/bindings/ride/ScTrackIterator.h b/src/openrct2/scripting/bindings/ride/ScTrackIterator.h index bca7081c0c..2464f65401 100644 --- a/src/openrct2/scripting/bindings/ride/ScTrackIterator.h +++ b/src/openrct2/scripting/bindings/ride/ScTrackIterator.h @@ -12,34 +12,40 @@ #ifdef ENABLE_SCRIPTING #include "../../../Identifiers.h" - #include "../../Duktape.hpp" + #include "../../ScriptEngine.h" #include - #include namespace OpenRCT2::Scripting { - class ScTrackIterator + class ScTrackIterator; + extern ScTrackIterator gScTrackIterator; + + class ScTrackIterator final : public ScBase { private: - CoordsXYZD _position; - TrackElemType _type; - [[maybe_unused]] RideId _ride; + struct TrackIteratorData + { + CoordsXYZD _position; + TrackElemType _type; + }; public: - static std::shared_ptr FromElement(const CoordsXY& position, int32_t elementIndex); - static void Register(duk_context* ctx); - - ScTrackIterator(const CoordsXYZD& position, TrackElemType type, RideId ride); + static JSValue FromElement(JSContext* ctx, const CoordsXY& position, int32_t elementIndex); + void Register(JSContext* ctx); + JSValue New(JSContext* ctx, const CoordsXYZD& position, TrackElemType type); private: - DukValue position_get() const; - DukValue segment_get() const; - DukValue previousPosition_get() const; - DukValue nextPosition_get() const; + static void Finalize(JSRuntime* rt, JSValue thisVal); + static TrackIteratorData* GetTrackIteratorData(JSValue thisVal); - bool previous(); - bool next(); + static JSValue position_get(JSContext* ctx, JSValue thisVal); + static JSValue segment_get(JSContext* ctx, JSValue thisVal); + static JSValue previousPosition_get(JSContext* ctx, JSValue thisVal); + static JSValue nextPosition_get(JSContext* ctx, JSValue thisVal); + + static JSValue previous(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + static JSValue next(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/ride/ScTrackSegment.cpp b/src/openrct2/scripting/bindings/ride/ScTrackSegment.cpp index 0cd52ca8a9..18c195c608 100644 --- a/src/openrct2/scripting/bindings/ride/ScTrackSegment.cpp +++ b/src/openrct2/scripting/bindings/ride/ScTrackSegment.cpp @@ -12,191 +12,231 @@ #include "ScTrackSegment.h" #include "../../../Context.h" + #include "../../../core/EnumMap.hpp" #include "../../../ride/TrackData.h" #include "../../../ride/Vehicle.h" #include "../../../ride/ted/TrackElementDescriptor.h" #include "../../ScriptEngine.h" + #include "../../ScriptUtil.hpp" using namespace OpenRCT2::Scripting; using namespace OpenRCT2::TrackMetadata; -ScTrackSegment::ScTrackSegment(OpenRCT2::TrackElemType type) - : _type(type) +static JSValue VehicleInfoToJSValue(JSContext* ctx, const VehicleInfo& value) { + JSValue obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "x", JS_NewInt32(ctx, value.x)); + JS_SetPropertyStr(ctx, obj, "y", JS_NewInt32(ctx, value.y)); + JS_SetPropertyStr(ctx, obj, "z", JS_NewInt32(ctx, value.z)); + JS_SetPropertyStr(ctx, obj, "yaw", JS_NewInt32(ctx, value.yaw)); + JS_SetPropertyStr(ctx, obj, "pitch", JS_NewInt32(ctx, EnumValue(value.pitch))); + JS_SetPropertyStr(ctx, obj, "roll", JS_NewInt32(ctx, EnumValue(value.roll))); + return obj; } -void ScTrackSegment::Register(duk_context* ctx) +void ScTrackSegment::Register(JSContext* ctx) { - dukglue_register_property(ctx, &ScTrackSegment::type_get, nullptr, "type"); - dukglue_register_property(ctx, &ScTrackSegment::description_get, nullptr, "description"); - dukglue_register_property(ctx, &ScTrackSegment::elements_get, nullptr, "elements"); - dukglue_register_property(ctx, &ScTrackSegment::beginDirection_get, nullptr, "beginDirection"); - dukglue_register_property(ctx, &ScTrackSegment::endDirection_get, nullptr, "endDirection"); - dukglue_register_property(ctx, &ScTrackSegment::beginSlope_get, nullptr, "beginSlope"); - dukglue_register_property(ctx, &ScTrackSegment::endSlope_get, nullptr, "endSlope"); - dukglue_register_property(ctx, &ScTrackSegment::beginBank_get, nullptr, "beginBank"); - dukglue_register_property(ctx, &ScTrackSegment::endBank_get, nullptr, "endBank"); - dukglue_register_property(ctx, &ScTrackSegment::beginZ_get, nullptr, "beginZ"); - dukglue_register_property(ctx, &ScTrackSegment::endZ_get, nullptr, "endZ"); - dukglue_register_property(ctx, &ScTrackSegment::endX_get, nullptr, "endX"); - dukglue_register_property(ctx, &ScTrackSegment::endY_get, nullptr, "endY"); - dukglue_register_property(ctx, &ScTrackSegment::length_get, nullptr, "length"); - dukglue_register_property(ctx, &ScTrackSegment::nextCurveElement_get, nullptr, "nextSuggestedSegment"); - dukglue_register_property(ctx, &ScTrackSegment::previousCurveElement_get, nullptr, "previousSuggestedSegment"); - dukglue_register_property(ctx, &ScTrackSegment::getMirrorElement, nullptr, "mirrorSegment"); - dukglue_register_property(ctx, &ScTrackSegment::getAlternativeElement, nullptr, "alternateTypeSegment"); - dukglue_register_property(ctx, &ScTrackSegment::getPriceModifier, nullptr, "priceModifier"); - dukglue_register_property(ctx, &ScTrackSegment::getTrackGroup, nullptr, "trackGroup"); - dukglue_register_property(ctx, &ScTrackSegment::getTrackCurvature, nullptr, "turnDirection"); - dukglue_register_property(ctx, &ScTrackSegment::getTrackPitchDirection, nullptr, "slopeDirection"); - - dukglue_register_property( - ctx, &ScTrackSegment::getTrackFlag, nullptr, "onlyAllowedUnderwater"); - dukglue_register_property( - ctx, &ScTrackSegment::getTrackFlag, nullptr, "onlyAllowedAboveGround"); - dukglue_register_property(ctx, &ScTrackSegment::getTrackFlag, nullptr, "allowsChainLift"); - dukglue_register_property(ctx, &ScTrackSegment::getTrackFlag, nullptr, "isBanked"); - dukglue_register_property(ctx, &ScTrackSegment::getTrackFlag, nullptr, "isInversion"); - dukglue_register_property(ctx, &ScTrackSegment::getTrackFlag, nullptr, "isSteepUp"); - dukglue_register_property( - ctx, &ScTrackSegment::getTrackFlag, nullptr, "startsHalfHeightUp"); - dukglue_register_property(ctx, &ScTrackSegment::getTrackFlag, nullptr, "countsAsInversion"); - dukglue_register_property(ctx, &ScTrackSegment::getTrackFlag, nullptr, "isBankedTurn"); - dukglue_register_property(ctx, &ScTrackSegment::getTrackFlag, nullptr, "isSlopedTurn"); - dukglue_register_property(ctx, &ScTrackSegment::getTrackFlag, nullptr, "isHelix"); - dukglue_register_property( - ctx, &ScTrackSegment::getTrackFlag, nullptr, "countsAsInversion"); - - dukglue_register_method(ctx, &ScTrackSegment::getSubpositionLength, "getSubpositionLength"); - dukglue_register_method(ctx, &ScTrackSegment::getSubpositions, "getSubpositions"); + RegisterBaseStr(ctx, "TrackSegment", Finalize); } -int32_t ScTrackSegment::type_get() const +JSValue ScTrackSegment::New(JSContext* ctx, OpenRCT2::TrackElemType type) { - return EnumValue(_type); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("type", ScTrackSegment::type_get, nullptr), + JS_CGETSET_DEF("description", ScTrackSegment::description_get, nullptr), + JS_CGETSET_DEF("elements", ScTrackSegment::elements_get, nullptr), + JS_CGETSET_DEF("beginDirection", ScTrackSegment::beginDirection_get, nullptr), + JS_CGETSET_DEF("endDirection", ScTrackSegment::endDirection_get, nullptr), + JS_CGETSET_DEF("beginSlope", ScTrackSegment::beginSlope_get, nullptr), + JS_CGETSET_DEF("endSlope", ScTrackSegment::endSlope_get, nullptr), + JS_CGETSET_DEF("beginBank", ScTrackSegment::beginBank_get, nullptr), + JS_CGETSET_DEF("endBank", ScTrackSegment::endBank_get, nullptr), + JS_CGETSET_DEF("beginZ", ScTrackSegment::beginZ_get, nullptr), + JS_CGETSET_DEF("endZ", ScTrackSegment::endZ_get, nullptr), + JS_CGETSET_DEF("endX", ScTrackSegment::endX_get, nullptr), + JS_CGETSET_DEF("endY", ScTrackSegment::endY_get, nullptr), + JS_CGETSET_DEF("length", ScTrackSegment::length_get, nullptr), + JS_CGETSET_DEF("nextSuggestedSegment", ScTrackSegment::nextCurveElement_get, nullptr), + JS_CGETSET_DEF("previousSuggestedSegment", ScTrackSegment::previousCurveElement_get, nullptr), + JS_CGETSET_DEF("mirrorSegment", ScTrackSegment::getMirrorElement, nullptr), + JS_CGETSET_DEF("alternateTypeSegment", ScTrackSegment::getAlternativeElement, nullptr), + JS_CGETSET_DEF("priceModifier", ScTrackSegment::getPriceModifier, nullptr), + JS_CGETSET_DEF("trackGroup", ScTrackSegment::getTrackGroup, nullptr), + JS_CGETSET_DEF("turnDirection", ScTrackSegment::getTrackCurvature, nullptr), + JS_CGETSET_DEF("slopeDirection", ScTrackSegment::getTrackPitchDirection, nullptr), + JS_CGETSET_DEF("onlyAllowedUnderwater", ScTrackSegment::getTrackFlag, nullptr), + JS_CGETSET_DEF("onlyAllowedAboveGround", ScTrackSegment::getTrackFlag, nullptr), + JS_CGETSET_DEF("allowsChainLift", ScTrackSegment::getTrackFlag, nullptr), + JS_CGETSET_DEF("isBanked", ScTrackSegment::getTrackFlag, nullptr), + JS_CGETSET_DEF("isInversion", ScTrackSegment::getTrackFlag, nullptr), + JS_CGETSET_DEF("isSteepUp", ScTrackSegment::getTrackFlag, nullptr), + JS_CGETSET_DEF("startsHalfHeightUp", ScTrackSegment::getTrackFlag, nullptr), + JS_CGETSET_DEF("countsAsGolfHole", ScTrackSegment::getTrackFlag, nullptr), + JS_CGETSET_DEF("isBankedTurn", ScTrackSegment::getTrackFlag, nullptr), + JS_CGETSET_DEF("isSlopedTurn", ScTrackSegment::getTrackFlag, nullptr), + JS_CGETSET_DEF("isHelix", ScTrackSegment::getTrackFlag, nullptr), + JS_CGETSET_DEF("countsAsInversion", ScTrackSegment::getTrackFlag, nullptr), + JS_CFUNC_DEF("getSubpositionLength", 2, ScTrackSegment::getSubpositionLength), + JS_CFUNC_DEF("getSubpositions", 2, ScTrackSegment::getSubpositions), + }; + return MakeWithOpaque(ctx, funcs, new TrackSegmentData{ type }); } -std::string ScTrackSegment::description_get() const +void ScTrackSegment::Finalize(JSRuntime* rt, JSValue thisVal) { - const auto& ted = GetTrackElementDescriptor(_type); - return LanguageGetString(ted.description); + TrackSegmentData* data = GetTrackSegmentData(thisVal); + if (data) + delete data; } -int32_t ScTrackSegment::beginZ_get() const +ScTrackSegment::TrackSegmentData* ScTrackSegment::GetTrackSegmentData(JSValue thisVal) { - const auto& ted = GetTrackElementDescriptor(_type); - return ted.coordinates.zBegin; + return gScTrackSegment.GetOpaque(thisVal); } -int32_t ScTrackSegment::beginDirection_get() const +JSValue ScTrackSegment::type_get(JSContext* ctx, JSValue thisVal) { - const auto& ted = GetTrackElementDescriptor(_type); - return ted.coordinates.rotationBegin; + const auto* data = GetTrackSegmentData(thisVal); + return JS_NewInt32(ctx, EnumValue(data->_type)); } -int32_t ScTrackSegment::beginSlope_get() const +JSValue ScTrackSegment::description_get(JSContext* ctx, JSValue thisVal) { - const auto& ted = GetTrackElementDescriptor(_type); - return EnumValue(ted.definition.pitchStart); + const auto* data = GetTrackSegmentData(thisVal); + const auto& ted = GetTrackElementDescriptor(data->_type); + return JS_NewString(ctx, LanguageGetString(ted.description)); } -int32_t ScTrackSegment::beginBank_get() const +JSValue ScTrackSegment::beginZ_get(JSContext* ctx, JSValue thisVal) { - const auto& ted = GetTrackElementDescriptor(_type); - return EnumValue(ted.definition.rollStart); + const auto* data = GetTrackSegmentData(thisVal); + const auto& ted = GetTrackElementDescriptor(data->_type); + return JS_NewInt32(ctx, ted.coordinates.zBegin); } -int32_t ScTrackSegment::endX_get() const +JSValue ScTrackSegment::beginDirection_get(JSContext* ctx, JSValue thisVal) { - const auto& ted = GetTrackElementDescriptor(_type); - return ted.coordinates.x; + const auto* data = GetTrackSegmentData(thisVal); + const auto& ted = GetTrackElementDescriptor(data->_type); + return JS_NewInt32(ctx, ted.coordinates.rotationBegin); } -int32_t ScTrackSegment::endY_get() const +JSValue ScTrackSegment::beginSlope_get(JSContext* ctx, JSValue thisVal) { - const auto& ted = GetTrackElementDescriptor(_type); - return ted.coordinates.y; + const auto* data = GetTrackSegmentData(thisVal); + const auto& ted = GetTrackElementDescriptor(data->_type); + return JS_NewInt32(ctx, EnumValue(ted.definition.pitchStart)); } -int32_t ScTrackSegment::endZ_get() const +JSValue ScTrackSegment::beginBank_get(JSContext* ctx, JSValue thisVal) { - const auto& ted = GetTrackElementDescriptor(_type); - return ted.coordinates.zEnd; + const auto* data = GetTrackSegmentData(thisVal); + const auto& ted = GetTrackElementDescriptor(data->_type); + return JS_NewInt32(ctx, EnumValue(ted.definition.rollStart)); } -int32_t ScTrackSegment::endDirection_get() const +JSValue ScTrackSegment::endX_get(JSContext* ctx, JSValue thisVal) { - const auto& ted = GetTrackElementDescriptor(_type); - return ted.coordinates.rotationEnd; + const auto* data = GetTrackSegmentData(thisVal); + const auto& ted = GetTrackElementDescriptor(data->_type); + return JS_NewInt32(ctx, ted.coordinates.x); } -int32_t ScTrackSegment::endSlope_get() const +JSValue ScTrackSegment::endY_get(JSContext* ctx, JSValue thisVal) { - const auto& ted = GetTrackElementDescriptor(_type); - return EnumValue(ted.definition.pitchEnd); + const auto* data = GetTrackSegmentData(thisVal); + const auto& ted = GetTrackElementDescriptor(data->_type); + return JS_NewInt32(ctx, ted.coordinates.y); } -int32_t ScTrackSegment::endBank_get() const +JSValue ScTrackSegment::endZ_get(JSContext* ctx, JSValue thisVal) { - const auto& ted = GetTrackElementDescriptor(_type); - return EnumValue(ted.definition.rollEnd); + const auto* data = GetTrackSegmentData(thisVal); + const auto& ted = GetTrackElementDescriptor(data->_type); + return JS_NewInt32(ctx, ted.coordinates.zEnd); } -int32_t ScTrackSegment::length_get() const +JSValue ScTrackSegment::endDirection_get(JSContext* ctx, JSValue thisVal) { - const auto& ted = GetTrackElementDescriptor(_type); - return ted.pieceLength; + const auto* data = GetTrackSegmentData(thisVal); + const auto& ted = GetTrackElementDescriptor(data->_type); + return JS_NewInt32(ctx, ted.coordinates.rotationEnd); } -DukValue ScTrackSegment::elements_get() const +JSValue ScTrackSegment::endSlope_get(JSContext* ctx, JSValue thisVal) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto ctx = scriptEngine.GetContext(); + const auto* data = GetTrackSegmentData(thisVal); + const auto& ted = GetTrackElementDescriptor(data->_type); + return JS_NewUint32(ctx, EnumValue(ted.definition.pitchEnd)); +} - const auto& ted = GetTrackElementDescriptor(_type); +JSValue ScTrackSegment::endBank_get(JSContext* ctx, JSValue thisVal) +{ + const auto* data = GetTrackSegmentData(thisVal); + const auto& ted = GetTrackElementDescriptor(data->_type); + return JS_NewUint32(ctx, EnumValue(ted.definition.rollEnd)); +} - duk_push_array(ctx); +JSValue ScTrackSegment::length_get(JSContext* ctx, JSValue thisVal) +{ + const auto* data = GetTrackSegmentData(thisVal); + const auto& ted = GetTrackElementDescriptor(data->_type); + return JS_NewUint32(ctx, ted.pieceLength); +} - duk_uarridx_t index = 0; - for (uint8_t i = 0; i < ted.sequenceData.numSequences; i++) +JSValue ScTrackSegment::elements_get(JSContext* ctx, JSValue thisVal) +{ + const auto* data = GetTrackSegmentData(thisVal); + const auto& ted = GetTrackElementDescriptor(data->_type); + + JSValue result = JS_NewArray(ctx); + + for (int64_t i = 0; i < ted.sequenceData.numSequences; i++) { auto& block = ted.sequenceData.sequences[i].clearance; - duk_push_object(ctx); - duk_push_number(ctx, block.x); - duk_put_prop_string(ctx, -2, "x"); - duk_push_number(ctx, block.y); - duk_put_prop_string(ctx, -2, "y"); - duk_push_number(ctx, block.z); - duk_put_prop_string(ctx, -2, "z"); - - duk_put_prop_index(ctx, -2, index); - index++; + JSValue element = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, element, "x", JS_NewInt32(ctx, block.x)); + JS_SetPropertyStr(ctx, element, "y", JS_NewInt32(ctx, block.y)); + JS_SetPropertyStr(ctx, element, "z", JS_NewInt32(ctx, block.z)); + JS_SetPropertyInt64(ctx, result, i, element); } - return DukValue::take_from_stack(ctx); -} - -uint16_t ScTrackSegment::getSubpositionLength(uint8_t trackSubposition, uint8_t direction) const -{ - return VehicleGetMoveInfoSize(static_cast(trackSubposition), _type, direction); -} - -std::vector ScTrackSegment::getSubpositions(uint8_t trackSubposition, uint8_t direction) const -{ - const auto ctx = GetContext()->GetScriptEngine().GetContext(); - const uint16_t size = getSubpositionLength(trackSubposition, direction); - const uint16_t typeAndDirection = (EnumValue(_type) << 2) | (direction & 3); - - std::vector result; - - for (auto idx = 0; idx < size; idx++) - { - result.push_back(ToDuk(ctx, gTrackVehicleInfo[trackSubposition][typeAndDirection]->info[idx])); - } return result; } -static DukValue _trackCurveToString(duk_context* ctx, TrackCurve curve) +JSValue ScTrackSegment::getSubpositionLength(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) +{ + JS_UNPACK_UINT32(trackSubposition, ctx, argv[0]); + JS_UNPACK_UINT32(direction, ctx, argv[1]); + + const auto* data = GetTrackSegmentData(thisVal); + uint16_t length = VehicleGetMoveInfoSize( + static_cast(trackSubposition), data->_type, static_cast(direction)); + return JS_NewUint32(ctx, length); +} + +JSValue ScTrackSegment::getSubpositions(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) +{ + JS_UNPACK_UINT32(trackSubposition, ctx, argv[0]); + JS_UNPACK_UINT32(direction, ctx, argv[1]); + + const auto* data = GetTrackSegmentData(thisVal); + const uint16_t size = VehicleGetMoveInfoSize( + static_cast(trackSubposition), data->_type, static_cast(direction)); + const uint16_t typeAndDirection = (EnumValue(data->_type) << 2) | (direction & 3); + + JSValue result = JS_NewArray(ctx); + + for (int64_t idx = 0; idx < size; idx++) + { + JSValue subposition = VehicleInfoToJSValue(ctx, gTrackVehicleInfo[trackSubposition][typeAndDirection]->info[idx]); + JS_SetPropertyInt64(ctx, result, idx, subposition); + } + + return result; +} + +static JSValue _trackCurveToString(JSContext* ctx, TrackCurve curve) { static const EnumMap map( { @@ -211,92 +251,100 @@ static DukValue _trackCurveToString(duk_context* ctx, TrackCurve curve) { "right_large", TrackCurve::rightLarge }, }); - u8string text = u8string(map[curve]); - return ToDuk(ctx, text); + const auto text = std::string(map[curve]); + return JSFromStdString(ctx, text); } -DukValue ScTrackSegment::nextCurveElement_get() const +JSValue ScTrackSegment::nextCurveElement_get(JSContext* ctx, JSValue thisVal) { - const auto ctx = GetContext()->GetScriptEngine().GetContext(); - const auto& ted = GetTrackElementDescriptor(_type); + const auto* data = GetTrackSegmentData(thisVal); + const auto& ted = GetTrackElementDescriptor(data->_type); auto nextInChain = ted.curveChain.next; if (nextInChain.isTrackType) - return ToDuk(ctx, EnumValue(nextInChain.trackType)); + return JS_NewInt32(ctx, EnumValue(nextInChain.trackType)); return _trackCurveToString(ctx, nextInChain.curve); } -DukValue ScTrackSegment::previousCurveElement_get() const +JSValue ScTrackSegment::previousCurveElement_get(JSContext* ctx, JSValue thisVal) { - const auto ctx = GetContext()->GetScriptEngine().GetContext(); - const auto& ted = GetTrackElementDescriptor(_type); + const auto* data = GetTrackSegmentData(thisVal); + const auto& ted = GetTrackElementDescriptor(data->_type); auto previousInChain = ted.curveChain.previous; if (previousInChain.isTrackType) - return ToDuk(ctx, EnumValue(previousInChain.trackType)); + return JS_NewInt32(ctx, EnumValue(previousInChain.trackType)); return _trackCurveToString(ctx, previousInChain.curve); } -DukValue ScTrackSegment::getMirrorElement() const +JSValue ScTrackSegment::getMirrorElement(JSContext* ctx, JSValue thisVal) { - const auto ctx = GetContext()->GetScriptEngine().GetContext(); - const auto& ted = GetTrackElementDescriptor(_type); + const auto* data = GetTrackSegmentData(thisVal); + const auto& ted = GetTrackElementDescriptor(data->_type); if (ted.mirrorElement == TrackElemType::none) - return ToDuk(ctx, nullptr); - return ToDuk(ctx, EnumValue(ted.mirrorElement)); + return JS_NULL; + return JS_NewInt32(ctx, EnumValue(ted.mirrorElement)); } -DukValue ScTrackSegment::getAlternativeElement() const +JSValue ScTrackSegment::getAlternativeElement(JSContext* ctx, JSValue thisVal) { - const auto ctx = GetContext()->GetScriptEngine().GetContext(); - const auto& ted = GetTrackElementDescriptor(_type); + const auto* data = GetTrackSegmentData(thisVal); + const auto& ted = GetTrackElementDescriptor(data->_type); if (ted.alternativeType == TrackElemType::none) - return ToDuk(ctx, nullptr); - return ToDuk(ctx, EnumValue(ted.alternativeType)); + return JS_NULL; + return JS_NewInt32(ctx, EnumValue(ted.alternativeType)); } -int32_t ScTrackSegment::getPriceModifier() const +JSValue ScTrackSegment::getPriceModifier(JSContext* ctx, JSValue thisVal) { - const auto& ted = GetTrackElementDescriptor(_type); - - return ted.priceModifier; + const auto* data = GetTrackSegmentData(thisVal); + const auto& ted = GetTrackElementDescriptor(data->_type); + return JS_NewInt32(ctx, ted.priceModifier); } template -bool ScTrackSegment::getTrackFlag() const +JSValue ScTrackSegment::getTrackFlag(JSContext* ctx, JSValue thisVal) { - const auto& ted = GetTrackElementDescriptor(_type); - - return ted.flags.has(flag); + const auto* data = GetTrackSegmentData(thisVal); + const auto& ted = GetTrackElementDescriptor(data->_type); + return JS_NewBool(ctx, ted.flags.has(flag)); } -int32_t ScTrackSegment::getTrackGroup() const +JSValue ScTrackSegment::getTrackGroup(JSContext* ctx, JSValue thisVal) { - const auto& ted = GetTrackElementDescriptor(_type); - - return EnumValue(ted.definition.group); + const auto* data = GetTrackSegmentData(thisVal); + const auto& ted = GetTrackElementDescriptor(data->_type); + return JS_NewInt32(ctx, EnumValue(ted.definition.group)); } -std::string ScTrackSegment::getTrackCurvature() const +JSValue ScTrackSegment::getTrackCurvature(JSContext* ctx, JSValue thisVal) { - const auto& ted = GetTrackElementDescriptor(_type); + const auto* data = GetTrackSegmentData(thisVal); + const auto& ted = GetTrackElementDescriptor(data->_type); + std::string curvature; if (ted.flags.has(TrackElementFlag::turnLeft)) - return "left"; - if (ted.flags.has(TrackElementFlag::turnRight)) - return "right"; - return "straight"; + curvature = "left"; + else if (ted.flags.has(TrackElementFlag::turnRight)) + curvature = "right"; + else + curvature = "straight"; + return JSFromStdString(ctx, curvature); } -std::string ScTrackSegment::getTrackPitchDirection() const +JSValue ScTrackSegment::getTrackPitchDirection(JSContext* ctx, JSValue thisVal) { - const auto& ted = GetTrackElementDescriptor(_type); + const auto* data = GetTrackSegmentData(thisVal); + const auto& ted = GetTrackElementDescriptor(data->_type); + std::string pitch; if (ted.flags.has(TrackElementFlag::up)) - return "up"; - if (ted.flags.has(TrackElementFlag::down)) - return "down"; - return "flat"; + pitch = "up"; + else if (ted.flags.has(TrackElementFlag::down)) + pitch = "down"; + else + pitch = "flat"; + return JSFromStdString(ctx, pitch); } #endif diff --git a/src/openrct2/scripting/bindings/ride/ScTrackSegment.h b/src/openrct2/scripting/bindings/ride/ScTrackSegment.h index ec0291b730..8b844667e6 100644 --- a/src/openrct2/scripting/bindings/ride/ScTrackSegment.h +++ b/src/openrct2/scripting/bindings/ride/ScTrackSegment.h @@ -11,7 +11,7 @@ #ifdef ENABLE_SCRIPTING - #include "../../Duktape.hpp" + #include "../../ScriptEngine.h" #include #include @@ -23,56 +23,51 @@ namespace OpenRCT2::TrackMetadata namespace OpenRCT2::Scripting { - template<> - inline DukValue ToDuk(duk_context* ctx, const VehicleInfo& value) - { - DukObject dukSubposition(ctx); - dukSubposition.Set("x", value.x); - dukSubposition.Set("y", value.y); - dukSubposition.Set("z", value.z); - dukSubposition.Set("yaw", value.yaw); - dukSubposition.Set("pitch", EnumValue(value.pitch)); - dukSubposition.Set("roll", EnumValue(value.roll)); - return dukSubposition.Take(); - } + class ScTrackSegment; + extern ScTrackSegment gScTrackSegment; - class ScTrackSegment + class ScTrackSegment final : public ScBase { private: - TrackElemType _type; + struct TrackSegmentData + { + TrackElemType _type; + }; public: - ScTrackSegment(TrackElemType type); - - static void Register(duk_context* ctx); + void Register(JSContext* ctx); + JSValue New(JSContext* ctx, TrackElemType type); private: - int32_t type_get() const; - std::string description_get() const; - int32_t beginZ_get() const; - int32_t beginDirection_get() const; - int32_t beginSlope_get() const; - int32_t beginBank_get() const; - int32_t endX_get() const; - int32_t endY_get() const; - int32_t endZ_get() const; - int32_t endDirection_get() const; - int32_t endSlope_get() const; - int32_t endBank_get() const; - int32_t length_get() const; - DukValue elements_get() const; - uint16_t getSubpositionLength(uint8_t trackSubposition, uint8_t direction) const; - std::vector getSubpositions(uint8_t trackSubposition, uint8_t direction) const; - DukValue nextCurveElement_get() const; - DukValue previousCurveElement_get() const; - DukValue getMirrorElement() const; - DukValue getAlternativeElement() const; - int32_t getPriceModifier() const; - int32_t getTrackGroup() const; + static void Finalize(JSRuntime* rt, JSValue thisVal); + static TrackSegmentData* GetTrackSegmentData(JSValue thisVal); + + static JSValue type_get(JSContext* ctx, JSValue thisVal); + static JSValue description_get(JSContext* ctx, JSValue thisVal); + static JSValue beginZ_get(JSContext* ctx, JSValue thisVal); + static JSValue beginDirection_get(JSContext* ctx, JSValue thisVal); + static JSValue beginSlope_get(JSContext* ctx, JSValue thisVal); + static JSValue beginBank_get(JSContext* ctx, JSValue thisVal); + static JSValue endX_get(JSContext* ctx, JSValue thisVal); + static JSValue endY_get(JSContext* ctx, JSValue thisVal); + static JSValue endZ_get(JSContext* ctx, JSValue thisVal); + static JSValue endDirection_get(JSContext* ctx, JSValue thisVal); + static JSValue endSlope_get(JSContext* ctx, JSValue thisVal); + static JSValue endBank_get(JSContext* ctx, JSValue thisVal); + static JSValue length_get(JSContext* ctx, JSValue thisVal); + static JSValue elements_get(JSContext* ctx, JSValue thisVal); + static JSValue getSubpositionLength(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + static JSValue getSubpositions(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + static JSValue nextCurveElement_get(JSContext* ctx, JSValue thisVal); + static JSValue previousCurveElement_get(JSContext* ctx, JSValue thisVal); + static JSValue getMirrorElement(JSContext* ctx, JSValue thisVal); + static JSValue getAlternativeElement(JSContext* ctx, JSValue thisVal); + static JSValue getPriceModifier(JSContext* ctx, JSValue thisVal); + static JSValue getTrackGroup(JSContext* ctx, JSValue thisVal); template - bool getTrackFlag() const; - std::string getTrackCurvature() const; - std::string getTrackPitchDirection() const; + static JSValue getTrackFlag(JSContext* ctx, JSValue thisVal); + static JSValue getTrackCurvature(JSContext* ctx, JSValue thisVal); + static JSValue getTrackPitchDirection(JSContext* ctx, JSValue thisVal); }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/world/ScAward.cpp b/src/openrct2/scripting/bindings/world/ScAward.cpp index 5bb814b238..d9fabc03ff 100644 --- a/src/openrct2/scripting/bindings/world/ScAward.cpp +++ b/src/openrct2/scripting/bindings/world/ScAward.cpp @@ -16,74 +16,84 @@ #include "../../../localisation/Formatting.h" #include "../../../management/Award.h" #include "../../../windows/Intent.h" - #include "../../Duktape.hpp" namespace OpenRCT2::Scripting { - ScAward::ScAward(size_t index) - : _index(index) + extern ScAward gScAward; + + using OpaqueAwardData = struct { + size_t index; + }; + + JSValue ScAward::New(JSContext* ctx, size_t index) + { + return MakeWithOpaque(ctx, funcs, new OpaqueAwardData{ index }); } - void ScAward::Register(duk_context* ctx) + void ScAward::Register(JSContext* ctx) { - dukglue_register_property(ctx, &ScAward::type_get, nullptr, "type"); - dukglue_register_property(ctx, &ScAward::text_get, nullptr, "text"); - dukglue_register_property(ctx, &ScAward::positive_get, nullptr, "positive"); - dukglue_register_property(ctx, &ScAward::imageId_get, nullptr, "imageId"); - dukglue_register_property(ctx, &ScAward::monthsRemaining_get, nullptr, "monthsRemaining"); + RegisterBaseStr(ctx, "Award", Finalize); } - Award* ScAward::GetAward() const + void ScAward::Finalize(JSRuntime* rt, JSValue thisVal) { - return &getGameState().park.currentAwards[_index]; + OpaqueAwardData* data = gScAward.GetOpaque(thisVal); + if (data) + delete data; } - std::string ScAward::type_get() const + Award* ScAward::GetAward(JSValue thisVal) { - auto award = GetAward(); + OpaqueAwardData* data = gScAward.GetOpaque(thisVal); + return &getGameState().park.currentAwards[data->index]; + } + + JSValue ScAward::type_get(JSContext* ctx, JSValue thisVal) + { + auto award = GetAward(thisVal); if (award == nullptr) - return {}; + return JSFromStdString(ctx, {}); - return AwardTypeToString(award->Type).value_or(std::string()); + return JSFromStdString(ctx, AwardTypeToString(award->Type).value_or(std::string())); } - std::string ScAward::text_get() const + JSValue ScAward::text_get(JSContext* ctx, JSValue thisVal) { - auto award = GetAward(); + auto award = GetAward(thisVal); if (award == nullptr) - return {}; + return JSFromStdString(ctx, {}); Formatter ft{}; ft.Add(AwardGetText(award->Type)); - return FormatStringIDLegacy(STR_STRINGID, ft.Data()); + return JSFromStdString(ctx, FormatStringIDLegacy(STR_STRINGID, ft.Data())); } - uint16_t ScAward::monthsRemaining_get() const + JSValue ScAward::monthsRemaining_get(JSContext* ctx, JSValue thisVal) { - auto award = GetAward(); + auto award = GetAward(thisVal); if (award == nullptr) - return {}; + return JS_NewInt32(ctx, {}); - return award->Time; + return JS_NewInt32(ctx, award->Time); } - bool ScAward::positive_get() const + JSValue ScAward::positive_get(JSContext* ctx, JSValue thisVal) { - auto award = GetAward(); + auto award = GetAward(thisVal); if (award == nullptr) - return {}; + return JS_NewBool(ctx, {}); - return AwardIsPositive(award->Type); + return JS_NewBool(ctx, AwardIsPositive(award->Type)); } - uint32_t ScAward::imageId_get() const + JSValue ScAward::imageId_get(JSContext* ctx, JSValue thisVal) { - auto award = GetAward(); + auto award = GetAward(thisVal); if (award == nullptr) - return {}; + return JS_NewUint32(ctx, {}); - return AwardGetSprite(award->Type); + return JS_NewUint32(ctx, AwardGetSprite(award->Type)); } } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/world/ScAward.hpp b/src/openrct2/scripting/bindings/world/ScAward.hpp index 71ab30b46c..4118805683 100644 --- a/src/openrct2/scripting/bindings/world/ScAward.hpp +++ b/src/openrct2/scripting/bindings/world/ScAward.hpp @@ -13,9 +13,9 @@ #include "../../../Context.h" #include "../../../management/Award.h" - #include "../../Duktape.hpp" #include "../../ScriptEngine.h" + #include #include namespace OpenRCT2::Scripting @@ -60,24 +60,31 @@ namespace OpenRCT2::Scripting return std::nullopt; } - class ScAward + class ScAward; + extern ScAward gScAward; + + class ScAward final : public ScBase { - private: - size_t _index{}; - public: - ScAward(size_t index); - - static void Register(duk_context* ctx); + JSValue New(JSContext* ctx, size_t index); + void Register(JSContext* ctx); private: - Award* GetAward() const; + static void Finalize(JSRuntime* rt, JSValue thisVal); + static Award* GetAward(JSValue thisVal); - std::string type_get() const; - std::string text_get() const; - uint16_t monthsRemaining_get() const; - bool positive_get() const; - uint32_t imageId_get() const; + static JSValue type_get(JSContext* ctx, JSValue thisVal); + static JSValue text_get(JSContext* ctx, JSValue thisVal); + static JSValue monthsRemaining_get(JSContext* ctx, JSValue thisVal); + static JSValue positive_get(JSContext* ctx, JSValue thisVal); + static JSValue imageId_get(JSContext* ctx, JSValue thisVal); + + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("type", &ScAward::type_get, nullptr), JS_CGETSET_DEF("text", &ScAward::text_get, nullptr), + JS_CGETSET_DEF("positive", &ScAward::positive_get, nullptr), + JS_CGETSET_DEF("imageId", &ScAward::imageId_get, nullptr), + JS_CGETSET_DEF("monthsRemaining", &ScAward::monthsRemaining_get, nullptr) + }; }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/world/ScDate.hpp b/src/openrct2/scripting/bindings/world/ScDate.hpp index c3e2e1c2fa..122474f8b6 100644 --- a/src/openrct2/scripting/bindings/world/ScDate.hpp +++ b/src/openrct2/scripting/bindings/world/ScDate.hpp @@ -15,74 +15,87 @@ #include "../../../Date.h" #include "../../../Game.h" #include "../../../GameState.h" - #include "../../Duktape.hpp" #include "../../ScriptEngine.h" namespace OpenRCT2::Scripting { - class ScDate + class ScDate; + extern ScDate gScDate; + class ScDate final : public ScBase { + static JSValue monthsElapsed_get(JSContext* ctx, JSValue) + { + const auto& date = GetDate(); + return JS_NewInt32(ctx, date.GetMonthsElapsed()); + } + + static JSValue monthsElapsed_set(JSContext* ctx, JSValue, JSValue jsValue) + { + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + OpenRCT2::getGameState().date = Date{ value, GetDate().GetMonthTicks() }; + return JS_UNDEFINED; + } + + static JSValue monthProgress_get(JSContext* ctx, JSValue) + { + const auto& date = GetDate(); + return JS_NewUint32(ctx, date.GetMonthTicks()); + } + + static JSValue monthProgress_set(JSContext* ctx, JSValue, JSValue jsValue) + { + JS_UNPACK_INT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + OpenRCT2::getGameState().date = Date{ GetDate().GetMonthsElapsed(), static_cast(value) }; + return JS_UNDEFINED; + } + + static JSValue yearsElapsed_get(JSContext* ctx, JSValue) + { + const auto& date = GetDate(); + return JS_NewUint32(ctx, date.GetYear()); + } + + static JSValue ticksElapsed_get(JSContext* ctx, JSValue) + { + return JS_NewUint32(ctx, getGameState().currentTicks); + } + + static JSValue day_get(JSContext* ctx, JSValue) + { + return JS_NewInt32(ctx, getGameState().date.GetDay() + 1); + } + + static JSValue month_get(JSContext* ctx, JSValue) + { + return JS_NewInt32(ctx, getGameState().date.GetMonth()); + } + + static JSValue year_get(JSContext* ctx, JSValue) + { + return JS_NewInt32(ctx, getGameState().date.GetYear() + 1); + } + public: - static void Register(duk_context* ctx) + JSValue New(JSContext* ctx) { - dukglue_register_property(ctx, &ScDate::monthsElapsed_get, &ScDate::monthsElapsed_set, "monthsElapsed"); - dukglue_register_property(ctx, &ScDate::monthProgress_get, &ScDate::monthProgress_set, "monthProgress"); - dukglue_register_property(ctx, &ScDate::yearsElapsed_get, nullptr, "yearsElapsed"); - dukglue_register_property(ctx, &ScDate::ticksElapsed_get, nullptr, "ticksElapsed"); - dukglue_register_property(ctx, &ScDate::day_get, nullptr, "day"); - dukglue_register_property(ctx, &ScDate::month_get, nullptr, "month"); - dukglue_register_property(ctx, &ScDate::year_get, nullptr, "year"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("monthsElapsed", ScDate::monthsElapsed_get, ScDate::monthsElapsed_set), + JS_CGETSET_DEF("monthProgress", ScDate::monthProgress_get, ScDate::monthProgress_set), + JS_CGETSET_DEF("yearsElapsed", ScDate::yearsElapsed_get, nullptr), + JS_CGETSET_DEF("ticksElapsed", ScDate::ticksElapsed_get, nullptr), + JS_CGETSET_DEF("day", ScDate::day_get, nullptr), + JS_CGETSET_DEF("month", ScDate::month_get, nullptr), + JS_CGETSET_DEF("year", ScDate::year_get, nullptr), + }; + + return MakeWithOpaque(ctx, funcs, nullptr); } - private: - int32_t monthsElapsed_get() const + void Register(JSContext* ctx) { - const auto& date = GetDate(); - return date.GetMonthsElapsed(); - } - - void monthsElapsed_set(uint32_t value) - { - ThrowIfGameStateNotMutable(); - getGameState().date = Date{ value, GetDate().GetMonthTicks() }; - } - - uint32_t monthProgress_get() const - { - const auto& date = GetDate(); - return date.GetMonthTicks(); - } - - void monthProgress_set(int32_t value) - { - ThrowIfGameStateNotMutable(); - getGameState().date = Date{ GetDate().GetMonthsElapsed(), static_cast(value) }; - } - - uint32_t yearsElapsed_get() const - { - const auto& date = GetDate(); - return date.GetYear(); - } - - uint32_t ticksElapsed_get() const - { - return getGameState().currentTicks; - } - - int32_t day_get() const - { - return getGameState().date.GetDay() + 1; - } - - int32_t month_get() const - { - return getGameState().date.GetMonth(); - } - - int32_t year_get() const - { - return getGameState().date.GetYear() + 1; + RegisterBaseStr(ctx, "Date"); } }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/world/ScMap.cpp b/src/openrct2/scripting/bindings/world/ScMap.cpp index d74ebe241e..dfef620e1f 100644 --- a/src/openrct2/scripting/bindings/world/ScMap.cpp +++ b/src/openrct2/scripting/bindings/world/ScMap.cpp @@ -24,7 +24,7 @@ #include "../../../ride/Ride.h" #include "../../../ride/RideManager.hpp" #include "../../../ride/TrainManager.h" - #include "../../Duktape.hpp" + #include "../../../ride/Vehicle.h" #include "../entity/ScBalloon.hpp" #include "../entity/ScEntity.hpp" #include "../entity/ScGuest.hpp" @@ -39,81 +39,85 @@ namespace OpenRCT2::Scripting { - ScMap::ScMap(duk_context* ctx) - : _context(ctx) + + JSValue ScMap::size_get(JSContext* ctx, JSValue thisVal) { + return ToJSValue(ctx, getGameState().mapSize); } - DukValue ScMap::size_get() const - { - return ToDuk(_context, getGameState().mapSize); - } - - int32_t ScMap::numRides_get() const + JSValue ScMap::numRides_get(JSContext* ctx, JSValue thisVal) { auto& gameState = getGameState(); - return static_cast(RideManager(gameState).size()); + return JS_NewInt64(ctx, RideManager(gameState).size()); } - int32_t ScMap::numEntities_get() const + JSValue ScMap::numEntities_get(JSContext* ctx, JSValue thisVal) { - return kMaxEntities; + return JS_NewInt32(ctx, kMaxEntities); } - std::vector> ScMap::rides_get() const + JSValue ScMap::rides_get(JSContext* ctx, JSValue thisVal) { - std::vector> result; + JSValue result = JS_NewArray(ctx); auto& gameState = getGameState(); auto rideManager = RideManager(gameState); + int64_t idx = 0; for (const auto& ride : rideManager) { - result.push_back(std::make_shared(ride.id)); + JS_SetPropertyInt64(ctx, result, idx++, gScRide.New(ctx, ride.id)); } return result; } - std::shared_ptr ScMap::getRide(int32_t id) const + JSValue ScMap::getRide(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { + JS_UNPACK_INT32(id, ctx, argv[0]); auto& gameState = getGameState(); auto rideManager = RideManager(gameState); auto ride = rideManager[RideId::FromUnderlying(id)]; if (ride != nullptr) { - return std::make_shared(ride->id); + return gScRide.New(ctx, ride->id); } - return {}; + return JS_NULL; } - std::shared_ptr ScMap::getTile(int32_t x, int32_t y) const + JSValue ScMap::getTile(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { + JS_UNPACK_INT32(x, ctx, argv[0]); + JS_UNPACK_INT32(y, ctx, argv[1]); auto coords = TileCoordsXY(x, y).ToCoordsXY(); - return std::make_shared(coords); + return gScTile.New(ctx, coords); } - DukValue ScMap::getEntity(int32_t id) const + JSValue ScMap::getEntity(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { + JS_UNPACK_INT32(id, ctx, argv[0]); + if (id >= 0 && id < kMaxEntities) { auto spriteId = EntityId::FromUnderlying(id); auto sprite = getGameState().entities.GetEntity(spriteId); if (sprite != nullptr && sprite->Type != EntityType::null) { - return GetEntityAsDukValue(sprite); + return GetEntityAsDukValue(ctx, sprite); } } - duk_push_null(_context); - return DukValue::take_from_stack(_context); + return JS_NULL; } - std::vector ScMap::getAllEntities(const std::string& type) const + JSValue ScMap::getAllEntities(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - std::vector result; + JS_UNPACK_STR(type, ctx, argv[0]); + + JSValue result = JS_NewArray(ctx); + uint64_t idx = 0; if (type == "balloon") { for (auto sprite : EntityList()) { - result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + JS_SetPropertyInt64(ctx, result, idx++, ScBalloon::New(ctx, sprite->Id)); } } else if (type == "car") @@ -129,7 +133,7 @@ namespace OpenRCT2::Scripting break; } - result.push_back(GetObjectAsDukValue(_context, std::make_shared(carId))); + JS_SetPropertyInt64(ctx, result, idx++, ScVehicle::New(ctx, carId)); // Prevent infinite loops: Ensure next_vehicle_on_train is valid and not self-referencing auto nextCarId = car->next_vehicle_on_train; @@ -146,39 +150,39 @@ namespace OpenRCT2::Scripting { for (auto sprite : EntityList()) { - result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + JS_SetPropertyInt64(ctx, result, idx++, ScLitter::New(ctx, sprite->Id)); } } else if (type == "money_effect") { for (auto sprite : EntityList()) { - result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + JS_SetPropertyInt64(ctx, result, idx++, ScMoneyEffect::New(ctx, sprite->Id)); } } else if (type == "duck") { for (auto sprite : EntityList()) { - result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + JS_SetPropertyInt64(ctx, result, idx++, gScEntity.New(ctx, sprite->Id)); } } else if (type == "peep") { for (auto sprite : EntityList()) { - result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + JS_SetPropertyInt64(ctx, result, idx++, ScGuest::New(ctx, sprite->Id)); } for (auto sprite : EntityList()) { - result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + JS_SetPropertyInt64(ctx, result, idx++, ScStaff::New(ctx, sprite->Id)); } } else if (type == "guest") { for (auto sprite : EntityList()) { - result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + JS_SetPropertyInt64(ctx, result, idx++, ScGuest::New(ctx, sprite->Id)); } } else if (type == "staff") @@ -191,22 +195,22 @@ namespace OpenRCT2::Scripting switch (staff->AssignedStaffType) { case StaffType::handyman: - result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + JS_SetPropertyInt64(ctx, result, idx++, ScHandyman::New(ctx, sprite->Id)); break; case StaffType::mechanic: - result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + JS_SetPropertyInt64(ctx, result, idx++, ScMechanic::New(ctx, sprite->Id)); break; case StaffType::security: - result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + JS_SetPropertyInt64(ctx, result, idx++, ScSecurity::New(ctx, sprite->Id)); break; default: - result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + JS_SetPropertyInt64(ctx, result, idx++, ScStaff::New(ctx, sprite->Id)); break; } } else { - result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + JS_SetPropertyInt64(ctx, result, idx++, ScStaff::New(ctx, sprite->Id)); } } } @@ -214,66 +218,71 @@ namespace OpenRCT2::Scripting { for (auto sprite : EntityList()) { - result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + JS_SetPropertyInt64(ctx, result, idx++, ScCrashedVehicleParticle::New(ctx, sprite->Id)); } } else { - duk_error(_context, DUK_ERR_ERROR, "Invalid entity type."); + JS_FreeValue(ctx, result); + JS_ThrowPlainError(ctx, "Invalid entity type."); + return JS_EXCEPTION; } return result; } - std::vector ScMap::getAllEntitiesOnTile(const std::string& type, const DukValue& tilePos) const + JSValue ScMap::getAllEntitiesOnTile(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - // Get the tile position - const auto pos = FromDuk(tilePos); + JS_UNPACK_STR(type, ctx, argv[0]); - // Declare a vector that will hold the result to return - std::vector result; + // Get the tile position + const auto pos = JSToCoordsXY(ctx, argv[1]); + + // Declare an array that will hold the result to return + JSValue result = JS_NewArray(ctx); + uint64_t idx = 0; // Use EntityTileList to iterate all entities of the given type on the tile, and push them to result if (type == "balloon") { for (auto sprite : EntityTileList(pos)) { - result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + JS_SetPropertyInt64(ctx, result, idx++, ScBalloon::New(ctx, sprite->Id)); } } else if (type == "car") { for (auto sprite : EntityTileList(pos)) { - result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + JS_SetPropertyInt64(ctx, result, idx++, ScVehicle::New(ctx, sprite->Id)); } } else if (type == "litter") { for (auto sprite : EntityTileList(pos)) { - result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + JS_SetPropertyInt64(ctx, result, idx++, ScLitter::New(ctx, sprite->Id)); } } else if (type == "duck") { for (auto sprite : EntityTileList(pos)) { - result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + JS_SetPropertyInt64(ctx, result, idx++, ScEntity::New(ctx, sprite->Id)); } } else if (type == "guest") { for (auto sprite : EntityTileList(pos)) { - result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + JS_SetPropertyInt64(ctx, result, idx++, ScGuest::New(ctx, sprite->Id)); } } else if (type == "money_effect") { for (auto sprite : EntityTileList(pos)) { - result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + JS_SetPropertyInt64(ctx, result, idx++, ScMoneyEffect::New(ctx, sprite->Id)); } } else if (type == "staff") @@ -286,22 +295,22 @@ namespace OpenRCT2::Scripting switch (staff->AssignedStaffType) { case StaffType::handyman: - result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + JS_SetPropertyInt64(ctx, result, idx++, ScHandyman::New(ctx, sprite->Id)); break; case StaffType::mechanic: - result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + JS_SetPropertyInt64(ctx, result, idx++, ScMechanic::New(ctx, sprite->Id)); break; case StaffType::security: - result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + JS_SetPropertyInt64(ctx, result, idx++, ScSecurity::New(ctx, sprite->Id)); break; default: - result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + JS_SetPropertyInt64(ctx, result, idx++, ScStaff::New(ctx, sprite->Id)); break; } } else { - result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + JS_SetPropertyInt64(ctx, result, idx++, ScStaff::New(ctx, sprite->Id)); } } } @@ -309,50 +318,55 @@ namespace OpenRCT2::Scripting { for (auto sprite : EntityTileList(pos)) { - result.push_back(GetObjectAsDukValue(_context, std::make_shared(sprite->Id))); + JS_SetPropertyInt64(ctx, result, idx++, ScCrashedVehicleParticle::New(ctx, sprite->Id)); } } else { // If the given type isn't valid, throw an error - duk_error(_context, DUK_ERR_ERROR, "Invalid entity type: %s", type.c_str()); + JS_FreeValue(ctx, result); + JS_ThrowPlainError(ctx, "Invalid entity type: %s", type.c_str()); + return JS_EXCEPTION; } return result; } template - DukValue createEntityType(duk_context* ctx, const DukValue& initializer) + JSValue createEntityType(JSContext* ctx, JSValue initializer) { TEntityType* entity = getGameState().entities.CreateEntity(); if (entity == nullptr) { // Probably no more space for entities for this specified entity type. - return ToDuk(ctx, undefined); + return JS_UNDEFINED; } - auto entityPos = CoordsXYZ{ AsOrDefault(initializer["x"], 0), AsOrDefault(initializer["y"], 0), - AsOrDefault(initializer["z"], 0) }; + auto entityPos = CoordsXYZ{ AsOrDefault(ctx, initializer, "x", 0), AsOrDefault(ctx, initializer, "y", 0), + AsOrDefault(ctx, initializer, "z", 0) }; entity->MoveTo(entityPos); - return GetObjectAsDukValue(ctx, std::make_shared(entity->Id)); + return TScriptType::New(ctx, entity->Id); } - DukValue ScMap::createEntity(const std::string& type, const DukValue& initializer) + JSValue ScMap::createEntity(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - DukValue res; + JS_UNPACK_STR(type, ctx, argv[0]); + JSValue initializer = argv[1]; + + JSValue res; if (type == "car") { Vehicle* entity = getGameState().entities.CreateEntity(); if (entity == nullptr) { // Probably no more space for entities for this specified entity type. - res = ToDuk(_context, undefined); + res = JS_UNDEFINED; } else { - auto entityPos = CoordsXYZ{ AsOrDefault(initializer["x"], 0), AsOrDefault(initializer["y"], 0), - AsOrDefault(initializer["z"], 0) }; + auto entityPos = CoordsXYZ{ AsOrDefault(ctx, initializer, "x", 0), AsOrDefault(ctx, initializer, "y", 0), + AsOrDefault(ctx, initializer, "z", 0) }; entity->MoveTo(entityPos); // Reset some important vehicle vars to their null values @@ -366,97 +380,105 @@ namespace OpenRCT2::Scripting } entity->BoatLocation.SetNull(); - res = GetObjectAsDukValue(_context, std::make_shared(entity->Id)); + res = ScVehicle::New(ctx, entity->Id); } } else if (type == "staff") { - res = createEntityType(_context, initializer); + res = createEntityType(ctx, initializer); } else if (type == "guest") { - res = createEntityType(_context, initializer); + res = createEntityType(ctx, initializer); } else if (type == "steam_particle") { - res = createEntityType(_context, initializer); + res = createEntityType(ctx, initializer); } else if (type == "money_effect") { - res = createEntityType(_context, initializer); + res = createEntityType(ctx, initializer); } else if (type == "crashed_vehicle_particle") { - res = createEntityType(_context, initializer); + res = createEntityType(ctx, initializer); } else if (type == "explosion_cloud") { - res = createEntityType(_context, initializer); + res = createEntityType(ctx, initializer); } else if (type == "crash_splash") { - res = createEntityType(_context, initializer); + res = createEntityType(ctx, initializer); } else if (type == "explosion_flare") { - res = createEntityType(_context, initializer); + res = createEntityType(ctx, initializer); } else if (type == "balloon") { - res = createEntityType(_context, initializer); + res = createEntityType(ctx, initializer); } else if (type == "duck") { - res = createEntityType(_context, initializer); + res = createEntityType(ctx, initializer); } else if (type == "jumping_fountain") { - res = createEntityType(_context, initializer); + res = createEntityType(ctx, initializer); } else if (type == "litter") { - res = createEntityType(_context, initializer); + res = createEntityType(ctx, initializer); } else { - duk_error(_context, DUK_ERR_ERROR, "Invalid entity type."); + JS_ThrowPlainError(ctx, "Invalid entity type."); + res = JS_EXCEPTION; } return res; } - DukValue ScMap::getTrackIterator(const DukValue& dukPosition, int32_t elementIndex) const + JSValue ScMap::getTrackIterator(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto position = FromDuk(dukPosition); - auto trackIterator = ScTrackIterator::FromElement(position, elementIndex); - if (trackIterator == nullptr) - return ToDuk(_context, undefined); + JS_UNPACK_OBJECT(pos, ctx, argv[0]); + JS_UNPACK_UINT32(elementIndex, ctx, argv[1]); - return GetObjectAsDukValue(_context, trackIterator); + const auto position = JSToCoordsXY(ctx, pos); + return ScTrackIterator::FromElement(ctx, position, elementIndex); } - void ScMap::Register(duk_context* ctx) + void ScMap::Register(JSContext* ctx) { - dukglue_register_property(ctx, &ScMap::size_get, nullptr, "size"); - dukglue_register_property(ctx, &ScMap::numRides_get, nullptr, "numRides"); - dukglue_register_property(ctx, &ScMap::numEntities_get, nullptr, "numEntities"); - dukglue_register_property(ctx, &ScMap::rides_get, nullptr, "rides"); - dukglue_register_method(ctx, &ScMap::getRide, "getRide"); - dukglue_register_method(ctx, &ScMap::getTile, "getTile"); - dukglue_register_method(ctx, &ScMap::getEntity, "getEntity"); - dukglue_register_method(ctx, &ScMap::getAllEntities, "getAllEntities"); - dukglue_register_method(ctx, &ScMap::getAllEntitiesOnTile, "getAllEntitiesOnTile"); - dukglue_register_method(ctx, &ScMap::createEntity, "createEntity"); - dukglue_register_method(ctx, &ScMap::getTrackIterator, "getTrackIterator"); + RegisterBaseStr(ctx, "Map"); } - DukValue ScMap::GetEntityAsDukValue(const EntityBase* sprite) const + JSValue ScMap::New(JSContext* ctx) + { + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("size", ScMap::size_get, nullptr), + JS_CGETSET_DEF("numRides", ScMap::numRides_get, nullptr), + JS_CGETSET_DEF("numEntities", ScMap::numEntities_get, nullptr), + JS_CGETSET_DEF("rides", ScMap::rides_get, nullptr), + JS_CFUNC_DEF("getRide", 1, ScMap::getRide), + JS_CFUNC_DEF("getTile", 2, ScMap::getTile), + JS_CFUNC_DEF("getEntity", 1, ScMap::getEntity), + JS_CFUNC_DEF("getAllEntities", 1, ScMap::getAllEntities), + JS_CFUNC_DEF("getAllEntitiesOnTile", 2, ScMap::getAllEntitiesOnTile), + JS_CFUNC_DEF("createEntity", 2, ScMap::createEntity), + JS_CFUNC_DEF("getTrackIterator", 2, ScMap::getTrackIterator), + }; + return MakeWithOpaque(ctx, funcs, nullptr); + } + + JSValue ScMap::GetEntityAsDukValue(JSContext* ctx, const EntityBase* sprite) { auto spriteId = sprite->Id; switch (sprite->Type) { case EntityType::vehicle: - return GetObjectAsDukValue(_context, std::make_shared(spriteId)); + return ScVehicle::New(ctx, spriteId); case EntityType::staff: { auto staff = getGameState().entities.GetEntity(spriteId); @@ -465,32 +487,32 @@ namespace OpenRCT2::Scripting switch (staff->AssignedStaffType) { case StaffType::handyman: - return GetObjectAsDukValue(_context, std::make_shared(spriteId)); + return ScHandyman::New(ctx, spriteId); case StaffType::mechanic: - return GetObjectAsDukValue(_context, std::make_shared(spriteId)); + return ScMechanic::New(ctx, spriteId); case StaffType::security: - return GetObjectAsDukValue(_context, std::make_shared(spriteId)); + return ScSecurity::New(ctx, spriteId); default: - return GetObjectAsDukValue(_context, std::make_shared(spriteId)); + return ScStaff::New(ctx, spriteId); } } else { - return GetObjectAsDukValue(_context, std::make_shared(spriteId)); + return ScStaff::New(ctx, spriteId); } } case EntityType::guest: - return GetObjectAsDukValue(_context, std::make_shared(spriteId)); + return ScGuest::New(ctx, spriteId); case EntityType::litter: - return GetObjectAsDukValue(_context, std::make_shared(spriteId)); + return ScLitter::New(ctx, spriteId); case EntityType::balloon: - return GetObjectAsDukValue(_context, std::make_shared(spriteId)); + return ScBalloon::New(ctx, spriteId); case EntityType::moneyEffect: - return GetObjectAsDukValue(_context, std::make_shared(spriteId)); + return ScMoneyEffect::New(ctx, spriteId); case EntityType::crashedVehicleParticle: - return GetObjectAsDukValue(_context, std::make_shared(spriteId)); + return ScCrashedVehicleParticle::New(ctx, spriteId); default: - return GetObjectAsDukValue(_context, std::make_shared(spriteId)); + return ScEntity::New(ctx, spriteId); } } diff --git a/src/openrct2/scripting/bindings/world/ScMap.hpp b/src/openrct2/scripting/bindings/world/ScMap.hpp index 91dbff4576..00734321d7 100644 --- a/src/openrct2/scripting/bindings/world/ScMap.hpp +++ b/src/openrct2/scripting/bindings/world/ScMap.hpp @@ -11,47 +11,47 @@ #ifdef ENABLE_SCRIPTING - #include "../../Duktape.hpp" + #include "../../ScriptEngine.h" #include "../ride/ScRide.hpp" #include "../ride/ScTrackIterator.h" #include "../world/ScTile.hpp" namespace OpenRCT2::Scripting { - class ScMap + class ScMap; + extern ScMap gScMap; + + class ScMap final : public ScBase { private: - duk_context* _context; + static JSValue size_get(JSContext* ctx, JSValue thisVal); + + static JSValue numRides_get(JSContext* ctx, JSValue thisVal); + + static JSValue numEntities_get(JSContext* ctx, JSValue thisVal); + + static JSValue rides_get(JSContext* ctx, JSValue thisVal); + + static JSValue getRide(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + + static JSValue getTile(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + + static JSValue getEntity(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + + static JSValue getAllEntities(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + + static JSValue getAllEntitiesOnTile(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + + static JSValue createEntity(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + + static JSValue getTrackIterator(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + + static JSValue GetEntityAsDukValue(JSContext* ctx, const EntityBase* sprite); public: - ScMap(duk_context* ctx); + JSValue New(JSContext* ctx); - DukValue size_get() const; - - int32_t numRides_get() const; - - int32_t numEntities_get() const; - - std::vector> rides_get() const; - - std::shared_ptr getRide(int32_t id) const; - - std::shared_ptr getTile(int32_t x, int32_t y) const; - - DukValue getEntity(int32_t id) const; - - std::vector getAllEntities(const std::string& type) const; - - std::vector getAllEntitiesOnTile(const std::string& type, const DukValue& tilePos) const; - - DukValue createEntity(const std::string& type, const DukValue& initializer); - - DukValue getTrackIterator(const DukValue& position, int32_t elementIndex) const; - - static void Register(duk_context* ctx); - - private: - DukValue GetEntityAsDukValue(const EntityBase* sprite) const; + void Register(JSContext* ctx); }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/world/ScPark.cpp b/src/openrct2/scripting/bindings/world/ScPark.cpp index a855fac0e9..eafede98c0 100644 --- a/src/openrct2/scripting/bindings/world/ScPark.cpp +++ b/src/openrct2/scripting/bindings/world/ScPark.cpp @@ -22,14 +22,13 @@ #include "../../../ui/WindowManager.h" #include "../../../windows/Intent.h" #include "../../../world/Park.h" - #include "../../Duktape.hpp" #include "../../ScriptEngine.h" #include "../entity/ScGuest.hpp" #include "ScParkMessage.hpp" namespace OpenRCT2::Scripting { - static const DukEnumMap ParkFlagMap( + static const EnumMap ParkFlagMap( { { "open", PARK_FLAGS_PARK_OPEN }, { "scenarioCompleteNameInput", PARK_FLAGS_SCENARIO_COMPLETE_NAME_INPUT }, @@ -46,273 +45,301 @@ namespace OpenRCT2::Scripting { "unlockAllPrices", PARK_FLAGS_UNLOCK_ALL_PRICES }, }); - ScPark::ScPark(duk_context* ctx) - : _context(ctx) + JSValue ScPark::cash_get(JSContext* ctx, JSValue thisVal) { + return JS_NewInt64(ctx, getGameState().park.cash); } - - money64 ScPark::cash_get() const + JSValue ScPark::cash_set(JSContext* ctx, JSValue thisVal, JSValue value) { - return getGameState().park.cash; - } - void ScPark::cash_set(money64 value) - { - ThrowIfGameStateNotMutable(); + JS_UNPACK_INT64(valueInt, ctx, value) + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); auto& gameState = getGameState(); - if (gameState.park.cash != value) + if (gameState.park.cash != valueInt) { - gameState.park.cash = value; + gameState.park.cash = valueInt; auto intent = Intent(INTENT_ACTION_UPDATE_CASH); ContextBroadcastIntent(&intent); } + return JS_UNDEFINED; } - int32_t ScPark::rating_get() const + JSValue ScPark::rating_get(JSContext* ctx, JSValue thisVal) { - return getGameState().park.rating; + return JS_NewInt32(ctx, getGameState().park.rating); } - void ScPark::rating_set(int32_t value) + JSValue ScPark::rating_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); + JS_UNPACK_INT32(valueInt, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); - auto valueClamped = std::min(std::max(0, value), 999); + auto valueClamped = std::min(std::max(0, valueInt), 999); auto& gameState = getGameState(); if (gameState.park.rating != valueClamped) { - gameState.park.rating = std::min(std::max(0, value), 999); + gameState.park.rating = std::min(std::max(0, valueInt), 999); auto intent = Intent(INTENT_ACTION_UPDATE_PARK_RATING); ContextBroadcastIntent(&intent); } + return JS_UNDEFINED; } - money64 ScPark::bankLoan_get() const + JSValue ScPark::bankLoan_get(JSContext* ctx, JSValue thisVal) { - return getGameState().park.bankLoan; + return JS_NewInt64(ctx, getGameState().park.bankLoan); } - void ScPark::bankLoan_set(money64 value) + JSValue ScPark::bankLoan_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); + JS_UNPACK_INT64(valueInt, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); auto& gameState = getGameState(); - if (gameState.park.bankLoan != value) + if (gameState.park.bankLoan != valueInt) { - gameState.park.bankLoan = value; + gameState.park.bankLoan = valueInt; auto intent = Intent(INTENT_ACTION_UPDATE_CASH); ContextBroadcastIntent(&intent); } + return JS_UNDEFINED; } - money64 ScPark::maxBankLoan_get() const + JSValue ScPark::maxBankLoan_get(JSContext* ctx, JSValue thisVal) { - return getGameState().park.maxBankLoan; + return JS_NewInt64(ctx, getGameState().park.maxBankLoan); } - void ScPark::maxBankLoan_set(money64 value) + JSValue ScPark::maxBankLoan_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); + JS_UNPACK_INT64(valueInt, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE() auto& gameState = getGameState(); - if (gameState.park.maxBankLoan != value) + if (gameState.park.maxBankLoan != valueInt) { - gameState.park.maxBankLoan = value; + gameState.park.maxBankLoan = valueInt; auto intent = Intent(INTENT_ACTION_UPDATE_CASH); ContextBroadcastIntent(&intent); } + return JS_UNDEFINED; } - money64 ScPark::entranceFee_get() const + JSValue ScPark::entranceFee_get(JSContext* ctx, JSValue thisVal) { - return getGameState().park.entranceFee; + return JS_NewInt64(ctx, getGameState().park.entranceFee); } - void ScPark::entranceFee_set(money64 value) + JSValue ScPark::entranceFee_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); + JS_UNPACK_INT64(valueInt, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); auto& gameState = getGameState(); - if (gameState.park.entranceFee != value) + if (gameState.park.entranceFee != valueInt) { - gameState.park.entranceFee = value; + gameState.park.entranceFee = valueInt; auto* windowMgr = Ui::GetWindowManager(); windowMgr->InvalidateByClass(WindowClass::parkInformation); } + return JS_UNDEFINED; } - uint32_t ScPark::guests_get() const + JSValue ScPark::guests_get(JSContext* ctx, JSValue thisVal) { - return getGameState().park.numGuestsInPark; + return JS_NewInt64(ctx, getGameState().park.numGuestsInPark); } - uint32_t ScPark::suggestedGuestMaximum_get() const + JSValue ScPark::suggestedGuestMaximum_get(JSContext* ctx, JSValue thisVal) { - return getGameState().park.suggestedGuestMaximum; + return JS_NewInt64(ctx, getGameState().park.suggestedGuestMaximum); } - int32_t ScPark::guestGenerationProbability_get() const + JSValue ScPark::guestGenerationProbability_get(JSContext* ctx, JSValue thisVal) { - return getGameState().park.guestGenerationProbability; + return JS_NewInt32(ctx, getGameState().park.guestGenerationProbability); } - DukValue ScPark::generateGuest() + JSValue ScPark::generateGuest(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - ThrowIfGameStateNotMutable(); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); auto guest = Park::GenerateGuest(); - return GetObjectAsDukValue(_context, std::make_shared(guest->Id)); + return ScGuest::New(ctx, guest->Id); } - money64 ScPark::guestInitialCash_get() const + JSValue ScPark::guestInitialCash_get(JSContext* ctx, JSValue thisVal) { - return getGameState().scenarioOptions.guestInitialCash; + return JS_NewInt64(ctx, getGameState().scenarioOptions.guestInitialCash); } - uint8_t ScPark::guestInitialHappiness_get() const + JSValue ScPark::guestInitialHappiness_get(JSContext* ctx, JSValue thisVal) { - return getGameState().scenarioOptions.guestInitialHappiness; + return JS_NewInt32(ctx, getGameState().scenarioOptions.guestInitialHappiness); } - uint8_t ScPark::guestInitialHunger_get() const + JSValue ScPark::guestInitialHunger_get(JSContext* ctx, JSValue thisVal) { - return getGameState().scenarioOptions.guestInitialHunger; + return JS_NewInt32(ctx, getGameState().scenarioOptions.guestInitialHunger); } - uint8_t ScPark::guestInitialThirst_get() const + JSValue ScPark::guestInitialThirst_get(JSContext* ctx, JSValue thisVal) { - return getGameState().scenarioOptions.guestInitialThirst; + return JS_NewInt32(ctx, getGameState().scenarioOptions.guestInitialThirst); } - money64 ScPark::value_get() const + JSValue ScPark::value_get(JSContext* ctx, JSValue thisVal) { - return getGameState().park.value; + return JS_NewInt64(ctx, getGameState().park.value); } - void ScPark::value_set(money64 value) + JSValue ScPark::value_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); + JS_UNPACK_INT64(valueInt, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); auto& gameState = getGameState(); - if (gameState.park.value != value) + if (gameState.park.value != valueInt) { - gameState.park.value = value; + gameState.park.value = valueInt; auto intent = Intent(INTENT_ACTION_UPDATE_CASH); ContextBroadcastIntent(&intent); } + return JS_UNDEFINED; } - money64 ScPark::companyValue_get() const + JSValue ScPark::companyValue_get(JSContext* ctx, JSValue thisVal) { - return getGameState().park.companyValue; + return JS_NewInt64(ctx, getGameState().park.companyValue); } - void ScPark::companyValue_set(money64 value) + JSValue ScPark::companyValue_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); + JS_UNPACK_INT64(valueInt, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto& gameState = getGameState(); - if (gameState.park.companyValue != value) + if (gameState.park.companyValue != valueInt) { - gameState.park.companyValue = value; + gameState.park.companyValue = valueInt; auto intent = Intent(INTENT_ACTION_UPDATE_CASH); ContextBroadcastIntent(&intent); } + return JS_UNDEFINED; } - money64 ScPark::totalRideValueForMoney_get() const + JSValue ScPark::totalRideValueForMoney_get(JSContext* ctx, JSValue thisVal) { - return getGameState().park.totalRideValueForMoney; + return JS_NewInt64(ctx, getGameState().park.totalRideValueForMoney); } - uint32_t ScPark::totalAdmissions_get() const + JSValue ScPark::totalAdmissions_get(JSContext* ctx, JSValue thisVal) { - return getGameState().park.totalAdmissions; + return JS_NewInt64(ctx, getGameState().park.totalAdmissions); } - void ScPark::totalAdmissions_set(uint32_t value) + JSValue ScPark::totalAdmissions_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); + JS_UNPACK_INT64(valueInt, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto& gameState = getGameState(); - if (gameState.park.totalAdmissions != value) + if (gameState.park.totalAdmissions != static_cast(valueInt)) { - gameState.park.totalAdmissions = value; + gameState.park.totalAdmissions = static_cast(valueInt); auto* windowMgr = Ui::GetWindowManager(); windowMgr->InvalidateByClass(WindowClass::parkInformation); } + return JS_UNDEFINED; } - money64 ScPark::totalIncomeFromAdmissions_get() const + JSValue ScPark::totalIncomeFromAdmissions_get(JSContext* ctx, JSValue thisVal) { - return getGameState().park.totalIncomeFromAdmissions; + return JS_NewInt64(ctx, getGameState().park.totalIncomeFromAdmissions); } - void ScPark::totalIncomeFromAdmissions_set(money64 value) + JSValue ScPark::totalIncomeFromAdmissions_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); + JS_UNPACK_INT64(valueInt, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto& gameState = getGameState(); - if (gameState.park.totalIncomeFromAdmissions != value) + if (gameState.park.totalIncomeFromAdmissions != valueInt) { - gameState.park.totalIncomeFromAdmissions = value; + gameState.park.totalIncomeFromAdmissions = valueInt; auto* windowMgr = Ui::GetWindowManager(); windowMgr->InvalidateByClass(WindowClass::parkInformation); } + return JS_UNDEFINED; } - money64 ScPark::landPrice_get() const + JSValue ScPark::landPrice_get(JSContext* ctx, JSValue thisVal) { - return getGameState().scenarioOptions.landPrice; + return JS_NewInt64(ctx, getGameState().scenarioOptions.landPrice); } - void ScPark::landPrice_set(money64 value) + JSValue ScPark::landPrice_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().scenarioOptions.landPrice = value; + JS_UNPACK_INT64(valueInt, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + getGameState().scenarioOptions.landPrice = valueInt; + return JS_UNDEFINED; } - money64 ScPark::constructionRightsPrice_get() const + JSValue ScPark::constructionRightsPrice_get(JSContext* ctx, JSValue thisVal) { - return getGameState().scenarioOptions.constructionRightsPrice; + return JS_NewInt64(ctx, getGameState().scenarioOptions.constructionRightsPrice); } - void ScPark::constructionRightsPrice_set(money64 value) + JSValue ScPark::constructionRightsPrice_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().scenarioOptions.constructionRightsPrice = value; + JS_UNPACK_INT64(valueInt, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + getGameState().scenarioOptions.constructionRightsPrice = valueInt; + return JS_UNDEFINED; } - int16_t ScPark::casualtyPenalty_get() const + JSValue ScPark::casualtyPenalty_get(JSContext* ctx, JSValue thisVal) { - return getGameState().park.ratingCasualtyPenalty; + return JS_NewInt32(ctx, getGameState().park.ratingCasualtyPenalty); } - void ScPark::casualtyPenalty_set(int16_t value) + JSValue ScPark::casualtyPenalty_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().park.ratingCasualtyPenalty = value; + JS_UNPACK_INT32(valueInt, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + getGameState().park.ratingCasualtyPenalty = valueInt; + return JS_UNDEFINED; } - uint16_t ScPark::parkSize_get() const + JSValue ScPark::parkSize_get(JSContext* ctx, JSValue thisVal) { - return getGameState().park.size; + return JS_NewInt64(ctx, getGameState().park.size); } - std::string ScPark::name_get() const + JSValue ScPark::name_get(JSContext* ctx, JSValue thisVal) { - return getGameState().park.name; + return JSFromStdString(ctx, getGameState().park.name); } - void ScPark::name_set(std::string value) + JSValue ScPark::name_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); + JS_UNPACK_STR(valueStr, ctx, value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); auto& park = getGameState().park; - if (park.name != value) + if (park.name != valueStr) { - park.name = std::move(value); + park.name = std::move(valueStr); GfxInvalidateScreen(); } + return JS_UNDEFINED; } - bool ScPark::getFlag(const std::string& key) const + JSValue ScPark::getFlag(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { + JS_UNPACK_STR(key, ctx, argv[0]) auto mask = ParkFlagMap[key]; - return (getGameState().park.flags & mask) != 0; + return JS_NewBool(ctx, (getGameState().park.flags & mask) != 0); } - void ScPark::setFlag(const std::string& key, bool value) + JSValue ScPark::setFlag(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - ThrowIfGameStateNotMutable(); + JS_UNPACK_STR(key, ctx, argv[0]); + JS_UNPACK_BOOL(value, ctx, argv[1]); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto mask = ParkFlagMap[key]; auto& gameState = getGameState(); if (value) @@ -320,37 +347,39 @@ namespace OpenRCT2::Scripting else gameState.park.flags &= ~mask; GfxInvalidateScreen(); + return JS_UNDEFINED; } - std::shared_ptr ScPark::research_get() const + JSValue ScPark::research_get(JSContext* ctx, JSValue thisVal) { - return std::make_shared(_context); + return gScResearch.New(ctx); } - std::vector> ScPark::messages_get() const + JSValue ScPark::messages_get(JSContext* ctx, JSValue thisVal) { - std::vector> result; + JSValue result = JS_NewArray(ctx); + int64_t resultIdx = 0; auto& gameState = getGameState(); for (size_t i = 0, newsSize = gameState.newsItems.GetRecent().size(); i < newsSize; i++) { - result.push_back(std::make_shared(i)); + JS_SetPropertyInt64(ctx, result, resultIdx++, gScParkMessage.New(ctx, i)); } for (size_t i = 0, newsSize = gameState.newsItems.GetArchived().size(); i < newsSize; i++) { - result.push_back(std::make_shared(i + News::ItemHistoryStart)); + auto offset = i + News::ItemHistoryStart; + JS_SetPropertyInt64(ctx, result, resultIdx++, gScParkMessage.New(ctx, offset)); } return result; } - void ScPark::messages_set(const std::vector& value) + JSValue ScPark::messages_set(JSContext* ctx, JSValue thisVal, JSValue value) { int32_t index = 0; int32_t archiveIndex = News::ItemHistoryStart; auto& gameState = getGameState(); - for (const auto& item : value) - { - auto isArchived = item["isArchived"].as_bool(); - auto newsItem = FromDuk(item); + JSIterateArray(ctx, value, [&index, &archiveIndex, &gameState](JSContext* ctx2, JSValue item) { + auto isArchived = AsOrDefault(ctx2, item, "isArchived", false); + auto newsItem = NewsItemFromJS(ctx2, item); if (isArchived) { if (archiveIndex < News::MaxItems) @@ -367,7 +396,7 @@ namespace OpenRCT2::Scripting index++; } } - } + }); // End the lists by setting next item to null if (index < News::ItemHistoryStart) @@ -378,127 +407,142 @@ namespace OpenRCT2::Scripting { gameState.newsItems[archiveIndex].type = News::ItemType::null; } + return JS_UNDEFINED; } - void ScPark::postMessage(DukValue message) + JSValue ScPark::postMessage(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - ThrowIfGameStateNotMutable(); - try - { - uint32_t assoc = std::numeric_limits::max(); - auto type = News::ItemType::blank; - std::string text; - if (message.type() == DukValue::Type::STRING) - { - text = message.as_string(); - } - else - { - type = GetParkMessageType(message["type"].as_string()); - text = message["text"].as_string(); - if (type == News::ItemType::blank) - { - assoc = static_cast(((kCoordsNull & 0xFFFF) << 16) | (kCoordsNull & 0xFFFF)); - } + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); - auto dukSubject = message["subject"]; - if (dukSubject.type() == DukValue::Type::NUMBER) - { - assoc = static_cast(dukSubject.as_uint()); - } - } - News::AddItemToQueue(type, text.c_str(), assoc); - } - catch (const DukException&) + uint32_t assoc = std::numeric_limits::max(); + auto type = News::ItemType::blank; + std::string text; + if (JS_IsString(argv[0])) { - duk_error(message.context(), DUK_ERR_ERROR, "Invalid message argument."); + text = JSToStdString(ctx, argv[0]); } + else + { + auto textOption = JSToOptionalStdString(ctx, argv[0], "text"); + auto typeOption = JSToOptionalStdString(ctx, argv[0], "type"); + if (!textOption.has_value() || !typeOption.has_value()) + { + return JS_ThrowPlainError(ctx, "Invalid message argument."); + } + + type = GetParkMessageType(typeOption.value()); + text = textOption.value(); + if (type == News::ItemType::blank) + { + assoc = static_cast(((kCoordsNull & 0xFFFF) << 16) | (kCoordsNull & 0xFFFF)); + } + + auto subject = JS_GetPropertyStr(ctx, argv[0], "subject"); + if (JS_IsNumber(subject)) + { + assoc = JSToUint(ctx, subject); + } + } + News::AddItemToQueue(type, text.c_str(), assoc); + return JS_UNDEFINED; } - std::vector ScPark::getMonthlyExpenditure(const std::string& expenditureType) const + JSValue ScPark::getMonthlyExpenditure(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { + JS_UNPACK_STR(expenditureType, ctx, argv[0]); + auto recordedMonths = std::clamp( GetDate().GetMonthsElapsed() + 1, static_cast(0), static_cast(kExpenditureTableMonthCount)); - std::vector result(recordedMonths, 0); + JSValue result = JS_NewArray(ctx); auto type = ScriptEngine::StringToExpenditureType(expenditureType); if (type != ExpenditureType::count) { auto& gameState = getGameState(); for (size_t i = 0; i < recordedMonths; ++i) { - result[i] = gameState.park.expenditureTable[i][EnumValue(type)]; + JS_SetPropertyInt64(ctx, result, i, JS_NewInt64(ctx, gameState.park.expenditureTable[i][EnumValue(type)])); } } return result; } - std::vector> ScPark::awards_get() const + JSValue ScPark::awards_get(JSContext* ctx, JSValue thisVal) { - std::vector> result; + JSValue result = JS_NewArray(ctx); auto& gameState = getGameState(); for (size_t i = 0; i < gameState.park.currentAwards.size(); i++) { - result.push_back(std::make_shared(i)); + JS_SetPropertyInt64(ctx, result, i, gScAward.New(ctx, i)); } return result; } - void ScPark::clearAwards() const + JSValue ScPark::clearAwards(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - ThrowIfGameStateNotMutable(); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); AwardReset(); + return JS_UNDEFINED; } - void ScPark::grantAward(const std::string& awardType) const + JSValue ScPark::grantAward(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - ThrowIfGameStateNotMutable(); + JS_UNPACK_STR(awardType, ctx, argv[0]); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto optType = StringToAwardType(awardType); if (optType.has_value()) { AwardGrant(optType.value()); } + return JS_UNDEFINED; } - void ScPark::Register(duk_context* ctx) + JSValue ScPark::New(JSContext* ctx) { - dukglue_register_property(ctx, &ScPark::cash_get, &ScPark::cash_set, "cash"); - dukglue_register_property(ctx, &ScPark::rating_get, &ScPark::rating_set, "rating"); - dukglue_register_property(ctx, &ScPark::bankLoan_get, &ScPark::bankLoan_set, "bankLoan"); - dukglue_register_property(ctx, &ScPark::maxBankLoan_get, &ScPark::maxBankLoan_set, "maxBankLoan"); - dukglue_register_property(ctx, &ScPark::entranceFee_get, &ScPark::entranceFee_set, "entranceFee"); - dukglue_register_property(ctx, &ScPark::guests_get, nullptr, "guests"); - dukglue_register_property(ctx, &ScPark::suggestedGuestMaximum_get, nullptr, "suggestedGuestMaximum"); - dukglue_register_property(ctx, &ScPark::guestGenerationProbability_get, nullptr, "guestGenerationProbability"); - dukglue_register_method(ctx, &ScPark::generateGuest, "generateGuest"); - dukglue_register_property(ctx, &ScPark::guestInitialCash_get, nullptr, "guestInitialCash"); - dukglue_register_property(ctx, &ScPark::guestInitialHappiness_get, nullptr, "guestInitialHappiness"); - dukglue_register_property(ctx, &ScPark::guestInitialHunger_get, nullptr, "guestInitialHunger"); - dukglue_register_property(ctx, &ScPark::guestInitialThirst_get, nullptr, "guestInitialThirst"); - dukglue_register_property(ctx, &ScPark::value_get, &ScPark::value_set, "value"); - dukglue_register_property(ctx, &ScPark::companyValue_get, &ScPark::companyValue_set, "companyValue"); - dukglue_register_property(ctx, &ScPark::totalRideValueForMoney_get, nullptr, "totalRideValueForMoney"); - dukglue_register_property(ctx, &ScPark::totalAdmissions_get, &ScPark::totalAdmissions_set, "totalAdmissions"); - dukglue_register_property( - ctx, &ScPark::totalIncomeFromAdmissions_get, &ScPark::totalIncomeFromAdmissions_set, "totalIncomeFromAdmissions"); - dukglue_register_property(ctx, &ScPark::landPrice_get, &ScPark::landPrice_set, "landPrice"); - dukglue_register_property( - ctx, &ScPark::constructionRightsPrice_get, &ScPark::constructionRightsPrice_set, "constructionRightsPrice"); - dukglue_register_property(ctx, &ScPark::parkSize_get, nullptr, "parkSize"); - dukglue_register_property(ctx, &ScPark::name_get, &ScPark::name_set, "name"); - dukglue_register_property(ctx, &ScPark::research_get, nullptr, "research"); - dukglue_register_property(ctx, &ScPark::messages_get, &ScPark::messages_set, "messages"); - dukglue_register_property(ctx, &ScPark::casualtyPenalty_get, &ScPark::casualtyPenalty_set, "casualtyPenalty"); - dukglue_register_method(ctx, &ScPark::getFlag, "getFlag"); - dukglue_register_method(ctx, &ScPark::setFlag, "setFlag"); - dukglue_register_method(ctx, &ScPark::postMessage, "postMessage"); - dukglue_register_method(ctx, &ScPark::getMonthlyExpenditure, "getMonthlyExpenditure"); - dukglue_register_property(ctx, &ScPark::awards_get, nullptr, "awards"); - dukglue_register_method(ctx, &ScPark::clearAwards, "clearAwards"); - dukglue_register_method(ctx, &ScPark::grantAward, "grantAward"); - } + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("cash", ScPark::cash_get, ScPark::cash_set), + JS_CGETSET_DEF("rating", ScPark::rating_get, ScPark::rating_set), + JS_CGETSET_DEF("bankLoan", ScPark::bankLoan_get, ScPark::bankLoan_set), + JS_CGETSET_DEF("maxBankLoan", ScPark::maxBankLoan_get, ScPark::maxBankLoan_set), + JS_CGETSET_DEF("entranceFee", ScPark::entranceFee_get, ScPark::entranceFee_set), + JS_CGETSET_DEF("guests", ScPark::guests_get, nullptr), + JS_CGETSET_DEF("suggestedGuestMaximum", ScPark::suggestedGuestMaximum_get, nullptr), + JS_CGETSET_DEF("guestGenerationProbability", ScPark::guestGenerationProbability_get, nullptr), + JS_CFUNC_DEF("generateGuest", 0, ScPark::generateGuest), + JS_CGETSET_DEF("guestInitialCash", ScPark::guestInitialCash_get, nullptr), + JS_CGETSET_DEF("guestInitialHappiness", ScPark::guestInitialHappiness_get, nullptr), + JS_CGETSET_DEF("guestInitialHunger", ScPark::guestInitialHunger_get, nullptr), + JS_CGETSET_DEF("guestInitialThirst", ScPark::guestInitialThirst_get, nullptr), + JS_CGETSET_DEF("value", ScPark::value_get, ScPark::value_set), + JS_CGETSET_DEF("companyValue", ScPark::companyValue_get, ScPark::companyValue_set), + JS_CGETSET_DEF("totalRideValueForMoney", ScPark::totalRideValueForMoney_get, nullptr), + JS_CGETSET_DEF("totalAdmissions", ScPark::totalAdmissions_get, ScPark::totalAdmissions_set), + JS_CGETSET_DEF( + "totalIncomeFromAdmissions", ScPark::totalIncomeFromAdmissions_get, ScPark::totalIncomeFromAdmissions_set), + JS_CGETSET_DEF("landPrice", ScPark::landPrice_get, ScPark::landPrice_set), + JS_CGETSET_DEF("constructionRightsPrice", ScPark::constructionRightsPrice_get, ScPark::constructionRightsPrice_set), + JS_CGETSET_DEF("parkSize", ScPark::parkSize_get, nullptr), + JS_CGETSET_DEF("name", ScPark::name_get, ScPark::name_set), + JS_CGETSET_DEF("research", ScPark::research_get, nullptr), + JS_CGETSET_DEF("messages", ScPark::messages_get, ScPark::messages_set), + JS_CGETSET_DEF("casualtyPenalty", ScPark::casualtyPenalty_get, ScPark::casualtyPenalty_set), + JS_CFUNC_DEF("getFlag", 1, ScPark::getFlag), + JS_CFUNC_DEF("setFlag", 2, ScPark::setFlag), + JS_CFUNC_DEF("postMessage", 1, ScPark::postMessage), + JS_CFUNC_DEF("getMonthlyExpenditure", 1, ScPark::getMonthlyExpenditure), + JS_CGETSET_DEF("awards", ScPark::awards_get, nullptr), + JS_CFUNC_DEF("clearAwards", 0, ScPark::clearAwards), + JS_CFUNC_DEF("grantAward", 1, ScPark::grantAward), + }; + return MakeWithOpaque(ctx, funcs, nullptr); + } // namespace OpenRCT2::Scripting + void ScPark::Register(JSContext* ctx) + { + RegisterBaseStr(ctx, "Park"); + } } // namespace OpenRCT2::Scripting #endif diff --git a/src/openrct2/scripting/bindings/world/ScPark.hpp b/src/openrct2/scripting/bindings/world/ScPark.hpp index b46fc2a123..f57be2d311 100644 --- a/src/openrct2/scripting/bindings/world/ScPark.hpp +++ b/src/openrct2/scripting/bindings/world/ScPark.hpp @@ -12,7 +12,7 @@ #ifdef ENABLE_SCRIPTING #include "../../../Context.h" - #include "../../Duktape.hpp" + #include "../../ScriptEngine.h" #include "ScAward.hpp" #include "ScParkMessage.hpp" #include "ScResearch.hpp" @@ -21,94 +21,95 @@ namespace OpenRCT2::Scripting { - class ScPark + class ScPark; + extern ScPark gScPark; + + class ScPark final : public ScBase { private: - duk_context* _context; + static JSValue cash_get(JSContext* ctx, JSValue thisVal); + static JSValue cash_set(JSContext* ctx, JSValue thisVal, JSValue value); + + static JSValue rating_get(JSContext* ctx, JSValue thisVal); + static JSValue rating_set(JSContext* ctx, JSValue thisVal, JSValue value); + + static JSValue bankLoan_get(JSContext* ctx, JSValue thisVal); + static JSValue bankLoan_set(JSContext* ctx, JSValue thisVal, JSValue value); + + static JSValue maxBankLoan_get(JSContext* ctx, JSValue thisVal); + static JSValue maxBankLoan_set(JSContext* ctx, JSValue thisVal, JSValue value); + + static JSValue entranceFee_get(JSContext* ctx, JSValue thisVal); + static JSValue entranceFee_set(JSContext* ctx, JSValue thisVal, JSValue value); + + static JSValue guests_get(JSContext* ctx, JSValue thisVal); + + static JSValue suggestedGuestMaximum_get(JSContext* ctx, JSValue thisVal); + + static JSValue guestGenerationProbability_get(JSContext* ctx, JSValue thisVal); + + static JSValue generateGuest(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + + static JSValue guestInitialCash_get(JSContext* ctx, JSValue thisVal); + + static JSValue guestInitialHappiness_get(JSContext* ctx, JSValue thisVal); + + static JSValue guestInitialHunger_get(JSContext* ctx, JSValue thisVal); + + static JSValue guestInitialThirst_get(JSContext* ctx, JSValue thisVal); + + static JSValue value_get(JSContext* ctx, JSValue thisVal); + static JSValue value_set(JSContext* ctx, JSValue thisVal, JSValue value); + + static JSValue companyValue_get(JSContext* ctx, JSValue thisVal); + static JSValue companyValue_set(JSContext* ctx, JSValue thisVal, JSValue value); + + static JSValue totalRideValueForMoney_get(JSContext* ctx, JSValue thisVal); + + static JSValue totalAdmissions_get(JSContext* ctx, JSValue thisVal); + static JSValue totalAdmissions_set(JSContext* ctx, JSValue thisVal, JSValue value); + + static JSValue totalIncomeFromAdmissions_get(JSContext* ctx, JSValue thisVal); + static JSValue totalIncomeFromAdmissions_set(JSContext* ctx, JSValue thisVal, JSValue value); + + static JSValue landPrice_get(JSContext* ctx, JSValue thisVal); + static JSValue landPrice_set(JSContext* ctx, JSValue thisVal, JSValue value); + + static JSValue constructionRightsPrice_get(JSContext* ctx, JSValue thisVal); + static JSValue constructionRightsPrice_set(JSContext* ctx, JSValue thisVal, JSValue value); + + static JSValue casualtyPenalty_get(JSContext* ctx, JSValue thisVal); + static JSValue casualtyPenalty_set(JSContext* ctx, JSValue thisVal, JSValue value); + + static JSValue parkSize_get(JSContext* ctx, JSValue thisVal); + + static JSValue name_get(JSContext* ctx, JSValue thisVal); + static JSValue name_set(JSContext* ctx, JSValue thisVal, JSValue value); + + static JSValue getFlag(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + + static JSValue setFlag(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + + static JSValue research_get(JSContext* ctx, JSValue thisVal); + + static JSValue messages_get(JSContext* ctx, JSValue thisVal); + + static JSValue messages_set(JSContext* ctx, JSValue thisVal, JSValue value); + + static JSValue postMessage(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + + static JSValue getMonthlyExpenditure(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + + static JSValue awards_get(JSContext* ctx, JSValue thisVal); + + static JSValue clearAwards(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); + + static JSValue grantAward(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); public: - ScPark(duk_context* ctx); + JSValue New(JSContext* ctx); - money64 cash_get() const; - void cash_set(money64 value); - - int32_t rating_get() const; - void rating_set(int32_t value); - - money64 bankLoan_get() const; - void bankLoan_set(money64 value); - - money64 maxBankLoan_get() const; - void maxBankLoan_set(money64 value); - - money64 entranceFee_get() const; - void entranceFee_set(money64 value); - - uint32_t guests_get() const; - - uint32_t suggestedGuestMaximum_get() const; - - int32_t guestGenerationProbability_get() const; - - DukValue generateGuest(); - - money64 guestInitialCash_get() const; - - uint8_t guestInitialHappiness_get() const; - - uint8_t guestInitialHunger_get() const; - - uint8_t guestInitialThirst_get() const; - - money64 value_get() const; - void value_set(money64 value); - - money64 companyValue_get() const; - void companyValue_set(money64 value); - - money64 totalRideValueForMoney_get() const; - - uint32_t totalAdmissions_get() const; - void totalAdmissions_set(uint32_t value); - - money64 totalIncomeFromAdmissions_get() const; - void totalIncomeFromAdmissions_set(money64 value); - - money64 landPrice_get() const; - void landPrice_set(money64 value); - - money64 constructionRightsPrice_get() const; - void constructionRightsPrice_set(money64 value); - - int16_t casualtyPenalty_get() const; - void casualtyPenalty_set(int16_t value); - - uint16_t parkSize_get() const; - - std::string name_get() const; - void name_set(std::string value); - - bool getFlag(const std::string& key) const; - - void setFlag(const std::string& key, bool value); - - std::shared_ptr research_get() const; - - std::vector> messages_get() const; - - void messages_set(const std::vector& value); - - void postMessage(DukValue message); - - std::vector getMonthlyExpenditure(const std::string& expenditureType) const; - - std::vector> awards_get() const; - - void clearAwards() const; - - void grantAward(const std::string& awardType) const; - - static void Register(duk_context* ctx); + void Register(JSContext* ctx); }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/world/ScParkMessage.cpp b/src/openrct2/scripting/bindings/world/ScParkMessage.cpp index a4ec168226..20494f5618 100644 --- a/src/openrct2/scripting/bindings/world/ScParkMessage.cpp +++ b/src/openrct2/scripting/bindings/world/ScParkMessage.cpp @@ -19,161 +19,198 @@ #include "../../../management/NewsItem.h" #include "../../../windows/Intent.h" #include "../../../world/Park.h" - #include "../../Duktape.hpp" #include "../../ScriptEngine.h" namespace OpenRCT2::Scripting { - ScParkMessage::ScParkMessage(size_t index) - : _index(index) + using OpaqueParkMessageData = struct { + size_t index; + }; + + News::Item* ScParkMessage::GetMessage(JSValue thisVal) + { + auto index = gScParkMessage.GetOpaque(thisVal)->index; + return &getGameState().newsItems[index]; } - void ScParkMessage::Register(duk_context* ctx) + JSValue ScParkMessage::isArchived_get(JSContext* ctx, JSValue thisVal) { - dukglue_register_property(ctx, &ScParkMessage::isArchived_get, nullptr, "isArchived"); - dukglue_register_property(ctx, &ScParkMessage::month_get, &ScParkMessage::month_set, "month"); - dukglue_register_property(ctx, &ScParkMessage::day_get, &ScParkMessage::day_set, "day"); - dukglue_register_property(ctx, &ScParkMessage::tickCount_get, &ScParkMessage::tickCount_set, "tickCount"); - dukglue_register_property(ctx, &ScParkMessage::type_get, &ScParkMessage::type_set, "type"); - dukglue_register_property(ctx, &ScParkMessage::subject_get, &ScParkMessage::subject_set, "subject"); - dukglue_register_property(ctx, &ScParkMessage::text_get, &ScParkMessage::text_set, "text"); - dukglue_register_method(ctx, &ScParkMessage::remove, "remove"); + auto index = gScParkMessage.GetOpaque(thisVal)->index; + return JS_NewBool(ctx, index >= News::ItemHistoryStart); } - News::Item* ScParkMessage::GetMessage() const + JSValue ScParkMessage::month_get(JSContext* ctx, JSValue thisVal) { - return &getGameState().newsItems[_index]; - } - - bool ScParkMessage::isArchived_get() const - { - return _index >= News::ItemHistoryStart; - } - - uint16_t ScParkMessage::month_get() const - { - auto msg = GetMessage(); + auto msg = GetMessage(thisVal); if (msg != nullptr) { - return msg->monthYear; + return JS_NewUint32(ctx, msg->monthYear); } - return 0; + return JS_NewUint32(ctx, 0); } - void ScParkMessage::month_set(uint16_t value) + JSValue ScParkMessage::month_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto msg = GetMessage(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto msg = GetMessage(thisVal); if (msg != nullptr) { - msg->monthYear = value; + msg->monthYear = static_cast(value); } + return JS_UNDEFINED; } - uint8_t ScParkMessage::day_get() const + JSValue ScParkMessage::day_get(JSContext* ctx, JSValue thisVal) { - auto msg = GetMessage(); + auto msg = GetMessage(thisVal); if (msg != nullptr) { - return msg->day; + return JS_NewUint32(ctx, msg->day); } - return 0; + return JS_NewUint32(ctx, 0); } - void ScParkMessage::day_set(uint8_t value) + JSValue ScParkMessage::day_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto msg = GetMessage(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto msg = GetMessage(thisVal); if (msg != nullptr) { - msg->day = value; + msg->day = static_cast(value); } + return JS_UNDEFINED; } - uint16_t ScParkMessage::tickCount_get() const + JSValue ScParkMessage::tickCount_get(JSContext* ctx, JSValue thisVal) { - auto msg = GetMessage(); + auto msg = GetMessage(thisVal); if (msg != nullptr) { - return msg->ticks; + return JS_NewUint32(ctx, msg->ticks); } - return 0; + return JS_NewUint32(ctx, 0); } - void ScParkMessage::tickCount_set(uint16_t value) + JSValue ScParkMessage::tickCount_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto msg = GetMessage(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto msg = GetMessage(thisVal); if (msg != nullptr) { - msg->ticks = value; + msg->ticks = static_cast(value); } + return JS_UNDEFINED; } - std::string ScParkMessage::type_get() const + JSValue ScParkMessage::type_get(JSContext* ctx, JSValue thisVal) { - auto msg = GetMessage(); + auto msg = GetMessage(thisVal); if (msg != nullptr) { - return GetParkMessageType(msg->type); + return JSFromStdString(ctx, GetParkMessageType(msg->type)); } - return {}; + return JSFromStdString(ctx, {}); } - void ScParkMessage::type_set(const std::string& value) + JSValue ScParkMessage::type_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto msg = GetMessage(); + JS_UNPACK_STR(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto msg = GetMessage(thisVal); if (msg != nullptr) { msg->type = GetParkMessageType(value); } + return JS_UNDEFINED; } - uint32_t ScParkMessage::subject_get() const + JSValue ScParkMessage::subject_get(JSContext* ctx, JSValue thisVal) { - auto msg = GetMessage(); + auto msg = GetMessage(thisVal); if (msg != nullptr) { - return msg->assoc; + return JS_NewUint32(ctx, msg->assoc); } - return 0; + return JS_NewUint32(ctx, 0); } - void ScParkMessage::subject_set(uint32_t value) + JSValue ScParkMessage::subject_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto msg = GetMessage(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto msg = GetMessage(thisVal); if (msg != nullptr) { msg->assoc = value; } + return JS_UNDEFINED; } - std::string ScParkMessage::text_get() const + JSValue ScParkMessage::text_get(JSContext* ctx, JSValue thisVal) { - auto msg = GetMessage(); + auto msg = GetMessage(thisVal); if (msg != nullptr) { - return msg->text; + return JSFromStdString(ctx, msg->text); } - return {}; + return JSFromStdString(ctx, {}); } - void ScParkMessage::text_set(const std::string& value) + JSValue ScParkMessage::text_set(JSContext* ctx, JSValue thisVal, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto msg = GetMessage(); + JS_UNPACK_STR(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + auto msg = GetMessage(thisVal); if (msg != nullptr) { msg->text = value; } + return JS_UNDEFINED; } - void ScParkMessage::remove() + JSValue ScParkMessage::remove(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - News::RemoveItem(static_cast(_index)); + auto index = gScParkMessage.GetOpaque(thisVal)->index; + News::RemoveItem(static_cast(index)); + return JS_UNDEFINED; + } + + JSValue ScParkMessage::New(JSContext* ctx, size_t index) + { + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("isArchived", ScParkMessage::isArchived_get, nullptr), + JS_CGETSET_DEF("month", ScParkMessage::month_get, ScParkMessage::month_set), + JS_CGETSET_DEF("day", ScParkMessage::day_get, ScParkMessage::day_set), + JS_CGETSET_DEF("tickCount", ScParkMessage::tickCount_get, ScParkMessage::tickCount_set), + JS_CGETSET_DEF("type", ScParkMessage::type_get, ScParkMessage::type_set), + JS_CGETSET_DEF("subject", ScParkMessage::subject_get, ScParkMessage::subject_set), + JS_CGETSET_DEF("text", ScParkMessage::text_get, ScParkMessage::text_set), + JS_CFUNC_DEF("remove", 0, ScParkMessage::remove), + }; + + return MakeWithOpaque(ctx, funcs, new OpaqueParkMessageData{ index }); + } + + void ScParkMessage::Register(JSContext* ctx) + { + RegisterBaseStr(ctx, "ParkMessage"); + } + + void ScParkMessage::Finalize(JSRuntime*, JSValue thisVal) + { + auto* data = gScParkMessage.GetOpaque(thisVal); + if (data) + delete data; } } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/world/ScParkMessage.hpp b/src/openrct2/scripting/bindings/world/ScParkMessage.hpp index ddaffac2bd..8d7185fd30 100644 --- a/src/openrct2/scripting/bindings/world/ScParkMessage.hpp +++ b/src/openrct2/scripting/bindings/world/ScParkMessage.hpp @@ -13,7 +13,6 @@ #include "../../../Context.h" #include "../../../management/NewsItem.h" - #include "../../Duktape.hpp" #include "../../ScriptEngine.h" #include @@ -48,53 +47,54 @@ namespace OpenRCT2::Scripting return {}; } - template<> - inline News::Item FromDuk(const DukValue& value) + inline News::Item NewsItemFromJS(JSContext* ctx, JSValue value) { News::Item result{}; - result.type = GetParkMessageType(value["type"].as_string()); - result.assoc = value["subject"].as_uint(); - result.ticks = value["tickCount"].as_uint(); - result.monthYear = value["month"].as_uint(); - result.day = value["day"].as_uint(); - result.text = value["text"].as_string(); + result.type = GetParkMessageType(JSToStdString(ctx, value, "type")); + result.assoc = JSToUint(ctx, value, "subject"); + result.ticks = JSToUint(ctx, value, "tickCount"); + result.monthYear = JSToUint(ctx, value, "month"); + result.day = JSToUint(ctx, value, "day"); + result.text = JSToStdString(ctx, value, "text"); return result; } - class ScParkMessage + class ScParkMessage; + extern ScParkMessage gScParkMessage; + class ScParkMessage : public ScBase { private: - size_t _index{}; + static News::Item* GetMessage(JSValue thisVal); + + static JSValue isArchived_get(JSContext* ctx, JSValue thisVal); + + static JSValue month_get(JSContext* ctx, JSValue thisVal); + static JSValue month_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); + + static JSValue day_get(JSContext* ctx, JSValue thisVal); + static JSValue day_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); + + static JSValue tickCount_get(JSContext* ctx, JSValue thisVal); + static JSValue tickCount_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); + + static JSValue type_get(JSContext* ctx, JSValue thisVal); + static JSValue type_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); + + static JSValue subject_get(JSContext* ctx, JSValue thisVal); + static JSValue subject_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); + + static JSValue text_get(JSContext* ctx, JSValue thisVal); + static JSValue text_set(JSContext* ctx, JSValue thisVal, JSValue jsValue); + + static JSValue remove(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); public: - ScParkMessage(size_t index); + JSValue New(JSContext* ctx, size_t index); - static void Register(duk_context* ctx); + void Register(JSContext* ctx); private: - News::Item* GetMessage() const; - - bool isArchived_get() const; - - uint16_t month_get() const; - void month_set(uint16_t value); - - uint8_t day_get() const; - void day_set(uint8_t value); - - uint16_t tickCount_get() const; - void tickCount_set(uint16_t value); - - std::string type_get() const; - void type_set(const std::string& value); - - uint32_t subject_get() const; - void subject_set(uint32_t value); - - std::string text_get() const; - void text_set(const std::string& value); - - void remove(); + void Finalize(JSRuntime* rt, JSValue thisVal); }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/world/ScResearch.cpp b/src/openrct2/scripting/bindings/world/ScResearch.cpp index f97e1577d8..a4c3f7428a 100644 --- a/src/openrct2/scripting/bindings/world/ScResearch.cpp +++ b/src/openrct2/scripting/bindings/world/ScResearch.cpp @@ -13,10 +13,10 @@ #include "../../../Context.h" #include "../../../GameState.h" + #include "../../../core/EnumMap.hpp" #include "../../../core/String.hpp" #include "../../../management/Research.h" #include "../../../ride/RideData.h" - #include "../../Duktape.hpp" #include "../../ScriptEngine.h" #include "../object/ScObject.hpp" @@ -24,7 +24,7 @@ using namespace OpenRCT2; namespace OpenRCT2::Scripting { - static const DukEnumMap ResearchStageMap( + static const EnumMap ResearchStageMap( { { "initial_research", RESEARCH_STAGE_INITIAL_RESEARCH }, { "designing", RESEARCH_STAGE_DESIGNING }, @@ -33,7 +33,7 @@ namespace OpenRCT2::Scripting { "finished_all", RESEARCH_STAGE_FINISHED_ALL }, }); - static const DukEnumMap ResearchCategoryMap( + static const EnumMap ResearchCategoryMap( { { "transport", ResearchCategory::transport }, { "gentle", ResearchCategory::gentle }, @@ -44,32 +44,31 @@ namespace OpenRCT2::Scripting { "scenery", ResearchCategory::sceneryGroup }, }); - static const DukEnumMap ResearchEntryTypeMap( + static const EnumMap ResearchEntryTypeMap( { { "ride", Research::EntryType::ride }, { "scenery", Research::EntryType::scenery }, }); - template<> - inline DukValue ToDuk(duk_context* ctx, const ResearchItem& value) + static inline JSValue ResearchItemToJSValue(JSContext* ctx, const ResearchItem& value) { - DukObject obj(ctx); - obj.Set("category", ResearchCategoryMap[value.category]); - obj.Set("type", ResearchEntryTypeMap[value.type]); + JSValue obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "category", JSFromStdString(ctx, ResearchCategoryMap[value.category])); + JS_SetPropertyStr(ctx, obj, "type", JSFromStdString(ctx, ResearchEntryTypeMap[value.type])); if (value.type == Research::EntryType::ride) { - obj.Set("rideType", value.baseRideType); + JS_SetPropertyStr(ctx, obj, "rideType", JS_NewUint32(ctx, value.baseRideType)); } - obj.Set("object", value.entryIndex); - return obj.Take(); + JS_SetPropertyStr(ctx, obj, "object", JS_NewUint32(ctx, value.entryIndex)); + return obj; } - template<> - Research::EntryType inline FromDuk(const DukValue& d) + static inline Research::EntryType GetResearchType(JSContext* ctx, JSValue value) { - if (d.type() == DukValue::STRING) + auto type = JSToOptionalStdString(ctx, value, "type"); + if (type.has_value()) { - auto it = ResearchEntryTypeMap.find(d.as_string()); + auto it = ResearchEntryTypeMap.find(type.value()); if (it != ResearchEntryTypeMap.end()) { return it->second; @@ -78,198 +77,219 @@ namespace OpenRCT2::Scripting return Research::EntryType::scenery; } - template<> - ResearchItem inline FromDuk(const DukValue& d) + static inline ResearchItem GetResearchItem(JSContext* ctx, JSValue value) { ResearchItem result; result.baseRideType = 0; result.category = {}; // We ignore category because it will be derived from ride type result.flags = 0; - result.type = FromDuk(d["type"]); - auto baseRideType = d["rideType"]; - if (baseRideType.type() == DukValue::NUMBER) - result.baseRideType = baseRideType.as_uint(); - result.entryIndex = d["object"].as_uint(); + result.type = GetResearchType(ctx, value); + result.entryIndex = JSToUint(ctx, value, "object"); + + auto rideType = JSToOptionalUint(ctx, value, "rideType"); + if (rideType.has_value()) + { + result.baseRideType = static_cast(rideType.value()); + } return result; } - ScResearch::ScResearch(duk_context* ctx) - : _context(ctx) + JSValue ScResearch::funding_get(JSContext* ctx, JSValue thisVal) { + return JS_NewUint32(ctx, getGameState().researchFundingLevel); } - uint8_t ScResearch::funding_get() const + JSValue ScResearch::funding_set(JSContext* ctx, JSValue thisVal, JSValue value) { - return getGameState().researchFundingLevel; + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + JS_UNPACK_UINT32(val, ctx, value); + getGameState().researchFundingLevel = std::clamp( + static_cast(val), RESEARCH_FUNDING_NONE, RESEARCH_FUNDING_MAXIMUM); + return JS_UNDEFINED; } - void ScResearch::funding_set(uint8_t value) + JSValue ScResearch::priorities_get(JSContext* ctx, JSValue thisVal) { - ThrowIfGameStateNotMutable(); - getGameState().researchFundingLevel = std::clamp(value, RESEARCH_FUNDING_NONE, RESEARCH_FUNDING_MAXIMUM); - } - - std::vector ScResearch::priorities_get() const - { - std::vector result; + JSValue result = JS_NewArray(ctx); + int64_t index = 0; for (auto i = EnumValue(ResearchCategory::transport); i <= EnumValue(ResearchCategory::sceneryGroup); i++) { auto category = static_cast(i); if (getGameState().researchPriorities & EnumToFlag(category)) { - result.emplace_back(ResearchCategoryMap[category]); + JS_SetPropertyInt64(ctx, result, index++, JSFromStdString(ctx, ResearchCategoryMap[category])); } } return result; } - void ScResearch::priorities_set(const std::vector& values) + JSValue ScResearch::priorities_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + JS_UNPACK_ARRAY(values, ctx, value); auto priorities = 0; - for (const auto& value : values) - { - auto category = ResearchCategoryMap.TryGet(value); + JSIterateArray(ctx, values, [&priorities](JSContext* ctx2, JSValue val) { + auto item = JSToStdString(ctx2, val); + auto category = ResearchCategoryMap.TryGet(item); if (category) { priorities |= EnumToFlag(*category); } - } + }); + getGameState().researchPriorities = priorities; + return JS_UNDEFINED; } - std::string ScResearch::stage_get() const + JSValue ScResearch::stage_get(JSContext* ctx, JSValue thisVal) { - return std::string(ResearchStageMap[getGameState().researchProgressStage]); + return JSFromStdString(ctx, ResearchStageMap[getGameState().researchProgressStage]); } - void ScResearch::stage_set(const std::string& value) + JSValue ScResearch::stage_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - auto it = ResearchStageMap.find(value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + JS_UNPACK_STR(stage, ctx, value); + auto it = ResearchStageMap.find(stage); if (it != ResearchStageMap.end()) { getGameState().researchProgressStage = it->second; } + return JS_UNDEFINED; } - uint16_t ScResearch::progress_get() const + JSValue ScResearch::progress_get(JSContext* ctx, JSValue thisVal) { - return getGameState().researchProgress; + return JS_NewUint32(ctx, getGameState().researchProgress); } - void ScResearch::progress_set(uint16_t value) + JSValue ScResearch::progress_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - getGameState().researchProgress = value; + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + JS_UNPACK_UINT32(progress, ctx, value); + getGameState().researchProgress = progress; + return JS_UNDEFINED; } - DukValue ScResearch::expectedMonth_get() const + JSValue ScResearch::expectedMonth_get(JSContext* ctx, JSValue thisVal) { const auto& gameState = getGameState(); if (gameState.researchProgressStage == RESEARCH_STAGE_INITIAL_RESEARCH || gameState.researchExpectedDay == 255) - return ToDuk(_context, nullptr); - return ToDuk(_context, gameState.researchExpectedMonth); + return JS_NULL; + return JS_NewUint32(ctx, gameState.researchExpectedMonth); } - DukValue ScResearch::expectedDay_get() const + JSValue ScResearch::expectedDay_get(JSContext* ctx, JSValue thisVal) { const auto& gameState = getGameState(); if (gameState.researchProgressStage == RESEARCH_STAGE_INITIAL_RESEARCH || gameState.researchExpectedDay == 255) - return ToDuk(_context, nullptr); - return ToDuk(_context, gameState.researchExpectedDay + 1); + return JS_NULL; + return JS_NewUint32(ctx, gameState.researchExpectedDay + 1); } - DukValue ScResearch::lastResearchedItem_get() const + JSValue ScResearch::lastResearchedItem_get(JSContext* ctx, JSValue thisVal) { const auto& gameState = getGameState(); if (!gameState.researchLastItem) - return ToDuk(_context, nullptr); - return ToDuk(_context, *gameState.researchLastItem); + return JS_NULL; + return ResearchItemToJSValue(ctx, *gameState.researchLastItem); } - DukValue ScResearch::expectedItem_get() const + JSValue ScResearch::expectedItem_get(JSContext* ctx, JSValue thisVal) { const auto& gameState = getGameState(); if (gameState.researchProgressStage == RESEARCH_STAGE_INITIAL_RESEARCH || !gameState.researchNextItem) - return ToDuk(_context, nullptr); - return ToDuk(_context, *gameState.researchNextItem); + return JS_NULL; + return ResearchItemToJSValue(ctx, *gameState.researchNextItem); } - std::vector ScResearch::inventedItems_get() const + JSValue ScResearch::inventedItems_get(JSContext* ctx, JSValue thisVal) { - std::vector result; + JSValue result = JS_NewArray(ctx); + int64_t index = 0; for (auto& item : getGameState().researchItemsInvented) { - result.push_back(ToDuk(_context, item)); + JS_SetPropertyInt64(ctx, result, index++, ResearchItemToJSValue(ctx, item)); } return result; } - void ScResearch::inventedItems_set(const std::vector& value) + JSValue ScResearch::inventedItems_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - auto list = ConvertResearchList(value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + JS_UNPACK_ARRAY(array, ctx, value); + auto list = ConvertResearchList(ctx, array); getGameState().researchItemsInvented = std::move(list); ResearchFix(); + return JS_UNDEFINED; } - std::vector ScResearch::uninventedItems_get() const + JSValue ScResearch::uninventedItems_get(JSContext* ctx, JSValue thisVal) { - std::vector result; + JSValue result = JS_NewArray(ctx); + int64_t index = 0; for (auto& item : getGameState().researchItemsUninvented) { - result.push_back(ToDuk(_context, item)); + JS_SetPropertyInt64(ctx, result, index++, ResearchItemToJSValue(ctx, item)); } return result; } - void ScResearch::uninventedItems_set(const std::vector& value) + JSValue ScResearch::uninventedItems_set(JSContext* ctx, JSValue thisVal, JSValue value) { - ThrowIfGameStateNotMutable(); - auto list = ConvertResearchList(value); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + JS_UNPACK_ARRAY(array, ctx, value); + auto list = ConvertResearchList(ctx, array); getGameState().researchItemsUninvented = std::move(list); ResearchFix(); + return JS_UNDEFINED; } - bool ScResearch::isObjectResearched(const std::string& typez, ObjectEntryIndex index) + JSValue ScResearch::isObjectResearched(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv) { - auto result = false; + JS_UNPACK_STR(typez, ctx, argv[0]); + JS_UNPACK_UINT32(index, ctx, argv[1]); + auto type = objectTypeFromString(typez); - if (type != ObjectType::none) + if (type == ObjectType::none) { - result = ResearchIsInvented(type, index); + return JS_ThrowPlainError(ctx, "Invalid object type."); } - else - { - duk_error(_context, DUK_ERR_ERROR, "Invalid object type."); - } - return result; + + auto result = ResearchIsInvented(type, static_cast(index)); + return JS_NewBool(ctx, result); } - void ScResearch::Register(duk_context* ctx) + void ScResearch::Register(JSContext* ctx) { - dukglue_register_property(ctx, &ScResearch::funding_get, &ScResearch::funding_set, "funding"); - dukglue_register_property(ctx, &ScResearch::priorities_get, &ScResearch::priorities_set, "priorities"); - dukglue_register_property(ctx, &ScResearch::stage_get, &ScResearch::stage_set, "stage"); - dukglue_register_property(ctx, &ScResearch::progress_get, &ScResearch::progress_set, "progress"); - dukglue_register_property(ctx, &ScResearch::expectedMonth_get, nullptr, "expectedMonth"); - dukglue_register_property(ctx, &ScResearch::expectedDay_get, nullptr, "expectedDay"); - dukglue_register_property(ctx, &ScResearch::lastResearchedItem_get, nullptr, "lastResearchedItem"); - dukglue_register_property(ctx, &ScResearch::expectedItem_get, nullptr, "expectedItem"); - dukglue_register_property(ctx, &ScResearch::inventedItems_get, &ScResearch::inventedItems_set, "inventedItems"); - dukglue_register_property(ctx, &ScResearch::uninventedItems_get, &ScResearch::uninventedItems_set, "uninventedItems"); - dukglue_register_method(ctx, &ScResearch::isObjectResearched, "isObjectResearched"); + RegisterBaseStr(ctx, "Research"); } - std::vector ScResearch::ConvertResearchList(const std::vector& value) + JSValue ScResearch::New(JSContext* ctx) + { + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("funding", ScResearch::funding_get, ScResearch::funding_set), + JS_CGETSET_DEF("priorities", ScResearch::priorities_get, ScResearch::priorities_set), + JS_CGETSET_DEF("stage", ScResearch::stage_get, ScResearch::stage_set), + JS_CGETSET_DEF("progress", ScResearch::progress_get, ScResearch::progress_set), + JS_CGETSET_DEF("expectedMonth", ScResearch::expectedMonth_get, nullptr), + JS_CGETSET_DEF("expectedDay", ScResearch::expectedDay_get, nullptr), + JS_CGETSET_DEF("lastResearchedItem", ScResearch::lastResearchedItem_get, nullptr), + JS_CGETSET_DEF("expectedItem", ScResearch::expectedItem_get, nullptr), + JS_CGETSET_DEF("inventedItems", ScResearch::inventedItems_get, ScResearch::inventedItems_set), + JS_CGETSET_DEF("uninventedItems", ScResearch::uninventedItems_get, ScResearch::uninventedItems_set), + JS_CFUNC_DEF("isObjectResearched", 2, ScResearch::isObjectResearched), + }; + return MakeWithOpaque(ctx, funcs, nullptr); + } + + std::vector ScResearch::ConvertResearchList(JSContext* ctx, JSValue value) { auto& objManager = GetContext()->GetObjectManager(); std::vector result; - for (auto& item : value) - { - auto researchItem = FromDuk(item); + JSIterateArray(ctx, value, [&result, &objManager](JSContext* ctx2, JSValue val) { + auto researchItem = GetResearchItem(ctx2, val); researchItem.flags = 0; if (researchItem.type == Research::EntryType::ride) { @@ -290,7 +310,7 @@ namespace OpenRCT2::Scripting result.push_back(researchItem); } } - } + }); return result; } } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/world/ScResearch.hpp b/src/openrct2/scripting/bindings/world/ScResearch.hpp index b62344c21c..c4d6e74ffb 100644 --- a/src/openrct2/scripting/bindings/world/ScResearch.hpp +++ b/src/openrct2/scripting/bindings/world/ScResearch.hpp @@ -15,43 +15,42 @@ namespace OpenRCT2::Scripting { - class ScResearch + class ScResearch; + extern ScResearch gScResearch; + + class ScResearch final : public ScBase { - private: - duk_context* _context; - public: - ScResearch(duk_context* ctx); - - static void Register(duk_context* ctx); + void Register(JSContext* ctx); + JSValue New(JSContext* ctx); private: - uint8_t funding_get() const; - void funding_set(uint8_t value); + static JSValue funding_get(JSContext* ctx, JSValue thisVal); + static JSValue funding_set(JSContext* ctx, JSValue thisVal, JSValue value); - std::vector priorities_get() const; - void priorities_set(const std::vector& values); + static JSValue priorities_get(JSContext* ctx, JSValue thisVal); + static JSValue priorities_set(JSContext* ctx, JSValue thisVal, JSValue value); - std::string stage_get() const; - void stage_set(const std::string& value); + static JSValue stage_get(JSContext* ctx, JSValue thisVal); + static JSValue stage_set(JSContext* ctx, JSValue thisVal, JSValue value); - uint16_t progress_get() const; - void progress_set(uint16_t value); + static JSValue progress_get(JSContext* ctx, JSValue thisVal); + static JSValue progress_set(JSContext* ctx, JSValue thisVal, JSValue value); - DukValue expectedMonth_get() const; - DukValue expectedDay_get() const; - DukValue lastResearchedItem_get() const; - DukValue expectedItem_get() const; + static JSValue expectedMonth_get(JSContext* ctx, JSValue thisVal); + static JSValue expectedDay_get(JSContext* ctx, JSValue thisVal); + static JSValue lastResearchedItem_get(JSContext* ctx, JSValue thisVal); + static JSValue expectedItem_get(JSContext* ctx, JSValue thisVal); - std::vector inventedItems_get() const; - void inventedItems_set(const std::vector& value); + static JSValue inventedItems_get(JSContext* ctx, JSValue thisVal); + static JSValue inventedItems_set(JSContext* ctx, JSValue thisVal, JSValue value); - std::vector uninventedItems_get() const; - void uninventedItems_set(const std::vector& value); + static JSValue uninventedItems_get(JSContext* ctx, JSValue thisVal); + static JSValue uninventedItems_set(JSContext* ctx, JSValue thisVal, JSValue value); - bool isObjectResearched(const std::string& typez, ObjectEntryIndex index); + static JSValue isObjectResearched(JSContext* ctx, JSValue thisVal, int argc, JSValue* argv); - static std::vector ConvertResearchList(const std::vector& value); + static std::vector ConvertResearchList(JSContext* ctx, JSValue value); }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/world/ScScenario.hpp b/src/openrct2/scripting/bindings/world/ScScenario.hpp index e00b0e5d28..729087d8de 100644 --- a/src/openrct2/scripting/bindings/world/ScScenario.hpp +++ b/src/openrct2/scripting/bindings/world/ScScenario.hpp @@ -13,17 +13,17 @@ #include "../../../Context.h" #include "../../../GameState.h" + #include "../../../core/EnumMap.hpp" #include "../../../core/StringTypes.h" #include "../../../scenario/Scenario.h" #include "../../../world/Park.h" - #include "../../Duktape.hpp" #include "../../ScriptEngine.h" namespace OpenRCT2::Scripting { using namespace OpenRCT2::Scenario; - static const DukEnumMap ScenarioObjectiveTypeMap( + static const EnumMap ScenarioObjectiveTypeMap( { { "none", ObjectiveType::none }, { "guestsBy", ObjectiveType::guestsBy }, @@ -39,255 +39,291 @@ namespace OpenRCT2::Scripting { "monthlyFoodIncome", ObjectiveType::monthlyFoodIncome }, }); - class ScScenarioObjective + class ScScenarioObjective; + extern ScScenarioObjective gScScenarioObjective; + class ScScenarioObjective final : public ScBase { private: - std::string type_get() + static JSValue type_get(JSContext* ctx, JSValue) { - return std::string(ScenarioObjectiveTypeMap[getGameState().scenarioOptions.objective.Type]); + return JSFromStdString(ctx, ScenarioObjectiveTypeMap[getGameState().scenarioOptions.objective.Type]); } - void type_set(const std::string& value) + static JSValue type_set(JSContext* ctx, JSValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); + JS_UNPACK_STR(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); getGameState().scenarioOptions.objective.Type = ScenarioObjectiveTypeMap[value]; + return JS_UNDEFINED; } - uint16_t guests_get() + static JSValue guests_get(JSContext* ctx, JSValue) { auto& gameState = getGameState(); if (gameState.scenarioOptions.objective.Type == ObjectiveType::guestsBy || gameState.scenarioOptions.objective.Type == ObjectiveType::guestsAndRating) { - return gameState.scenarioOptions.objective.NumGuests; + return JS_NewUint32(ctx, gameState.scenarioOptions.objective.NumGuests); } - return 0; + return JS_NewUint32(ctx, 0); } - void guests_set(uint16_t value) + static JSValue guests_set(JSContext* ctx, JSValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto& gameState = getGameState(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto& gameState = OpenRCT2::getGameState(); if (gameState.scenarioOptions.objective.Type == ObjectiveType::guestsBy || gameState.scenarioOptions.objective.Type == ObjectiveType::guestsAndRating) { gameState.scenarioOptions.objective.NumGuests = value; } + return JS_UNDEFINED; } - uint8_t year_get() + static JSValue year_get(JSContext* ctx, JSValue) { const auto& gameState = getGameState(); if (gameState.scenarioOptions.objective.Type == ObjectiveType::guestsBy || gameState.scenarioOptions.objective.Type == ObjectiveType::parkValueBy) { - return gameState.scenarioOptions.objective.Year; + return JS_NewUint32(ctx, gameState.scenarioOptions.objective.Year); } - return 0; + return JS_NewUint32(ctx, 0); } - void year_set(uint8_t value) + static JSValue year_set(JSContext* ctx, JSValue, JSValue jsValue) { - auto& gameState = getGameState(); - ThrowIfGameStateNotMutable(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto& gameState = OpenRCT2::getGameState(); if (gameState.scenarioOptions.objective.Type == ObjectiveType::guestsBy || gameState.scenarioOptions.objective.Type == ObjectiveType::parkValueBy) { gameState.scenarioOptions.objective.Year = value; } + return JS_UNDEFINED; } - uint16_t length_get() + static JSValue length_get(JSContext* ctx, JSValue) { - const auto& gameState = getGameState(); + const auto& gameState = OpenRCT2::getGameState(); if (gameState.scenarioOptions.objective.Type == ObjectiveType::tenRollercoastersLength) { - return gameState.scenarioOptions.objective.NumGuests; + return JS_NewUint32(ctx, gameState.scenarioOptions.objective.NumGuests); } - return 0; + return JS_NewUint32(ctx, 0); } - void length_set(uint16_t value) + static JSValue length_set(JSContext* ctx, JSValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto& gameState = getGameState(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto& gameState = OpenRCT2::getGameState(); if (gameState.scenarioOptions.objective.Type == ObjectiveType::tenRollercoastersLength) { gameState.scenarioOptions.objective.NumGuests = value; } + return JS_UNDEFINED; } - money64 excitement_get() + static JSValue excitement_get(JSContext* ctx, JSValue) { const auto& gameState = getGameState(); if (gameState.scenarioOptions.objective.Type == ObjectiveType::finishFiveRollercoasters) { - return gameState.scenarioOptions.objective.Currency; + return JS_NewInt64(ctx, gameState.scenarioOptions.objective.Currency); } - return 0; + return JS_NewInt64(ctx, 0); } - void excitement_set(money64 value) + static JSValue excitement_set(JSContext* ctx, JSValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto& gameState = getGameState(); + JS_UNPACK_INT64(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto& gameState = OpenRCT2::getGameState(); if (gameState.scenarioOptions.objective.Type == ObjectiveType::finishFiveRollercoasters) { gameState.scenarioOptions.objective.Currency = value; } + return JS_UNDEFINED; } - money64 parkValue_get() + static JSValue parkValue_get(JSContext* ctx, JSValue) { const auto& gameState = getGameState(); if (gameState.scenarioOptions.objective.Type == ObjectiveType::parkValueBy || gameState.scenarioOptions.objective.Type == ObjectiveType::repayLoanAndParkValue) { - return gameState.scenarioOptions.objective.Currency; + return JS_NewInt64(ctx, gameState.scenarioOptions.objective.Currency); } - return 0; + return JS_NewInt64(ctx, 0); } - void parkValue_set(money64 value) + static JSValue parkValue_set(JSContext* ctx, JSValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto& gameState = getGameState(); + JS_UNPACK_INT64(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto& gameState = OpenRCT2::getGameState(); if (gameState.scenarioOptions.objective.Type == ObjectiveType::parkValueBy || gameState.scenarioOptions.objective.Type == ObjectiveType::repayLoanAndParkValue) { gameState.scenarioOptions.objective.Currency = value; } + return JS_UNDEFINED; } - money64 monthlyIncome_get() + static JSValue monthlyIncome_get(JSContext* ctx, JSValue) { const auto& gameState = getGameState(); if (gameState.scenarioOptions.objective.Type == ObjectiveType::monthlyRideIncome || gameState.scenarioOptions.objective.Type == ObjectiveType::monthlyFoodIncome) { - return gameState.scenarioOptions.objective.Currency; + return JS_NewInt64(ctx, gameState.scenarioOptions.objective.Currency); } - return 0; + return JS_NewInt64(ctx, 0); } - void monthlyIncome_set(money64 value) + static JSValue monthlyIncome_set(JSContext* ctx, JSValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto& gameState = getGameState(); + JS_UNPACK_INT64(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto& gameState = OpenRCT2::getGameState(); if (gameState.scenarioOptions.objective.Type == ObjectiveType::parkValueBy || gameState.scenarioOptions.objective.Type == ObjectiveType::repayLoanAndParkValue) { gameState.scenarioOptions.objective.Currency = value; } + return JS_UNDEFINED; } public: - static void Register(duk_context* ctx) + JSValue New(JSContext* ctx) { - dukglue_register_property(ctx, &ScScenarioObjective::type_get, &ScScenarioObjective::type_set, "type"); - dukglue_register_property(ctx, &ScScenarioObjective::guests_get, &ScScenarioObjective::guests_set, "guests"); - dukglue_register_property(ctx, &ScScenarioObjective::year_get, &ScScenarioObjective::year_set, "year"); - dukglue_register_property( - ctx, &ScScenarioObjective::excitement_get, &ScScenarioObjective::excitement_set, "excitement"); - dukglue_register_property( - ctx, &ScScenarioObjective::monthlyIncome_get, &ScScenarioObjective::monthlyIncome_set, "monthlyIncome"); - dukglue_register_property( - ctx, &ScScenarioObjective::parkValue_get, &ScScenarioObjective::parkValue_set, "parkValue"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("type", ScScenarioObjective::type_get, ScScenarioObjective::type_set), + JS_CGETSET_DEF("guests", ScScenarioObjective::guests_get, ScScenarioObjective::guests_set), + JS_CGETSET_DEF("year", ScScenarioObjective::year_get, ScScenarioObjective::year_set), + JS_CGETSET_DEF("excitement", ScScenarioObjective::excitement_get, ScScenarioObjective::excitement_set), + JS_CGETSET_DEF("monthlyIncome", ScScenarioObjective::monthlyIncome_get, ScScenarioObjective::monthlyIncome_set), + JS_CGETSET_DEF("parkValue", ScScenarioObjective::parkValue_get, ScScenarioObjective::parkValue_set), + }; + + return MakeWithOpaque(ctx, funcs, nullptr); + } + + void Register(JSContext* ctx) + { + RegisterBaseStr(ctx, "ScenarioObjective"); } }; - class ScScenario + class ScScenario; + extern ScScenario gScScenario; + class ScScenario final : public ScBase { - public: - std::string name_get() + private: + static JSValue name_get(JSContext* ctx, JSValue) { - return getGameState().scenarioOptions.name; + return JSFromStdString(ctx, getGameState().scenarioOptions.name); } - void name_set(const std::string& value) + static JSValue name_set(JSContext* ctx, JSValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); + JS_UNPACK_STR(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); getGameState().scenarioOptions.name = value; + return JS_UNDEFINED; } - std::string details_get() + static JSValue details_get(JSContext* ctx, JSValue) { - return getGameState().scenarioOptions.details; + return JSFromStdString(ctx, getGameState().scenarioOptions.details); } - void details_set(const std::string& value) + static JSValue details_set(JSContext* ctx, JSValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); + JS_UNPACK_STR(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); getGameState().scenarioOptions.details = value; + return JS_UNDEFINED; } - std::string completedBy_get() + static JSValue completedBy_get(JSContext* ctx, JSValue) { - return getGameState().scenarioCompletedBy; + return JSFromStdString(ctx, getGameState().scenarioCompletedBy); } - void completedBy_set(const std::string& value) + static JSValue completedBy_set(JSContext* ctx, JSValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); + JS_UNPACK_STR(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); getGameState().scenarioCompletedBy = value; + return JS_UNDEFINED; } - std::string filename_get() + static JSValue filename_get(JSContext* ctx, JSValue) { - return getGameState().scenarioFileName; + return JSFromStdString(ctx, getGameState().scenarioFileName); } - void filename_set(const std::string& value) + static JSValue filename_set(JSContext* ctx, JSValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); + JS_UNPACK_STR(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); getGameState().scenarioFileName = value; + return JS_UNDEFINED; } - std::shared_ptr objective_get() const + static JSValue objective_get(JSContext* ctx, JSValue) { - return std::make_shared(); + return gScScenarioObjective.New(ctx); } - uint16_t parkRatingWarningDays_get() const + static JSValue parkRatingWarningDays_get(JSContext* ctx, JSValue) { - return getGameState().scenarioParkRatingWarningDays; + return JS_NewUint32(ctx, getGameState().scenarioParkRatingWarningDays); } - void parkRatingWarningDays_set(uint16_t value) + static JSValue parkRatingWarningDays_set(JSContext* ctx, JSValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); getGameState().scenarioParkRatingWarningDays = value; + return JS_UNDEFINED; } - DukValue completedCompanyValue_get() const + static JSValue completedCompanyValue_get(JSContext* ctx, JSValue) { const auto& gameState = getGameState(); - auto ctx = GetContext()->GetScriptEngine().GetContext(); if (gameState.scenarioCompletedCompanyValue == kMoney64Undefined || gameState.scenarioCompletedCompanyValue == kCompanyValueOnFailedObjective) { - return ToDuk(ctx, nullptr); + return JS_NULL; } - return ToDuk(ctx, gameState.scenarioCompletedCompanyValue); + return JS_NewInt64(ctx, gameState.scenarioCompletedCompanyValue); } - void completedCompanyValue_set(int32_t value) + static JSValue completedCompanyValue_set(JSContext* ctx, JSValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); + JS_UNPACK_INT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); getGameState().scenarioCompletedCompanyValue = value; + return JS_UNDEFINED; } - std::string status_get() const + static JSValue status_get(JSContext* ctx, JSValue) { - const auto& gameState = getGameState(); + const auto& gameState = OpenRCT2::getGameState(); if (gameState.scenarioCompletedCompanyValue == kMoney64Undefined) - return "inProgress"; + return JSFromStdString(ctx, "inProgress"); if (gameState.scenarioCompletedCompanyValue == kCompanyValueOnFailedObjective) - return "failed"; - return "completed"; + return JSFromStdString(ctx, "failed"); + return JSFromStdString(ctx, "completed"); } - void status_set(const std::string& value) + static JSValue status_set(JSContext* ctx, JSValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); + JS_UNPACK_STR(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); auto& gameState = getGameState(); if (value == "inProgress") gameState.scenarioCompletedCompanyValue = kMoney64Undefined; @@ -295,33 +331,44 @@ namespace OpenRCT2::Scripting gameState.scenarioCompletedCompanyValue = kCompanyValueOnFailedObjective; else if (value == "completed") gameState.scenarioCompletedCompanyValue = gameState.park.companyValue; + return JS_UNDEFINED; } - money64 companyValueRecord_get() const + static JSValue companyValueRecord_get(JSContext* ctx, JSValue) { - return getGameState().scenarioCompanyValueRecord; + return JS_NewInt64(ctx, OpenRCT2::getGameState().scenarioCompanyValueRecord); } - void companyValueRecord_set(money64 value) + static JSValue companyValueRecord_set(JSContext* ctx, JSValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); + JS_UNPACK_MONEY64(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); getGameState().scenarioCompanyValueRecord = value; + return JS_UNDEFINED; } public: - static void Register(duk_context* ctx) + JSValue New(JSContext* ctx) { - dukglue_register_property(ctx, &ScScenario::name_get, &ScScenario::name_set, "name"); - dukglue_register_property(ctx, &ScScenario::details_get, &ScScenario::details_set, "details"); - dukglue_register_property(ctx, &ScScenario::completedBy_get, &ScScenario::completedBy_set, "completedBy"); - dukglue_register_property(ctx, &ScScenario::filename_get, &ScScenario::filename_set, "filename"); - dukglue_register_property( - ctx, &ScScenario::parkRatingWarningDays_get, &ScScenario::parkRatingWarningDays_set, "parkRatingWarningDays"); - dukglue_register_property(ctx, &ScScenario::objective_get, nullptr, "objective"); - dukglue_register_property(ctx, &ScScenario::status_get, &ScScenario::status_set, "status"); - dukglue_register_property( - ctx, &ScScenario::completedCompanyValue_get, &ScScenario::completedCompanyValue_set, "completedCompanyValue"); - dukglue_register_property( - ctx, &ScScenario::companyValueRecord_get, &ScScenario::companyValueRecord_set, "companyValueRecord"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("name", ScScenario::name_get, ScScenario::name_set), + JS_CGETSET_DEF("details", ScScenario::details_get, ScScenario::details_set), + JS_CGETSET_DEF("completedBy", ScScenario::completedBy_get, ScScenario::completedBy_set), + JS_CGETSET_DEF("filename", ScScenario::filename_get, ScScenario::filename_set), + JS_CGETSET_DEF( + "parkRatingWarningDays", ScScenario::parkRatingWarningDays_get, ScScenario::parkRatingWarningDays_set), + JS_CGETSET_DEF("objective", ScScenario::objective_get, nullptr), + JS_CGETSET_DEF("status", ScScenario::status_get, ScScenario::status_set), + JS_CGETSET_DEF( + "completedCompanyValue", ScScenario::completedCompanyValue_get, ScScenario::completedCompanyValue_set), + JS_CGETSET_DEF("companyValueRecord", ScScenario::companyValueRecord_get, ScScenario::companyValueRecord_set), + }; + + return MakeWithOpaque(ctx, funcs, nullptr); + } + + void Register(JSContext* ctx) + { + RegisterBaseStr(ctx, "Scenario"); } }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/world/ScTile.cpp b/src/openrct2/scripting/bindings/world/ScTile.cpp index d404fde70b..9802eb92bc 100644 --- a/src/openrct2/scripting/bindings/world/ScTile.cpp +++ b/src/openrct2/scripting/bindings/world/ScTile.cpp @@ -20,7 +20,6 @@ #include "../../../world/Map.h" #include "../../../world/Scenery.h" #include "../../../world/tile_element/LargeSceneryElement.h" - #include "../../Duktape.hpp" #include "../../ScriptEngine.h" #include "ScTileElement.hpp" @@ -30,79 +29,80 @@ namespace OpenRCT2::Scripting { - ScTile::ScTile(const CoordsXY& coords) - : _coords(coords) + using OpaqueTileData = struct { + CoordsXY coords; + }; + + JSValue ScTile::x_get(JSContext* ctx, JSValue thisValue) + { + auto coords = GetCoordinates(thisValue); + return JS_NewInt32(ctx, coords.x / kCoordsXYStep); } - int32_t ScTile::x_get() const + JSValue ScTile::y_get(JSContext* ctx, JSValue thisValue) { - return _coords.x / kCoordsXYStep; + auto coords = GetCoordinates(thisValue); + return JS_NewInt32(ctx, coords.y / kCoordsXYStep); } - int32_t ScTile::y_get() const + JSValue ScTile::numElements_get(JSContext* ctx, JSValue thisValue) { - return _coords.y / kCoordsXYStep; + auto first = GetFirstElement(thisValue); + return JS_NewUint32(ctx, GetNumElements(first)); } - uint32_t ScTile::numElements_get() const + JSValue ScTile::elements_get(JSContext* ctx, JSValue thisValue) { - auto first = GetFirstElement(); - return static_cast(GetNumElements(first)); - } - - std::vector> ScTile::elements_get() const - { - std::vector> result; - auto first = GetFirstElement(); + auto array = JS_NewArray(ctx); + auto coords = GetCoordinates(thisValue); + auto first = MapGetFirstElementAt(coords); auto currentNumElements = GetNumElements(first); if (currentNumElements != 0) { - result.reserve(currentNumElements); + JS_SetLength(ctx, array, currentNumElements); for (size_t i = 0; i < currentNumElements; i++) { - result.push_back(std::make_shared(_coords, &first[i])); + JS_SetPropertyInt64(ctx, array, i, gScTileElement.New(ctx, &first[i], coords)); } } - return result; + return array; } - DukValue ScTile::data_get() const + JSValue ScTile::data_get(JSContext* ctx, JSValue thisValue) { - auto ctx = GetDukContext(); - auto first = MapGetFirstElementAt(_coords); + auto first = GetFirstElement(thisValue); auto dataLen = GetNumElements(first) * sizeof(TileElement); - auto data = duk_push_fixed_buffer(ctx, dataLen); if (first != nullptr) { - std::memcpy(data, first, dataLen); + return JS_NewUint8ArrayCopy(ctx, reinterpret_cast(first), dataLen); } - duk_push_buffer_object(ctx, -1, 0, dataLen, DUK_BUFOBJ_UINT8ARRAY); - return DukValue::take_from_stack(ctx); + return JS_NewUint8ArrayCopy(ctx, nullptr, 0); } - void ScTile::data_set(DukValue value) + JSValue ScTile::data_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto ctx = value.context(); - value.push(); - if (duk_is_buffer_data(ctx, -1)) + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + if (JS_GetTypedArrayType(jsValue) == JSTypedArrayEnum::JS_TYPED_ARRAY_UINT8) { - duk_size_t dataLen{}; - auto data = duk_get_buffer_data(ctx, -1, &dataLen); - auto numElements = dataLen / sizeof(TileElement); + auto coords = GetCoordinates(thisValue); + int64_t dataLength{}; + JS_GetLength(ctx, jsValue, &dataLength); + auto dataSize = static_cast(dataLength); + auto* array = JS_GetUint8Array(ctx, &dataSize, jsValue); + auto numElements = dataLength / sizeof(TileElement); if (numElements == 0) { - MapSetTileElement(TileCoordsXY(_coords), nullptr); + MapSetTileElement(TileCoordsXY(coords), nullptr); } else { - auto first = GetFirstElement(); + auto first = MapGetFirstElementAt(coords); auto currentNumElements = GetNumElements(first); if (numElements > currentNumElements) { // Allocate space for the extra tile elements (inefficient but works) - auto pos = TileCoordsXYZ(TileCoordsXY(_coords), 0).ToCoordsXYZ(); + auto pos = TileCoordsXYZ(TileCoordsXY(coords), 0).ToCoordsXYZ(); auto numToInsert = numElements - currentNumElements; for (size_t i = 0; i < numToInsert; i++) { @@ -110,112 +110,122 @@ namespace OpenRCT2::Scripting } // Copy data to element span - first = MapGetFirstElementAt(_coords); + first = MapGetFirstElementAt(coords); currentNumElements = GetNumElements(first); if (currentNumElements != 0) { - std::memcpy(first, data, currentNumElements * sizeof(TileElement)); + std::memcpy(first, array, currentNumElements * sizeof(TileElement)); // Safely force last tile flag for last element to avoid read overrun first[numElements - 1].SetLastForTile(true); } } else { - std::memcpy(first, data, numElements * sizeof(TileElement)); + std::memcpy(first, array, numElements * sizeof(TileElement)); // Safely force last tile flag for last element to avoid read overrun first[numElements - 1].SetLastForTile(true); } } - MapInvalidateTileFull(_coords); + MapInvalidateTileFull(coords); } + return JS_UNDEFINED; } - std::shared_ptr ScTile::getElement(uint32_t index) const + JSValue ScTile::getElement(JSContext* ctx, JSValue thisValue, int argc, JSValue* argv) { - auto first = GetFirstElement(); + JS_UNPACK_UINT32(index, ctx, argv[0]); + auto coords = GetCoordinates(thisValue); + auto first = MapGetFirstElementAt(coords); if (static_cast(index) < GetNumElements(first)) { - return std::make_shared(_coords, &first[index]); + return gScTileElement.New(ctx, &first[index], coords); } - return {}; + return JS_UNDEFINED; } - std::shared_ptr ScTile::insertElement(uint32_t index) + JSValue ScTile::insertElement(JSContext* ctx, JSValue thisValue, int argc, JSValue* argv) { - ThrowIfGameStateNotMutable(); - std::shared_ptr result; - auto first = GetFirstElement(); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + JS_UNPACK_UINT32(index, ctx, argv[0]); + auto coords = GetCoordinates(thisValue); + auto first = MapGetFirstElementAt(coords); auto origNumElements = GetNumElements(first); if (index <= origNumElements) { std::vector data(first, first + origNumElements); - auto pos = TileCoordsXYZ(TileCoordsXY(_coords), 0).ToCoordsXYZ(); + auto pos = TileCoordsXYZ(TileCoordsXY(coords), 0).ToCoordsXYZ(); auto newElement = TileElementInsert(pos, 0, TileElementType::Surface); if (newElement == nullptr) { - auto ctx = GetDukContext(); - duk_error(ctx, DUK_ERR_ERROR, "Unable to allocate element."); + JS_ThrowPlainError(ctx, "Unable to allocate element."); + return JS_EXCEPTION; } - else + + // Inefficient, requires a dedicated method in tile element manager + first = MapGetFirstElementAt(coords); + // Copy elements before index + if (index > 0) { - // Inefficient, requires a dedicated method in tile element manager - first = GetFirstElement(); - // Copy elements before index - if (index > 0) - { - std::memcpy(first, &data[0], index * sizeof(TileElement)); - } - // Zero new element - std::memset(first + index, 0, sizeof(TileElement)); - // Copy elements after index - if (index < origNumElements) - { - std::memcpy(first + index + 1, &data[index], (origNumElements - index) * sizeof(TileElement)); - } - for (size_t i = 0; i < origNumElements; i++) - { - first[i].SetLastForTile(false); - } - first[origNumElements].SetLastForTile(true); - MapInvalidateTileFull(_coords); - result = std::make_shared(_coords, &first[index]); + std::memcpy(first, &data[0], index * sizeof(TileElement)); } + // Zero new element + std::memset(first + index, 0, sizeof(TileElement)); + // Copy elements after index + if (index < origNumElements) + { + std::memcpy(first + index + 1, &data[index], (origNumElements - index) * sizeof(TileElement)); + } + for (size_t i = 0; i < origNumElements; i++) + { + first[i].SetLastForTile(false); + } + first[origNumElements].SetLastForTile(true); + MapInvalidateTileFull(coords); + return gScTileElement.New(ctx, &first[index], coords); } else { - auto ctx = GetDukContext(); - duk_error(ctx, DUK_ERR_RANGE_ERROR, "Index must be between zero and the number of elements on the tile."); + JS_ThrowPlainError(ctx, "Index must be between zero and the number of elements on the tile."); + return JS_EXCEPTION; } - return result; } - void ScTile::removeElement(uint32_t index) + JSValue ScTile::removeElement(JSContext* ctx, JSValue thisValue, int argc, JSValue* argv) { - ThrowIfGameStateNotMutable(); - auto first = GetFirstElement(); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + JS_UNPACK_UINT32(index, ctx, argv[0]); + auto coords = GetCoordinates(thisValue); + auto first = MapGetFirstElementAt(coords); if (index < GetNumElements(first)) { auto element = &first[index]; if (element->GetType() != TileElementType::LargeScenery || element->AsLargeScenery()->GetEntry()->scrolling_mode == kScrollingModeNone - || ScTileElement::GetOtherLargeSceneryElement(_coords, element->AsLargeScenery()) == nullptr) + || ScTileElement::GetOtherLargeSceneryElement(coords, element->AsLargeScenery()) == nullptr) { element->RemoveBannerEntry(); } TileElementRemove(&first[index]); - MapInvalidateTileFull(_coords); + MapInvalidateTileFull(coords); } + return JS_UNDEFINED; } - TileElement* ScTile::GetFirstElement() const + CoordsXY ScTile::GetCoordinates(JSValue thisValue) { - return MapGetFirstElementAt(_coords); + return gScTile.GetOpaque(thisValue)->coords; } - size_t ScTile::GetNumElements(const TileElement* first) + TileElement* ScTile::GetFirstElement(JSValue thisValue) { - size_t count = 0; + auto coords = GetCoordinates(thisValue); + return MapGetFirstElementAt(coords); + } + + uint32_t ScTile::GetNumElements(const TileElement* first) + { + uint32_t count = 0; if (first != nullptr) { auto element = first; @@ -227,23 +237,29 @@ namespace OpenRCT2::Scripting return count; } - duk_context* ScTile::GetDukContext() const + JSValue ScTile::New(JSContext* ctx, CoordsXY& coords) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto ctx = scriptEngine.GetContext(); - return ctx; + static constexpr JSCFunctionListEntry funcs[] = { JS_CGETSET_DEF("x", ScTile::x_get, nullptr), + JS_CGETSET_DEF("y", ScTile::y_get, nullptr), + JS_CGETSET_DEF("elements", ScTile::elements_get, nullptr), + JS_CGETSET_DEF("numElements", ScTile::numElements_get, nullptr), + JS_CGETSET_DEF("data", ScTile::data_get, ScTile::data_set), + JS_CFUNC_DEF("getElement", 1, ScTile::getElement), + JS_CFUNC_DEF("insertElement", 1, ScTile::insertElement), + JS_CFUNC_DEF("removeElement", 0, ScTile::removeElement) }; + return MakeWithOpaque(ctx, funcs, new OpaqueTileData{ coords }); } - void ScTile::Register(duk_context* ctx) + void ScTile::Register(JSContext* ctx) { - dukglue_register_property(ctx, &ScTile::x_get, nullptr, "x"); - dukglue_register_property(ctx, &ScTile::y_get, nullptr, "y"); - dukglue_register_property(ctx, &ScTile::elements_get, nullptr, "elements"); - dukglue_register_property(ctx, &ScTile::numElements_get, nullptr, "numElements"); - dukglue_register_property(ctx, &ScTile::data_get, &ScTile::data_set, "data"); - dukglue_register_method(ctx, &ScTile::getElement, "getElement"); - dukglue_register_method(ctx, &ScTile::insertElement, "insertElement"); - dukglue_register_method(ctx, &ScTile::removeElement, "removeElement"); + RegisterBaseStr(ctx, "Tile", Finalize); + } + + void ScTile::Finalize(JSRuntime* rt, JSValue thisVal) + { + OpaqueTileData* data = gScTile.GetOpaque(thisVal); + if (data) + delete data; } } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/world/ScTile.hpp b/src/openrct2/scripting/bindings/world/ScTile.hpp index 43acb2d2ed..e4bf0589a0 100644 --- a/src/openrct2/scripting/bindings/world/ScTile.hpp +++ b/src/openrct2/scripting/bindings/world/ScTile.hpp @@ -11,7 +11,6 @@ #ifdef ENABLE_SCRIPTING - #include "../../Duktape.hpp" #include "ScTileElement.hpp" #include @@ -22,39 +21,39 @@ namespace OpenRCT2::Scripting { - class ScTile + class ScTile; + extern ScTile gScTile; + + class ScTile : public ScBase { private: - CoordsXY _coords; + static JSValue x_get(JSContext* ctx, JSValue thisValue); + + static JSValue y_get(JSContext* ctx, JSValue thisValue); + + static JSValue numElements_get(JSContext* ctx, JSValue thisValue); + + static JSValue elements_get(JSContext* ctx, JSValue thisValue); + + static JSValue data_get(JSContext* ctx, JSValue thisValue); + static JSValue data_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); + + static JSValue getElement(JSContext* ctx, JSValue thisValue, int argc, JSValue* argv); + static JSValue insertElement(JSContext* ctx, JSValue thisValue, int argc, JSValue* argv); + + static JSValue removeElement(JSContext* ctx, JSValue thisValue, int argc, JSValue* argv); + + static CoordsXY GetCoordinates(JSValue thisValue); + static TileElement* GetFirstElement(JSValue thisValue); + + static uint32_t GetNumElements(const TileElement* first); public: - ScTile(const CoordsXY& coords); + JSValue New(JSContext* ctx, CoordsXY& coords); + void Register(JSContext* ctx); private: - int32_t x_get() const; - - int32_t y_get() const; - - uint32_t numElements_get() const; - - std::vector> elements_get() const; - - DukValue data_get() const; - void data_set(DukValue value); - - std::shared_ptr getElement(uint32_t index) const; - std::shared_ptr insertElement(uint32_t index); - - void removeElement(uint32_t index); - - TileElement* GetFirstElement() const; - - static size_t GetNumElements(const TileElement* first); - - duk_context* GetDukContext() const; - - public: - static void Register(duk_context* ctx); + static void Finalize(JSRuntime* rt, JSValue thisValue); }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/world/ScTileElement.cpp b/src/openrct2/scripting/bindings/world/ScTileElement.cpp index 55a5d1506e..c19e731009 100644 --- a/src/openrct2/scripting/bindings/world/ScTileElement.cpp +++ b/src/openrct2/scripting/bindings/world/ScTileElement.cpp @@ -30,7 +30,6 @@ #include "../../../world/tile_element/SurfaceElement.h" #include "../../../world/tile_element/TrackElement.h" #include "../../../world/tile_element/WallElement.h" - #include "../../Duktape.hpp" #include "../../ScriptEngine.h" #include @@ -39,15 +38,9 @@ namespace OpenRCT2::Scripting { - ScTileElement::ScTileElement(const CoordsXY& coords, TileElement* element) - : _coords(coords) - , _element(element) + static inline std::string TileElementTypeToString(const TileElement* element) { - } - - std::string ScTileElement::type_get() const - { - switch (_element->GetType()) + switch (element->GetType()) { case TileElementType::Surface: return "surface"; @@ -70,1809 +63,1964 @@ namespace OpenRCT2::Scripting } } - void ScTileElement::type_set(std::string value) + using OpaqueTileElementData = struct { - RemoveBannerEntryIfNeeded(); + TileElement* element; + CoordsXY coords; + }; + + static inline void Invalidate(OpaqueTileElementData* data) + { + MapInvalidateTileFull(data->coords); + } + + JSValue ScTileElement::type_get(JSContext* ctx, JSValue thisValue) + { + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; + auto type = TileElementTypeToString(element); + return JSFromStdString(ctx, type); + } + + JSValue ScTileElement::type_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) + { + JS_UNPACK_STR(value, ctx, jsValue); + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; + RemoveBannerEntryIfNeeded(element, data->coords); if (value == "surface") - _element->SetType(TileElementType::Surface); + element->SetType(TileElementType::Surface); else if (value == "footpath") - _element->SetType(TileElementType::Path); + element->SetType(TileElementType::Path); else if (value == "track") - _element->SetType(TileElementType::Track); + element->SetType(TileElementType::Track); else if (value == "small_scenery") - _element->SetType(TileElementType::SmallScenery); + element->SetType(TileElementType::SmallScenery); else if (value == "entrance") - _element->SetType(TileElementType::Entrance); + element->SetType(TileElementType::Entrance); else if (value == "wall") - _element->SetType(TileElementType::Wall); + element->SetType(TileElementType::Wall); else if (value == "large_scenery") - _element->SetType(TileElementType::LargeScenery); + element->SetType(TileElementType::LargeScenery); else if (value == "banner") - _element->SetType(TileElementType::Banner); + element->SetType(TileElementType::Banner); else { auto& scriptEngine = GetContext()->GetScriptEngine(); scriptEngine.LogPluginInfo("Element type not recognised!"); - return; + return JS_UNDEFINED; } - CreateBannerEntryIfNeeded(); - Invalidate(); + CreateBannerEntryIfNeeded(element, data->coords); + Invalidate(data); + return JS_UNDEFINED; } - uint8_t ScTileElement::baseHeight_get() const + JSValue ScTileElement::baseHeight_get(JSContext* ctx, JSValue thisValue) { - return _element->BaseHeight; + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; + return JS_NewUint32(ctx, element->BaseHeight); } - void ScTileElement::baseHeight_set(uint8_t newBaseHeight) + JSValue ScTileElement::baseHeight_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - _element->BaseHeight = newBaseHeight; - Invalidate(); + JS_UNPACK_UINT32(newBaseHeight, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + data->element->BaseHeight = newBaseHeight; + Invalidate(data); + return JS_UNDEFINED; } - uint16_t ScTileElement::baseZ_get() const + JSValue ScTileElement::baseZ_get(JSContext* ctx, JSValue thisValue) { - return _element->GetBaseZ(); + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; + return JS_NewUint32(ctx, element->GetBaseZ()); } - void ScTileElement::baseZ_set(uint16_t value) + JSValue ScTileElement::baseZ_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - _element->SetBaseZ(value); - Invalidate(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + data->element->SetBaseZ(value); + Invalidate(data); + return JS_UNDEFINED; } - uint8_t ScTileElement::clearanceHeight_get() const + JSValue ScTileElement::clearanceHeight_get(JSContext* ctx, JSValue thisValue) { - return _element->ClearanceHeight; + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; + return JS_NewUint32(ctx, element->ClearanceHeight); } - void ScTileElement::clearanceHeight_set(uint8_t newClearanceHeight) + JSValue ScTileElement::clearanceHeight_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - _element->ClearanceHeight = newClearanceHeight; - Invalidate(); + JS_UNPACK_UINT32(newClearanceHeight, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + data->element->ClearanceHeight = newClearanceHeight; + Invalidate(data); + return JS_UNDEFINED; } - uint16_t ScTileElement::clearanceZ_get() const + JSValue ScTileElement::clearanceZ_get(JSContext* ctx, JSValue thisValue) { - return _element->GetClearanceZ(); + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; + return JS_NewUint32(ctx, element->GetClearanceZ()); } - void ScTileElement::clearanceZ_set(uint16_t value) + JSValue ScTileElement::clearanceZ_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - _element->SetClearanceZ(value); - Invalidate(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + data->element->SetClearanceZ(value); + Invalidate(data); + return JS_UNDEFINED; } - DukValue ScTileElement::slope_get() const + JSValue ScTileElement::slope_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - switch (_element->GetType()) + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; + switch (element->GetType()) { case TileElementType::Surface: { - auto* el = _element->AsSurface(); - duk_push_int(ctx, el->GetSlope()); - break; + auto* el = element->AsSurface(); + return JS_NewUint32(ctx, el->GetSlope()); } case TileElementType::Wall: { - auto* el = _element->AsWall(); - duk_push_int(ctx, el->GetSlope()); - break; + auto* el = element->AsWall(); + return JS_NewUint32(ctx, el->GetSlope()); } default: { + auto& scriptEngine = GetContext()->GetScriptEngine(); scriptEngine.LogPluginInfo( "Cannot read 'slope' property, tile element is not a SurfaceElement or WallElement."); - duk_push_null(ctx); - break; + return JS_NULL; } } - return DukValue::take_from_stack(ctx); } - void ScTileElement::slope_set(uint8_t value) + JSValue ScTileElement::slope_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - const auto type = _element->GetType(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; + const auto type = element->GetType(); if (type == TileElementType::Surface) { - auto* el = _element->AsSurface(); + auto* el = element->AsSurface(); el->SetSlope(value); - Invalidate(); + Invalidate(data); } else if (type == TileElementType::Wall) { - auto* el = _element->AsWall(); + auto* el = element->AsWall(); el->SetSlope(value); - Invalidate(); + Invalidate(data); } else { auto& scriptEngine = GetContext()->GetScriptEngine(); scriptEngine.LogPluginInfo("Cannot set 'slope' property, tile element is not a SurfaceElement or WallElement."); } + return JS_UNDEFINED; } - DukValue ScTileElement::waterHeight_get() const + JSValue ScTileElement::waterHeight_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto* el = _element->AsSurface(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsSurface(); if (el != nullptr) { - duk_push_int(ctx, el->GetWaterHeight()); + return JS_NewInt32(ctx, el->GetWaterHeight()); } else { + auto& scriptEngine = GetContext()->GetScriptEngine(); scriptEngine.LogPluginInfo("Cannot read 'waterHeight' property, tile element is not a SurfaceElement."); - duk_push_null(ctx); + return JS_NULL; } - return DukValue::take_from_stack(ctx); } - void ScTileElement::waterHeight_set(int32_t value) + JSValue ScTileElement::waterHeight_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto* el = _element->AsSurface(); + JS_UNPACK_INT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsSurface(); if (el == nullptr) { auto& scriptEngine = GetContext()->GetScriptEngine(); scriptEngine.LogPluginInfo("Cannot set 'waterHeight' property, tile element is not a SurfaceElement."); - return; + return JS_UNDEFINED; } el->SetWaterHeight(value); - Invalidate(); + Invalidate(data); + return JS_UNDEFINED; } - DukValue ScTileElement::surfaceStyle_get() const + JSValue ScTileElement::surfaceStyle_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto* el = _element->AsSurface(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsSurface(); if (el != nullptr) { - duk_push_int(ctx, el->GetSurfaceObjectIndex()); + return JS_NewUint32(ctx, el->GetSurfaceObjectIndex()); } else { + auto& scriptEngine = GetContext()->GetScriptEngine(); scriptEngine.LogPluginInfo("Cannot read 'surfaceStyle' property, tile element is not a SurfaceElement."); - duk_push_null(ctx); + return JS_NULL; } - return DukValue::take_from_stack(ctx); } - void ScTileElement::surfaceStyle_set(uint32_t value) + JSValue ScTileElement::surfaceStyle_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto* el = _element->AsSurface(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsSurface(); if (el == nullptr) { auto& scriptEngine = GetContext()->GetScriptEngine(); scriptEngine.LogPluginInfo("Cannot set 'surfaceStyle' property, tile element is not a SurfaceElement."); - return; + return JS_UNDEFINED; } el->SetSurfaceObjectIndex(value); - Invalidate(); + Invalidate(data); + return JS_UNDEFINED; } - DukValue ScTileElement::edgeStyle_get() const + JSValue ScTileElement::edgeStyle_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto* el = _element->AsSurface(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsSurface(); if (el != nullptr) { - duk_push_int(ctx, el->GetEdgeObjectIndex()); + return JS_NewUint32(ctx, el->GetEdgeObjectIndex()); } else { + auto& scriptEngine = GetContext()->GetScriptEngine(); scriptEngine.LogPluginInfo("Cannot read 'edgeStyle' property, tile element is not a SurfaceElement."); - duk_push_null(ctx); + return JS_NULL; } - return DukValue::take_from_stack(ctx); } - void ScTileElement::edgeStyle_set(uint32_t value) + JSValue ScTileElement::edgeStyle_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto* el = _element->AsSurface(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsSurface(); if (el == nullptr) { auto& scriptEngine = GetContext()->GetScriptEngine(); scriptEngine.LogPluginInfo("Cannot set 'edgeStyle' property, tile element is not a SurfaceElement."); - return; + return JS_UNDEFINED; } el->SetEdgeObjectIndex(value); - Invalidate(); + Invalidate(data); + return JS_UNDEFINED; } - DukValue ScTileElement::grassLength_get() const + JSValue ScTileElement::grassLength_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto* el = _element->AsSurface(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsSurface(); if (el != nullptr) { - duk_push_int(ctx, el->GetGrassLength()); + return JS_NewUint32(ctx, el->GetGrassLength()); } else { + auto& scriptEngine = GetContext()->GetScriptEngine(); scriptEngine.LogPluginInfo("Cannot read 'grassLength' property, tile element is not a SurfaceElement."); - duk_push_null(ctx); + return JS_NULL; } - return DukValue::take_from_stack(ctx); } - void ScTileElement::grassLength_set(uint8_t value) + JSValue ScTileElement::grassLength_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto* el = _element->AsSurface(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsSurface(); if (el == nullptr) { auto& scriptEngine = GetContext()->GetScriptEngine(); scriptEngine.LogPluginInfo("Cannot set 'grassLength' property, tile element is not a SurfaceElement."); - return; + return JS_UNDEFINED; } // TODO: Give warning when value > GRASS_LENGTH_CLUMPS_2 - el->SetGrassLengthAndInvalidate(value, _coords); - Invalidate(); + el->SetGrassLengthAndInvalidate(value, data->coords); + Invalidate(data); + return JS_UNDEFINED; } - DukValue ScTileElement::hasOwnership_get() const + JSValue ScTileElement::hasOwnership_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto* el = _element->AsSurface(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsSurface(); if (el != nullptr) { - duk_push_boolean(ctx, el->GetOwnership() & OWNERSHIP_OWNED); + return JS_NewBool(ctx, el->GetOwnership() & OWNERSHIP_OWNED); } else { + auto& scriptEngine = GetContext()->GetScriptEngine(); scriptEngine.LogPluginInfo("Cannot read 'hasOwnership' property, tile element is not a SurfaceElement."); - duk_push_null(ctx); + return JS_NULL; } - return DukValue::take_from_stack(ctx); } - DukValue ScTileElement::hasConstructionRights_get() + JSValue ScTileElement::hasConstructionRights_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto* el = _element->AsSurface(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsSurface(); if (el != nullptr) { auto ownership = el->GetOwnership(); - duk_push_boolean(ctx, (ownership & OWNERSHIP_OWNED) || (ownership & OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED)); + return JS_NewBool(ctx, (ownership & OWNERSHIP_OWNED) || (ownership & OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED)); } else { + auto& scriptEngine = GetContext()->GetScriptEngine(); scriptEngine.LogPluginInfo("Cannot read 'hasConstructionRights' property, tile element is not a SurfaceElement."); - duk_push_null(ctx); + return JS_NULL; } - return DukValue::take_from_stack(ctx); } - DukValue ScTileElement::ownership_get() const + JSValue ScTileElement::ownership_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto* el = _element->AsSurface(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsSurface(); if (el != nullptr) { - duk_push_int(ctx, el->GetOwnership()); + return JS_NewUint32(ctx, el->GetOwnership()); } else { + auto& scriptEngine = GetContext()->GetScriptEngine(); scriptEngine.LogPluginInfo("Cannot read 'ownership' property, tile element is not a SurfaceElement."); - duk_push_null(ctx); + return JS_NULL; } - return DukValue::take_from_stack(ctx); } - void ScTileElement::ownership_set(uint8_t value) + JSValue ScTileElement::ownership_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto* el = _element->AsSurface(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsSurface(); if (el == nullptr) { auto& scriptEngine = GetContext()->GetScriptEngine(); scriptEngine.LogPluginInfo("Cannot set 'ownership' property, tile element is not a SurfaceElement."); - return; + return JS_UNDEFINED; } el->SetOwnership(value); - Invalidate(); + Invalidate(data); + return JS_UNDEFINED; } - DukValue ScTileElement::parkFences_get() const + JSValue ScTileElement::parkFences_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto* el = _element->AsSurface(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsSurface(); if (el != nullptr) { - duk_push_int(ctx, el->GetParkFences()); + return JS_NewUint32(ctx, el->GetParkFences()); } else { + auto& scriptEngine = GetContext()->GetScriptEngine(); scriptEngine.LogPluginInfo("Cannot read 'parkFences' property, tile element is not a SurfaceElement."); - duk_push_null(ctx); + return JS_NULL; } - return DukValue::take_from_stack(ctx); } - void ScTileElement::parkFences_set(uint8_t value) + JSValue ScTileElement::parkFences_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto* el = _element->AsSurface(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsSurface(); if (el == nullptr) { auto& scriptEngine = GetContext()->GetScriptEngine(); scriptEngine.LogPluginInfo("Cannot set 'parkFences' property, tile element is not a SurfaceElement."); - return; + return JS_UNDEFINED; } el->SetParkFences(value); - Invalidate(); + return JS_UNDEFINED; } - DukValue ScTileElement::trackType_get() const + JSValue ScTileElement::trackType_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto* el = _element->AsTrack(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsTrack(); if (el != nullptr) { - duk_push_int(ctx, EnumValue(el->GetTrackType())); + return JS_NewUint32(ctx, EnumValue(el->GetTrackType())); } else { + auto& scriptEngine = GetContext()->GetScriptEngine(); scriptEngine.LogPluginInfo("Cannot read 'trackType' property, tile element is not a TrackElement."); - duk_push_null(ctx); + return JS_NULL; } - return DukValue::take_from_stack(ctx); } - void ScTileElement::trackType_set(uint16_t value) + JSValue ScTileElement::trackType_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto* el = _element->AsTrack(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsTrack(); if (el == nullptr) { auto& scriptEngine = GetContext()->GetScriptEngine(); scriptEngine.LogPluginInfo("Cannot set 'trackType' property, tile element is not a TrackElement."); - return; + return JS_UNDEFINED; } el->SetTrackType(static_cast(value)); - Invalidate(); + Invalidate(data); + return JS_UNDEFINED; } - DukValue ScTileElement::rideType_get() const + JSValue ScTileElement::rideType_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto* el = _element->AsTrack(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsTrack(); if (el != nullptr) { - duk_push_int(ctx, el->GetRideType()); + return JS_NewUint32(ctx, el->GetRideType()); } else { + auto& scriptEngine = GetContext()->GetScriptEngine(); scriptEngine.LogPluginInfo("Cannot read 'rideType' property, tile element is not a TrackElement."); - duk_push_null(ctx); + return JS_NULL; } - return DukValue::take_from_stack(ctx); } - void ScTileElement::rideType_set(uint16_t value) + JSValue ScTileElement::rideType_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); - try - { - if (value >= RIDE_TYPE_COUNT) - throw DukException() << "'rideType' value is invalid."; - - auto* el = _element->AsTrack(); - if (el == nullptr) - throw DukException() << "Cannot set 'rideType' property, tile element is not a TrackElement."; - - el->SetRideType(value); - Invalidate(); - } - catch (const DukException& e) + if (value >= RIDE_TYPE_COUNT) { auto& scriptEngine = GetContext()->GetScriptEngine(); - scriptEngine.LogPluginInfo(e.what()); + scriptEngine.LogPluginInfo("'rideType' value is invalid."); + return JS_UNDEFINED; } + + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsTrack(); + if (el == nullptr) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("Cannot set 'rideType' property, tile element is not a TrackElement."); + return JS_UNDEFINED; + } + + el->SetRideType(value); + Invalidate(data); + return JS_UNDEFINED; } - DukValue ScTileElement::sequence_get() const + JSValue ScTileElement::sequence_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - try + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; + switch (element->GetType()) { - switch (_element->GetType()) + case TileElementType::LargeScenery: { - case TileElementType::LargeScenery: - { - auto* el = _element->AsLargeScenery(); - duk_push_int(ctx, el->GetSequenceIndex()); - break; - } - case TileElementType::Track: - { - auto* el = _element->AsTrack(); - auto* ride = GetRide(el->GetRideIndex()); + auto* el = element->AsLargeScenery(); + return JS_NewUint32(ctx, el->GetSequenceIndex()); + } + case TileElementType::Track: + { + auto* el = element->AsTrack(); + auto* ride = GetRide(el->GetRideIndex()); - if (ride != nullptr) + if (ride != nullptr) + { + const auto& rtd = ride->getRideTypeDescriptor(); + if (rtd.specialType == RtdSpecialType::maze) { - const auto& rtd = ride->getRideTypeDescriptor(); - if (rtd.specialType == RtdSpecialType::maze) - throw DukException() << "Cannot read 'sequence' property, TrackElement belongs to a maze."; + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("Cannot read 'sequence' property, TrackElement belongs to a maze."); + return JS_NULL; } + } - duk_push_int(ctx, el->GetSequenceIndex()); - break; - } - case TileElementType::Entrance: - { - auto* el = _element->AsEntrance(); - duk_push_int(ctx, el->GetSequenceIndex()); - break; - } - default: - throw DukException() << "Cannot read 'sequence' property, tile element is not a TrackElement, " - "LargeSceneryElement, or EntranceElement."; + return JS_NewUint32(ctx, el->GetSequenceIndex()); + } + case TileElementType::Entrance: + { + auto* el = element->AsEntrance(); + return JS_NewUint32(ctx, el->GetSequenceIndex()); + } + default: + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo( + "Cannot read 'sequence' property, tile element is not a TrackElement, " + "LargeSceneryElement, or EntranceElement."); + return JS_NULL; } } - catch (const DukException& e) - { - scriptEngine.LogPluginInfo(e.what()); - duk_push_null(ctx); - } - return DukValue::take_from_stack(ctx); } - void ScTileElement::sequence_set(const DukValue& value) + JSValue ScTileElement::sequence_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; - try + switch (element->GetType()) { - if (value.type() != DukValue::Type::NUMBER) - throw DukException() << "'sequence' must be a number."; - - switch (_element->GetType()) + case TileElementType::LargeScenery: { - case TileElementType::LargeScenery: - { - RemoveBannerEntryIfNeeded(); - auto* el = _element->AsLargeScenery(); - el->SetSequenceIndex(value.as_uint()); - CreateBannerEntryIfNeeded(); - Invalidate(); - break; - } - case TileElementType::Track: - { - auto* el = _element->AsTrack(); - auto ride = GetRide(el->GetRideIndex()); + RemoveBannerEntryIfNeeded(element, data->coords); + auto* el = element->AsLargeScenery(); + el->SetSequenceIndex(value); + CreateBannerEntryIfNeeded(element, data->coords); + Invalidate(data); + break; + } + case TileElementType::Track: + { + auto* el = element->AsTrack(); + auto ride = GetRide(el->GetRideIndex()); - if (ride != nullptr) + if (ride != nullptr) + { + const auto& rtd = ride->getRideTypeDescriptor(); + if (rtd.specialType == RtdSpecialType::maze) { - const auto& rtd = ride->getRideTypeDescriptor(); - if (rtd.specialType == RtdSpecialType::maze) - throw DukException() << "Cannot set 'sequence' property, TrackElement belongs to a maze."; + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("Cannot read 'sequence' property, TrackElement belongs to a maze."); + return JS_UNDEFINED; } + } - el->SetSequenceIndex(value.as_uint()); - Invalidate(); - break; - } - case TileElementType::Entrance: - { - auto* el = _element->AsEntrance(); - el->SetSequenceIndex(value.as_uint()); - Invalidate(); - break; - } - default: - throw DukException() << "Cannot set 'sequence' property, tile element is not a TrackElement, " - "LargeSceneryElement, or EntranceElement."; + el->SetSequenceIndex(value); + Invalidate(data); + break; } - } - catch (const DukException& e) - { - auto& scriptEngine = GetContext()->GetScriptEngine(); - scriptEngine.LogPluginInfo(e.what()); - } - } - - DukValue ScTileElement::ride_get() const - { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - try - { - switch (_element->GetType()) + case TileElementType::Entrance: { - case TileElementType::Path: - { - auto* el = _element->AsPath(); - if (!el->IsQueue()) - throw DukException() << "Cannot read 'ride' property, path is not a queue."; - - if (!el->GetRideIndex().IsNull()) - duk_push_int(ctx, el->GetRideIndex().ToUnderlying()); - else - duk_push_null(ctx); - break; - } - case TileElementType::Track: - { - auto* el = _element->AsTrack(); - duk_push_int(ctx, el->GetRideIndex().ToUnderlying()); - break; - } - case TileElementType::Entrance: - { - auto* el = _element->AsEntrance(); - duk_push_int(ctx, el->GetRideIndex().ToUnderlying()); - break; - } - default: - throw DukException() - << "Cannot read 'ride' property, tile element is not PathElement, TrackElement, or EntranceElement"; + auto* el = element->AsEntrance(); + el->SetSequenceIndex(value); + Invalidate(data); + break; } - } - catch (const DukException& e) - { - scriptEngine.LogPluginInfo(e.what()); - duk_push_null(ctx); - } - return DukValue::take_from_stack(ctx); - } - void ScTileElement::ride_set(const DukValue& value) - { - ThrowIfGameStateNotMutable(); - - try - { - switch (_element->GetType()) + default: { - case TileElementType::Path: - { - auto* el = _element->AsPath(); - if (!el->IsQueue()) - throw DukException() << "Cannot set ride property, path is not a queue."; - - if (value.type() == DukValue::Type::NUMBER) - el->SetRideIndex(RideId::FromUnderlying(value.as_uint())); - else if (value.type() == DukValue::Type::NULLREF) - el->SetRideIndex(RideId::GetNull()); - else - throw DukException() << "'ride' must be a number or null."; - Invalidate(); - break; - } - case TileElementType::Track: - { - if (value.type() != DukValue::Type::NUMBER) - throw DukException() << "'ride' must be a number."; - - auto* el = _element->AsTrack(); - el->SetRideIndex(RideId::FromUnderlying(value.as_uint())); - Invalidate(); - break; - } - case TileElementType::Entrance: - { - if (value.type() != DukValue::Type::NUMBER) - throw DukException() << "'ride' must be a number."; - - auto* el = _element->AsEntrance(); - el->SetRideIndex(RideId::FromUnderlying(value.as_uint())); - Invalidate(); - break; - } - default: - throw DukException() - << "Cannot set 'ride' property, tile element is not PathElement, TrackElement, or EntranceElement"; + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo( + "Cannot read 'sequence' property, tile element is not a TrackElement, " + "LargeSceneryElement, or EntranceElement."); + break; } } - catch (const DukException& e) - { - auto& scriptEngine = GetContext()->GetScriptEngine(); - scriptEngine.LogPluginInfo(e.what()); - } + return JS_UNDEFINED; } - DukValue ScTileElement::station_get() const + JSValue ScTileElement::ride_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - try + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; + switch (element->GetType()) { - switch (_element->GetType()) + case TileElementType::Path: { - case TileElementType::Path: + auto* el = element->AsPath(); + if (!el->IsQueue()) { - auto* el = _element->AsPath(); - if (!el->IsQueue()) - throw DukException() << "Cannot read 'station' property, path is not a queue."; - - if (el->GetRideIndex().IsNull()) - throw DukException() << "Cannot read 'station' property, queue is not linked to a ride."; - - if (!el->GetStationIndex().IsNull()) - duk_push_int(ctx, el->GetStationIndex().ToUnderlying()); - else - duk_push_null(ctx); - break; + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("Cannot read 'ride' property, path is not a queue."); + return JS_NULL; } - case TileElementType::Track: - { - auto* el = _element->AsTrack(); - if (!el->IsStation()) - throw DukException() << "Cannot read 'station' property, track is not a station."; - duk_push_int(ctx, el->GetStationIndex().ToUnderlying()); - break; - } - case TileElementType::Entrance: - { - auto* el = _element->AsEntrance(); - duk_push_int(ctx, el->GetStationIndex().ToUnderlying()); - break; - } - default: - throw DukException() - << "Cannot read 'station' property, tile element is not PathElement, TrackElement, or EntranceElement"; + if (!el->GetRideIndex().IsNull()) + return JS_NewUint32(ctx, el->GetRideIndex().ToUnderlying()); + + return JS_NULL; } - } - catch (const DukException& e) - { - scriptEngine.LogPluginInfo(e.what()); - duk_push_null(ctx); - } - return DukValue::take_from_stack(ctx); - } - void ScTileElement::station_set(const DukValue& value) - { - ThrowIfGameStateNotMutable(); - - try - { - switch (_element->GetType()) + case TileElementType::Track: { - case TileElementType::Path: - { - auto* el = _element->AsPath(); - if (value.type() == DukValue::Type::NUMBER) - el->SetStationIndex(StationIndex::FromUnderlying(value.as_uint())); - else if (value.type() == DukValue::Type::NULLREF) - el->SetStationIndex(StationIndex::GetNull()); - else - throw DukException() << "'station' must be a number or null."; - Invalidate(); - break; - } - case TileElementType::Track: - { - if (value.type() != DukValue::Type::NUMBER) - throw DukException() << "'station' must be a number."; - - auto* el = _element->AsTrack(); - el->SetStationIndex(StationIndex::FromUnderlying(value.as_uint())); - Invalidate(); - break; - } - case TileElementType::Entrance: - { - if (value.type() != DukValue::Type::NUMBER) - throw DukException() << "'station' must be a number."; - - auto* el = _element->AsEntrance(); - el->SetStationIndex(StationIndex::FromUnderlying(value.as_uint())); - Invalidate(); - break; - } - default: - break; + auto* el = element->AsTrack(); + return JS_NewUint32(ctx, el->GetRideIndex().ToUnderlying()); + } + case TileElementType::Entrance: + { + auto* el = element->AsEntrance(); + return JS_NewUint32(ctx, el->GetRideIndex().ToUnderlying()); + } + default: + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo( + "Cannot read 'ride' property, tile element is not PathElement, TrackElement, or EntranceElement"); + return JS_NULL; } } - catch (const DukException& e) + } + JSValue ScTileElement::ride_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) + { + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; + + switch (element->GetType()) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - scriptEngine.LogPluginInfo(e.what()); + case TileElementType::Path: + { + auto* el = element->AsPath(); + if (!el->IsQueue()) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("Cannot set ride property, path is not a queue."); + return JS_UNDEFINED; + } + + if (JS_IsNumber(jsValue)) + { + JS_UNPACK_UINT32(value, ctx, jsValue); + el->SetRideIndex(RideId::FromUnderlying(value)); + } + else if (JS_IsNull(jsValue)) + { + el->SetRideIndex(RideId::GetNull()); + } + else + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("'ride' must be a number or null."); + return JS_UNDEFINED; + } + Invalidate(data); + break; + } + case TileElementType::Track: + { + if (!JS_IsNumber(jsValue)) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("'ride' must be a number."); + return JS_UNDEFINED; + } + + JS_UNPACK_UINT32(value, ctx, jsValue); + auto* el = element->AsTrack(); + el->SetRideIndex(RideId::FromUnderlying(value)); + Invalidate(data); + break; + } + case TileElementType::Entrance: + { + if (!JS_IsNumber(jsValue)) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("'ride' must be a number."); + return JS_UNDEFINED; + } + + JS_UNPACK_UINT32(value, ctx, jsValue); + auto* el = element->AsEntrance(); + el->SetRideIndex(RideId::FromUnderlying(value)); + Invalidate(data); + break; + } + default: + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo( + "Cannot set 'ride' property, tile element is not PathElement, TrackElement, or EntranceElement"); + break; + } } + return JS_UNDEFINED; } - DukValue ScTileElement::hasChainLift_get() const + JSValue ScTileElement::station_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto* el = _element->AsTrack(); + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; + switch (element->GetType()) + { + case TileElementType::Path: + { + auto* el = element->AsPath(); + if (!el->IsQueue()) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("Cannot read 'station' property, path is not a queue."); + return JS_NULL; + } + + if (el->GetRideIndex().IsNull()) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("Cannot read 'station' property, queue is not linked to a ride."); + return JS_NULL; + } + + if (!el->GetStationIndex().IsNull()) + return JS_NewUint32(ctx, el->GetStationIndex().ToUnderlying()); + + return JS_NULL; + } + case TileElementType::Track: + { + auto* el = element->AsTrack(); + if (!el->IsStation()) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("Cannot read 'station' property, track is not a station."); + return JS_NULL; + } + + return JS_NewUint32(ctx, el->GetStationIndex().ToUnderlying()); + } + case TileElementType::Entrance: + { + auto* el = element->AsEntrance(); + return JS_NewUint32(ctx, el->GetStationIndex().ToUnderlying()); + } + default: + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo( + "Cannot read 'station' property, tile element is not PathElement, TrackElement, or EntranceElement"); + return JS_NULL; + } + } + } + JSValue ScTileElement::station_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) + { + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; + + switch (element->GetType()) + { + case TileElementType::Path: + { + auto* el = element->AsPath(); + if (JS_IsNumber(jsValue)) + { + JS_UNPACK_UINT32(value, ctx, jsValue); + el->SetStationIndex(StationIndex::FromUnderlying(value)); + } + else if (JS_IsNull(jsValue)) + { + el->SetStationIndex(StationIndex::GetNull()); + } + else + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("'station' must be a number or null."); + return JS_UNDEFINED; + } + Invalidate(data); + break; + } + case TileElementType::Track: + { + if (!JS_IsNumber(jsValue)) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("'station' must be a number."); + return JS_UNDEFINED; + } + + JS_UNPACK_UINT32(value, ctx, jsValue); + auto* el = element->AsTrack(); + el->SetStationIndex(StationIndex::FromUnderlying(value)); + Invalidate(data); + break; + } + case TileElementType::Entrance: + { + if (!JS_IsNumber(jsValue)) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("'station' must be a number."); + return JS_UNDEFINED; + } + + JS_UNPACK_UINT32(value, ctx, jsValue); + auto* el = element->AsEntrance(); + el->SetStationIndex(StationIndex::FromUnderlying(value)); + Invalidate(data); + break; + } + default: + break; + } + return JS_UNDEFINED; + } + + JSValue ScTileElement::hasChainLift_get(JSContext* ctx, JSValue thisValue) + { + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsTrack(); if (el != nullptr) { - duk_push_boolean(ctx, el->HasChain()); + return JS_NewBool(ctx, el->HasChain()); } else { + auto& scriptEngine = GetContext()->GetScriptEngine(); scriptEngine.LogPluginInfo("Cannot read 'hasChainLift' property, tile element is not a TrackElement."); - duk_push_null(ctx); + return JS_NULL; } - return DukValue::take_from_stack(ctx); } - void ScTileElement::hasChainLift_set(bool value) + JSValue ScTileElement::hasChainLift_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto* el = _element->AsTrack(); + JS_UNPACK_BOOL(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsTrack(); if (el == nullptr) { auto& scriptEngine = GetContext()->GetScriptEngine(); scriptEngine.LogPluginInfo("Cannot set 'hasChainLift' property, tile element is not a TrackElement."); - return; + return JS_UNDEFINED; } el->SetHasChain(value); - Invalidate(); + Invalidate(data); + return JS_UNDEFINED; } - DukValue ScTileElement::mazeEntry_get() const + JSValue ScTileElement::mazeEntry_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - try - { - auto* el = _element->AsTrack(); - if (el == nullptr) - throw DukException() << "Cannot read 'mazeEntry' property, element is not a TrackElement."; - - Ride* ride = GetRide(el->GetRideIndex()); - if (ride == nullptr) - throw DukException() << "Cannot read 'mazeEntry' property, ride is invalid."; - - const auto& rtd = ride->getRideTypeDescriptor(); - if (rtd.specialType != RtdSpecialType::maze) - throw DukException() << "Cannot read 'mazeEntry' property, ride is not a maze."; - - duk_push_int(ctx, el->GetMazeEntry()); - } - catch (const DukException& e) - { - scriptEngine.LogPluginInfo(e.what()); - duk_push_null(ctx); - } - return DukValue::take_from_stack(ctx); - } - void ScTileElement::mazeEntry_set(const DukValue& value) - { - ThrowIfGameStateNotMutable(); - - try - { - if (value.type() != DukValue::Type::NUMBER) - throw DukException() << "'mazeEntry' property must be a number."; - - auto* el = _element->AsTrack(); - if (el == nullptr) - throw DukException() << "Cannot set 'mazeEntry' property, tile element is not a TrackElement."; - - auto* ride = GetRide(el->GetRideIndex()); - if (ride == nullptr) - throw DukException() << "Cannot set 'mazeEntry' property, ride is invalid."; - - const auto& rtd = ride->getRideTypeDescriptor(); - if (rtd.specialType != RtdSpecialType::maze) - throw DukException() << "Cannot set 'mazeEntry' property, ride is not a maze."; - - el->SetMazeEntry(value.as_uint()); - Invalidate(); - } - catch (const DukException& e) + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsTrack(); + if (el == nullptr) { auto& scriptEngine = GetContext()->GetScriptEngine(); - scriptEngine.LogPluginInfo(e.what()); + scriptEngine.LogPluginInfo("Cannot read 'mazeEntry' property, element is not a TrackElement."); + return JS_NULL; } - } - DukValue ScTileElement::colourScheme_get() const - { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - try - { - auto* el = _element->AsTrack(); - if (el == nullptr) - throw DukException() << "Cannot read 'colourScheme' property, tile element is not a TrackElement."; - - auto* ride = GetRide(el->GetRideIndex()); - if (ride == nullptr) - throw DukException() << "Cannot read 'colourScheme' property, ride is invalid."; - - const auto& rtd = ride->getRideTypeDescriptor(); - if (rtd.specialType == RtdSpecialType::maze) - throw DukException() << "Cannot read 'colourScheme' property, TrackElement belongs to a maze."; - - duk_push_int(ctx, el->GetColourScheme()); - } - catch (const DukException& e) - { - scriptEngine.LogPluginInfo(e.what()); - duk_push_null(ctx); - } - return DukValue::take_from_stack(ctx); - } - void ScTileElement::colourScheme_set(const DukValue& value) - { - ThrowIfGameStateNotMutable(); - - try - { - if (value.type() != DukValue::Type::NUMBER) - throw DukException() << "'colourScheme' must be a number."; - - auto* el = _element->AsTrack(); - if (el == nullptr) - throw DukException() << "Cannot set 'colourScheme' property, tile element is not a TrackElement."; - - auto* ride = GetRide(el->GetRideIndex()); - if (ride == nullptr) - throw DukException() << "Cannot set 'colourScheme', ride is invalid."; - - const auto& rtd = ride->getRideTypeDescriptor(); - if (rtd.specialType == RtdSpecialType::maze) - throw DukException() << "Cannot set 'colourScheme' property, TrackElement belongs to a maze."; - - el->SetColourScheme(static_cast(value.as_uint())); - Invalidate(); - } - catch (const DukException& e) + Ride* ride = GetRide(el->GetRideIndex()); + if (ride == nullptr) { auto& scriptEngine = GetContext()->GetScriptEngine(); - scriptEngine.LogPluginInfo(e.what()); + scriptEngine.LogPluginInfo("Cannot read 'mazeEntry' property, ride is invalid."); + return JS_NULL; } - } - DukValue ScTileElement::seatRotation_get() const - { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - try - { - auto* el = _element->AsTrack(); - if (el == nullptr) - throw DukException() << "Cannot read 'seatRotation' property, tile element is not a TrackElement."; - - auto* ride = GetRide(el->GetRideIndex()); - if (ride == nullptr) - throw DukException() << "Cannot read 'seatRotation' property, ride is invalid."; - - const auto& rtd = ride->getRideTypeDescriptor(); - if (rtd.specialType == RtdSpecialType::maze) - throw DukException() << "Cannot read 'seatRotation' property, TrackElement belongs to a maze."; - - duk_push_int(ctx, el->GetSeatRotation()); - } - catch (const DukException& e) - { - scriptEngine.LogPluginInfo(e.what()); - duk_push_null(ctx); - } - return DukValue::take_from_stack(ctx); - } - void ScTileElement::seatRotation_set(const DukValue& value) - { - ThrowIfGameStateNotMutable(); - - try - { - if (value.type() != DukValue::Type::NUMBER) - throw DukException() << "'seatRotation' must be a number."; - - auto* el = _element->AsTrack(); - if (el == nullptr) - throw DukException() << "Cannot set 'seatRotation' property, tile element is not a TrackElement."; - - auto* ride = GetRide(el->GetRideIndex()); - if (ride == nullptr) - throw DukException() << "Cannot set 'seatRotation' property, ride is invalid."; - - const auto& rtd = ride->getRideTypeDescriptor(); - if (rtd.specialType != RtdSpecialType::maze) - throw DukException() << "Cannot set 'seatRotation' property, TrackElement belongs to a maze."; - - el->SetSeatRotation(value.as_uint()); - Invalidate(); - } - catch (const DukException& e) + const auto& rtd = ride->getRideTypeDescriptor(); + if (rtd.specialType != RtdSpecialType::maze) { auto& scriptEngine = GetContext()->GetScriptEngine(); - scriptEngine.LogPluginInfo(e.what()); + scriptEngine.LogPluginInfo("Cannot read 'mazeEntry' property, ride is not a maze."); + return JS_NULL; } + + return JS_NewUint32(ctx, el->GetMazeEntry()); } - - DukValue ScTileElement::brakeBoosterSpeed_get() const + JSValue ScTileElement::mazeEntry_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - try - { - auto* el = _element->AsTrack(); - if (el == nullptr) - throw DukException() << "Cannot read 'brakeBoosterSpeed' property, tile element is not a TrackElement."; + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); - if (!trackTypeHasSpeedSetting(el->GetTrackType())) - throw DukException() << "Cannot read 'brakeBoosterSpeed' property, track element has no speed setting."; - - duk_push_int(ctx, el->GetBrakeBoosterSpeed()); - } - catch (const DukException& e) - { - scriptEngine.LogPluginInfo(e.what()); - duk_push_null(ctx); - } - return DukValue::take_from_stack(ctx); - } - void ScTileElement::brakeBoosterSpeed_set(const DukValue& value) - { - ThrowIfGameStateNotMutable(); - - try - { - if (value.type() != DukValue::Type::NUMBER) - throw DukException() << "'brakeBoosterSpeed' must be a number."; - - auto* el = _element->AsTrack(); - if (el == nullptr) - throw DukException() << "Cannot set 'brakeBoosterSpeed' property, tile element is not a TrackElement."; - - if (!trackTypeHasSpeedSetting(el->GetTrackType())) - throw DukException() << "Cannot set 'brakeBoosterSpeed' property, track element has no speed setting."; - - el->SetBrakeBoosterSpeed(value.as_uint()); - Invalidate(); - } - catch (const DukException& e) + if (!JS_IsNumber(jsValue)) { auto& scriptEngine = GetContext()->GetScriptEngine(); - scriptEngine.LogPluginInfo(e.what()); + scriptEngine.LogPluginInfo("'mazeEntry' property must be a number."); + return JS_UNDEFINED; } + + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsTrack(); + if (el == nullptr) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("Cannot set 'mazeEntry' property, tile element is not a TrackElement."); + return JS_UNDEFINED; + } + + auto* ride = GetRide(el->GetRideIndex()); + if (ride == nullptr) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("Cannot set 'mazeEntry' property, ride is invalid."); + return JS_UNDEFINED; + } + + const auto& rtd = ride->getRideTypeDescriptor(); + if (rtd.specialType != RtdSpecialType::maze) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("Cannot set 'mazeEntry' property, ride is not a maze."); + return JS_UNDEFINED; + } + + JS_UNPACK_UINT32(value, ctx, jsValue); + el->SetMazeEntry(value); + Invalidate(data); + return JS_UNDEFINED; } - DukValue ScTileElement::isInverted_get() const + JSValue ScTileElement::colourScheme_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto* el = _element->AsTrack(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsTrack(); + if (el == nullptr) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("Cannot read 'colourScheme' property, tile element is not a TrackElement."); + return JS_NULL; + } + + auto* ride = GetRide(el->GetRideIndex()); + if (ride == nullptr) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("Cannot read 'colourScheme' property, ride is invalid."); + return JS_NULL; + } + + const auto& rtd = ride->getRideTypeDescriptor(); + if (rtd.specialType == RtdSpecialType::maze) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("Cannot read 'colourScheme' property, TrackElement belongs to a maze."); + return JS_NULL; + } + + return JS_NewUint32(ctx, el->GetColourScheme()); + } + JSValue ScTileElement::colourScheme_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) + { + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + if (!JS_IsNumber(jsValue)) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("'colourScheme' must be a number."); + return JS_UNDEFINED; + } + + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsTrack(); + if (el == nullptr) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("Cannot set 'colourScheme' property, tile element is not a TrackElement."); + return JS_UNDEFINED; + } + + auto* ride = GetRide(el->GetRideIndex()); + if (ride == nullptr) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("Cannot set 'colourScheme', ride is invalid."); + return JS_UNDEFINED; + } + + const auto& rtd = ride->getRideTypeDescriptor(); + if (rtd.specialType == RtdSpecialType::maze) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("Cannot set 'colourScheme' property, TrackElement belongs to a maze."); + return JS_UNDEFINED; + } + + JS_UNPACK_UINT32(value, ctx, jsValue); + el->SetColourScheme(static_cast(value)); + Invalidate(data); + return JS_UNDEFINED; + } + + JSValue ScTileElement::seatRotation_get(JSContext* ctx, JSValue thisValue) + { + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsTrack(); + if (el == nullptr) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("Cannot read 'seatRotation' property, tile element is not a TrackElement."); + return JS_NULL; + } + + auto* ride = GetRide(el->GetRideIndex()); + if (ride == nullptr) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("Cannot read 'seatRotation' property, ride is invalid."); + return JS_NULL; + } + + const auto& rtd = ride->getRideTypeDescriptor(); + if (rtd.specialType == RtdSpecialType::maze) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("Cannot read 'seatRotation' property, TrackElement belongs to a maze."); + return JS_NULL; + } + + return JS_NewUint32(ctx, el->GetSeatRotation()); + } + JSValue ScTileElement::seatRotation_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) + { + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + if (!JS_IsNumber(jsValue)) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("'seatRotation' must be a number."); + return JS_UNDEFINED; + } + + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsTrack(); + if (el == nullptr) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("Cannot set 'seatRotation' property, tile element is not a TrackElement."); + return JS_UNDEFINED; + } + + auto* ride = GetRide(el->GetRideIndex()); + if (ride == nullptr) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("Cannot set 'seatRotation' property, ride is invalid."); + return JS_UNDEFINED; + } + + const auto& rtd = ride->getRideTypeDescriptor(); + if (rtd.specialType != RtdSpecialType::maze) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("Cannot set 'seatRotation' property, TrackElement belongs to a maze."); + return JS_UNDEFINED; + } + + JS_UNPACK_UINT32(value, ctx, jsValue); + el->SetSeatRotation(value); + Invalidate(data); + return JS_UNDEFINED; + } + + JSValue ScTileElement::brakeBoosterSpeed_get(JSContext* ctx, JSValue thisValue) + { + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsTrack(); + if (el == nullptr) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("Cannot read 'brakeBoosterSpeed' property, tile element is not a TrackElement."); + return JS_NULL; + } + + if (!trackTypeHasSpeedSetting(el->GetTrackType())) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("Cannot read 'brakeBoosterSpeed' property, track element has no speed setting."); + return JS_NULL; + } + + return JS_NewUint32(ctx, el->GetBrakeBoosterSpeed()); + } + JSValue ScTileElement::brakeBoosterSpeed_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) + { + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + + if (!JS_IsNumber(jsValue)) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("'brakeBoosterSpeed' must be a number."); + return JS_UNDEFINED; + } + + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsTrack(); + if (el == nullptr) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("Cannot set 'brakeBoosterSpeed' property, tile element is not a TrackElement."); + return JS_UNDEFINED; + } + + if (!trackTypeHasSpeedSetting(el->GetTrackType())) + { + auto& scriptEngine = GetContext()->GetScriptEngine(); + scriptEngine.LogPluginInfo("Cannot set 'brakeBoosterSpeed' property, track element has no speed setting."); + return JS_UNDEFINED; + } + + JS_UNPACK_UINT32(value, ctx, jsValue); + el->SetBrakeBoosterSpeed(value); + Invalidate(data); + return JS_UNDEFINED; + } + + JSValue ScTileElement::isInverted_get(JSContext* ctx, JSValue thisValue) + { + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsTrack(); if (el != nullptr) { - duk_push_boolean(ctx, el->IsInverted()); + return JS_NewBool(ctx, el->IsInverted()); } else { + auto& scriptEngine = GetContext()->GetScriptEngine(); scriptEngine.LogPluginInfo("Cannot read 'isInverted' property, tile element is not a TrackElement."); - duk_push_null(ctx); + return JS_NULL; } - return DukValue::take_from_stack(ctx); } - void ScTileElement::isInverted_set(bool value) + JSValue ScTileElement::isInverted_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto* el = _element->AsTrack(); + JS_UNPACK_BOOL(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsTrack(); if (el == nullptr) { auto& scriptEngine = GetContext()->GetScriptEngine(); scriptEngine.LogPluginInfo("Cannot set 'isInverted' property, tile element is not a TrackElement."); - return; + return JS_UNDEFINED; } el->SetInverted(value); - Invalidate(); + Invalidate(data); + return JS_UNDEFINED; } - DukValue ScTileElement::hasCableLift_get() const + JSValue ScTileElement::hasCableLift_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto* el = _element->AsTrack(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsTrack(); if (el != nullptr) { - duk_push_boolean(ctx, el->HasCableLift()); + return JS_NewBool(ctx, el->HasCableLift()); } else { + auto& scriptEngine = GetContext()->GetScriptEngine(); scriptEngine.LogPluginInfo("Cannot read 'hasCableLift' property, tile element is not a TrackElement."); - duk_push_null(ctx); + return JS_NULL; } - return DukValue::take_from_stack(ctx); } - void ScTileElement::hasCableLift_set(bool value) + JSValue ScTileElement::hasCableLift_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto* el = _element->AsTrack(); + JS_UNPACK_BOOL(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsTrack(); if (el == nullptr) { auto& scriptEngine = GetContext()->GetScriptEngine(); scriptEngine.LogPluginInfo("Cannot set 'hasCableLift' property, tile element is not a TrackElement."); - return; + return JS_UNDEFINED; } el->SetHasCableLift(value); - Invalidate(); + Invalidate(data); + return JS_UNDEFINED; } - DukValue ScTileElement::isHighlighted_get() const + JSValue ScTileElement::isHighlighted_get(JSContext* ctx, JSValue thisValue) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - auto el = _element->AsTrack(); + auto data = gScTileElement.GetOpaque(thisValue); + auto el = data->element->AsTrack(); if (el != nullptr) - duk_push_boolean(ctx, el->IsHighlighted()); + return JS_NewBool(ctx, el->IsHighlighted()); else - duk_push_null(ctx); - return DukValue::take_from_stack(ctx); + return JS_NULL; } - void ScTileElement::isHighlighted_set(bool value) + JSValue ScTileElement::isHighlighted_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto el = _element->AsTrack(); + JS_UNPACK_BOOL(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto el = data->element->AsTrack(); if (el != nullptr) { el->SetHighlight(value); - Invalidate(); + Invalidate(data); } + return JS_UNDEFINED; } - DukValue ScTileElement::object_get() const + JSValue ScTileElement::object_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - switch (_element->GetType()) + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; + switch (element->GetType()) { case TileElementType::Path: { - auto* el = _element->AsPath(); + auto* el = element->AsPath(); auto index = el->GetLegacyPathEntryIndex(); if (index != kObjectEntryIndexNull) - duk_push_int(ctx, index); - else - duk_push_null(ctx); - break; + return JS_NewUint32(ctx, index); + + return JS_NULL; } case TileElementType::SmallScenery: { - auto* el = _element->AsSmallScenery(); - duk_push_int(ctx, el->GetEntryIndex()); - break; + auto* el = element->AsSmallScenery(); + return JS_NewUint32(ctx, el->GetEntryIndex()); } case TileElementType::LargeScenery: { - auto* el = _element->AsLargeScenery(); - duk_push_int(ctx, el->GetEntryIndex()); - break; + auto* el = element->AsLargeScenery(); + return JS_NewUint32(ctx, el->GetEntryIndex()); } case TileElementType::Wall: { - auto* el = _element->AsWall(); - duk_push_int(ctx, el->GetEntryIndex()); - break; + auto* el = element->AsWall(); + return JS_NewUint32(ctx, el->GetEntryIndex()); } case TileElementType::Entrance: { - auto* el = _element->AsEntrance(); - duk_push_int(ctx, el->GetEntranceType()); - break; + auto* el = element->AsEntrance(); + return JS_NewUint32(ctx, el->GetEntranceType()); } case TileElementType::Banner: { - auto* el = _element->AsBanner(); - duk_push_int(ctx, el->GetBanner()->type); - break; + auto* el = element->AsBanner(); + return JS_NewUint32(ctx, el->GetBanner()->type); } default: - { - duk_push_null(ctx); - break; - } + return JS_NULL; } - return DukValue::take_from_stack(ctx); } - void ScTileElement::object_set(const DukValue& value) + JSValue ScTileElement::object_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; - auto index = FromDuk(value); - switch (_element->GetType()) + switch (element->GetType()) { case TileElementType::Path: { - if (value.type() == DukValue::Type::NUMBER) + if (JS_IsNumber(jsValue)) { - auto* el = _element->AsPath(); + JS_UNPACK_UINT32(index, ctx, jsValue); + auto* el = element->AsPath(); el->SetLegacyPathEntryIndex(index); - Invalidate(); + Invalidate(data); } break; } case TileElementType::SmallScenery: { - auto* el = _element->AsSmallScenery(); + JS_UNPACK_UINT32(index, ctx, jsValue); + auto* el = element->AsSmallScenery(); el->SetEntryIndex(index); - Invalidate(); + Invalidate(data); break; } case TileElementType::LargeScenery: { - RemoveBannerEntryIfNeeded(); - auto* el = _element->AsLargeScenery(); + JS_UNPACK_UINT32(index, ctx, jsValue); + RemoveBannerEntryIfNeeded(element, data->coords); + auto* el = element->AsLargeScenery(); el->SetEntryIndex(index); - CreateBannerEntryIfNeeded(); - Invalidate(); + CreateBannerEntryIfNeeded(element, data->coords); + Invalidate(data); break; } case TileElementType::Wall: { - RemoveBannerEntryIfNeeded(); - auto* el = _element->AsWall(); + JS_UNPACK_UINT32(index, ctx, jsValue); + RemoveBannerEntryIfNeeded(element, data->coords); + auto* el = element->AsWall(); el->SetEntryIndex(index); - CreateBannerEntryIfNeeded(); - Invalidate(); + CreateBannerEntryIfNeeded(element, data->coords); + Invalidate(data); break; } case TileElementType::Entrance: { - auto* el = _element->AsEntrance(); + JS_UNPACK_UINT32(index, ctx, jsValue); + auto* el = element->AsEntrance(); el->SetEntranceType(index); - Invalidate(); + Invalidate(data); break; } case TileElementType::Banner: { - auto* el = _element->AsBanner(); + JS_UNPACK_UINT32(index, ctx, jsValue); + auto* el = element->AsBanner(); el->GetBanner()->type = index; - Invalidate(); + Invalidate(data); break; } default: break; } + return JS_UNDEFINED; } - bool ScTileElement::isHidden_get() const + JSValue ScTileElement::isHidden_get(JSContext* ctx, JSValue thisValue) { - return _element->IsInvisible(); + auto data = gScTileElement.GetOpaque(thisValue); + return JS_NewBool(ctx, data->element->IsInvisible()); } - void ScTileElement::isHidden_set(bool hide) + JSValue ScTileElement::isHidden_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - _element->SetInvisible(hide); - Invalidate(); + JS_UNPACK_BOOL(hide, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + data->element->SetInvisible(hide); + Invalidate(data); + return JS_UNDEFINED; } - DukValue ScTileElement::age_get() const + JSValue ScTileElement::age_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto* el = _element->AsSmallScenery(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsSmallScenery(); if (el != nullptr) - duk_push_int(ctx, el->GetAge()); + return JS_NewUint32(ctx, el->GetAge()); else - duk_push_null(ctx); - return DukValue::take_from_stack(ctx); + return JS_NULL; } - void ScTileElement::age_set(uint8_t value) + JSValue ScTileElement::age_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto* el = _element->AsSmallScenery(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsSmallScenery(); if (el != nullptr) { el->SetAge(value); - Invalidate(); + Invalidate(data); } + return JS_UNDEFINED; } - DukValue ScTileElement::quadrant_get() const + JSValue ScTileElement::quadrant_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto* el = _element->AsSmallScenery(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsSmallScenery(); if (el != nullptr) - duk_push_int(ctx, el->GetSceneryQuadrant()); + return JS_NewUint32(ctx, el->GetSceneryQuadrant()); else - duk_push_null(ctx); - return DukValue::take_from_stack(ctx); + return JS_NULL; } - void ScTileElement::quadrant_set(uint8_t value) + JSValue ScTileElement::quadrant_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto* el = _element->AsSmallScenery(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsSmallScenery(); if (el != nullptr) { el->SetSceneryQuadrant(value); - Invalidate(); + Invalidate(data); } + return JS_UNDEFINED; } - uint8_t ScTileElement::occupiedQuadrants_get() const + JSValue ScTileElement::occupiedQuadrants_get(JSContext* ctx, JSValue thisValue) { - return _element->GetOccupiedQuadrants(); + auto data = gScTileElement.GetOpaque(thisValue); + return JS_NewUint32(ctx, data->element->GetOccupiedQuadrants()); } - void ScTileElement::occupiedQuadrants_set(uint8_t value) + JSValue ScTileElement::occupiedQuadrants_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - _element->SetOccupiedQuadrants(value); - Invalidate(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + data->element->SetOccupiedQuadrants(value); + Invalidate(data); + return JS_UNDEFINED; } - bool ScTileElement::isGhost_get() const + JSValue ScTileElement::isGhost_get(JSContext* ctx, JSValue thisValue) { - return _element->IsGhost(); + auto data = gScTileElement.GetOpaque(thisValue); + return JS_NewBool(ctx, data->element->IsGhost()); } - void ScTileElement::isGhost_set(bool value) + JSValue ScTileElement::isGhost_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - _element->SetGhost(value); - Invalidate(); + JS_UNPACK_BOOL(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + data->element->SetGhost(value); + Invalidate(data); + return JS_UNDEFINED; } - DukValue ScTileElement::primaryColour_get() const + JSValue ScTileElement::primaryColour_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - switch (_element->GetType()) + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; + switch (element->GetType()) { case TileElementType::SmallScenery: { - auto* el = _element->AsSmallScenery(); - duk_push_int(ctx, EnumValue(el->GetPrimaryColour())); - break; + auto* el = element->AsSmallScenery(); + return JS_NewUint32(ctx, EnumValue(el->GetPrimaryColour())); } case TileElementType::LargeScenery: { - auto* el = _element->AsLargeScenery(); - duk_push_int(ctx, EnumValue(el->GetPrimaryColour())); - break; + auto* el = element->AsLargeScenery(); + return JS_NewUint32(ctx, EnumValue(el->GetPrimaryColour())); } case TileElementType::Wall: { - auto* el = _element->AsWall(); - duk_push_int(ctx, EnumValue(el->GetPrimaryColour())); - break; + auto* el = element->AsWall(); + return JS_NewUint32(ctx, EnumValue(el->GetPrimaryColour())); } case TileElementType::Banner: { - auto* el = _element->AsBanner(); - duk_push_int(ctx, EnumValue(el->GetBanner()->colour)); - break; + auto* el = element->AsBanner(); + return JS_NewUint32(ctx, EnumValue(el->GetBanner()->colour)); } default: - { - duk_push_null(ctx); - break; - } + return JS_NULL; } - return DukValue::take_from_stack(ctx); } - void ScTileElement::primaryColour_set(uint8_t value) + JSValue ScTileElement::primaryColour_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - switch (_element->GetType()) + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; + switch (element->GetType()) { case TileElementType::SmallScenery: { - auto* el = _element->AsSmallScenery(); + auto* el = element->AsSmallScenery(); el->SetPrimaryColour(static_cast(value)); - Invalidate(); + Invalidate(data); break; } case TileElementType::LargeScenery: { - auto* el = _element->AsLargeScenery(); + auto* el = element->AsLargeScenery(); el->SetPrimaryColour(static_cast(value)); - Invalidate(); + Invalidate(data); break; } case TileElementType::Wall: { - auto* el = _element->AsWall(); + auto* el = element->AsWall(); el->SetPrimaryColour(static_cast(value)); - Invalidate(); + Invalidate(data); break; } case TileElementType::Banner: { - auto* el = _element->AsBanner(); + auto* el = element->AsBanner(); el->GetBanner()->colour = static_cast(value); - Invalidate(); + Invalidate(data); break; } default: break; } + return JS_UNDEFINED; } - DukValue ScTileElement::secondaryColour_get() const + JSValue ScTileElement::secondaryColour_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - switch (_element->GetType()) + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; + switch (element->GetType()) { case TileElementType::SmallScenery: { - auto* el = _element->AsSmallScenery(); - duk_push_int(ctx, EnumValue(el->GetSecondaryColour())); - break; + auto* el = element->AsSmallScenery(); + return JS_NewUint32(ctx, EnumValue(el->GetSecondaryColour())); } case TileElementType::LargeScenery: { - auto* el = _element->AsLargeScenery(); - duk_push_int(ctx, EnumValue(el->GetSecondaryColour())); - break; + auto* el = element->AsLargeScenery(); + return JS_NewUint32(ctx, EnumValue(el->GetSecondaryColour())); } case TileElementType::Wall: { - auto* el = _element->AsWall(); - duk_push_int(ctx, EnumValue(el->GetSecondaryColour())); - break; + auto* el = element->AsWall(); + return JS_NewUint32(ctx, EnumValue(el->GetSecondaryColour())); } case TileElementType::Banner: { - auto* el = _element->AsBanner(); - duk_push_int(ctx, EnumValue(el->GetBanner()->textColour)); - break; + auto* el = element->AsBanner(); + return JS_NewUint32(ctx, EnumValue(el->GetBanner()->textColour)); } default: - { - duk_push_null(ctx); - break; - } + return JS_NULL; } - return DukValue::take_from_stack(ctx); } - void ScTileElement::secondaryColour_set(uint8_t value) + JSValue ScTileElement::secondaryColour_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - switch (_element->GetType()) + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; + switch (element->GetType()) { case TileElementType::SmallScenery: { - auto* el = _element->AsSmallScenery(); + auto* el = element->AsSmallScenery(); el->SetSecondaryColour(static_cast(value)); - Invalidate(); + Invalidate(data); break; } case TileElementType::LargeScenery: { - auto* el = _element->AsLargeScenery(); + auto* el = element->AsLargeScenery(); el->SetSecondaryColour(static_cast(value)); - Invalidate(); + Invalidate(data); break; } case TileElementType::Wall: { - auto* el = _element->AsWall(); + auto* el = element->AsWall(); el->SetSecondaryColour(static_cast(value)); - Invalidate(); + Invalidate(data); break; } case TileElementType::Banner: { - auto* el = _element->AsBanner(); + auto* el = element->AsBanner(); el->GetBanner()->textColour = static_cast(value); - Invalidate(); + Invalidate(data); break; } default: break; } + return JS_UNDEFINED; } - DukValue ScTileElement::tertiaryColour_get() const + JSValue ScTileElement::tertiaryColour_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - switch (_element->GetType()) + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; + switch (element->GetType()) { case TileElementType::SmallScenery: { - auto* el = _element->AsSmallScenery(); - duk_push_int(ctx, EnumValue(el->GetTertiaryColour())); + auto* el = element->AsSmallScenery(); + return JS_NewUint32(ctx, EnumValue(el->GetTertiaryColour())); + } + case TileElementType::LargeScenery: + { + auto* el = element->AsLargeScenery(); + return JS_NewUint32(ctx, EnumValue(el->GetTertiaryColour())); + } + case TileElementType::Wall: + { + auto* el = element->AsWall(); + return JS_NewUint32(ctx, EnumValue(el->GetTertiaryColour())); + } + default: + return JS_NULL; + } + } + JSValue ScTileElement::tertiaryColour_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) + { + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; + switch (element->GetType()) + { + case TileElementType::SmallScenery: + { + auto* el = element->AsSmallScenery(); + el->SetTertiaryColour(static_cast(value)); + Invalidate(data); break; } case TileElementType::LargeScenery: { - auto* el = _element->AsLargeScenery(); - duk_push_int(ctx, EnumValue(el->GetTertiaryColour())); + auto* el = element->AsLargeScenery(); + el->SetTertiaryColour(static_cast(value)); + Invalidate(data); break; } case TileElementType::Wall: { - auto* el = _element->AsWall(); - duk_push_int(ctx, EnumValue(el->GetTertiaryColour())); - break; - } - default: - { - duk_push_null(ctx); - break; - } - } - return DukValue::take_from_stack(ctx); - } - void ScTileElement::tertiaryColour_set(uint8_t value) - { - ThrowIfGameStateNotMutable(); - switch (_element->GetType()) - { - case TileElementType::SmallScenery: - { - auto* el = _element->AsSmallScenery(); + auto* el = element->AsWall(); el->SetTertiaryColour(static_cast(value)); - Invalidate(); - break; - } - case TileElementType::LargeScenery: - { - auto* el = _element->AsLargeScenery(); - el->SetTertiaryColour(static_cast(value)); - Invalidate(); - break; - } - case TileElementType::Wall: - { - auto* el = _element->AsWall(); - el->SetTertiaryColour(static_cast(value)); - Invalidate(); + Invalidate(data); break; } default: break; } + return JS_UNDEFINED; } - DukValue ScTileElement::bannerIndex_get() const + JSValue ScTileElement::bannerIndex_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - BannerIndex idx = _element->GetBannerIndex(); + auto data = gScTileElement.GetOpaque(thisValue); + BannerIndex idx = data->element->GetBannerIndex(); if (idx == BannerIndex::GetNull()) - duk_push_null(ctx); + return JS_NULL; else - duk_push_int(ctx, idx.ToUnderlying()); - return DukValue::take_from_stack(ctx); + return JS_NewUint32(ctx, idx.ToUnderlying()); } - void ScTileElement::bannerIndex_set(const DukValue& value) + JSValue ScTileElement::bannerIndex_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - switch (_element->GetType()) + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; + switch (element->GetType()) { case TileElementType::LargeScenery: { - auto* el = _element->AsLargeScenery(); - if (value.type() == DukValue::Type::NUMBER) - el->SetBannerIndex(BannerIndex::FromUnderlying(value.as_uint())); + auto* el = element->AsLargeScenery(); + if (JS_IsNumber(jsValue)) + { + JS_UNPACK_UINT32(value, ctx, jsValue); + el->SetBannerIndex(BannerIndex::FromUnderlying(value)); + } else el->SetBannerIndex(BannerIndex::GetNull()); - Invalidate(); + Invalidate(data); break; } case TileElementType::Wall: { - auto* el = _element->AsWall(); - if (value.type() == DukValue::Type::NUMBER) - el->SetBannerIndex(BannerIndex::FromUnderlying(value.as_uint())); + auto* el = element->AsWall(); + if (JS_IsNumber(jsValue)) + { + JS_UNPACK_UINT32(value, ctx, jsValue); + el->SetBannerIndex(BannerIndex::FromUnderlying(value)); + } else el->SetBannerIndex(BannerIndex::GetNull()); - Invalidate(); + Invalidate(data); break; } case TileElementType::Banner: { - auto* el = _element->AsBanner(); - if (value.type() == DukValue::Type::NUMBER) - el->SetIndex(BannerIndex::FromUnderlying(value.as_uint())); + auto* el = element->AsBanner(); + if (JS_IsNumber(jsValue)) + { + JS_UNPACK_UINT32(value, ctx, jsValue); + el->SetIndex(BannerIndex::FromUnderlying(value)); + } else el->SetIndex(BannerIndex::GetNull()); - Invalidate(); + Invalidate(data); break; } default: break; } + return JS_UNDEFINED; } // Deprecated in favor of separate 'edges' and 'corners' properties, // left here to maintain compatibility with older plugins. - /** @deprecated */ - uint8_t ScTileElement::edgesAndCorners_get() const + JSValue ScTileElement::edgesAndCorners_get(JSContext* ctx, JSValue thisValue) { - auto* el = _element->AsPath(); - return el != nullptr ? el->GetEdgesAndCorners() : 0; + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsPath(); + return JS_NewUint32(ctx, el != nullptr ? el->GetEdgesAndCorners() : 0); } - /** @deprecated */ - void ScTileElement::edgesAndCorners_set(uint8_t value) + JSValue ScTileElement::edgesAndCorners_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto* el = _element->AsPath(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsPath(); if (el != nullptr) { el->SetEdgesAndCorners(value); - Invalidate(); + Invalidate(data); } + return JS_UNDEFINED; } - DukValue ScTileElement::edges_get() const + JSValue ScTileElement::edges_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto* el = _element->AsPath(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsPath(); if (el != nullptr) - duk_push_int(ctx, el->GetEdges()); + return JS_NewUint32(ctx, el->GetEdges()); else - duk_push_null(ctx); - return DukValue::take_from_stack(ctx); + return JS_NULL; } - void ScTileElement::edges_set(uint8_t value) + JSValue ScTileElement::edges_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto* el = _element->AsPath(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsPath(); if (el != nullptr) { el->SetEdges(value); - Invalidate(); + Invalidate(data); } + return JS_UNDEFINED; } - DukValue ScTileElement::corners_get() const + JSValue ScTileElement::corners_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto* el = _element->AsPath(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsPath(); if (el != nullptr) - duk_push_int(ctx, el->GetCorners()); + return JS_NewUint32(ctx, el->GetCorners()); else - duk_push_null(ctx); - return DukValue::take_from_stack(ctx); + return JS_NULL; } - void ScTileElement::corners_set(uint8_t value) + JSValue ScTileElement::corners_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto* el = _element->AsPath(); + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsPath(); if (el != nullptr) { el->SetCorners(value); - Invalidate(); + Invalidate(data); } + return JS_UNDEFINED; } - DukValue ScTileElement::slopeDirection_get() const + JSValue ScTileElement::slopeDirection_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto* el = _element->AsPath(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsPath(); if (el != nullptr && el->IsSloped()) - duk_push_int(ctx, el->GetSlopeDirection()); + return JS_NewUint32(ctx, el->GetSlopeDirection()); else - duk_push_null(ctx); - return DukValue::take_from_stack(ctx); + return JS_NULL; } - void ScTileElement::slopeDirection_set(const DukValue& value) + JSValue ScTileElement::slopeDirection_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto* el = _element->AsPath(); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsPath(); if (el != nullptr) { - if (value.type() == DukValue::Type::NUMBER) + if (JS_IsNumber(jsValue)) { + JS_UNPACK_UINT32(value, ctx, jsValue); el->SetSloped(true); - el->SetSlopeDirection(value.as_uint()); + el->SetSlopeDirection(value); } else { el->SetSloped(false); el->SetSlopeDirection(0); } - Invalidate(); + Invalidate(data); } + return JS_UNDEFINED; } - DukValue ScTileElement::isQueue_get() const + JSValue ScTileElement::isQueue_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto* el = _element->AsPath(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsPath(); if (el != nullptr) - duk_push_boolean(ctx, el->IsQueue()); + return JS_NewBool(ctx, el->IsQueue()); else - duk_push_null(ctx); - return DukValue::take_from_stack(ctx); + return JS_NULL; } - void ScTileElement::isQueue_set(bool value) + JSValue ScTileElement::isQueue_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto* el = _element->AsPath(); + JS_UNPACK_BOOL(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsPath(); if (el != nullptr) { el->SetIsQueue(value); - Invalidate(); + Invalidate(data); } + return JS_UNDEFINED; } - DukValue ScTileElement::queueBannerDirection_get() const + JSValue ScTileElement::queueBannerDirection_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto* el = _element->AsPath(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsPath(); if (el != nullptr && el->HasQueueBanner()) - duk_push_int(ctx, el->GetQueueBannerDirection()); + return JS_NewUint32(ctx, el->GetQueueBannerDirection()); else - duk_push_null(ctx); - return DukValue::take_from_stack(ctx); + return JS_NULL; } - void ScTileElement::queueBannerDirection_set(const DukValue& value) + JSValue ScTileElement::queueBannerDirection_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto* el = _element->AsPath(); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsPath(); if (el != nullptr) { - if (value.type() == DukValue::Type::NUMBER) + if (JS_IsNumber(jsValue)) { + JS_UNPACK_UINT32(value, ctx, jsValue); el->SetHasQueueBanner(true); - el->SetQueueBannerDirection(value.as_uint()); + el->SetQueueBannerDirection(value); } else { el->SetHasQueueBanner(false); el->SetQueueBannerDirection(0); } - Invalidate(); + Invalidate(data); } + return JS_UNDEFINED; } - DukValue ScTileElement::isBlockedByVehicle_get() const + JSValue ScTileElement::isBlockedByVehicle_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto* el = _element->AsPath(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsPath(); if (el != nullptr) - duk_push_boolean(ctx, el->IsBlockedByVehicle()); + return JS_NewBool(ctx, el->IsBlockedByVehicle()); else - duk_push_null(ctx); - return DukValue::take_from_stack(ctx); + return JS_NULL; } - void ScTileElement::isBlockedByVehicle_set(bool value) + JSValue ScTileElement::isBlockedByVehicle_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto* el = _element->AsPath(); + JS_UNPACK_BOOL(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsPath(); if (el != nullptr) { el->SetIsBlockedByVehicle(value); - Invalidate(); + Invalidate(data); } + return JS_UNDEFINED; } - DukValue ScTileElement::isWide_get() const + JSValue ScTileElement::isWide_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto* el = _element->AsPath(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsPath(); if (el != nullptr) - duk_push_boolean(ctx, el->IsWide()); + return JS_NewBool(ctx, el->IsWide()); else - duk_push_null(ctx); - return DukValue::take_from_stack(ctx); + return JS_NULL; } - void ScTileElement::isWide_set(bool value) + JSValue ScTileElement::isWide_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto* el = _element->AsPath(); + JS_UNPACK_BOOL(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsPath(); if (el != nullptr) { el->SetWide(value); - Invalidate(); + Invalidate(data); } + return JS_UNDEFINED; } - DukValue ScTileElement::surfaceObject_get() const + JSValue ScTileElement::surfaceObject_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - if (_element->GetType() == TileElementType::Path) + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; + if (element->GetType() == TileElementType::Path) { - auto* el = _element->AsPath(); + auto* el = element->AsPath(); auto index = el->GetSurfaceEntryIndex(); if (index != kObjectEntryIndexNull) { - duk_push_int(ctx, index); - } - else - { - duk_push_null(ctx); + return JS_NewUint32(ctx, index); } } - else - { - duk_push_null(ctx); - } - return DukValue::take_from_stack(ctx); + return JS_NULL; } - void ScTileElement::surfaceObject_set(const DukValue& value) + JSValue ScTileElement::surfaceObject_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - if (value.type() == DukValue::Type::NUMBER) + if (JS_IsNumber(jsValue)) { - ThrowIfGameStateNotMutable(); - if (_element->GetType() == TileElementType::Path) + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; + if (element->GetType() == TileElementType::Path) { - auto* el = _element->AsPath(); - el->SetSurfaceEntryIndex(FromDuk(value)); - Invalidate(); + JS_UNPACK_UINT32(value, ctx, jsValue); + auto* el = element->AsPath(); + el->SetSurfaceEntryIndex(value); + Invalidate(data); } } + return JS_UNDEFINED; } - DukValue ScTileElement::railingsObject_get() const + JSValue ScTileElement::railingsObject_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - if (_element->GetType() == TileElementType::Path) + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; + if (element->GetType() == TileElementType::Path) { - auto* el = _element->AsPath(); + auto* el = element->AsPath(); auto index = el->GetRailingsEntryIndex(); if (index != kObjectEntryIndexNull) { - duk_push_int(ctx, index); - } - else - { - duk_push_null(ctx); + return JS_NewUint32(ctx, index); } } - else - { - duk_push_null(ctx); - } - return DukValue::take_from_stack(ctx); + return JS_NULL; } - void ScTileElement::railingsObject_set(const DukValue& value) + JSValue ScTileElement::railingsObject_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - if (value.type() == DukValue::Type::NUMBER) + if (JS_IsNumber(jsValue)) { - ThrowIfGameStateNotMutable(); - if (_element->GetType() == TileElementType::Path) + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; + if (element->GetType() == TileElementType::Path) { - auto* el = _element->AsPath(); - el->SetRailingsEntryIndex(FromDuk(value)); - Invalidate(); + JS_UNPACK_UINT32(value, ctx, jsValue); + auto* el = element->AsPath(); + el->SetRailingsEntryIndex(value); + Invalidate(data); } } + return JS_UNDEFINED; } - DukValue ScTileElement::addition_get() const + JSValue ScTileElement::addition_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto* el = _element->AsPath(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsPath(); if (el != nullptr && el->HasAddition()) - duk_push_int(ctx, el->GetAdditionEntryIndex()); + return JS_NewUint32(ctx, el->GetAdditionEntryIndex()); else - duk_push_null(ctx); - return DukValue::take_from_stack(ctx); + return JS_NULL; } - void ScTileElement::addition_set(const DukValue& value) + JSValue ScTileElement::addition_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto* el = _element->AsPath(); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsPath(); if (el != nullptr) { - if (value.type() == DukValue::Type::NUMBER) + if (JS_IsNumber(jsValue)) { - auto addition = value.as_uint(); + JS_UNPACK_UINT32(addition, ctx, jsValue); if (addition <= 254) { el->SetAdditionEntryIndex(addition); @@ -1882,197 +2030,190 @@ namespace OpenRCT2::Scripting { el->SetAddition(0); } - Invalidate(); + Invalidate(data); } + return JS_UNDEFINED; } - DukValue ScTileElement::additionStatus_get() const + JSValue ScTileElement::additionStatus_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto* el = _element->AsPath(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsPath(); if (el != nullptr && el->HasAddition() && !el->IsQueue()) - duk_push_int(ctx, el->GetAdditionStatus()); + return JS_NewUint32(ctx, el->GetAdditionStatus()); else - duk_push_null(ctx); - return DukValue::take_from_stack(ctx); + return JS_NULL; } - void ScTileElement::additionStatus_set(const DukValue& value) + JSValue ScTileElement::additionStatus_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - if (value.type() == DukValue::Type::NUMBER) + if (JS_IsNumber(jsValue)) { - ThrowIfGameStateNotMutable(); - auto* el = _element->AsPath(); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsPath(); if (el != nullptr) + { if (el->HasAddition() && !el->IsQueue()) { - el->SetAdditionStatus(value.as_uint()); - Invalidate(); + JS_UNPACK_UINT32(value, ctx, jsValue); + el->SetAdditionStatus(value); + Invalidate(data); } - } - } - - DukValue ScTileElement::isAdditionBroken_get() const - { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto* el = _element->AsPath(); - if (el != nullptr && el->HasAddition()) - duk_push_boolean(ctx, el->IsBroken()); - else - duk_push_null(ctx); - return DukValue::take_from_stack(ctx); - } - void ScTileElement::isAdditionBroken_set(const DukValue& value) - { - if (value.type() == DukValue::Type::BOOLEAN) - { - ThrowIfGameStateNotMutable(); - auto* el = _element->AsPath(); - if (el != nullptr) - { - el->SetIsBroken(value.as_bool()); - Invalidate(); } } + return JS_UNDEFINED; } - DukValue ScTileElement::isAdditionGhost_get() const + JSValue ScTileElement::isAdditionBroken_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto* el = _element->AsPath(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsPath(); if (el != nullptr && el->HasAddition()) - duk_push_boolean(ctx, el->AdditionIsGhost()); + return JS_NewBool(ctx, el->IsBroken()); else - duk_push_null(ctx); - return DukValue::take_from_stack(ctx); + return JS_NULL; } - void ScTileElement::isAdditionGhost_set(const DukValue& value) + JSValue ScTileElement::isAdditionBroken_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - if (value.type() == DukValue::Type::BOOLEAN) + if (JS_IsBool(jsValue)) { - ThrowIfGameStateNotMutable(); - auto* el = _element->AsPath(); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsPath(); if (el != nullptr) { - el->SetAdditionIsGhost(value.as_bool()); - Invalidate(); + JS_UNPACK_BOOL(value, ctx, jsValue); + el->SetIsBroken(value); + Invalidate(data); } } + return JS_UNDEFINED; } - DukValue ScTileElement::footpathObject_get() const + JSValue ScTileElement::isAdditionGhost_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto* el = _element->AsEntrance(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsPath(); + if (el != nullptr && el->HasAddition()) + return JS_NewBool(ctx, el->AdditionIsGhost()); + else + return JS_NULL; + } + JSValue ScTileElement::isAdditionGhost_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) + { + if (JS_IsBool(jsValue)) + { + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsPath(); + if (el != nullptr) + { + JS_UNPACK_BOOL(value, ctx, jsValue); + el->SetAdditionIsGhost(value); + Invalidate(data); + } + } + return JS_UNDEFINED; + } + + JSValue ScTileElement::footpathObject_get(JSContext* ctx, JSValue thisValue) + { + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsEntrance(); if (el != nullptr) { auto index = el->GetLegacyPathEntryIndex(); if (index != kObjectEntryIndexNull) { - duk_push_int(ctx, index); - } - else - { - duk_push_null(ctx); + return JS_NewUint32(ctx, index); } } - else - { - duk_push_null(ctx); - } - return DukValue::take_from_stack(ctx); + return JS_NULL; } - void ScTileElement::footpathObject_set(const DukValue& value) + JSValue ScTileElement::footpathObject_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - if (value.type() == DukValue::Type::NUMBER) + if (JS_IsNumber(jsValue)) { - ThrowIfGameStateNotMutable(); - auto* el = _element->AsEntrance(); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsEntrance(); if (el != nullptr) { - el->SetLegacyPathEntryIndex(FromDuk(value)); - Invalidate(); + JS_UNPACK_UINT32(value, ctx, jsValue); + el->SetLegacyPathEntryIndex(value); + Invalidate(data); } } + return JS_UNDEFINED; } - DukValue ScTileElement::footpathSurfaceObject_get() const + JSValue ScTileElement::footpathSurfaceObject_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto* el = _element->AsEntrance(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsEntrance(); if (el != nullptr) { auto index = el->GetSurfaceEntryIndex(); if (index != kObjectEntryIndexNull) { - duk_push_int(ctx, index); - } - else - { - duk_push_null(ctx); + return JS_NewUint32(ctx, index); } } - else - { - duk_push_null(ctx); - } - return DukValue::take_from_stack(ctx); + return JS_NULL; } - void ScTileElement::footpathSurfaceObject_set(const DukValue& value) + JSValue ScTileElement::footpathSurfaceObject_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - if (value.type() == DukValue::Type::NUMBER) + if (JS_IsNumber(jsValue)) { - ThrowIfGameStateNotMutable(); - auto* el = _element->AsEntrance(); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsEntrance(); if (el != nullptr) { - el->SetSurfaceEntryIndex(FromDuk(value)); - Invalidate(); + JS_UNPACK_UINT32(value, ctx, jsValue); + el->SetSurfaceEntryIndex(value); + Invalidate(data); } } + return JS_UNDEFINED; } - DukValue ScTileElement::direction_get() const + JSValue ScTileElement::direction_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - switch (_element->GetType()) + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; + switch (element->GetType()) { case TileElementType::Banner: { - auto* el = _element->AsBanner(); - duk_push_int(ctx, el->GetPosition()); - break; + auto* el = element->AsBanner(); + return JS_NewUint32(ctx, el->GetPosition()); } case TileElementType::Path: case TileElementType::Surface: { - duk_push_null(ctx); - break; + return JS_NULL; } default: { - duk_push_int(ctx, _element->GetDirection()); - break; + return JS_NewUint32(ctx, element->GetDirection()); } } - return DukValue::take_from_stack(ctx); } - void ScTileElement::direction_set(uint8_t value) + JSValue ScTileElement::direction_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - switch (_element->GetType()) + JS_UNPACK_UINT32(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; + switch (element->GetType()) { case TileElementType::Banner: { - auto* el = _element->AsBanner(); + auto* el = element->AsBanner(); el->SetPosition(value); - Invalidate(); + Invalidate(data); break; } case TileElementType::Path: @@ -2082,32 +2223,34 @@ namespace OpenRCT2::Scripting } default: { - _element->SetDirection(value); - Invalidate(); + element->SetDirection(value); + Invalidate(data); } } + return JS_UNDEFINED; } - DukValue ScTileElement::bannerText_get() const + JSValue ScTileElement::bannerText_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - BannerIndex idx = _element->GetBannerIndex(); + auto data = gScTileElement.GetOpaque(thisValue); + BannerIndex idx = data->element->GetBannerIndex(); if (idx == BannerIndex::GetNull()) - duk_push_null(ctx); + return JS_NULL; else - duk_push_string(ctx, GetBanner(idx)->getText().c_str()); - return DukValue::take_from_stack(ctx); + return JSFromStdString(ctx, GetBanner(idx)->getText()); } - void ScTileElement::bannerText_set(std::string value) + JSValue ScTileElement::bannerText_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - BannerIndex idx = _element->GetBannerIndex(); + JS_UNPACK_STR(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto element = data->element; + BannerIndex idx = element->GetBannerIndex(); if (idx != BannerIndex::GetNull()) { auto banner = GetBanner(idx); banner->text = value; - if (_element->GetType() != TileElementType::Banner) + if (element->GetType() != TileElementType::Banner) { if (value.empty()) banner->rideIndex = BannerGetClosestRideIndex({ banner->position.ToCoordsXY(), 16 }); @@ -2117,33 +2260,30 @@ namespace OpenRCT2::Scripting banner->flags.set(BannerFlag::linkedToRide, !banner->rideIndex.IsNull()); } } + return JS_UNDEFINED; } - DukValue ScTileElement::isNoEntry_get() const + JSValue ScTileElement::isNoEntry_get(JSContext* ctx, JSValue thisValue) { - auto& scriptEngine = GetContext()->GetScriptEngine(); - auto* ctx = scriptEngine.GetContext(); - auto* el = _element->AsBanner(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsBanner(); if (el != nullptr) - duk_push_boolean(ctx, el->GetBanner()->flags.has(BannerFlag::noEntry)); + return JS_NewBool(ctx, el->GetBanner()->flags.has(BannerFlag::noEntry)); else - duk_push_null(ctx); - return DukValue::take_from_stack(ctx); + return JS_NULL; } - void ScTileElement::isNoEntry_set(bool value) + JSValue ScTileElement::isNoEntry_set(JSContext* ctx, JSValue thisValue, JSValue jsValue) { - ThrowIfGameStateNotMutable(); - auto* el = _element->AsBanner(); + JS_UNPACK_BOOL(value, ctx, jsValue); + JS_THROW_IF_GAME_STATE_NOT_MUTABLE(); + auto data = gScTileElement.GetOpaque(thisValue); + auto* el = data->element->AsBanner(); if (el != nullptr) { el->GetBanner()->flags.set(BannerFlag::noEntry, value); - Invalidate(); + Invalidate(data); } - } - - void ScTileElement::Invalidate() - { - MapInvalidateTileFull(_coords); + return JS_UNDEFINED; } const LargeSceneryElement* ScTileElement::GetOtherLargeSceneryElement( @@ -2192,39 +2332,39 @@ namespace OpenRCT2::Scripting return nullptr; } - void ScTileElement::RemoveBannerEntryIfNeeded() + void ScTileElement::RemoveBannerEntryIfNeeded(TileElement* element, CoordsXY& coords) { // check if other element still uses the banner entry - if (_element->GetType() == TileElementType::LargeScenery - && _element->AsLargeScenery()->GetEntry()->scrolling_mode != kScrollingModeNone - && GetOtherLargeSceneryElement(_coords, _element->AsLargeScenery()) != nullptr) + if (element->GetType() == TileElementType::LargeScenery + && element->AsLargeScenery()->GetEntry()->scrolling_mode != kScrollingModeNone + && GetOtherLargeSceneryElement(coords, element->AsLargeScenery()) != nullptr) return; // remove banner entry (if one exists) - _element->RemoveBannerEntry(); + element->RemoveBannerEntry(); } - void ScTileElement::CreateBannerEntryIfNeeded() + void ScTileElement::CreateBannerEntryIfNeeded(TileElement* element, CoordsXY& coords) { // check if creation is needed - switch (_element->GetType()) + switch (element->GetType()) { case TileElementType::Banner: break; case TileElementType::Wall: { - auto wallEntry = _element->AsWall()->GetEntry(); + auto wallEntry = element->AsWall()->GetEntry(); if (wallEntry == nullptr || wallEntry->scrolling_mode == kScrollingModeNone) return; break; } case TileElementType::LargeScenery: { - auto largeScenery = _element->AsLargeScenery(); + auto largeScenery = element->AsLargeScenery(); auto largeSceneryEntry = largeScenery->GetEntry(); if (largeSceneryEntry == nullptr || largeSceneryEntry->scrolling_mode == kScrollingModeNone) return; - auto otherElement = GetOtherLargeSceneryElement(_coords, largeScenery); + auto otherElement = GetOtherLargeSceneryElement(coords, largeScenery); if (otherElement != nullptr) { largeScenery->SetBannerIndex(otherElement->GetBannerIndex()); @@ -2247,16 +2387,16 @@ namespace OpenRCT2::Scripting banner->colour = Drawing::Colour::black; banner->textColour = Drawing::TextColour::black; banner->flags = {}; - if (_element->GetType() == TileElementType::Wall) + if (element->GetType() == TileElementType::Wall) banner->flags.set(BannerFlag::isWall); - if (_element->GetType() == TileElementType::LargeScenery) + if (element->GetType() == TileElementType::LargeScenery) banner->flags.set(BannerFlag::isLargeScenery); banner->type = 0; - banner->position = TileCoordsXY(_coords); + banner->position = TileCoordsXY(coords); - if (_element->GetType() == TileElementType::Wall || _element->GetType() == TileElementType::LargeScenery) + if (element->GetType() == TileElementType::Wall || element->GetType() == TileElementType::LargeScenery) { - RideId rideIndex = BannerGetClosestRideIndex({ _coords, _element->BaseHeight }); + RideId rideIndex = BannerGetClosestRideIndex({ coords, element->BaseHeight }); if (!rideIndex.IsNull()) { banner->rideIndex = rideIndex; @@ -2264,115 +2404,122 @@ namespace OpenRCT2::Scripting } } - _element->SetBannerIndex(banner->id); + element->SetBannerIndex(banner->id); } } - void ScTileElement::Register(duk_context* ctx) + TileElement* ScTileElement::GetTileElement(JSValue thisValue) { - // All - dukglue_register_property(ctx, &ScTileElement::type_get, &ScTileElement::type_set, "type"); - dukglue_register_property(ctx, &ScTileElement::baseHeight_get, &ScTileElement::baseHeight_set, "baseHeight"); - dukglue_register_property(ctx, &ScTileElement::baseZ_get, &ScTileElement::baseZ_set, "baseZ"); - dukglue_register_property( - ctx, &ScTileElement::clearanceHeight_get, &ScTileElement::clearanceHeight_set, "clearanceHeight"); - dukglue_register_property(ctx, &ScTileElement::clearanceZ_get, &ScTileElement::clearanceZ_set, "clearanceZ"); - dukglue_register_property( - ctx, &ScTileElement::occupiedQuadrants_get, &ScTileElement::occupiedQuadrants_set, "occupiedQuadrants"); - dukglue_register_property(ctx, &ScTileElement::isGhost_get, &ScTileElement::isGhost_set, "isGhost"); - dukglue_register_property(ctx, &ScTileElement::isHidden_get, &ScTileElement::isHidden_set, "isHidden"); + return gScTileElement.GetOpaque(thisValue)->element; + } - // Track | Small Scenery | Wall | Entrance | Large Scenery | Banner - dukglue_register_property(ctx, &ScTileElement::direction_get, &ScTileElement::direction_set, "direction"); + JSValue ScTileElement::New(JSContext* ctx, TileElement* element, CoordsXY& coords) + { + static constexpr JSCFunctionListEntry funcs[] = { + // All + JS_CGETSET_DEF("type", ScTileElement::type_get, ScTileElement::type_set), + JS_CGETSET_DEF("baseHeight", ScTileElement::baseHeight_get, ScTileElement::baseHeight_set), + JS_CGETSET_DEF("baseZ", ScTileElement::baseZ_get, ScTileElement::baseZ_set), + JS_CGETSET_DEF("clearanceHeight", ScTileElement::clearanceHeight_get, ScTileElement::clearanceHeight_set), + JS_CGETSET_DEF("clearanceZ", ScTileElement::clearanceZ_get, ScTileElement::clearanceZ_set), + JS_CGETSET_DEF("occupiedQuadrants", ScTileElement::occupiedQuadrants_get, ScTileElement::occupiedQuadrants_set), + JS_CGETSET_DEF("isGhost", ScTileElement::isGhost_get, ScTileElement::isGhost_set), + JS_CGETSET_DEF("isHidden", ScTileElement::isHidden_get, ScTileElement::isHidden_set), - // Path | Small Scenery | Wall | Entrance | Large Scenery | Banner - dukglue_register_property(ctx, &ScTileElement::object_get, &ScTileElement::object_set, "object"); + // Track | Small Scenery | Wall | Entrance | Large Scenery | Banner + JS_CGETSET_DEF("direction", ScTileElement::direction_get, ScTileElement::direction_set), - // Small Scenery | Wall | Large Scenery | Banner - dukglue_register_property(ctx, &ScTileElement::primaryColour_get, &ScTileElement::primaryColour_set, "primaryColour"); - dukglue_register_property( - ctx, &ScTileElement::secondaryColour_get, &ScTileElement::secondaryColour_set, "secondaryColour"); + // Path | Small Scenery | Wall | Entrance | Large Scenery | Banner + JS_CGETSET_DEF("object", ScTileElement::object_get, ScTileElement::object_set), - // Small Scenery | Wall | Large Scenery - dukglue_register_property( - ctx, &ScTileElement::tertiaryColour_get, &ScTileElement::tertiaryColour_set, "tertiaryColour"); + // Small Scenery | Wall | Large Scenery | Banner + JS_CGETSET_DEF("primaryColour", ScTileElement::primaryColour_get, ScTileElement::primaryColour_set), + JS_CGETSET_DEF("secondaryColour", ScTileElement::secondaryColour_get, ScTileElement::secondaryColour_set), - // Wall | Large Scenery | Banner - dukglue_register_property(ctx, &ScTileElement::bannerText_get, &ScTileElement::bannerText_set, "bannerText"); - dukglue_register_property(ctx, &ScTileElement::bannerIndex_get, &ScTileElement::bannerIndex_set, "bannerIndex"); + // Small Scenery | Wall | Large Scenery + JS_CGETSET_DEF("tertiaryColour", ScTileElement::tertiaryColour_get, ScTileElement::tertiaryColour_set), - // Path | Track | Entrance - dukglue_register_property(ctx, &ScTileElement::ride_get, &ScTileElement::ride_set, "ride"); - dukglue_register_property(ctx, &ScTileElement::station_get, &ScTileElement::station_set, "station"); + // Wall | Large Scenery | Banner + JS_CGETSET_DEF("bannerText", ScTileElement::bannerText_get, ScTileElement::bannerText_set), + JS_CGETSET_DEF("bannerIndex", ScTileElement::bannerIndex_get, ScTileElement::bannerIndex_set), - // Track | Entrance | Large Scenery - dukglue_register_property(ctx, &ScTileElement::sequence_get, &ScTileElement::sequence_set, "sequence"); + // Path | Track | Entrance + JS_CGETSET_DEF("ride", ScTileElement::ride_get, ScTileElement::ride_set), + JS_CGETSET_DEF("station", ScTileElement::station_get, ScTileElement::station_set), - // Surface | Wall - dukglue_register_property(ctx, &ScTileElement::slope_get, &ScTileElement::slope_set, "slope"); + // Track | Entrance | Large Scenery + JS_CGETSET_DEF("sequence", ScTileElement::sequence_get, ScTileElement::sequence_set), - // Surface only - dukglue_register_property(ctx, &ScTileElement::waterHeight_get, &ScTileElement::waterHeight_set, "waterHeight"); - dukglue_register_property(ctx, &ScTileElement::surfaceStyle_get, &ScTileElement::surfaceStyle_set, "surfaceStyle"); - dukglue_register_property(ctx, &ScTileElement::edgeStyle_get, &ScTileElement::edgeStyle_set, "edgeStyle"); - dukglue_register_property(ctx, &ScTileElement::grassLength_get, &ScTileElement::grassLength_set, "grassLength"); - dukglue_register_property(ctx, &ScTileElement::hasOwnership_get, nullptr, "hasOwnership"); - dukglue_register_property(ctx, &ScTileElement::hasConstructionRights_get, nullptr, "hasConstructionRights"); - dukglue_register_property(ctx, &ScTileElement::ownership_get, &ScTileElement::ownership_set, "ownership"); - dukglue_register_property(ctx, &ScTileElement::parkFences_get, &ScTileElement::parkFences_set, "parkFences"); + // Surface | Wall + JS_CGETSET_DEF("slope", ScTileElement::slope_get, ScTileElement::slope_set), - // Footpath only - dukglue_register_property( - ctx, &ScTileElement::edgesAndCorners_get, &ScTileElement::edgesAndCorners_set, "edgesAndCorners"); - dukglue_register_property(ctx, &ScTileElement::edges_get, &ScTileElement::edges_set, "edges"); - dukglue_register_property(ctx, &ScTileElement::corners_get, &ScTileElement::corners_set, "corners"); - dukglue_register_property( - ctx, &ScTileElement::slopeDirection_get, &ScTileElement::slopeDirection_set, "slopeDirection"); - dukglue_register_property(ctx, &ScTileElement::isQueue_get, &ScTileElement::isQueue_set, "isQueue"); - dukglue_register_property( - ctx, &ScTileElement::queueBannerDirection_get, &ScTileElement::queueBannerDirection_set, "queueBannerDirection"); + // Surface only + JS_CGETSET_DEF("waterHeight", ScTileElement::waterHeight_get, ScTileElement::waterHeight_set), + JS_CGETSET_DEF("surfaceStyle", ScTileElement::surfaceStyle_get, ScTileElement::surfaceStyle_set), + JS_CGETSET_DEF("edgeStyle", ScTileElement::edgeStyle_get, ScTileElement::edgeStyle_set), + JS_CGETSET_DEF("grassLength", ScTileElement::grassLength_get, ScTileElement::grassLength_set), + JS_CGETSET_DEF("hasOwnership", ScTileElement::hasOwnership_get, nullptr), + JS_CGETSET_DEF("hasConstructionRights", ScTileElement::hasConstructionRights_get, nullptr), + JS_CGETSET_DEF("ownership", ScTileElement::ownership_get, ScTileElement::ownership_set), + JS_CGETSET_DEF("parkFences", ScTileElement::parkFences_get, ScTileElement::parkFences_set), - dukglue_register_property( - ctx, &ScTileElement::isBlockedByVehicle_get, &ScTileElement::isBlockedByVehicle_set, "isBlockedByVehicle"); - dukglue_register_property(ctx, &ScTileElement::isWide_get, &ScTileElement::isWide_set, "isWide"); + // Footpath only + JS_CGETSET_DEF("edgesAndCorners", ScTileElement::edgesAndCorners_get, ScTileElement::edgesAndCorners_set), + JS_CGETSET_DEF("edges", ScTileElement::edges_get, ScTileElement::edges_set), + JS_CGETSET_DEF("corners", ScTileElement::corners_get, ScTileElement::corners_set), + JS_CGETSET_DEF("slopeDirection", ScTileElement::slopeDirection_get, ScTileElement::slopeDirection_set), + JS_CGETSET_DEF("isQueue", ScTileElement::isQueue_get, ScTileElement::isQueue_set), + JS_CGETSET_DEF( + "queueBannerDirection", ScTileElement::queueBannerDirection_get, ScTileElement::queueBannerDirection_set), - dukglue_register_property(ctx, &ScTileElement::surfaceObject_get, &ScTileElement::surfaceObject_set, "surfaceObject"); - dukglue_register_property( - ctx, &ScTileElement::railingsObject_get, &ScTileElement::railingsObject_set, "railingsObject"); + JS_CGETSET_DEF("isBlockedByVehicle", ScTileElement::isBlockedByVehicle_get, ScTileElement::isBlockedByVehicle_set), + JS_CGETSET_DEF("isWide", ScTileElement::isWide_get, ScTileElement::isWide_set), - dukglue_register_property(ctx, &ScTileElement::addition_get, &ScTileElement::addition_set, "addition"); - dukglue_register_property( - ctx, &ScTileElement::additionStatus_get, &ScTileElement::additionStatus_set, "additionStatus"); - dukglue_register_property( - ctx, &ScTileElement::isAdditionBroken_get, &ScTileElement::isAdditionBroken_set, "isAdditionBroken"); - dukglue_register_property( - ctx, &ScTileElement::isAdditionGhost_get, &ScTileElement::isAdditionGhost_set, "isAdditionGhost"); + JS_CGETSET_DEF("surfaceObject", ScTileElement::surfaceObject_get, ScTileElement::surfaceObject_set), + JS_CGETSET_DEF("railingsObject", ScTileElement::railingsObject_get, ScTileElement::railingsObject_set), - // Track only - dukglue_register_property(ctx, &ScTileElement::trackType_get, &ScTileElement::trackType_set, "trackType"); - dukglue_register_property(ctx, &ScTileElement::rideType_get, &ScTileElement::rideType_set, "rideType"); - dukglue_register_property(ctx, &ScTileElement::mazeEntry_get, &ScTileElement::mazeEntry_set, "mazeEntry"); - dukglue_register_property(ctx, &ScTileElement::colourScheme_get, &ScTileElement::colourScheme_set, "colourScheme"); - dukglue_register_property(ctx, &ScTileElement::seatRotation_get, &ScTileElement::seatRotation_set, "seatRotation"); - dukglue_register_property( - ctx, &ScTileElement::brakeBoosterSpeed_get, &ScTileElement::brakeBoosterSpeed_set, "brakeBoosterSpeed"); - dukglue_register_property(ctx, &ScTileElement::hasChainLift_get, &ScTileElement::hasChainLift_set, "hasChainLift"); - dukglue_register_property(ctx, &ScTileElement::isInverted_get, &ScTileElement::isInverted_set, "isInverted"); - dukglue_register_property(ctx, &ScTileElement::hasCableLift_get, &ScTileElement::hasCableLift_set, "hasCableLift"); - dukglue_register_property(ctx, &ScTileElement::isHighlighted_get, &ScTileElement::isHighlighted_set, "isHighlighted"); + JS_CGETSET_DEF("addition", ScTileElement::addition_get, ScTileElement::addition_set), + JS_CGETSET_DEF("additionStatus", ScTileElement::additionStatus_get, ScTileElement::additionStatus_set), + JS_CGETSET_DEF("isAdditionBroken", ScTileElement::isAdditionBroken_get, ScTileElement::isAdditionBroken_set), + JS_CGETSET_DEF("isAdditionGhost", ScTileElement::isAdditionGhost_get, ScTileElement::isAdditionGhost_set), - // Small Scenery only - dukglue_register_property(ctx, &ScTileElement::age_get, &ScTileElement::age_set, "age"); - dukglue_register_property(ctx, &ScTileElement::quadrant_get, &ScTileElement::quadrant_set, "quadrant"); + // Track only + JS_CGETSET_DEF("trackType", ScTileElement::trackType_get, ScTileElement::trackType_set), + JS_CGETSET_DEF("rideType", ScTileElement::rideType_get, ScTileElement::rideType_set), + JS_CGETSET_DEF("mazeEntry", ScTileElement::mazeEntry_get, ScTileElement::mazeEntry_set), + JS_CGETSET_DEF("colourScheme", ScTileElement::colourScheme_get, ScTileElement::colourScheme_set), + JS_CGETSET_DEF("seatRotation", ScTileElement::seatRotation_get, ScTileElement::seatRotation_set), + JS_CGETSET_DEF("brakeBoosterSpeed", ScTileElement::brakeBoosterSpeed_get, ScTileElement::brakeBoosterSpeed_set), + JS_CGETSET_DEF("hasChainLift", ScTileElement::hasChainLift_get, ScTileElement::hasChainLift_set), + JS_CGETSET_DEF("isInverted", ScTileElement::isInverted_get, ScTileElement::isInverted_set), + JS_CGETSET_DEF("hasCableLift", ScTileElement::hasCableLift_get, ScTileElement::hasCableLift_set), + JS_CGETSET_DEF("isHighlighted", ScTileElement::isHighlighted_get, ScTileElement::isHighlighted_set), - // Entrance only - dukglue_register_property( - ctx, &ScTileElement::footpathObject_get, &ScTileElement::footpathObject_set, "footpathObject"); - dukglue_register_property( - ctx, &ScTileElement::footpathSurfaceObject_get, &ScTileElement::footpathSurfaceObject_set, "footpathSurfaceObject"); + // Small Scenery only + JS_CGETSET_DEF("age", ScTileElement::age_get, ScTileElement::age_set), + JS_CGETSET_DEF("quadrant", ScTileElement::quadrant_get, ScTileElement::quadrant_set), - // Banner only - dukglue_register_property(ctx, &ScTileElement::isNoEntry_get, &ScTileElement::isNoEntry_set, "isNoEntry"); + // Entrance only + JS_CGETSET_DEF("footpathObject", ScTileElement::footpathObject_get, ScTileElement::footpathObject_set), + JS_CGETSET_DEF( + "footpathSurfaceObject", ScTileElement::footpathSurfaceObject_get, ScTileElement::footpathSurfaceObject_set), + + // Banner only + JS_CGETSET_DEF("isNoEntry", ScTileElement::isNoEntry_get, ScTileElement::isNoEntry_set) + }; + return MakeWithOpaque(ctx, funcs, new OpaqueTileElementData{ element, coords }); + } + + void ScTileElement::Register(JSContext* ctx) + { + RegisterBaseStr(ctx, "TileElement", Finalize); + } + + void ScTileElement::Finalize(JSRuntime* rt, JSValue thisVal) + { + OpaqueTileElementData* data = gScTileElement.GetOpaque(thisVal); + if (data) + delete data; } } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/world/ScTileElement.hpp b/src/openrct2/scripting/bindings/world/ScTileElement.hpp index b4a9d0b936..8b1ad6ef10 100644 --- a/src/openrct2/scripting/bindings/world/ScTileElement.hpp +++ b/src/openrct2/scripting/bindings/world/ScTileElement.hpp @@ -16,7 +16,6 @@ #include "../../../entity/EntityRegistry.h" #include "../../../world/Footpath.h" #include "../../../world/Scenery.h" - #include "../../Duktape.hpp" #include "../../ScriptEngine.h" #include @@ -25,195 +24,194 @@ namespace OpenRCT2::Scripting { - class ScTileElement + class ScTileElement; + extern ScTileElement gScTileElement; + + class ScTileElement : public ScBase { - protected: - CoordsXY _coords; - TileElement* _element; - - public: - ScTileElement(const CoordsXY& coords, TileElement* element); - private: - std::string type_get() const; - void type_set(std::string value); + static JSValue type_get(JSContext* ctx, JSValue thisValue); + static JSValue type_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - uint8_t baseHeight_get() const; - void baseHeight_set(uint8_t newBaseHeight); + static JSValue baseHeight_get(JSContext* ctx, JSValue thisValue); + static JSValue baseHeight_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - uint16_t baseZ_get() const; - void baseZ_set(uint16_t value); + static JSValue baseZ_get(JSContext* ctx, JSValue thisValue); + static JSValue baseZ_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - uint8_t clearanceHeight_get() const; - void clearanceHeight_set(uint8_t newClearanceHeight); + static JSValue clearanceHeight_get(JSContext* ctx, JSValue thisValue); + static JSValue clearanceHeight_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - uint16_t clearanceZ_get() const; - void clearanceZ_set(uint16_t value); + static JSValue clearanceZ_get(JSContext* ctx, JSValue thisValue); + static JSValue clearanceZ_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue slope_get() const; - void slope_set(uint8_t value); + static JSValue slope_get(JSContext* ctx, JSValue thisValue); + static JSValue slope_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue waterHeight_get() const; - void waterHeight_set(int32_t value); + static JSValue waterHeight_get(JSContext* ctx, JSValue thisValue); + static JSValue waterHeight_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue surfaceStyle_get() const; - void surfaceStyle_set(uint32_t value); + static JSValue surfaceStyle_get(JSContext* ctx, JSValue thisValue); + static JSValue surfaceStyle_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue edgeStyle_get() const; - void edgeStyle_set(uint32_t value); + static JSValue edgeStyle_get(JSContext* ctx, JSValue thisValue); + static JSValue edgeStyle_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue grassLength_get() const; - void grassLength_set(uint8_t value); + static JSValue grassLength_get(JSContext* ctx, JSValue thisValue); + static JSValue grassLength_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue hasOwnership_get() const; + static JSValue hasOwnership_get(JSContext* ctx, JSValue thisValue); - DukValue hasConstructionRights_get(); + static JSValue hasConstructionRights_get(JSContext* ctx, JSValue thisValue); - DukValue ownership_get() const; - void ownership_set(uint8_t value); + static JSValue ownership_get(JSContext* ctx, JSValue thisValue); + static JSValue ownership_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue parkFences_get() const; - void parkFences_set(uint8_t value); + static JSValue parkFences_get(JSContext* ctx, JSValue thisValue); + static JSValue parkFences_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue trackType_get() const; - void trackType_set(uint16_t value); + static JSValue trackType_get(JSContext* ctx, JSValue thisValue); + static JSValue trackType_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue rideType_get() const; - void rideType_set(uint16_t value); + static JSValue rideType_get(JSContext* ctx, JSValue thisValue); + static JSValue rideType_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue sequence_get() const; - void sequence_set(const DukValue& value); + static JSValue sequence_get(JSContext* ctx, JSValue thisValue); + static JSValue sequence_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue ride_get() const; - void ride_set(const DukValue& value); + static JSValue ride_get(JSContext* ctx, JSValue thisValue); + static JSValue ride_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue station_get() const; - void station_set(const DukValue& value); + static JSValue station_get(JSContext* ctx, JSValue thisValue); + static JSValue station_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue hasChainLift_get() const; - void hasChainLift_set(bool value); + static JSValue hasChainLift_get(JSContext* ctx, JSValue thisValue); + static JSValue hasChainLift_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue mazeEntry_get() const; - void mazeEntry_set(const DukValue& value); + static JSValue mazeEntry_get(JSContext* ctx, JSValue thisValue); + static JSValue mazeEntry_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue colourScheme_get() const; - void colourScheme_set(const DukValue& value); + static JSValue colourScheme_get(JSContext* ctx, JSValue thisValue); + static JSValue colourScheme_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue seatRotation_get() const; - void seatRotation_set(const DukValue& value); + static JSValue seatRotation_get(JSContext* ctx, JSValue thisValue); + static JSValue seatRotation_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue brakeBoosterSpeed_get() const; - void brakeBoosterSpeed_set(const DukValue& value); + static JSValue brakeBoosterSpeed_get(JSContext* ctx, JSValue thisValue); + static JSValue brakeBoosterSpeed_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue isInverted_get() const; - void isInverted_set(bool value); + static JSValue isInverted_get(JSContext* ctx, JSValue thisValue); + static JSValue isInverted_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue hasCableLift_get() const; - void hasCableLift_set(bool value); + static JSValue hasCableLift_get(JSContext* ctx, JSValue thisValue); + static JSValue hasCableLift_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue isHighlighted_get() const; - void isHighlighted_set(bool value); + static JSValue isHighlighted_get(JSContext* ctx, JSValue thisValue); + static JSValue isHighlighted_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue object_get() const; - void object_set(const DukValue& value); + static JSValue object_get(JSContext* ctx, JSValue thisValue); + static JSValue object_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - bool isHidden_get() const; - void isHidden_set(bool hide); + static JSValue isHidden_get(JSContext* ctx, JSValue thisValue); + static JSValue isHidden_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue age_get() const; - void age_set(uint8_t value); + static JSValue age_get(JSContext* ctx, JSValue thisValue); + static JSValue age_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue quadrant_get() const; - void quadrant_set(uint8_t value); + static JSValue quadrant_get(JSContext* ctx, JSValue thisValue); + static JSValue quadrant_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - uint8_t occupiedQuadrants_get() const; - void occupiedQuadrants_set(uint8_t value); + static JSValue occupiedQuadrants_get(JSContext* ctx, JSValue thisValue); + static JSValue occupiedQuadrants_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - bool isGhost_get() const; - void isGhost_set(bool value); + static JSValue isGhost_get(JSContext* ctx, JSValue thisValue); + static JSValue isGhost_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue primaryColour_get() const; - void primaryColour_set(uint8_t value); + static JSValue primaryColour_get(JSContext* ctx, JSValue thisValue); + static JSValue primaryColour_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue secondaryColour_get() const; - void secondaryColour_set(uint8_t value); + static JSValue secondaryColour_get(JSContext* ctx, JSValue thisValue); + static JSValue secondaryColour_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue tertiaryColour_get() const; - void tertiaryColour_set(uint8_t value); + static JSValue tertiaryColour_get(JSContext* ctx, JSValue thisValue); + static JSValue tertiaryColour_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue bannerIndex_get() const; - void bannerIndex_set(const DukValue& value); + static JSValue bannerIndex_get(JSContext* ctx, JSValue thisValue); + static JSValue bannerIndex_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); // Deprecated in favor of separate 'edges' and 'corners' properties, // left here to maintain compatibility with older plugins. - /** @deprecated */ - uint8_t edgesAndCorners_get() const; - /** @deprecated */ - void edgesAndCorners_set(uint8_t value); + static JSValue edgesAndCorners_get(JSContext* ctx, JSValue thisValue); + static JSValue edgesAndCorners_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue edges_get() const; - void edges_set(uint8_t value); + static JSValue edges_get(JSContext* ctx, JSValue thisValue); + static JSValue edges_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue corners_get() const; - void corners_set(uint8_t value); + static JSValue corners_get(JSContext* ctx, JSValue thisValue); + static JSValue corners_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue slopeDirection_get() const; - void slopeDirection_set(const DukValue& value); + static JSValue slopeDirection_get(JSContext* ctx, JSValue thisValue); + static JSValue slopeDirection_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue isQueue_get() const; - void isQueue_set(bool value); + static JSValue isQueue_get(JSContext* ctx, JSValue thisValue); + static JSValue isQueue_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue queueBannerDirection_get() const; - void queueBannerDirection_set(const DukValue& value); + static JSValue queueBannerDirection_get(JSContext* ctx, JSValue thisValue); + static JSValue queueBannerDirection_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue isBlockedByVehicle_get() const; - void isBlockedByVehicle_set(bool value); + static JSValue isBlockedByVehicle_get(JSContext* ctx, JSValue thisValue); + static JSValue isBlockedByVehicle_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue isWide_get() const; - void isWide_set(bool value); + static JSValue isWide_get(JSContext* ctx, JSValue thisValue); + static JSValue isWide_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue addition_get() const; - void addition_set(const DukValue& value); + static JSValue addition_get(JSContext* ctx, JSValue thisValue); + static JSValue addition_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue surfaceObject_get() const; - void surfaceObject_set(const DukValue& value); + static JSValue surfaceObject_get(JSContext* ctx, JSValue thisValue); + static JSValue surfaceObject_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue railingsObject_get() const; - void railingsObject_set(const DukValue& value); + static JSValue railingsObject_get(JSContext* ctx, JSValue thisValue); + static JSValue railingsObject_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue additionStatus_get() const; - void additionStatus_set(const DukValue& value); + static JSValue additionStatus_get(JSContext* ctx, JSValue thisValue); + static JSValue additionStatus_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue isAdditionBroken_get() const; - void isAdditionBroken_set(const DukValue& value); + static JSValue isAdditionBroken_get(JSContext* ctx, JSValue thisValue); + static JSValue isAdditionBroken_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue isAdditionGhost_get() const; - void isAdditionGhost_set(const DukValue& value); + static JSValue isAdditionGhost_get(JSContext* ctx, JSValue thisValue); + static JSValue isAdditionGhost_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue footpathObject_get() const; - void footpathObject_set(const DukValue& value); + static JSValue footpathObject_get(JSContext* ctx, JSValue thisValue); + static JSValue footpathObject_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue footpathSurfaceObject_get() const; - void footpathSurfaceObject_set(const DukValue& value); + static JSValue footpathSurfaceObject_get(JSContext* ctx, JSValue thisValue); + static JSValue footpathSurfaceObject_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue direction_get() const; - void direction_set(uint8_t value); + static JSValue direction_get(JSContext* ctx, JSValue thisValue); + static JSValue direction_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue bannerText_get() const; - void bannerText_set(std::string value); + static JSValue bannerText_get(JSContext* ctx, JSValue thisValue); + static JSValue bannerText_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - DukValue isNoEntry_get() const; - void isNoEntry_set(bool value); + static JSValue isNoEntry_get(JSContext* ctx, JSValue thisValue); + static JSValue isNoEntry_set(JSContext* ctx, JSValue thisValue, JSValue jsValue); - void Invalidate(); + static void RemoveBannerEntryIfNeeded(TileElement* element, CoordsXY& coords); + static void CreateBannerEntryIfNeeded(TileElement* element, CoordsXY& coords); - void RemoveBannerEntryIfNeeded(); - void CreateBannerEntryIfNeeded(); + static TileElement* GetTileElement(JSValue thisValue); public: static const LargeSceneryElement* GetOtherLargeSceneryElement( const CoordsXY& loc, const LargeSceneryElement* largeScenery); - static void Register(duk_context* ctx); + + JSValue New(JSContext* ctx, TileElement* element, CoordsXY& coords); + void Register(JSContext* ctx); + + private: + static void Finalize(JSRuntime* rt, JSValue thisValue); }; } // namespace OpenRCT2::Scripting diff --git a/src/openrct2/scripting/bindings/world/ScWeather.hpp b/src/openrct2/scripting/bindings/world/ScWeather.hpp index d6f4ee47a3..ed08c4ddce 100644 --- a/src/openrct2/scripting/bindings/world/ScWeather.hpp +++ b/src/openrct2/scripting/bindings/world/ScWeather.hpp @@ -17,44 +17,60 @@ #include "../../../object/ClimateObject.h" #include "../../../object/ObjectManager.h" #include "../../../world/Weather.h" - #include "../../Duktape.hpp" #include "../../ScriptEngine.h" namespace OpenRCT2::Scripting { - class ScWeatherState + class ScWeatherState; + extern ScWeatherState gScWeatherState; + class ScWeatherState final : public ScBase { private: - std::string _weather; - int8_t _temperature; + using OpaqueWeatherStateData = struct + { + std::string weather; + int8_t temperature; + }; + + static JSValue weather_get(JSContext* ctx, JSValue thisVal) + { + return JSFromStdString(ctx, gScWeatherState.GetOpaque(thisVal)->weather); + } + + static JSValue temperature_get(JSContext* ctx, JSValue thisVal) + { + return JS_NewInt32(ctx, gScWeatherState.GetOpaque(thisVal)->temperature); + } public: - ScWeatherState(std::string weather, int8_t temperature) - : _weather(weather) - , _temperature(temperature) + JSValue New(JSContext* ctx, const std::string& weather, int8_t temperature) { + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("weather", ScWeatherState::weather_get, nullptr), + JS_CGETSET_DEF("temperature", ScWeatherState::temperature_get, nullptr), + }; + + return MakeWithOpaque(ctx, funcs, new OpaqueWeatherStateData{ weather, temperature }); } - std::string weather_get() const + void Register(JSContext* ctx) { - return _weather; + RegisterBaseStr(ctx, "WeatherState"); } - int8_t temperature_get() const + void Finalize(JSRuntime*, JSValue thisVal) { - return _temperature; - } - - static void Register(duk_context* ctx) - { - dukglue_register_property(ctx, &ScWeatherState::weather_get, nullptr, "weather"); - dukglue_register_property(ctx, &ScWeatherState::temperature_get, nullptr, "temperature"); + OpaqueWeatherStateData* data = gScWeatherState.GetOpaque(thisVal); + if (data) + delete data; } }; - class ScWeather + class ScWeather; + extern ScWeather gScWeather; + class ScWeather final : public ScBase { - public: + private: static std::string WeatherTypeToString(Weather::Type token) { switch (token) @@ -83,35 +99,45 @@ namespace OpenRCT2::Scripting return {}; } - std::string type_get() const + static JSValue type_get(JSContext* ctx, JSValue) { - auto& objManager = GetContext()->GetObjectManager(); + auto& objManager = OpenRCT2::GetContext()->GetObjectManager(); auto* climateObj = objManager.GetLoadedObject(0); if (climateObj == nullptr) - return {}; + return JSFromStdString(ctx, {}); - return climateObj->getScriptName(); + return JSFromStdString(ctx, climateObj->getScriptName()); } - std::shared_ptr current_get() const + static JSValue current_get(JSContext* ctx, JSValue) { auto& gameState = getGameState(); std::string weatherType = WeatherTypeToString(gameState.weatherCurrent.weatherType); - return std::make_shared(weatherType, gameState.weatherCurrent.temperature); + return gScWeatherState.New(ctx, weatherType, gameState.weatherCurrent.temperature); } - std::shared_ptr future_get() const + static JSValue future_get(JSContext* ctx, JSValue) { auto& gameState = getGameState(); std::string weatherType = WeatherTypeToString(gameState.weatherNext.weatherType); - return std::make_shared(weatherType, gameState.weatherNext.temperature); + return gScWeatherState.New(ctx, weatherType, gameState.weatherCurrent.temperature); } - static void Register(duk_context* ctx) + public: + JSValue New(JSContext* ctx) { - dukglue_register_property(ctx, &ScWeather::type_get, nullptr, "type"); - dukglue_register_property(ctx, &ScWeather::current_get, nullptr, "current"); - dukglue_register_property(ctx, &ScWeather::future_get, nullptr, "future"); + static constexpr JSCFunctionListEntry funcs[] = { + JS_CGETSET_DEF("type", ScWeather::type_get, nullptr), + JS_CGETSET_DEF("current", ScWeather::current_get, nullptr), + JS_CGETSET_DEF("future", ScWeather::future_get, nullptr), + }; + + return MakeWithOpaque(ctx, funcs, nullptr); + } + + void Register(JSContext* ctx) + { + RegisterBaseStr(ctx, "Weather"); } }; diff --git a/src/openrct2/ui/DummyWindowManager.cpp b/src/openrct2/ui/DummyWindowManager.cpp index 0830774a5a..35fc24ad6f 100644 --- a/src/openrct2/ui/DummyWindowManager.cpp +++ b/src/openrct2/ui/DummyWindowManager.cpp @@ -86,6 +86,7 @@ namespace OpenRCT2::Ui void CloseAllExceptFlags(WindowFlags flags) override {}; void CloseAllExceptNumberAndClass(WindowNumber number, WindowClass cls) override {}; void CloseConstructionWindows() override {}; + void Cleanup() override {}; WindowBase* FindByClass(WindowClass cls) override { diff --git a/src/openrct2/ui/WindowManager.h b/src/openrct2/ui/WindowManager.h index acec8e38e3..78c40a3546 100644 --- a/src/openrct2/ui/WindowManager.h +++ b/src/openrct2/ui/WindowManager.h @@ -97,6 +97,7 @@ namespace OpenRCT2::Ui virtual void CloseAllExceptFlags(WindowFlags flags) = 0; virtual void CloseAllExceptNumberAndClass(WindowNumber number, WindowClass cls) = 0; virtual void CloseConstructionWindows() = 0; + virtual void Cleanup() = 0; virtual WindowBase* FindByClass(WindowClass cls) = 0; virtual WindowBase* FindByNumber(WindowClass cls, WindowNumber number) = 0; diff --git a/src/openrct2/world/Park.cpp b/src/openrct2/world/Park.cpp index cf1036f2f1..5e8ca979fd 100644 --- a/src/openrct2/world/Park.cpp +++ b/src/openrct2/world/Park.cpp @@ -152,14 +152,15 @@ namespace OpenRCT2::Park auto& hookEngine = GetContext()->GetScriptEngine().GetHookEngine(); if (hookEngine.HasSubscriptions(HookType::parkCalculateGuestCap)) { - auto ctx = GetContext()->GetScriptEngine().GetContext(); - auto obj = DukObject(ctx); - obj.Set("suggestedGuestMaximum", suggestedMaxGuests); - auto e = obj.Take(); - hookEngine.Call(HookType::parkCalculateGuestCap, e, true); + JSContext* ctx = GetContext()->GetScriptEngine().GetContext(); + JSValue obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj, "suggestedGuestMaximum", JS_NewInt64(ctx, suggestedMaxGuests)); + hookEngine.Call(HookType::parkCalculateGuestCap, obj, true, true); - suggestedMaxGuests = AsOrDefault(e["suggestedGuestMaximum"], static_cast(suggestedMaxGuests)); + suggestedMaxGuests = AsOrDefault(ctx, obj, "suggestedGuestMaximum", static_cast(suggestedMaxGuests)); suggestedMaxGuests = std::clamp(suggestedMaxGuests, 0, UINT16_MAX); + + JS_FreeValue(ctx, obj); } #endif return suggestedMaxGuests; diff --git a/src/thirdparty/base64.hpp b/src/thirdparty/base64.hpp new file mode 100644 index 0000000000..81d7164bc5 --- /dev/null +++ b/src/thirdparty/base64.hpp @@ -0,0 +1,721 @@ +/* +MIT License + +Copyright (c) 2019 Tobias Locker + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef BASE64_HPP_ +#define BASE64_HPP_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__cpp_lib_bit_cast) +#include // For std::bit_cast. +#endif + +namespace base64 { + +namespace detail { + +#if defined(__cpp_lib_bit_cast) +using std::bit_cast; +#else +template +std::enable_if_t && + std::is_trivially_copyable_v, + To> +bit_cast(const From& src) noexcept { + static_assert(std::is_trivially_constructible_v, + "This implementation additionally requires " + "destination type to be trivially constructible"); + + To dst; + std::memcpy(&dst, &src, sizeof(To)); + return dst; +} +#endif + +inline constexpr char padding_char{'='}; +inline constexpr uint32_t bad_char{0x01FFFFFF}; + +#if !defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__) +#if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || \ + (defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN) || \ + (defined(_BYTE_ORDER) && _BYTE_ORDER == _BIG_ENDIAN) || \ + (defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN) || \ + (defined(__sun) && defined(__SVR4) && defined(_BIG_ENDIAN)) || \ + defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || \ + defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__) || \ + defined(_M_PPC) +#define __BIG_ENDIAN__ +#elif (defined(__BYTE_ORDER__) && \ + __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || /* gcc */ \ + (defined(__BYTE_ORDER) && \ + __BYTE_ORDER == __LITTLE_ENDIAN) /* linux header */ \ + || (defined(_BYTE_ORDER) && _BYTE_ORDER == _LITTLE_ENDIAN) || \ + (defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN) /* mingw header */ || \ + (defined(__sun) && defined(__SVR4) && \ + defined(_LITTLE_ENDIAN)) || /* solaris */ \ + defined(__ARMEL__) || \ + defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(_MIPSEL) || \ + defined(__MIPSEL) || defined(__MIPSEL__) || defined(_M_IX86) || \ + defined(_M_X64) || defined(_M_IA64) || /* msvc for intel processors */ \ + defined(_M_ARM) || defined(_M_ARM64) /* msvc code on arm executes in little endian mode */ +#define __LITTLE_ENDIAN__ +#endif +#endif + +#if !defined(__LITTLE_ENDIAN__) & !defined(__BIG_ENDIAN__) +#error "UNKNOWN Platform / endianness. Configure endianness explicitly." +#endif + +#if defined(__LITTLE_ENDIAN__) +std::array constexpr decode_table_0 = { + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x000000f8, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000fc, + 0x000000d0, 0x000000d4, 0x000000d8, 0x000000dc, 0x000000e0, 0x000000e4, + 0x000000e8, 0x000000ec, 0x000000f0, 0x000000f4, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, + 0x00000004, 0x00000008, 0x0000000c, 0x00000010, 0x00000014, 0x00000018, + 0x0000001c, 0x00000020, 0x00000024, 0x00000028, 0x0000002c, 0x00000030, + 0x00000034, 0x00000038, 0x0000003c, 0x00000040, 0x00000044, 0x00000048, + 0x0000004c, 0x00000050, 0x00000054, 0x00000058, 0x0000005c, 0x00000060, + 0x00000064, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x00000068, 0x0000006c, 0x00000070, 0x00000074, 0x00000078, + 0x0000007c, 0x00000080, 0x00000084, 0x00000088, 0x0000008c, 0x00000090, + 0x00000094, 0x00000098, 0x0000009c, 0x000000a0, 0x000000a4, 0x000000a8, + 0x000000ac, 0x000000b0, 0x000000b4, 0x000000b8, 0x000000bc, 0x000000c0, + 0x000000c4, 0x000000c8, 0x000000cc, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; + +std::array constexpr decode_table_1 = { + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x0000e003, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000f003, + 0x00004003, 0x00005003, 0x00006003, 0x00007003, 0x00008003, 0x00009003, + 0x0000a003, 0x0000b003, 0x0000c003, 0x0000d003, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, + 0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000, + 0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000, + 0x0000d000, 0x0000e000, 0x0000f000, 0x00000001, 0x00001001, 0x00002001, + 0x00003001, 0x00004001, 0x00005001, 0x00006001, 0x00007001, 0x00008001, + 0x00009001, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x0000a001, 0x0000b001, 0x0000c001, 0x0000d001, 0x0000e001, + 0x0000f001, 0x00000002, 0x00001002, 0x00002002, 0x00003002, 0x00004002, + 0x00005002, 0x00006002, 0x00007002, 0x00008002, 0x00009002, 0x0000a002, + 0x0000b002, 0x0000c002, 0x0000d002, 0x0000e002, 0x0000f002, 0x00000003, + 0x00001003, 0x00002003, 0x00003003, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; + +std::array constexpr decode_table_2 = { + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x00800f00, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00c00f00, + 0x00000d00, 0x00400d00, 0x00800d00, 0x00c00d00, 0x00000e00, 0x00400e00, + 0x00800e00, 0x00c00e00, 0x00000f00, 0x00400f00, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, + 0x00400000, 0x00800000, 0x00c00000, 0x00000100, 0x00400100, 0x00800100, + 0x00c00100, 0x00000200, 0x00400200, 0x00800200, 0x00c00200, 0x00000300, + 0x00400300, 0x00800300, 0x00c00300, 0x00000400, 0x00400400, 0x00800400, + 0x00c00400, 0x00000500, 0x00400500, 0x00800500, 0x00c00500, 0x00000600, + 0x00400600, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x00800600, 0x00c00600, 0x00000700, 0x00400700, 0x00800700, + 0x00c00700, 0x00000800, 0x00400800, 0x00800800, 0x00c00800, 0x00000900, + 0x00400900, 0x00800900, 0x00c00900, 0x00000a00, 0x00400a00, 0x00800a00, + 0x00c00a00, 0x00000b00, 0x00400b00, 0x00800b00, 0x00c00b00, 0x00000c00, + 0x00400c00, 0x00800c00, 0x00c00c00, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; + +std::array constexpr decode_table_3 = { + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x003e0000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003f0000, + 0x00340000, 0x00350000, 0x00360000, 0x00370000, 0x00380000, 0x00390000, + 0x003a0000, 0x003b0000, 0x003c0000, 0x003d0000, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, + 0x00010000, 0x00020000, 0x00030000, 0x00040000, 0x00050000, 0x00060000, + 0x00070000, 0x00080000, 0x00090000, 0x000a0000, 0x000b0000, 0x000c0000, + 0x000d0000, 0x000e0000, 0x000f0000, 0x00100000, 0x00110000, 0x00120000, + 0x00130000, 0x00140000, 0x00150000, 0x00160000, 0x00170000, 0x00180000, + 0x00190000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x001a0000, 0x001b0000, 0x001c0000, 0x001d0000, 0x001e0000, + 0x001f0000, 0x00200000, 0x00210000, 0x00220000, 0x00230000, 0x00240000, + 0x00250000, 0x00260000, 0x00270000, 0x00280000, 0x00290000, 0x002a0000, + 0x002b0000, 0x002c0000, 0x002d0000, 0x002e0000, 0x002f0000, 0x00300000, + 0x00310000, 0x00320000, 0x00330000, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; + +// TODO fix decoding tables to avoid the need for different indices in big +// endian? +inline constexpr size_t decidx0{0}; +inline constexpr size_t decidx1{1}; +inline constexpr size_t decidx2{2}; + +#elif defined(__BIG_ENDIAN__) + +std::array constexpr decode_table_0 = { + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x00f80000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00fc0000, + 0x00d00000, 0x00d40000, 0x00d80000, 0x00dc0000, 0x00e00000, 0x00e40000, + 0x00e80000, 0x00ec0000, 0x00f00000, 0x00f40000, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, + 0x00040000, 0x00080000, 0x000c0000, 0x00100000, 0x00140000, 0x00180000, + 0x001c0000, 0x00200000, 0x00240000, 0x00280000, 0x002c0000, 0x00300000, + 0x00340000, 0x00380000, 0x003c0000, 0x00400000, 0x00440000, 0x00480000, + 0x004c0000, 0x00500000, 0x00540000, 0x00580000, 0x005c0000, 0x00600000, + 0x00640000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x00680000, 0x006c0000, 0x00700000, 0x00740000, 0x00780000, + 0x007c0000, 0x00800000, 0x00840000, 0x00880000, 0x008c0000, 0x00900000, + 0x00940000, 0x00980000, 0x009c0000, 0x00a00000, 0x00a40000, 0x00a80000, + 0x00ac0000, 0x00b00000, 0x00b40000, 0x00b80000, 0x00bc0000, 0x00c00000, + 0x00c40000, 0x00c80000, 0x00cc0000, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; + +std::array constexpr decode_table_1 = { + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x0003e000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0003f000, + 0x00034000, 0x00035000, 0x00036000, 0x00037000, 0x00038000, 0x00039000, + 0x0003a000, 0x0003b000, 0x0003c000, 0x0003d000, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, + 0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000, + 0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000, + 0x0000d000, 0x0000e000, 0x0000f000, 0x00010000, 0x00011000, 0x00012000, + 0x00013000, 0x00014000, 0x00015000, 0x00016000, 0x00017000, 0x00018000, + 0x00019000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x0001a000, 0x0001b000, 0x0001c000, 0x0001d000, 0x0001e000, + 0x0001f000, 0x00020000, 0x00021000, 0x00022000, 0x00023000, 0x00024000, + 0x00025000, 0x00026000, 0x00027000, 0x00028000, 0x00029000, 0x0002a000, + 0x0002b000, 0x0002c000, 0x0002d000, 0x0002e000, 0x0002f000, 0x00030000, + 0x00031000, 0x00032000, 0x00033000, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; + +std::array constexpr decode_table_2 = { + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x00000f80, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000fc0, + 0x00000d00, 0x00000d40, 0x00000d80, 0x00000dc0, 0x00000e00, 0x00000e40, + 0x00000e80, 0x00000ec0, 0x00000f00, 0x00000f40, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, + 0x00000040, 0x00000080, 0x000000c0, 0x00000100, 0x00000140, 0x00000180, + 0x000001c0, 0x00000200, 0x00000240, 0x00000280, 0x000002c0, 0x00000300, + 0x00000340, 0x00000380, 0x000003c0, 0x00000400, 0x00000440, 0x00000480, + 0x000004c0, 0x00000500, 0x00000540, 0x00000580, 0x000005c0, 0x00000600, + 0x00000640, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x00000680, 0x000006c0, 0x00000700, 0x00000740, 0x00000780, + 0x000007c0, 0x00000800, 0x00000840, 0x00000880, 0x000008c0, 0x00000900, + 0x00000940, 0x00000980, 0x000009c0, 0x00000a00, 0x00000a40, 0x00000a80, + 0x00000ac0, 0x00000b00, 0x00000b40, 0x00000b80, 0x00000bc0, 0x00000c00, + 0x00000c40, 0x00000c80, 0x00000cc0, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; + +std::array constexpr decode_table_3 = { + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x0000003e, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000003f, + 0x00000034, 0x00000035, 0x00000036, 0x00000037, 0x00000038, 0x00000039, + 0x0000003a, 0x0000003b, 0x0000003c, 0x0000003d, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, + 0x00000001, 0x00000002, 0x00000003, 0x00000004, 0x00000005, 0x00000006, + 0x00000007, 0x00000008, 0x00000009, 0x0000000a, 0x0000000b, 0x0000000c, + 0x0000000d, 0x0000000e, 0x0000000f, 0x00000010, 0x00000011, 0x00000012, + 0x00000013, 0x00000014, 0x00000015, 0x00000016, 0x00000017, 0x00000018, + 0x00000019, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x0000001a, 0x0000001b, 0x0000001c, 0x0000001d, 0x0000001e, + 0x0000001f, 0x00000020, 0x00000021, 0x00000022, 0x00000023, 0x00000024, + 0x00000025, 0x00000026, 0x00000027, 0x00000028, 0x00000029, 0x0000002a, + 0x0000002b, 0x0000002c, 0x0000002d, 0x0000002e, 0x0000002f, 0x00000030, + 0x00000031, 0x00000032, 0x00000033, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; + +// TODO fix decoding tables to avoid the need for different indices in big +// endian? +inline constexpr size_t decidx0{1}; +inline constexpr size_t decidx1{2}; +inline constexpr size_t decidx2{3}; + +#endif + +std::array constexpr encode_table_0 = { + 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'C', 'C', 'C', 'C', 'D', 'D', 'D', + 'D', 'E', 'E', 'E', 'E', 'F', 'F', 'F', 'F', 'G', 'G', 'G', 'G', 'H', 'H', + 'H', 'H', 'I', 'I', 'I', 'I', 'J', 'J', 'J', 'J', 'K', 'K', 'K', 'K', 'L', + 'L', 'L', 'L', 'M', 'M', 'M', 'M', 'N', 'N', 'N', 'N', 'O', 'O', 'O', 'O', + 'P', 'P', 'P', 'P', 'Q', 'Q', 'Q', 'Q', 'R', 'R', 'R', 'R', 'S', 'S', 'S', + 'S', 'T', 'T', 'T', 'T', 'U', 'U', 'U', 'U', 'V', 'V', 'V', 'V', 'W', 'W', + 'W', 'W', 'X', 'X', 'X', 'X', 'Y', 'Y', 'Y', 'Y', 'Z', 'Z', 'Z', 'Z', 'a', + 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'c', 'd', 'd', 'd', 'd', + 'e', 'e', 'e', 'e', 'f', 'f', 'f', 'f', 'g', 'g', 'g', 'g', 'h', 'h', 'h', + 'h', 'i', 'i', 'i', 'i', 'j', 'j', 'j', 'j', 'k', 'k', 'k', 'k', 'l', 'l', + 'l', 'l', 'm', 'm', 'm', 'm', 'n', 'n', 'n', 'n', 'o', 'o', 'o', 'o', 'p', + 'p', 'p', 'p', 'q', 'q', 'q', 'q', 'r', 'r', 'r', 'r', 's', 's', 's', 's', + 't', 't', 't', 't', 'u', 'u', 'u', 'u', 'v', 'v', 'v', 'v', 'w', 'w', 'w', + 'w', 'x', 'x', 'x', 'x', 'y', 'y', 'y', 'y', 'z', 'z', 'z', 'z', '0', '0', + '0', '0', '1', '1', '1', '1', '2', '2', '2', '2', '3', '3', '3', '3', '4', + '4', '4', '4', '5', '5', '5', '5', '6', '6', '6', '6', '7', '7', '7', '7', + '8', '8', '8', '8', '9', '9', '9', '9', '+', '+', '+', '+', '/', '/', '/', + '/'}; + +std::array constexpr encode_table_1 = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', + 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', + 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', + 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', + 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', 'C', + 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', + '/'}; + +} // namespace detail + +template +inline OutputBuffer encode_into(InputIterator begin, InputIterator end) { + typedef std::decay_t input_value_type; + static_assert(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v); + typedef typename OutputBuffer::value_type output_value_type; + static_assert(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v); + const size_t binarytextsize = end - begin; + const size_t encodedsize = (binarytextsize / 3 + (binarytextsize % 3 > 0)) + << 2; + OutputBuffer encoded(encodedsize, detail::padding_char); + + const uint8_t* bytes = reinterpret_cast(&*begin); + char* currEncoding = reinterpret_cast(&encoded[0]); + + for (size_t i = binarytextsize / 3; i; --i) { + const uint8_t t1 = *bytes++; + const uint8_t t2 = *bytes++; + const uint8_t t3 = *bytes++; + *currEncoding++ = detail::encode_table_0[t1]; + *currEncoding++ = + detail::encode_table_1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; + *currEncoding++ = + detail::encode_table_1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)]; + *currEncoding++ = detail::encode_table_1[t3]; + } + + switch (binarytextsize % 3) { + case 0: { + break; + } + case 1: { + const uint8_t t1 = bytes[0]; + *currEncoding++ = detail::encode_table_0[t1]; + *currEncoding++ = detail::encode_table_1[(t1 & 0x03) << 4]; + // *currEncoding++ = detail::padding_char; + // *currEncoding++ = detail::padding_char; + break; + } + case 2: { + const uint8_t t1 = bytes[0]; + const uint8_t t2 = bytes[1]; + *currEncoding++ = detail::encode_table_0[t1]; + *currEncoding++ = + detail::encode_table_1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; + *currEncoding++ = detail::encode_table_1[(t2 & 0x0F) << 2]; + // *currEncoding++ = detail::padding_char; + break; + } + default: { + throw std::runtime_error{"Invalid base64 encoded data"}; + } + } + + return encoded; +} + +template +inline OutputBuffer encode_into(std::string_view data) { + return encode_into(std::begin(data), std::end(data)); +} + +inline std::string to_base64(std::string_view data) { + return encode_into(std::begin(data), std::end(data)); +} + +template +inline OutputBuffer decode_into(std::string_view base64Text) { + typedef typename OutputBuffer::value_type output_value_type; + static_assert(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v); + if (base64Text.empty()) { + return OutputBuffer(); + } + + if ((base64Text.size() & 3) != 0) { + throw std::runtime_error{ + "Invalid base64 encoded data - Size not divisible by 4"}; + } + + const size_t numPadding = + std::count(base64Text.rbegin(), base64Text.rbegin() + 4, '='); + if (numPadding > 2) { + throw std::runtime_error{ + "Invalid base64 encoded data - Found more than 2 padding signs"}; + } + + const size_t decodedsize = (base64Text.size() * 3 >> 2) - numPadding; + OutputBuffer decoded(decodedsize, '.'); + + const uint8_t* bytes = reinterpret_cast(&base64Text[0]); + char* currDecoding = reinterpret_cast(&decoded[0]); + + for (size_t i = (base64Text.size() >> 2) - (numPadding != 0); i; --i) { + const uint8_t t1 = *bytes++; + const uint8_t t2 = *bytes++; + const uint8_t t3 = *bytes++; + const uint8_t t4 = *bytes++; + + const uint32_t d1 = detail::decode_table_0[t1]; + const uint32_t d2 = detail::decode_table_1[t2]; + const uint32_t d3 = detail::decode_table_2[t3]; + const uint32_t d4 = detail::decode_table_3[t4]; + + const uint32_t temp = d1 | d2 | d3 | d4; + + if (temp >= detail::bad_char) { + throw std::runtime_error{ + "Invalid base64 encoded data - Invalid character"}; + } + + // Use bit_cast instead of union and type punning to avoid + // undefined behaviour risk: + // https://en.wikipedia.org/wiki/Type_punning#Use_of_union + const std::array tempBytes = + detail::bit_cast, uint32_t>(temp); + + *currDecoding++ = tempBytes[detail::decidx0]; + *currDecoding++ = tempBytes[detail::decidx1]; + *currDecoding++ = tempBytes[detail::decidx2]; + } + + switch (numPadding) { + case 0: { + break; + } + case 1: { + const uint8_t t1 = *bytes++; + const uint8_t t2 = *bytes++; + const uint8_t t3 = *bytes++; + + const uint32_t d1 = detail::decode_table_0[t1]; + const uint32_t d2 = detail::decode_table_1[t2]; + const uint32_t d3 = detail::decode_table_2[t3]; + + const uint32_t temp = d1 | d2 | d3; + + if (temp >= detail::bad_char) { + throw std::runtime_error{ + "Invalid base64 encoded data - Invalid character"}; + } + + // Use bit_cast instead of union and type punning to avoid + // undefined behaviour risk: + // https://en.wikipedia.org/wiki/Type_punning#Use_of_union + const std::array tempBytes = + detail::bit_cast, uint32_t>(temp); + *currDecoding++ = tempBytes[detail::decidx0]; + *currDecoding++ = tempBytes[detail::decidx1]; + break; + } + case 2: { + const uint8_t t1 = *bytes++; + const uint8_t t2 = *bytes++; + + const uint32_t d1 = detail::decode_table_0[t1]; + const uint32_t d2 = detail::decode_table_1[t2]; + + const uint32_t temp = d1 | d2; + + if (temp >= detail::bad_char) { + throw std::runtime_error{ + "Invalid base64 encoded data - Invalid character"}; + } + + const std::array tempBytes = + detail::bit_cast, uint32_t>(temp); + *currDecoding++ = tempBytes[detail::decidx0]; + break; + } + default: { + throw std::runtime_error{ + "Invalid base64 encoded data - Invalid padding number"}; + } + } + + return decoded; +} + +template +inline OutputBuffer decode_into(InputIterator begin, InputIterator end) { + typedef std::decay_t input_value_type; + static_assert(std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v); + std::string_view data(reinterpret_cast(&*begin), end - begin); + return decode_into(data); +} + +inline std::string from_base64(std::string_view data) { + return decode_into(data); +} + +} // namespace base64 + +#endif // BASE64_HPP_ \ No newline at end of file diff --git a/src/thirdparty/dukglue/detail_class_proto.h b/src/thirdparty/dukglue/detail_class_proto.h deleted file mode 100644 index 7b008fccd1..0000000000 --- a/src/thirdparty/dukglue/detail_class_proto.h +++ /dev/null @@ -1,201 +0,0 @@ -#pragma once - -#include "detail_typeinfo.h" -#include - -namespace dukglue { - namespace detail { - - struct ProtoManager - { - public: - template - static void push_prototype(duk_context* ctx) - { - push_prototype(ctx, TypeInfo(typeid(Cls))); - } - - static void push_prototype(duk_context* ctx, const TypeInfo& check_info) - { - if (!find_and_push_prototype(ctx, check_info)) { - // nope, need to create our prototype object - duk_push_object(ctx); - - // add reference to this class' info object so we can do type checking - // when trying to pass this object into method calls - typedef dukglue::detail::TypeInfo TypeInfo; - TypeInfo* info = new TypeInfo(check_info); - - duk_push_pointer(ctx, info); - duk_put_prop_string(ctx, -2, "\xFF" "type_info"); - - // Clean up the TypeInfo object when this prototype is destroyed. - // We can't put a finalizer directly on this prototype, because it - // will be run whenever the wrapper for an object of this class is - // destroyed; instead, we make a dummy object and put the finalizer - // on that. - // If you're memory paranoid: this duplicates the type_info pointer - // once per registered class. If you don't care about freeing memory - // during shutdown, you can probably comment out this part. - duk_push_object(ctx); - duk_push_pointer(ctx, info); - duk_put_prop_string(ctx, -2, "\xFF" "type_info"); - duk_push_c_function(ctx, type_info_finalizer, 1); - duk_set_finalizer(ctx, -2); - duk_put_prop_string(ctx, -2, "\xFF" "type_info_finalizer"); - - // register it in the stash - register_prototype(ctx, info); - } - } - - template - static void make_script_object(duk_context* ctx, Cls* obj) - { - assert(obj != NULL); - - duk_push_object(ctx); - duk_push_pointer(ctx, obj); - duk_put_prop_string(ctx, -2, "\xFF" "obj_ptr"); - - // push the appropriate prototype -#ifdef DUKGLUE_INFER_BASE_CLASS - // In the "infer base class" case, we push the prototype - // corresponding to the compile-time class if no prototype - // for the run-time type has been defined. This allows us to - // skip calling dukglue_set_base_class() for every derived class, - // so long as we: - // (1) Always use the derived class as a pointer typed as the base class - // (2) Do not create a prototype for the derived class - // (i.e. do not register any functions on the derived class). - - // For big projects with hundreds of derived classes, this is preferrable - // to registering each type's base class individually. However, - // registering a native method on a derived class will cause the - // base class's methods to disappear until dukglue_set_base_class() is - // also called (because registering the native method causes a prototype - // to be created for the run-time type). This behavior may be unexpected, - // and for "small" projects it is reasonable to require - // dukglue_set_base_class() to be called, so it is opt-in via an ifdef. - - // does a prototype exist for the run-time type? if so, push it - if (!find_and_push_prototype(ctx, TypeInfo(typeid(*obj)))) { - // nope, find or create the prototype for the compile-time type - // and push that - push_prototype(ctx); - } -#else - // always use the prototype for the run-time type - push_prototype(ctx, TypeInfo(typeid(*obj))); -#endif - - duk_set_prototype(ctx, -2); - } - - private: - static duk_ret_t type_info_finalizer(duk_context* ctx) - { - duk_get_prop_string(ctx, 0, "\xFF" "type_info"); - dukglue::detail::TypeInfo* info = static_cast(duk_require_pointer(ctx, -1)); - delete info; - - // set pointer to NULL in case this finalizer runs again - duk_push_pointer(ctx, NULL); - duk_put_prop_string(ctx, 0, "\xFF" "type_info"); - - return 0; - } - - // puts heap_stash["dukglue_prototypes"] on the stack, - // or creates it if it doesn't exist - static void push_prototypes_array(duk_context* ctx) - { - static const char* DUKGLUE_PROTOTYPES = "dukglue_prototypes"; - - duk_push_heap_stash(ctx); - - // does the prototype array already exist? - if (!duk_has_prop_string(ctx, -1, DUKGLUE_PROTOTYPES)) { - // nope, we need to create it - duk_push_array(ctx); - duk_put_prop_string(ctx, -2, DUKGLUE_PROTOTYPES); - } - - duk_get_prop_string(ctx, -1, DUKGLUE_PROTOTYPES); - - // remove the heap stash from the stack - duk_remove(ctx, -2); - } - - // Stack: ... [proto] -> ... [proto] - static void register_prototype(duk_context* ctx, const TypeInfo* info) { - // 1. We assume info is not in the prototype array already - // 2. Duktape has no efficient "shift array indices" operation (at least publicly) - // 3. This method doesn't need to be fast, it's only called during registration - - // Work from high to low in the prototypes array, shifting as we go, - // until we find the spot for info. - - push_prototypes_array(ctx); - duk_size_t i = duk_get_length(ctx, -1); - while (i > 0) { - duk_get_prop_index(ctx, -1, i - 1); - - duk_get_prop_string(ctx, -1, "\xFF" "type_info"); - const TypeInfo* chk_info = static_cast(duk_require_pointer(ctx, -1)); - duk_pop(ctx); // pop type_info - - if (*chk_info > *info) { - duk_put_prop_index(ctx, -2, i); - i--; - } else { - duk_pop(ctx); // pop prototypes_array[i] - break; - } - } - - //std::cout << "Registering prototype for " << typeid(Cls).name() << " at " << i << std::endl; - - duk_dup(ctx, -2); // copy proto to top - duk_put_prop_index(ctx, -2, i); - duk_pop(ctx); // pop prototypes_array - } - - static bool find_and_push_prototype(duk_context* ctx, const TypeInfo& search_info) { - push_prototypes_array(ctx); - - // these are ints and not duk_size_t to deal with negative indices - int min = 0; - int max = duk_get_length(ctx, -1) - 1; - - while (min <= max) { - int mid = (max - min) / 2 + min; - - duk_get_prop_index(ctx, -1, mid); - - duk_get_prop_string(ctx, -1, "\xFF" "type_info"); - TypeInfo* mid_info = static_cast(duk_require_pointer(ctx, -1)); - duk_pop(ctx); // pop type_info pointer - - if (*mid_info == search_info) { - // found it - duk_remove(ctx, -2); // pop prototypes_array - return true; - } - else if (*mid_info < search_info) { - min = mid + 1; - } - else { - max = mid - 1; - } - - duk_pop(ctx); // pop prototypes_array[mid] - } - - duk_pop(ctx); // pop prototypes_array - return false; - } - - }; - } -} diff --git a/src/thirdparty/dukglue/detail_constructor.h b/src/thirdparty/dukglue/detail_constructor.h deleted file mode 100644 index de68e85d5e..0000000000 --- a/src/thirdparty/dukglue/detail_constructor.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include "detail_stack.h" -#include "detail_traits.h" - -namespace dukglue { - namespace detail { - - template - static duk_ret_t call_native_constructor(duk_context* ctx) - { - if (!duk_is_constructor_call(ctx)) { - duk_error(ctx, DUK_RET_TYPE_ERROR, "Constructor must be called with new T()."); - return DUK_RET_TYPE_ERROR; - } - - // construct the new instance - auto constructor_args = dukglue::detail::get_stack_values(ctx); - Cls* obj = dukglue::detail::apply_constructor(std::move(constructor_args)); - - duk_push_this(ctx); - - // make the new script object keep the pointer to the new object instance - duk_push_pointer(ctx, obj); - duk_put_prop_string(ctx, -2, "\xFF" "obj_ptr"); - - // register it - if (!managed) - dukglue::detail::RefManager::register_native_object(ctx, obj); - - duk_pop(ctx); // pop this - - return 0; - } - - template - static duk_ret_t managed_finalizer(duk_context* ctx) - { - duk_get_prop_string(ctx, 0, "\xFF" "obj_ptr"); - Cls* obj = (Cls*) duk_require_pointer(ctx, -1); - duk_pop(ctx); // pop obj_ptr - - if (obj != NULL) { - delete obj; - - // for safety, set the pointer to undefined - duk_push_undefined(ctx); - duk_put_prop_string(ctx, 0, "\xFF" "obj_ptr"); - } - - return 0; - } - - template - static duk_ret_t call_native_deleter(duk_context* ctx) - { - duk_push_this(ctx); - duk_get_prop_string(ctx, -1, "\xFF" "obj_ptr"); - - if (!duk_is_pointer(ctx, -1)) { - duk_error(ctx, DUK_RET_REFERENCE_ERROR, "Object has already been invalidated; cannot delete."); - return DUK_RET_REFERENCE_ERROR; - } - - Cls* obj = static_cast(duk_require_pointer(ctx, -1)); - dukglue_invalidate_object(ctx, obj); - delete obj; - - duk_pop_2(ctx); - return 0; - } - } -} diff --git a/src/thirdparty/dukglue/detail_function.h b/src/thirdparty/dukglue/detail_function.h deleted file mode 100644 index 84ecbcb4cc..0000000000 --- a/src/thirdparty/dukglue/detail_function.h +++ /dev/null @@ -1,102 +0,0 @@ -#pragma once - -#include "detail_stack.h" -#include "detail_types.h" -#include "detail_primitive_types.h" - -namespace dukglue -{ - namespace detail - { - // This struct can be used to generate a Duktape C function that - // pulls the argument values off the stack (with type checking), - // calls the appropriate function with them, and puts the function's - // return value (if any) onto the stack. - template - struct FuncInfoHolder - { - typedef RetType(*FuncType)(Ts...); - - template - struct FuncCompiletime - { - // The function to call is embedded into call_native_function at - // compile-time through template magic. - // Performance is so similar to run-time function calls that - // this is not recommended due to the ugly syntax it requires. - static duk_ret_t call_native_function(duk_context* ctx) - { - auto bakedArgs = dukglue::detail::get_stack_values(ctx); - actually_call(ctx, bakedArgs); - return std::is_void::value ? 0 : 1; - } - - private: - // this mess is to support functions with void return values - - template - static typename std::enable_if::value>::type actually_call(duk_context* ctx, const std::tuple& args) - { - // ArgStorage has some static_asserts in it that validate value types, - // so we typedef it to force ArgStorage to compile and run the asserts - typedef typename dukglue::types::ArgStorage::type ValidateReturnType; - - RetType return_val = dukglue::detail::apply_fp(funcToCall, args); - - using namespace dukglue::types; - DukType::type>::template push(ctx, std::move(return_val)); - } - - template - static typename std::enable_if::value>::type actually_call(duk_context* ctx, const std::tuple& args) - { - dukglue::detail::apply_fp(funcToCall, args); - } - }; - - struct FuncRuntime - { - // Pull the address of the function to call from the - // Duktape function object at run time. - static duk_ret_t call_native_function(duk_context* ctx) - { - duk_push_current_function(ctx); - duk_get_prop_string(ctx, -1, "\xFF" "func_ptr"); - void* fp_void = duk_require_pointer(ctx, -1); - if (fp_void == NULL) { - duk_error(ctx, DUK_RET_TYPE_ERROR, "what even"); - return DUK_RET_TYPE_ERROR; - } - - duk_pop_2(ctx); - - static_assert(sizeof(RetType(*)(Ts...)) == sizeof(void*), "Function pointer and data pointer are different sizes"); - RetType(*funcToCall)(Ts...) = reinterpret_cast(fp_void); - - actually_call(ctx, funcToCall, dukglue::detail::get_stack_values(ctx)); - return std::is_void::value ? 0 : 1; - } - - // this mess is to support functions with void return values - template - static typename std::enable_if::value>::type actually_call(duk_context* ctx, RetType(*funcToCall)(Ts...), const std::tuple& args) - { - // ArgStorage has some static_asserts in it that validate value types, - // so we typedef it to force ArgStorage to compile and run the asserts - typedef typename dukglue::types::ArgStorage::type ValidateReturnType; - - RetType return_val = dukglue::detail::apply_fp(funcToCall, args); - - using namespace dukglue::types; - DukType::type>::template push(ctx, std::move(return_val)); - } - - template - static typename std::enable_if::value>::type actually_call(duk_context* ctx, RetType(*funcToCall)(Ts...), const std::tuple& args) - { - dukglue::detail::apply_fp(funcToCall, args); - } - }; - }; - } -} diff --git a/src/thirdparty/dukglue/detail_method.h b/src/thirdparty/dukglue/detail_method.h deleted file mode 100644 index d3e929034d..0000000000 --- a/src/thirdparty/dukglue/detail_method.h +++ /dev/null @@ -1,183 +0,0 @@ -#pragma once - -#include "detail_stack.h" - -namespace dukglue -{ - namespace detail - { - template - struct MethodInfo - { - typedef typename std::conditional::type MethodType; - - // The size of a method pointer is not guaranteed to be the same size as a function pointer. - // This means we can't just use duk_push_pointer(ctx, &MyClass::method) to store the method at run time. - // To get around this, we wrap the method pointer in a MethodHolder (on the heap), and push a pointer to - // that. The MethodHolder is cleaned up by the finalizer. - struct MethodHolder - { - MethodType method; - }; - - template - struct MethodCompiletime - { - static duk_ret_t call_native_method(duk_context* ctx) - { - // get this.obj_ptr - duk_push_this(ctx); - duk_get_prop_string(ctx, -1, "\xFF" "obj_ptr"); - void* obj_void = duk_require_pointer(ctx, -1); - if (obj_void == nullptr) { - duk_error(ctx, DUK_RET_REFERENCE_ERROR, "Native object missing."); - return DUK_RET_REFERENCE_ERROR; - } - - duk_pop_2(ctx); - - // (should always be valid unless someone is intentionally messing with this.obj_ptr...) - Cls* obj = static_cast(obj_void); - - // read arguments and call function - auto bakedArgs = dukglue::detail::get_stack_values(ctx); - actually_call(ctx, obj, bakedArgs); - return std::is_void::value ? 0 : 1; - } - - // this mess is to support functions with void return values - template - static typename std::enable_if::value>::type actually_call(duk_context* ctx, Cls* obj, const std::tuple& args) - { - // ArgStorage has some static_asserts in it that validate value types, - // so we typedef it to force ArgStorage to compile and run the asserts - typedef typename dukglue::types::ArgStorage::type ValidateReturnType; - - RetType return_val = dukglue::detail::apply_method(methodToCall, obj, args); - - using namespace dukglue::types; - DukType::type>::template push(ctx, std::move(return_val)); - } - - template - static typename std::enable_if::value>::type actually_call(duk_context* ctx, Cls* obj, const std::tuple& args) - { - dukglue::detail::apply_method(methodToCall, obj, args); - } - }; - - - struct MethodRuntime - { - static duk_ret_t finalize_method(duk_context* ctx) - { - // clean up the MethodHolder reference - duk_get_prop_string(ctx, 0, "\xFF" "method_holder"); - - void* method_holder_void = duk_require_pointer(ctx, -1); - MethodHolder* method_holder = static_cast(method_holder_void); - delete method_holder; - - return 0; - } - - static duk_ret_t call_native_method(duk_context* ctx) - { - // get this.obj_ptr - duk_push_this(ctx); - duk_get_prop_string(ctx, -1, "\xFF" "obj_ptr"); - void* obj_void = duk_get_pointer(ctx, -1); - if (obj_void == nullptr) { - duk_error(ctx, DUK_RET_REFERENCE_ERROR, "Invalid native object for 'this'"); - return DUK_RET_REFERENCE_ERROR; - } - - duk_pop_2(ctx); // pop this.obj_ptr and this - - // get current_function.method_info - duk_push_current_function(ctx); - duk_get_prop_string(ctx, -1, "\xFF" "method_holder"); - void* method_holder_void = duk_require_pointer(ctx, -1); - if (method_holder_void == nullptr) { - duk_error(ctx, DUK_RET_TYPE_ERROR, "Method pointer missing?!"); - return DUK_RET_TYPE_ERROR; - } - - duk_pop_2(ctx); - - // (should always be valid unless someone is intentionally messing with this.obj_ptr...) - Cls* obj = static_cast(obj_void); - MethodHolder* method_holder = static_cast(method_holder_void); - - // read arguments and call method - auto bakedArgs = dukglue::detail::get_stack_values(ctx); - actually_call(ctx, method_holder->method, obj, bakedArgs); - return std::is_void::value ? 0 : 1; - } - - // this mess is to support functions with void return values - template - static typename std::enable_if::value>::type actually_call(duk_context* ctx, MethodType method, Cls* obj, const std::tuple& args) - { - // ArgStorage has some static_asserts in it that validate value types, - // so we typedef it to force ArgStorage to compile and run the asserts - typedef typename dukglue::types::ArgStorage::type ValidateReturnType; - - RetType return_val = dukglue::detail::apply_method(method, obj, args); - - using namespace dukglue::types; - DukType::type>::template push(ctx, std::move(return_val)); - } - - template - static typename std::enable_if::value>::type actually_call(duk_context* ctx, MethodType method, Cls* obj, const std::tuple& args) - { - dukglue::detail::apply_method(method, obj, args); - } - }; - }; - - template - struct MethodVariadicRuntime - { - typedef MethodInfo MethodInfoVariadic; - typedef typename MethodInfoVariadic::MethodHolder MethodHolderVariadic; - - static duk_ret_t finalize_method(duk_context* ctx) - { - return MethodInfoVariadic::MethodRuntime::finalize_method(ctx); - } - - static duk_ret_t call_native_method(duk_context* ctx) - { - // get this.obj_ptr - duk_push_this(ctx); - duk_get_prop_string(ctx, -1, "\xFF" "obj_ptr"); - void* obj_void = duk_get_pointer(ctx, -1); - if (obj_void == nullptr) { - duk_error(ctx, DUK_RET_REFERENCE_ERROR, "Invalid native object for 'this'"); - return DUK_RET_REFERENCE_ERROR; - } - - duk_pop_2(ctx); // pop this.obj_ptr and this - - // get current_function.method_info - duk_push_current_function(ctx); - duk_get_prop_string(ctx, -1, "\xFF" "method_holder"); - void* method_holder_void = duk_require_pointer(ctx, -1); - if (method_holder_void == nullptr) { - duk_error(ctx, DUK_RET_TYPE_ERROR, "Method pointer missing?!"); - return DUK_RET_TYPE_ERROR; - } - - duk_pop_2(ctx); - - // (should always be valid unless someone is intentionally messing with this.obj_ptr...) - Cls* obj = static_cast(obj_void); - MethodHolderVariadic* method_holder = static_cast(method_holder_void); - - return (*obj.*method_holder->method)(ctx); - } - }; - } -} diff --git a/src/thirdparty/dukglue/detail_primitive_types.h b/src/thirdparty/dukglue/detail_primitive_types.h deleted file mode 100644 index 6ff83c1735..0000000000 --- a/src/thirdparty/dukglue/detail_primitive_types.h +++ /dev/null @@ -1,256 +0,0 @@ -#pragma once - -#include "detail_types.h" -#include "detail_typeinfo.h" -#include "dukvalue.h" - -#include -#include -#include // for std::shared_ptr - -namespace dukglue { - namespace types { - -#define DUKGLUE_SIMPLE_VALUE_TYPE(TYPE, DUK_IS_FUNC, DUK_GET_FUNC, DUK_PUSH_FUNC, PUSH_VALUE) \ - template<> \ - struct DukType { \ - typedef std::true_type IsValueType; \ - \ - template \ - static TYPE read(duk_context* ctx, duk_idx_t arg_idx) { \ - if (DUK_IS_FUNC(ctx, arg_idx)) { \ - return static_cast(DUK_GET_FUNC(ctx, arg_idx)); \ - } else { \ - duk_int_t type_idx = duk_get_type(ctx, arg_idx); \ - duk_error(ctx, DUK_RET_TYPE_ERROR, "Argument %d: expected " #TYPE ", got %s", arg_idx, detail::get_type_name(type_idx)); \ - } \ - } \ - \ - template \ - static void push(duk_context* ctx, TYPE value) { \ - DUK_PUSH_FUNC(ctx, PUSH_VALUE); \ - } \ - }; - - DUKGLUE_SIMPLE_VALUE_TYPE(bool, duk_is_boolean, 0 != duk_get_boolean, duk_push_boolean, value) - - DUKGLUE_SIMPLE_VALUE_TYPE(uint8_t, duk_is_number, duk_get_uint, duk_push_uint, value) - DUKGLUE_SIMPLE_VALUE_TYPE(uint16_t, duk_is_number, duk_get_uint, duk_push_uint, value) - DUKGLUE_SIMPLE_VALUE_TYPE(uint32_t, duk_is_number, duk_get_uint, duk_push_uint, value) - DUKGLUE_SIMPLE_VALUE_TYPE(uint64_t, duk_is_number, duk_get_number, duk_push_number, value) // have to cast to double - - DUKGLUE_SIMPLE_VALUE_TYPE(int8_t, duk_is_number, duk_get_int, duk_push_int, value) - DUKGLUE_SIMPLE_VALUE_TYPE(int16_t, duk_is_number, duk_get_int, duk_push_int, value) - DUKGLUE_SIMPLE_VALUE_TYPE(int32_t, duk_is_number, duk_get_int, duk_push_int, value) - DUKGLUE_SIMPLE_VALUE_TYPE(int64_t, duk_is_number, duk_get_number, duk_push_number, value) // have to cast to double - - // signed char and unsigned char are surprisingly *both* different from char, at least in MSVC - DUKGLUE_SIMPLE_VALUE_TYPE(char, duk_is_number, duk_get_int, duk_push_int, value) - - DUKGLUE_SIMPLE_VALUE_TYPE(float, duk_is_number, duk_get_number, duk_push_number, value) - DUKGLUE_SIMPLE_VALUE_TYPE(double, duk_is_number, duk_get_number, duk_push_number, value) - - DUKGLUE_SIMPLE_VALUE_TYPE(std::string, duk_is_string, duk_get_string, duk_push_string, value.c_str()) - - // We have to do some magic for const char* to work correctly. - // We override the "bare type" and "storage type" to both be const char*. - // char* is a bit tricky because its "bare type" should still be const char*, to differentiate it from just char - template<> - struct Bare { - typedef const char* type; - }; - template<> - struct Bare { - typedef const char* type; - }; - - // the storage type should also be const char* - if we don't do this, it will end up as just "char" - template<> - struct ArgStorage { - typedef const char* type; - }; - - template<> - struct DukType { - typedef std::true_type IsValueType; - - template - static const char* read(duk_context* ctx, duk_idx_t arg_idx) { - if (duk_is_string(ctx, arg_idx)) { - return duk_get_string(ctx, arg_idx); - } else { - duk_int_t type_idx = duk_get_type(ctx, arg_idx); - duk_error(ctx, DUK_RET_TYPE_ERROR, "Argument %d: expected string, got %s", arg_idx, detail::get_type_name(type_idx)); - } - } - - template - static void push(duk_context* ctx, const char* value) { - duk_push_string(ctx, value); - } - }; - - // DukValue - template<> - struct DukType { - typedef std::true_type IsValueType; - - template - static DukValue read(duk_context* ctx, duk_idx_t arg_idx) { - try { - return DukValue::copy_from_stack(ctx, arg_idx); - } catch (DukException& e) { - // only DukException can be thrown by DukValue::copy_from_stack - duk_error(ctx, DUK_ERR_ERROR, e.what()); - } - } - - template - static void push(duk_context* ctx, const DukValue& value) { - if (value.context() == NULL) { - duk_error(ctx, DUK_ERR_ERROR, "DukValue is uninitialized"); - return; - } - - if (value.context() != ctx) { - duk_error(ctx, DUK_ERR_ERROR, "DukValue comes from a different context"); - return; - } - - try { - value.push(); - } catch (DukException& e) { - // only DukException can be thrown by DukValue::copy_from_stack - duk_error(ctx, DUK_ERR_ERROR, e.what()); - } - } - }; - - // std::vector (as value) - template - struct DukType< std::vector > { - typedef std::true_type IsValueType; - - template - static std::vector read(duk_context* ctx, duk_idx_t arg_idx) { - if (!duk_is_array(ctx, arg_idx)) { - duk_int_t type_idx = duk_get_type(ctx, arg_idx); - duk_error(ctx, DUK_ERR_TYPE_ERROR, "Argument %d: expected array, got %s", arg_idx, detail::get_type_name(type_idx)); - } - - duk_size_t len = duk_get_length(ctx, arg_idx); - const duk_idx_t elem_idx = duk_get_top(ctx); - - std::vector vec; - vec.reserve(len); - for (duk_size_t i = 0; i < len; i++) { - duk_get_prop_index(ctx, arg_idx, i); - vec.push_back(DukType< typename Bare::type >::template read(ctx, elem_idx)); - duk_pop(ctx); - } - return vec; - } - - template - static void push(duk_context* ctx, const std::vector& value) { - duk_idx_t obj_idx = duk_push_array(ctx); - - for (size_t i = 0; i < value.size(); i++) { - DukType< typename Bare::type >::template push(ctx, value[i]); - duk_put_prop_index(ctx, obj_idx, i); - } - } - }; - - // std::shared_ptr (as value) - template - struct DukType< std::shared_ptr > { - typedef std::true_type IsValueType; - - static_assert(std::is_same::IsValueType, std::false_type>::value, "Dukglue can only use std::shared_ptr to non-value types!"); - - template - static std::shared_ptr read(duk_context* ctx, duk_idx_t arg_idx) { - if (duk_is_null(ctx, arg_idx)) - return nullptr; - - if (!duk_is_object(ctx, arg_idx)) { - duk_int_t type_idx = duk_get_type(ctx, arg_idx); - duk_error(ctx, DUK_RET_TYPE_ERROR, "Argument %d: expected shared_ptr object, got ", arg_idx, detail::get_type_name(type_idx)); - } - - duk_get_prop_string(ctx, arg_idx, "\xFF" "type_info"); - if (!duk_is_pointer(ctx, -1)) // missing type_info, must not be a native object - duk_error(ctx, DUK_RET_TYPE_ERROR, "Argument %d: expected shared_ptr object (missing type_info)", arg_idx); - - // make sure this object can be safely returned as a T* - dukglue::detail::TypeInfo* info = static_cast(duk_get_pointer(ctx, -1)); - if (!info->can_cast()) - duk_error(ctx, DUK_RET_TYPE_ERROR, "Argument %d: wrong type of shared_ptr object", arg_idx); - duk_pop(ctx); // pop type_info - - duk_get_prop_string(ctx, arg_idx, "\xFF" "shared_ptr"); - if (!duk_is_pointer(ctx, -1)) - duk_error(ctx, DUK_RET_TYPE_ERROR, "Argument %d: not a shared_ptr object (missing shared_ptr)", arg_idx); - void* ptr = duk_get_pointer(ctx, -1); - duk_pop(ctx); // pop pointer to shared_ptr - - return *((std::shared_ptr*) ptr); - } - - static duk_ret_t shared_ptr_finalizer(duk_context* ctx) - { - duk_get_prop_string(ctx, 0, "\xFF" "shared_ptr"); - std::shared_ptr* ptr = (std::shared_ptr*) duk_require_pointer(ctx, -1); - duk_pop(ctx); // pop shared_ptr ptr - - if (ptr != NULL) { - delete ptr; - - // for safety, set the pointer to undefined - // (finalizers can run multiple times) - duk_push_undefined(ctx); - duk_put_prop_string(ctx, 0, "\xFF" "shared_ptr"); - } - - return 0; - } - - template - static void push(duk_context* ctx, const std::shared_ptr& value) { - if (value == nullptr) { - duk_push_null(ctx); - } else { - dukglue::detail::ProtoManager::make_script_object(ctx, value.get()); - - // create + set shared_ptr - duk_push_pointer(ctx, new std::shared_ptr(value)); - duk_put_prop_string(ctx, -2, "\xFF" "shared_ptr"); - - // set shared_ptr finalizer - duk_push_c_function(ctx, &shared_ptr_finalizer, 1); - duk_set_finalizer(ctx, -2); - } - } - }; - - // std::function - /*template - struct DukType< std::function > { - typedef std::true_type IsValueType; - - template - static std::function read(duk_context* ctx, duk_idx_t arg_idx) { - DukValue callable = DukValue::copy_from_stack(ctx, -1, DUK_TYPE_MASK_OBJECT); - return [ctx, callable] (ArgTs... args) -> RetT { - dukglue_call(ctx, callable, args...); - }; - } - - template - static void push(duk_context* ctx, std::function value) { - static_assert(false, "Pushing an std::function has not been implemented yet. Sorry!"); - } - };*/ - } -} diff --git a/src/thirdparty/dukglue/detail_refs.h b/src/thirdparty/dukglue/detail_refs.h deleted file mode 100644 index 67ad671639..0000000000 --- a/src/thirdparty/dukglue/detail_refs.h +++ /dev/null @@ -1,199 +0,0 @@ -#pragma once - -#include - -#include - -namespace dukglue -{ - namespace detail - { - // This class handles keeping a map of void* -> script object. - // It also prevents script objects from being GC'd until someone - // explicitly frees the underlying native object. - - // Implemented by keeping an array of script objects in the heap stash. - // An std::unordered_map maps pointer -> array index. - // Thanks to std::unordered_map, lookup time is O(1) on average. - - // Using std::unordered_map has some memory overhead (~32 bytes per object), - // which could be removed by using a different data structure: - - // 1. Use no data structure. Blindly scan through the reference registry, - // checking \xFFobj_ptr on every object until you find yours. - // Performance when returning native objects from functions when a lot - // of native objects are registered will suffer. - - // 2. Implement a self-balancing binary tree on top of a Duktape array - // for the registry. Still fast - O(log(N)) - and no memory overhead. - - // 3. A sorted list would work too, though insertion speed might be worse - // than a binary tree. - - struct RefManager - { - public: - - // Find the script object corresponding to obj_ptr and push it. - // Returns true if successful, false if obj_ptr has not been registered. - // Stack: ... -> ... (if object has been registered before) - // ... -> ... [object] (if object has not been registered) - static bool find_and_push_native_object(duk_context* ctx, void* obj_ptr) - { - RefMap* ref_map = get_ref_map(ctx); - - const auto it = ref_map->find(obj_ptr); - - if (it == ref_map->end()) { - return false; - } else { - push_ref_array(ctx); - duk_get_prop_index(ctx, -1, it->second); - duk_remove(ctx, -2); - return true; - } - } - - // Takes a script object and adds it to the registry, associating - // it with obj_ptr. unregistered_object is not modified. - // If obj_ptr has already been registered with another object, - // the old registry entry will be overidden. - // Does nothing if obj_ptr is NULL. - // Stack: ... [object] -> ... [object] - static void register_native_object(duk_context* ctx, void* obj_ptr) - { - if (obj_ptr == NULL) - return; - - RefMap* ref_map = get_ref_map(ctx); - - push_ref_array(ctx); - - // find next free index - // free indices are kept in a linked list, starting at ref_array[0] - duk_get_prop_index(ctx, -1, 0); - duk_uarridx_t next_free_idx = duk_get_uint(ctx, -1); - duk_pop(ctx); - - if (next_free_idx == 0) { - // no free spots in the array, make a new one at arr.length - next_free_idx = duk_get_length(ctx, -1); - } else { - // free spot found, need to remove it from the free list - // ref_array[0] = ref_array[next_free_idx] - duk_get_prop_index(ctx, -1, next_free_idx); - duk_put_prop_index(ctx, -2, 0); - } - - // std::cout << "putting reference at ref_array[" << next_free_idx << "]" << std::endl; - (*ref_map)[obj_ptr] = next_free_idx; - - duk_dup(ctx, -2); // put object on top - - // ... [object] [ref_array] [object] - duk_put_prop_index(ctx, -2, next_free_idx); - - duk_pop(ctx); // pop ref_array - } - - // Remove the object associated with obj_ptr from the registry - // and invalidate the object's internal native pointer (by setting it to undefined). - // Does nothing if obj_ptr if object was never registered or obj_ptr is NULL. - // Does not affect the stack. - static void find_and_invalidate_native_object(duk_context* ctx, void* obj_ptr) - { - if (obj_ptr == NULL) - return; - - RefMap* ref_map = get_ref_map(ctx); - auto it = ref_map->find(obj_ptr); - if (it == ref_map->end()) // was never registered - return; - - push_ref_array(ctx); - duk_get_prop_index(ctx, -1, it->second); - - // invalidate internal pointer - duk_push_undefined(ctx); - duk_put_prop_string(ctx, -2, "\xFF" "obj_ptr"); - duk_pop(ctx); // pop object - - // remove from references array and add the space it was in to free list - // (refs[0] -> tail) -> (refs[0] -> old_obj_idx -> tail) - - // refs[old_obj_idx] = refs[0] - duk_get_prop_index(ctx, -1, 0); - duk_put_prop_index(ctx, -2, it->second); - - // refs[0] = old_obj_idx - duk_push_uint(ctx, it->second); - duk_put_prop_index(ctx, -2, 0); - - duk_pop(ctx); // pop ref_array - - // also remove from map - // std::cout << "Freeing ref_array[" << it->second << "]" << std::endl; - ref_map->erase(it); - } - - private: - typedef std::unordered_map RefMap; - - static RefMap* get_ref_map(duk_context* ctx) - { - static const char* DUKGLUE_REF_MAP = "dukglue_ref_map"; - static const char* PTR = "ptr"; - - duk_push_heap_stash(ctx); - - if (!duk_has_prop_string(ctx, -1, DUKGLUE_REF_MAP)) { - // doesn't exist yet, need to create it - duk_push_object(ctx); - - duk_push_pointer(ctx, new RefMap()); - duk_put_prop_string(ctx, -2, PTR); - - duk_push_c_function(ctx, ref_map_finalizer, 1); - duk_set_finalizer(ctx, -2); - - duk_put_prop_string(ctx, -2, DUKGLUE_REF_MAP); - } - - duk_get_prop_string(ctx, -1, DUKGLUE_REF_MAP); - duk_get_prop_string(ctx, -1, PTR); - RefMap* map = static_cast(duk_require_pointer(ctx, -1)); - duk_pop_3(ctx); - - return map; - } - - static duk_ret_t ref_map_finalizer(duk_context* ctx) - { - duk_get_prop_string(ctx, 0, "ptr"); - RefMap* map = static_cast(duk_require_pointer(ctx, -1)); - delete map; - - return 0; - } - - static void push_ref_array(duk_context* ctx) - { - static const char* DUKGLUE_REF_ARRAY = "dukglue_ref_array"; - duk_push_heap_stash(ctx); - - if (!duk_has_prop_string(ctx, -1, DUKGLUE_REF_ARRAY)) { - duk_push_array(ctx); - - // ref_array[0] = 0 (initialize free list as empty) - duk_push_int(ctx, 0); - duk_put_prop_index(ctx, -2, 0); - - duk_put_prop_string(ctx, -2, DUKGLUE_REF_ARRAY); - } - - duk_get_prop_string(ctx, -1, DUKGLUE_REF_ARRAY); - duk_remove(ctx, -2); // pop heap stash - } - }; - } -} diff --git a/src/thirdparty/dukglue/detail_stack.h b/src/thirdparty/dukglue/detail_stack.h deleted file mode 100644 index b8164f9404..0000000000 --- a/src/thirdparty/dukglue/detail_stack.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include - -#include "detail_primitive_types.h" -#include "detail_traits.h" -#include "detail_types.h" - -#include - -namespace dukglue -{ - namespace detail - { - // Helper to get the argument tuple type, with correct storage types. - template - struct ArgsTuple { - typedef std::tuple::type...> type; - }; - - // Helper to get argument indices. - // Call read for every Ts[i], for matching argument index Index[i]. - // The traits::index_tuple is used for type inference. - // A concrete example: - // get_values(duktape_context) - // get_values_helper<{int, bool}, {0, 1}>(ctx, ignored) - // std::make_tuple(read(ctx, 0), read(ctx, 1)) - template - typename ArgsTuple::type get_stack_values_helper(duk_context* ctx, dukglue::detail::index_tuple) - { - using namespace dukglue::types; - return std::forward_as_tuple(DukType::type>::template read::type>(ctx, Indexes)...); - } - - // Returns an std::tuple of the values asked for in the template parameters. - // Values will remain on the stack. - // Values are indexed from the bottom of the stack up (0, 1, ...). - // If a value does not exist or does not have the expected type, an error is thrown - // through Duktape (with duk_error(...)), and the function does not return - template - typename ArgsTuple::type get_stack_values(duk_context* ctx) - { - // We need the argument indices for read_value, and we need to be able - // to unpack them as a template argument to match Ts. - // So, we use traits::make_indexes, which returns a traits::index_tuple<0, 1, 2, ...> object. - // We pass that into a helper function so we can put a name to that <0, 1, ...> template argument. - // Here, the type of Args isn't important, the length of it is. - auto indices = typename dukglue::detail::make_indexes::type(); - return get_stack_values_helper(ctx, indices); - } - } -} diff --git a/src/thirdparty/dukglue/detail_traits.h b/src/thirdparty/dukglue/detail_traits.h deleted file mode 100644 index 247e2e52bf..0000000000 --- a/src/thirdparty/dukglue/detail_traits.h +++ /dev/null @@ -1,122 +0,0 @@ -#pragma once - -#include - -namespace dukglue -{ - namespace detail - { - ////////////////////////////////////////////////////////////////////////////////////////////// - - // Credit to LuaState for this code: - // https://github.com/AdUki/LuaState/blob/master/include/Traits.h - - template struct index_tuple {}; - - template - struct make_indexes_impl; - - template - struct make_indexes_impl, T, Types...> - { - typedef typename make_indexes_impl, Types...>::type type; - }; - - template - struct make_indexes_impl > - { - typedef index_tuple type; - }; - - template - struct make_indexes : make_indexes_impl<0, index_tuple<>, Types...> - {}; - - ////////////////////////////////////////////////////////////////////////////////////////////// - - template - struct indexes {}; - - template - struct indexes_builder : indexes_builder {}; - - template - struct indexes_builder<0, Is...> { - typedef indexes index; - }; - - ////////////////////////////////////////////////////////////////////////////////////////////// - - // This mess is used to use function arugments stored in an std::tuple to an - // std::function, function pointer, or method. - - // std::function - template - Ret apply_helper(std::function pf, index_tuple< Indexes... >, std::tuple&& tup) - { - return pf(std::forward(std::get(tup))...); - } - - template - Ret apply(std::function pf, const std::tuple& tup) - { - return apply_helper(pf, typename make_indexes::type(), std::tuple(tup)); - } - - // function pointer - template - Ret apply_fp_helper(Ret(*pf)(Args...), index_tuple< Indexes... >, std::tuple&& tup) - { - return pf(std::forward(std::get(tup))...); - } - - template - Ret apply_fp(Ret(*pf)(Args...), const std::tuple& tup) - { - return apply_fp_helper(pf, typename make_indexes::type(), std::tuple(tup)); - } - - // method pointer - template - Ret apply_method_helper(Ret(Cls::*pf)(Args...), index_tuple< Indexes... >, Cls* obj, std::tuple&& tup) - { - return (*obj.*pf)(std::forward(std::get(tup))...); - } - - template - Ret apply_method(Ret(Cls::*pf)(Args...), Cls* obj, const std::tuple& tup) - { - return apply_method_helper(pf, typename make_indexes::type(), obj, std::tuple(tup)); - } - - // const method pointer - template - Ret apply_method_helper(Ret(Cls::*pf)(Args...) const, index_tuple< Indexes... >, Cls* obj, std::tuple&& tup) - { - return (*obj.*pf)(std::forward(std::get(tup))...); - } - - template - Ret apply_method(Ret(Cls::*pf)(Args...) const, Cls* obj, const std::tuple& tup) - { - return apply_method_helper(pf, typename make_indexes::type(), obj, std::tuple(tup)); - } - - // constructor - template - Cls* apply_constructor_helper(index_tuple< Indexes... >, std::tuple&& tup) - { - return new Cls(std::forward(std::get(tup))...); - } - - template - Cls* apply_constructor(const std::tuple& tup) - { - return apply_constructor_helper(typename make_indexes::type(), std::tuple(tup)); - } - - ////////////////////////////////////////////////////////////////////////////////////////////// - - - } -} \ No newline at end of file diff --git a/src/thirdparty/dukglue/detail_typeinfo.h b/src/thirdparty/dukglue/detail_typeinfo.h deleted file mode 100644 index e906857d02..0000000000 --- a/src/thirdparty/dukglue/detail_typeinfo.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include -#include - -namespace dukglue -{ - namespace detail - { - // same as duk_get_type_name, which is private for some reason *shakes fist* - static inline const char* get_type_name(duk_int_t type_idx) { - static const char* names[] = { - "none", - "undefined", - "null", - "boolean", - "number", - "string", - "object", - "buffer", - "pointer", - "lightfunc" - }; - - if (type_idx >= 0 && type_idx < static_cast(sizeof(names) / sizeof(names[0]))) - return names[type_idx]; - else - return "unknown"; - } - - class TypeInfo - { - public: - TypeInfo(std::type_index&& idx) : index_(idx), base_(nullptr) {} - TypeInfo(const TypeInfo& rhs) : index_(rhs.index_), base_(rhs.base_) {} - - inline void set_base(TypeInfo* base) { - base_ = base; - } - - template - bool can_cast() const { - if (index_ == typeid(T)) - return true; - - if (base_) - return base_->can_cast(); - - return false; - } - - inline bool operator<(const TypeInfo& rhs) const { return index_ < rhs.index_; } - inline bool operator<=(const TypeInfo& rhs) const { return index_ <= rhs.index_; } - inline bool operator>(const TypeInfo& rhs) const { return index_ > rhs.index_; } - inline bool operator>=(const TypeInfo& rhs) const { return index_ >= rhs.index_; } - inline bool operator==(const TypeInfo& rhs) const { return index_ == rhs.index_; } - inline bool operator!=(const TypeInfo& rhs) const { return index_ != rhs.index_; } - - private: - std::type_index index_; - TypeInfo* base_; - }; - } -} diff --git a/src/thirdparty/dukglue/detail_types.h b/src/thirdparty/dukglue/detail_types.h deleted file mode 100644 index 56f16eb579..0000000000 --- a/src/thirdparty/dukglue/detail_types.h +++ /dev/null @@ -1,158 +0,0 @@ -#pragma once - -#include - -#include "detail_refs.h" -#include "detail_typeinfo.h" -#include "detail_class_proto.h" - -// TODO try adding a using namespace std in here if I can scope it to just this file - -namespace dukglue { - namespace types { - - // Bare::type is T stripped of reference, pointer, and const off a type, like so: - // Bare::type = Dog - // Bare::type = Dog - // Bare::type = Dog - // Bare::type = Dog - // Bare::type = Dog - // Bare::type = Dog - template - struct Bare { - typedef typename std::remove_const::type>::type>::type type; - }; - - // DukType provides functions for reading and writing T from the Duktape stack. - // T is always a "bare type," i.e. "Dog" rather than "Dog*". - - // There are two kinds of DukTypes: - // 1. "Native" DukTypes. This is the default. - // These types use an underlying native object allocated on the heap. - // A pointer to the object (of type T*) is expected at script_object.\xFFobj_ptr. - // "Native" DukTypes can return a value (returns a copy-constructed T from the native object), - // a pointer (just returns script_object.\xFFobj_ptr), or a reference (dereferences script_object.\xFFobj_ptr if it is not null). - - // 2. "Value" DukTypes. These are implemented through template specialization. - // This is how primitive types are implemented (int, float, const char*). - // These types can only be returned by value (T) or by const reference (const T&). - // Attempting to read a pointer (T*) or non-const reference (T&) will give a compile-time error. - // You can also use this to implement your own lightweight types, such as a 3D vector. - // (Strictly speaking, non-const references (T&) *could* be returned, but any changes to the reference would - // be discarded. So, I wrote a static assert to disable the option. If you understand the implications, - // you should be able to safely comment out the static_assert in ArgStorage.) - template - struct DukType { - static_assert(std::is_same::type >::value, "Invalid base type, expected bare type"); - - typedef std::false_type IsValueType; - - // read pointer - template::value>::type > - static T* read(duk_context* ctx, duk_idx_t arg_idx) { - using namespace dukglue::detail; - - if (duk_is_null(ctx, arg_idx)) - return nullptr; - - if (!duk_is_object(ctx, arg_idx)) { - duk_int_t type_idx = duk_get_type(ctx, arg_idx); - duk_error(ctx, DUK_RET_TYPE_ERROR, "Argument %d: expected native object, got %s", arg_idx, get_type_name(type_idx)); - } - - duk_get_prop_string(ctx, arg_idx, "\xFF" "type_info"); - if (!duk_is_pointer(ctx, -1)) // missing type_info, must not be a native object - duk_error(ctx, DUK_RET_TYPE_ERROR, "Argument %d: expected native object (missing type_info)", arg_idx); - - // make sure this object can be safely returned as a T* - TypeInfo* info = static_cast(duk_get_pointer(ctx, -1)); - if (!info->can_cast()) - duk_error(ctx, DUK_RET_TYPE_ERROR, "Argument %d: wrong type of native object", arg_idx); - - duk_pop(ctx); // pop type_info - - duk_get_prop_string(ctx, arg_idx, "\xFF" "obj_ptr"); - if (!duk_is_pointer(ctx, -1)) - duk_error(ctx, DUK_RET_TYPE_ERROR, "Argument %d: invalid native object.", arg_idx); - - T* obj = static_cast(duk_get_pointer(ctx, -1)); - - duk_pop(ctx); // pop obj_ptr - - return obj; - } - - // read reference - template::value>::type > - static T& read(duk_context* ctx, duk_idx_t arg_idx) { - T* obj = read(ctx, arg_idx); - if (obj == nullptr) - duk_error(ctx, DUK_RET_TYPE_ERROR, "Argument %d: cannot be null (native function expects reference)", arg_idx); - - return *obj; - } - - // read value - // commented out because it breaks for abstract classes - /*template::type >::value>::type > - static T read(duk_context* ctx, duk_idx_t arg_idx) { - static_assert(std::is_copy_constructible::value, "Reading a value requires a copy-constructable type"); - const T& obj = read(ctx, arg_idx); - return T(obj); - }*/ - - // ----------------------------------------------------- - // Writing - - // Reference - template::value>::type > - static void push(duk_context* ctx, T& value) { - using namespace dukglue::detail; - - if (!RefManager::find_and_push_native_object(ctx, &value)) { - // need to create new script object - ProtoManager::make_script_object(ctx, &value); - RefManager::register_native_object(ctx, &value); - } - } - - // Pointer - template::value>::type > - static void push(duk_context* ctx, T* value) { - if (value == nullptr) - duk_push_null(ctx); - else - push(ctx, *value); - } - - // Value (create new instance on the heap) - // commented out because this is an easy way to accidentally cause a memory leak - /*template::type >::value>::type > - static void push(duk_context* ctx, T value) { - static_assert(std::is_copy_constructible::value, "Cannot push value for non-copy-constructable type."); - return push(ctx, new T(value)); - }*/ - }; - - // Figure out what the type for an argument should be inside the tuple. - // If a function expects a reference to a value type, we need temporary storage for the value. - // For example, a reference to a value type (const int&) will need to be temporarily - // stored in the tuple, so ArgStorage::type == int. - // Native objects are already allocated on the heap, so there's no problem storing, say, const Dog& in the tuple. - template - struct ArgStorage { - private: - typedef typename Bare::type BareType; - //typedef DukType ThisDukType; - typedef typename DukType::IsValueType IsValueType; - - static_assert(!IsValueType::value || !std::is_pointer::value, "Cannot return pointer to value type."); - static_assert(!IsValueType::value || - (!std::is_reference::value || std::is_const::type>::value), - "Value types can only be returned as const references."); - - public: - typedef typename std::conditional::type type; - }; - } -} diff --git a/src/thirdparty/dukglue/dukexception.h b/src/thirdparty/dukglue/dukexception.h deleted file mode 100644 index 9643c27974..0000000000 --- a/src/thirdparty/dukglue/dukexception.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include -#include -#include - -class DukException : public std::exception -{ -public: - virtual const char* what() const noexcept override - { - return mMsg.c_str(); - } - - template - DukException& operator<<(T rhs) - { - std::stringstream ss; - ss << mMsg << rhs; - mMsg = ss.str(); - return *this; - } - -protected: - std::string mMsg; -}; - -class DukErrorException : public DukException -{ -public: - DukErrorException(duk_context* ctx, int return_code, bool pop_error = true) { - if (return_code != 0) { - duk_get_prop_string(ctx, -1, "stack"); - mMsg = duk_safe_to_string(ctx, -1); - duk_pop(ctx); - - if (pop_error) - duk_pop(ctx); - } - } -}; diff --git a/src/thirdparty/dukglue/dukglue.h b/src/thirdparty/dukglue/dukglue.h deleted file mode 100644 index 1e305e91cb..0000000000 --- a/src/thirdparty/dukglue/dukglue.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#pragma warning(push) -#pragma warning(disable : 4267) // conversion from a to b, possible loss of data -#pragma warning(disable : 4505) // unreferenced local function has been removed -#pragma warning(disable : 4702) // unreachable code - -#include "register_function.h" -#include "register_class.h" -#include "register_property.h" -#include "public_util.h" -#include "dukvalue.h" - -#pragma warning(pop) diff --git a/src/thirdparty/dukglue/dukvalue.h b/src/thirdparty/dukglue/dukvalue.h deleted file mode 100644 index 61382acd85..0000000000 --- a/src/thirdparty/dukglue/dukvalue.h +++ /dev/null @@ -1,638 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include "dukexception.h" - -// A variant class for Duktape values. -// This class is not really dependant on the rest of dukglue, but the rest of dukglue is integrated to support it. -// Script objects are persisted by copying a reference to the object into an array in the heap stash. -// When we need to push a reference to the object, we just look up that reference in the stash. - -// DukValues can be copied freely. We use reference counting behind the scenes to keep track of when we need -// to remove our reference from the heap stash. Memory for reference counting is only allocated once a DukValue -// is copied (either by copy constructor or operator=). std::move can be used if you are trying to avoid ref counting -// for some reason. - -// One script object can have multiple, completely separate DukValues "pointing" to it - in this case, there will be -// multiple entries in the "ref array" that point to the same object. This will happen if the same script object is -// put on the stack and turned into a DukValue multiple times independently (copy-constructing/operator=-ing -// DukValues will not do this!). This is okay, as we are only keeping track of these objects to prevent garbage -// collection (and access them later). This could be changed to use a map structure to look up one canonical entry per -// script object in the "ref array" (I guess it would be more like a ref map in this case), but this would require a map -// lookup every time we construct a DukValue. The performance difference probably isn't *that* noticeable (a good map -// would probably be amortized constant-time lookup), but I am guessing constructing many separate DukValues that point -// to the same script object isn't a very common thing. -class DukValue { -public: - enum Type : uint8_t { - //NONE = DUK_TYPE_NONE, - UNDEFINED = DUK_TYPE_UNDEFINED, - NULLREF = DUK_TYPE_NULL, - BOOLEAN = DUK_TYPE_BOOLEAN, - NUMBER = DUK_TYPE_NUMBER, - STRING = DUK_TYPE_STRING, - OBJECT = DUK_TYPE_OBJECT, - BUFFER = DUK_TYPE_BUFFER, // not implemented - POINTER = DUK_TYPE_POINTER, - LIGHTFUNC = DUK_TYPE_LIGHTFUNC // not implemented - }; - - // default constructor just makes an undefined-type DukValue - inline DukValue() : mContext(NULL), mType(UNDEFINED), mRefCount(NULL) {} - - virtual ~DukValue() { - // release any references we have - release_ref_count(); - } - - // move constructor - inline DukValue(DukValue&& move) { - mContext = move.mContext; - mType = move.mType; - mPOD = move.mPOD; - mRefCount = move.mRefCount; - - if (mType == STRING) - mString = std::move(move.mString); - - move.mType = UNDEFINED; - move.mRefCount = NULL; - } - - inline DukValue& operator=(const DukValue& rhs) { - // free whatever we had - release_ref_count(); - - // copy things - mContext = rhs.mContext; - mType = rhs.mType; - mPOD = rhs.mPOD; - - if (mType == STRING) - mString = rhs.mString; - - if (mType == OBJECT) - { - // ref counting increment - if (rhs.mRefCount == NULL) { - // not ref counted before, need to allocate memory - const_cast(rhs).mRefCount = new int(2); - mRefCount = rhs.mRefCount; - } else { - // already refcounting, just increment - mRefCount = rhs.mRefCount; - *mRefCount = *mRefCount + 1; - } - } - - return *this; - } - - // copy constructor - inline DukValue(const DukValue& copy) : DukValue() { - *this = copy; - } - - // equality operator - inline bool operator==(const DukValue& rhs) const - { - if (mType != rhs.mType || mContext != rhs.mContext) - return false; - - switch (mType) { - case UNDEFINED: - case NULLREF: - return true; - case BOOLEAN: - return mPOD.boolean == rhs.mPOD.boolean; - case NUMBER: - return mPOD.number == rhs.mPOD.number; - case STRING: - return mString == rhs.mString; - - case OBJECT: - { - // this could be optimized to only push ref_array once... - this->push(); - rhs.push(); - bool equal = duk_equals(mContext, -1, -2) ? true : false; - duk_pop_2(mContext); - return equal; - } - - case POINTER: - return mPOD.pointer == rhs.mPOD.pointer; - - case BUFFER: - case LIGHTFUNC: - default: - throw DukException() << "operator== not implemented (" << type_name() << ")"; - } - } - - inline bool operator!=(const DukValue& rhs) const { - return !(*this == rhs); - } - - // copies the object at idx on the stack into a new DukValue and returns it - static DukValue copy_from_stack(duk_context* ctx, duk_idx_t idx = -1) { - DukValue value; - value.mContext = ctx; - value.mType = (Type) duk_get_type(ctx, idx); - switch (value.mType) { - case UNDEFINED: - break; - - case NULLREF: - value.mPOD.pointer = NULL; - break; - - case BOOLEAN: - value.mPOD.boolean = duk_require_boolean(ctx, idx) ? true : false; - break; - - case NUMBER: - value.mPOD.number = duk_require_number(ctx, idx); - break; - - case STRING: - { - duk_size_t len; - const char* data = duk_get_lstring(ctx, idx, &len); - value.mString.assign(data, len); - break; - } - - case OBJECT: - value.mPOD.ref_array_idx = stash_ref(ctx, idx); - break; - - case POINTER: - value.mPOD.pointer = duk_require_pointer(ctx, idx); - break; - - case BUFFER: - case LIGHTFUNC: - default: - throw DukException() << "Cannot turn type into DukValue (" << value.type_name() << ")"; - } - - return value; - } - -protected: - static duk_ret_t json_decode_safe(duk_context* ctx, void* user_data) - { - duk_json_decode(ctx, -1); - return 1; - } - -public: - static_assert(sizeof(char) == 1, "Serialization probably broke"); - static DukValue deserialize(duk_context* ctx, const char* data, size_t data_len) { - DukValue v; - v.mContext = ctx; - v.mType = *((Type*)data); - - const char* data_ptr = data + sizeof(Type); - data_len -= sizeof(Type); - - switch (v.mType) { - case UNDEFINED: - case NULLREF: - break; - - case BOOLEAN: - { - if (data_len < 1) - throw DukException() << "Malformed boolean data"; - - v.mPOD.boolean = data[1] == 1 ? true : false; - break; - } - - case NUMBER: - { - if (data_len < sizeof(double)) - throw DukException() << "Malformed number data"; - - v.mPOD.number = *((double*)data_ptr); - break; - } - - case STRING: - { - if (data_len < sizeof(uint32_t)) - throw DukException() << "Malformed string data (no length)"; - uint32_t str_len = *((uint32_t*)data_ptr); - - if (data_len < sizeof(uint32_t) + str_len) - throw DukException() << "Malformed string data (appears truncated)"; - - const char* str_data = (data_ptr + sizeof(uint32_t)); - v.mString.assign(str_data, str_len); - break; - } - - case OBJECT: - { - if (data_len < sizeof(uint32_t)) - throw DukException() << "Malformed object JSON data (no length)"; - uint32_t json_len = *((uint32_t*)data_ptr); - - if (data_len < sizeof(uint32_t) + json_len) - throw DukException() << "Malformed object JSON data (appears truncated)"; - - const char* json_data = (data_ptr + sizeof(uint32_t)); - duk_push_lstring(ctx, json_data, json_len); - int rc = duk_safe_call(ctx, &json_decode_safe, NULL, 1, 1); - if (rc) { - throw DukErrorException(ctx, rc) << "Could not decode JSON"; - } else { - v.mPOD.ref_array_idx = stash_ref(ctx, -1); - duk_pop(ctx); - } - break; - } - - default: - throw DukException() << "not implemented"; - } - - return v; - } - - // same as above (copy_from_stack), but also removes the value we copied from the stack - static DukValue take_from_stack(duk_context* ctx, duk_idx_t idx = -1) { - DukValue val = copy_from_stack(ctx, idx); - duk_remove(ctx, idx); - return val; - } - - // push the value we hold onto the stack - inline void push() const { - duk_context* ctx = mContext; - - switch (mType) { - case UNDEFINED: - duk_push_undefined(ctx); - break; - case NULLREF: - duk_push_null(ctx); - break; - - case BOOLEAN: - duk_push_boolean(ctx, mPOD.boolean); - break; - - case NUMBER: - duk_push_number(ctx, mPOD.number); - break; - - case STRING: - duk_push_lstring(ctx, mString.data(), mString.size()); - break; - - case OBJECT: - push_ref_array(ctx); - duk_get_prop_index(ctx, -1, mPOD.ref_array_idx); - duk_remove(ctx, -2); - break; - - case POINTER: - duk_push_pointer(ctx, mPOD.pointer); - break; - - case BUFFER: - case LIGHTFUNC: - default: - throw DukException() << "DukValue.push() not implemented for type (" << type_name() << ")"; - } - } - - // various (type-safe) getters - inline bool as_bool() const { - if (mType != BOOLEAN) - throw DukException() << "Expected boolean, got " << type_name(); - return mPOD.boolean; - } - - inline double as_double() const { - if (mType != NUMBER) - throw DukException() << "Expected number, got " << type_name(); - return mPOD.number; - } - - inline float as_float() const { - if (mType != NUMBER) - throw DukException() << "Expected number, got " << type_name(); - return static_cast(mPOD.number); - } - - inline duk_int_t as_int() const { - if (mType != NUMBER) - throw DukException() << "Expected number, got " << type_name(); - return static_cast(mPOD.number); - } - - inline duk_uint_t as_uint() const { - if (mType != NUMBER) - throw DukException() << "Expected number, got " << type_name(); - return static_cast(mPOD.number); - } - - inline void* as_pointer() const { - if (mType != POINTER && mType != NULLREF) - throw DukException() << "Expected pointer or null, got " << type_name(); - return mPOD.pointer; - } - - inline const std::string& as_string() const { - if (mType != STRING) - throw DukException() << "Expected string, got " << type_name(); - return mString; - } - - inline const char* as_c_string() const { - if (mType != STRING) - throw DukException() << "Expected string, got " << type_name(); - return mString.data(); - } - - inline Type type() const { - return mType; - } - - // same as duk_get_type_name(), but that's internal to Duktape, so we shouldn't use it - inline const char* type_name() const { - switch (mType) { - case UNDEFINED: return "undefined"; - case NULLREF: return "null"; - case BOOLEAN: return "boolean"; - case NUMBER: return "number"; - case STRING: return "string"; - case OBJECT: return "object"; - case BUFFER: return "buffer"; - case POINTER: return "pointer"; - case LIGHTFUNC: return "lightfunc"; - } - return "?"; - } - - inline duk_context* context() const { - return mContext; - } - - // Important limitations: - // - The returned value is binary and will not behave well if you treat it like a string (it will almost certainly contain '\0'). - // If you need to transport it like a string, maybe try encoding it as base64. - // - Strings longer than 2^32 (UINT32_MAX) characters will throw an exception. You can raise this to be a uint64_t if you need - // really long strings for some reason (be sure to change DukValue::deserialize() as well). - // - Objects are encoded to JSON and then sent like a string. If your object can't be encoded as JSON (i.e. it's a function), - // this will not work. This can be done, but I chose not to because it poses a security issue if you deserializing untrusted data. - // If you require this functionality, you'll have to add it yourself with using duk_dump_function(...). - static_assert(sizeof(char) == 1, "Serialization probably broke"); - std::vector serialize() const { - std::vector buff; - buff.resize(sizeof(Type)); - *((Type*)buff.data()) = mType; - - switch (mType) { - case UNDEFINED: - case NULLREF: - break; - - case BOOLEAN: - { - buff.push_back(mPOD.boolean ? 1 : 0); - break; - } - - case NUMBER: - { - buff.resize(buff.size() + sizeof(double)); - *((double*)(buff.data() + sizeof(Type))) = mPOD.number; - break; - } - - case STRING: - { - if (mString.length() > static_cast(UINT32_MAX)) - throw DukException() << "String length larger than uint32_t max"; - - uint32_t len = mString.length(); - buff.resize(buff.size() + sizeof(uint32_t) + len); - - uint32_t* len_ptr = (uint32_t*)(buff.data() + sizeof(Type)); - *len_ptr = len; - - char* out_ptr = (char*)(buff.data() + sizeof(Type) + sizeof(uint32_t)); - strncpy(out_ptr, mString.data(), len); // note: this will NOT be null-terminated - break; - } - - case OBJECT: - { - push(); - if (duk_is_function(mContext, -1)) { - duk_pop(mContext); - throw DukException() << "Functions cannot be serialized"; - // well, technically they can...see the comments at the start of this method - } - - std::string json = duk_json_encode(mContext, -1); - duk_pop(mContext); - - if (json.length() > static_cast(UINT32_MAX)) - throw DukException() << "JSON length larger than uint32_t max"; - - uint32_t len = json.length(); - buff.resize(buff.size() + sizeof(uint32_t) + len); - - uint32_t* len_ptr = (uint32_t*)(buff.data() + sizeof(Type)); - *len_ptr = len; - - char* out_ptr = (char*)(buff.data() + sizeof(Type) + sizeof(uint32_t)); - strncpy(out_ptr, json.data(), len); // note: this will NOT be null-terminated - break; - } - - default: - throw DukException() << "Type not implemented for serialization."; - } - - return buff; - } - -private: - // THIS IS COMPLETELY UNRELATED TO DETAIL_REFS.H. - // detail_refs.h stores a mapping of native object -> script object. - // This just stores arbitrary script objects (which likely have no native object backing them). - // If I was smarter I might merge the two implementations, but this one is simpler - // (since we don't need the std::map here). - static void push_ref_array(duk_context* ctx) - { - static const char* DUKVALUE_REF_ARRAY = "dukglue_dukvalue_refs"; - duk_push_heap_stash(ctx); - - if (!duk_has_prop_string(ctx, -1, DUKVALUE_REF_ARRAY)) { - duk_push_array(ctx); - - // ref_array[0] = 0 (initialize free list as empty) - duk_push_int(ctx, 0); - duk_put_prop_index(ctx, -2, 0); - - duk_put_prop_string(ctx, -2, DUKVALUE_REF_ARRAY); - } - - duk_get_prop_string(ctx, -1, DUKVALUE_REF_ARRAY); - duk_remove(ctx, -2); // pop heap stash - } - - // put a new reference into the ref array and return its index in the array - static duk_uint_t stash_ref(duk_context* ctx, duk_idx_t idx) - { - push_ref_array(ctx); - - // if idx is relative, we need to adjust it to deal with the array we just pushed - if (idx < 0) - idx--; - - // find next free index - // free indices are kept in a linked list, starting at ref_array[0] - duk_get_prop_index(ctx, -1, 0); - duk_uarridx_t next_free_idx = duk_get_uint(ctx, -1); - duk_pop(ctx); - - if (next_free_idx == 0) { - // no free spots in the array, make a new one at arr.length - next_free_idx = duk_get_length(ctx, -1); - } else { - // free spot found, need to remove it from the free list - // ref_array[0] = ref_array[next_free_idx] - duk_get_prop_index(ctx, -1, next_free_idx); - duk_put_prop_index(ctx, -2, 0); - } - - duk_dup(ctx, idx); // copy value we are storing (since store consumes it) - duk_put_prop_index(ctx, -2, next_free_idx); // store it (consumes duplicated value) - duk_pop(ctx); // pop ref array - - return next_free_idx; - } - - // remove ref_array_idx from the ref array and add its spot to the free list (at refs[0]) - static void free_ref(duk_context* ctx, duk_uarridx_t ref_array_idx) - { - push_ref_array(ctx); - - // add this spot to the free list - // refs[old_obj_idx] = refs[0] (implicitly gives up our reference) - duk_get_prop_index(ctx, -1, 0); - duk_put_prop_index(ctx, -2, ref_array_idx); - - // refs[0] = old_obj_idx - duk_push_uint(ctx, ref_array_idx); - duk_put_prop_index(ctx, -2, 0); - - duk_pop(ctx); // pop ref array - } - - // this is for reference counting - used to release our reference based on the state - // of mRefCount. If mRefCount is NULL, we never got copy constructed, so we have ownership - // of our reference and can free it. If it's not null and above 1, we decrement the counter - // (someone else owns the reference). If it's not null and equal to 1, we are the last owner - // of a previously shared reference, so we can free it. - void release_ref_count() - { - if (mType == OBJECT) - { - if (mRefCount != NULL) - { - // sharing with another DukValue, are we the only one left? - if (*mRefCount > 1) { // still someone else referencing this - *mRefCount = *mRefCount - 1; - } else { - // not sharing anymore, we can free it - free_ref(mContext, mPOD.ref_array_idx); - delete mRefCount; - } - - mRefCount = NULL; - } else { - // not sharing with any other DukValue, free it - free_ref(mContext, mPOD.ref_array_idx); - } - - mType = UNDEFINED; - } - } - - duk_context* mContext; - Type mType; // our type - one of the standard Duktape DUK_TYPE_* values - - // This holds the plain-old-data types. Since this is a variant, - // we hold only one value at a time, so this is a union to save - // a bit of space. - union ValueTypes { - bool boolean; - double number; - void* pointer; // if mType == NULLREF, this is 0 (otherwise holds pointer value when mType == POINTER) - duk_uarridx_t ref_array_idx; - } mPOD; - - std::string mString; // if it's a string, we store it with std::string - int* mRefCount; // if mType == OBJECT and we're sharing, this will point to our ref counter - -public: - DukValue operator [](const std::string_view &key) const - { - push(); - duk_get_prop_lstring(mContext, -1, key.data(), key.size()); - auto result = DukValue::take_from_stack(mContext); - duk_pop(mContext); - return result; - } - - bool is_array() const - { - push(); - bool result = duk_is_array(mContext, -1); - duk_pop(mContext); - return result; - } - - std::vector as_array() const - { - push(); - if (!duk_is_array(mContext, -1)) - { - duk_pop(mContext); - throw DukException() << "Expected array, got " << type_name(); - } - - auto arrayLength = duk_get_length(mContext, -1); - std::vector result; - result.reserve(arrayLength); - for (size_t i = 0; i < arrayLength; i++) - { - duk_get_prop_index(mContext, -1, i); - result.push_back(take_from_stack(mContext)); - } - - duk_pop(mContext); - return result; - } - - bool is_function() const - { - push(); - bool result = duk_is_function(mContext, -1); - duk_pop(mContext); - return result; - } -}; diff --git a/src/thirdparty/dukglue/public_util.h b/src/thirdparty/dukglue/public_util.h deleted file mode 100644 index 5dc7947cab..0000000000 --- a/src/thirdparty/dukglue/public_util.h +++ /dev/null @@ -1,316 +0,0 @@ -#pragma once - -#include "dukexception.h" -#include "detail_traits.h" // for index_tuple/make_indexes -#include "detail_types.h" - -// This file has some useful utility functions for users. -// Hopefully this saves you from wading through the implementation. - -/** - * @brief Push a value onto the duktape stack. - * - * WARNING: THIS IS NOT "PROTECTED." If an error occurs when pushing (unlikely, but possible), - * the Duktape fatal error handler will be invoked (and the program will probably terminate). - * - * @param ctx duktape context - * @param[in] val value to push - */ -template -void dukglue_push(duk_context* ctx, const FullT& val) { - // ArgStorage has some static_asserts in it that validate value types, - // so we typedef it to force ArgStorage to compile and run the asserts - typedef typename dukglue::types::ArgStorage::type ValidateReturnType; - - using namespace dukglue::types; - DukType::type>::template push(ctx, std::move(val)); -} - -template -void dukglue_push(duk_context* ctx, const T& arg, ArgTs... args) -{ - dukglue_push(ctx, arg); - dukglue_push(ctx, args...); -} - -inline void dukglue_push(duk_context* ctx) -{ - // no-op -} - - -/** - * WARNING: THIS IS NOT "PROTECTED." If an error occurs while reading (which is possible if you didn't - * explicitly check the type), the fatal Duktape error handler will be invoked, and the program - * will probably abort. - */ -template -void dukglue_read(duk_context* ctx, duk_idx_t arg_idx, RetT* out) -{ - // ArgStorage has some static_asserts in it that validate value types, - // so we typedef it to force ArgStorage to compile and run the asserts - typedef typename dukglue::types::ArgStorage::type ValidateReturnType; - - using namespace dukglue::types; - *out = DukType::type>::template read(ctx, arg_idx); -} - - -// methods - -// leaves return value on stack -template -void dukglue_call_method(duk_context* ctx, const ObjT& obj, const char* method_name, ArgTs... args) -{ - dukglue_push(ctx, obj); - duk_get_prop_string(ctx, -1, method_name); - - if (duk_check_type(ctx, -1, DUK_TYPE_UNDEFINED)) { - duk_error(ctx, DUK_ERR_REFERENCE_ERROR, "Method does not exist", method_name); - return; - } - - if (!duk_is_callable(ctx, -1)) { - duk_error(ctx, DUK_ERR_TYPE_ERROR, "Property is not callable"); - return; - } - - duk_swap_top(ctx, -2); - dukglue_push(ctx, args...); - duk_call_method(ctx, sizeof...(args)); -} - -namespace dukglue { -namespace detail { - -template -struct SafeMethodCallData { - const ObjT* obj; - const char* method_name; - std::tuple args; - RetT* out; -}; - -template -void call_method_safe_helper(duk_context* ctx, const ObjT& obj, const char* method_name, std::tuple& tup, index_tuple indexes) -{ - dukglue_call_method(ctx, obj, method_name, std::forward(std::get(tup))...); -} - -template -typename std::enable_if::value, duk_idx_t>::type call_method_safe(duk_context* ctx, void* udata) -{ - typedef SafeMethodCallData DataT; - DataT* data = (DataT*) udata; - call_method_safe_helper(ctx, *(data->obj), data->method_name, data->args, typename make_indexes::type()); - return 1; -} - -template -typename std::enable_if::value, duk_idx_t>::type call_method_safe(duk_context* ctx, void* udata) -{ - typedef SafeMethodCallData DataT; - DataT* data = (DataT*)udata; - - call_method_safe_helper(ctx, *(data->obj), data->method_name, data->args, typename make_indexes::type()); - dukglue_read(ctx, -1, data->out); - return 1; -} - -} -} - -template -typename std::enable_if::value, RetT>::type dukglue_pcall_method(duk_context* ctx, const ObjT& obj, const char* method_name, ArgTs... args) -{ - dukglue::detail::SafeMethodCallData data { - &obj, method_name, std::tuple(args...), nullptr - }; - - duk_idx_t rc = duk_safe_call(ctx, &dukglue::detail::call_method_safe, (void*) &data, 0, 1); - if (rc != 0) - throw DukErrorException(ctx, rc); - - duk_pop(ctx); // remove result from stack -} - -template -typename std::enable_if::value, RetT>::type dukglue_pcall_method(duk_context* ctx, const ObjT& obj, const char* method_name, ArgTs... args) -{ - RetT out; - dukglue::detail::SafeMethodCallData data { - &obj, method_name, std::tuple(args...), &out - }; - - duk_idx_t rc = duk_safe_call(ctx, &dukglue::detail::call_method_safe, (void*) &data, 0, 1); - if (rc != 0) - throw DukErrorException(ctx, rc); - - duk_pop(ctx); // remove result from stack - return std::move(out); -} - - -// calls - -// leaves return value on the stack -template -void dukglue_call(duk_context* ctx, const ObjT& func, ArgTs... args) -{ - dukglue_push(ctx, func); - if (!duk_is_callable(ctx, -1)) { - duk_pop(ctx); - duk_error(ctx, DUK_ERR_TYPE_ERROR, "Object is not callable"); - return; - } - - dukglue_push(ctx, args...); - duk_call(ctx, sizeof...(args)); -} - - -// safe call -namespace dukglue { -namespace detail { - -template -struct SafeCallData { - const ObjT* obj; - std::tuple args; - RetT* out; -}; - -template -void call_safe_helper(duk_context* ctx, const ObjT& obj, std::tuple& tup, index_tuple indexes) -{ - dukglue_call(ctx, obj, std::forward(std::get(tup))...); -} - -// leaves result on stack -template -typename std::enable_if::value, duk_ret_t>::type call_safe(duk_context* ctx, void* udata) -{ - typedef SafeCallData DataT; - DataT* data = (DataT*)udata; - - call_safe_helper(ctx, *(data->obj), data->args, typename make_indexes::type()); - return 1; -} - -// leaves result on stack -// The result is read into RetT here because it can potentially trigger an error (with duk_error). -// If we did it "above" this function, that error would trigger a panic instead of error handling. -template -typename std::enable_if::value, duk_ret_t>::type call_safe(duk_context* ctx, void* udata) -{ - typedef SafeCallData DataT; - DataT* data = (DataT*)udata; - - call_safe_helper(ctx, *(data->obj), data->args, typename make_indexes::type()); - dukglue_read(ctx, -1, data->out); - return 1; -} - -} -} - -// Unlike duktape, this will remove the return value from the stack! -template -typename std::enable_if::value, RetT>::type dukglue_pcall(duk_context* ctx, const ObjT& obj, ArgTs... args) -{ - dukglue::detail::SafeCallData data{ - &obj, std::tuple(args...), nullptr - }; - - duk_int_t rc = duk_safe_call(ctx, &dukglue::detail::call_safe, (void*) &data, 0, 1); - if (rc != 0) - throw DukErrorException(ctx, rc); - duk_pop(ctx); // remove result from stack -} - -template -typename std::enable_if::value, RetT>::type dukglue_pcall(duk_context* ctx, const ObjT& obj, ArgTs... args) -{ - RetT result; - dukglue::detail::SafeCallData data{ - &obj, std::tuple(args...), &result - }; - - duk_int_t rc = duk_safe_call(ctx, &dukglue::detail::call_safe, (void*) &data, 0, 1); - if (rc != 0) - throw DukErrorException(ctx, rc); - - duk_pop(ctx); // remove result from stack - return std::move(result); -} - -// same as dukglue_pcall, but leaves the result or error on the stack and returns the Duktape return code -template -duk_int_t dukglue_pcall_raw(duk_context* ctx, const ObjT& obj, ArgTs... args) -{ - dukglue::detail::SafeCallData data{ - &obj, std::tuple(args...), nullptr - }; - - return duk_safe_call(ctx, &dukglue::detail::call_safe, (void*)&data, 0, 1); -} - - -// peval -namespace dukglue { -namespace detail { - -template -struct SafeEvalData { - const char* str; - RetT* out; -}; - -template -duk_ret_t eval_safe(duk_context* ctx, void* udata) -{ - SafeEvalData* data = (SafeEvalData*) udata; - - duk_eval_string(ctx, data->str); - dukglue_read(ctx, -1, data->out); - return 1; -} - -} -} - -template -typename std::enable_if::value, RetT>::type dukglue_peval(duk_context* ctx, const char* str) -{ - int prev_top = duk_get_top(ctx); - int rc = duk_peval_string(ctx, str); - if (rc != 0) - throw DukErrorException(ctx, rc); - - duk_pop_n(ctx, duk_get_top(ctx) - prev_top); // pop any results -} - -template -typename std::enable_if::value, RetT>::type dukglue_peval(duk_context* ctx, const char* str) -{ - int prev_top = duk_get_top(ctx); - - RetT ret; - dukglue::detail::SafeEvalData data{ - str, &ret - }; - - int rc = duk_safe_call(ctx, &dukglue::detail::eval_safe, (void*) &data, 0, 1); - if (rc != 0) - throw DukErrorException(ctx, rc); - duk_pop_n(ctx, duk_get_top(ctx) - prev_top); // pop any results - return ret; -} - -// register a global object (very simple helper, but very common for "Hello World"-ish applications) -template -inline void dukglue_register_global(duk_context* ctx, const T& obj, const char* name) -{ - dukglue_push(ctx, obj); - duk_put_global_string(ctx, name); -} \ No newline at end of file diff --git a/src/thirdparty/dukglue/register_class.h b/src/thirdparty/dukglue/register_class.h deleted file mode 100644 index bb7669029f..0000000000 --- a/src/thirdparty/dukglue/register_class.h +++ /dev/null @@ -1,202 +0,0 @@ -#pragma once - -#include "detail_class_proto.h" -#include "detail_constructor.h" -#include "detail_method.h" - -// Set the constructor for the given type. -template -void dukglue_register_constructor(duk_context* ctx, const char* name) -{ - duk_c_function constructor_func = dukglue::detail::call_native_constructor; - - duk_push_c_function(ctx, constructor_func, sizeof...(Ts)); - - // set constructor_func.prototype - dukglue::detail::ProtoManager::push_prototype(ctx); - duk_put_prop_string(ctx, -2, "prototype"); - - // set name = constructor_func - duk_put_global_string(ctx, name); -} - -template -void dukglue_register_constructor_managed(duk_context* ctx, const char* name) -{ - duk_c_function constructor_func = dukglue::detail::call_native_constructor; - duk_c_function finalizer_func = dukglue::detail::managed_finalizer; - - duk_push_c_function(ctx, constructor_func, sizeof...(Ts)); - - // create new prototype with finalizer - duk_push_object(ctx); - - // set the finalizer - duk_push_c_function(ctx, finalizer_func, 1); - duk_set_finalizer(ctx, -2); - - // hook prototype with finalizer up to real class prototype - // must use duk_set_prototype, not set the .prototype property - dukglue::detail::ProtoManager::push_prototype(ctx); - duk_set_prototype(ctx, -2); - - // set constructor_func.prototype to the prototype with the finalizer - duk_put_prop_string(ctx, -2, "prototype"); - - // set name = constructor_func - duk_put_global_string(ctx, name); -} - -template -void dukglue_set_base_class(duk_context* ctx) -{ - static_assert(!std::is_pointer::value && !std::is_pointer::value - && !std::is_const::value && !std::is_const::value, "Use bare class names."); - static_assert(std::is_base_of::value, "Invalid class hierarchy!"); - - using namespace dukglue::detail; - - // Derived.type_info->set_base(Base.type_info) - ProtoManager::push_prototype(ctx); - duk_get_prop_string(ctx, -1, "\xFF" "type_info"); - TypeInfo* derived_type_info = static_cast(duk_require_pointer(ctx, -1)); - duk_pop_2(ctx); - - ProtoManager::push_prototype(ctx); - duk_get_prop_string(ctx, -1, "\xFF" "type_info"); - TypeInfo* base_type_info = static_cast(duk_require_pointer(ctx, -1)); - duk_pop_2(ctx); - - derived_type_info->set_base(base_type_info); - - // also set up the prototype chain - ProtoManager::push_prototype(ctx); - ProtoManager::push_prototype(ctx); - duk_set_prototype(ctx, -2); - duk_pop(ctx); -} - -// methods -template -void dukglue_register_method_compiletime(duk_context* ctx, RetType(Cls::*method)(Ts...), const char* name) -{ - static_assert(std::is_same::value, "Mismatching method types."); - dukglue_register_method_compiletime(ctx, name); -} - -template -void dukglue_register_method_compiletime(duk_context* ctx, RetType(Cls::*method)(Ts...) const, const char* name) -{ - static_assert(std::is_same::value, "Mismatching method types."); - dukglue_register_method_compiletime(ctx, name); -} - -template -void dukglue_register_method_compiletime(duk_context* ctx, const char* name) -{ - using namespace dukglue::detail; - typedef MethodInfo MethodInfo; - - duk_c_function method_func = MethodInfo::template MethodCompiletime::call_native_method; - - ProtoManager::push_prototype(ctx); - - duk_push_c_function(ctx, method_func, sizeof...(Ts)); - duk_put_prop_string(ctx, -2, name); // consumes func above - - duk_pop(ctx); // pop prototype -} - -template -void dukglue_register_method(duk_context* ctx, RetType(Cls::*method)(Ts...), const char* name) -{ - dukglue_register_method(ctx, method, name); -} - -template -void dukglue_register_method(duk_context* ctx, RetType(Cls::*method)(Ts...) const, const char* name) -{ - dukglue_register_method(ctx, method, name); -} - -// I'm sorry this signature is so long, but I figured it was better than duplicating the method, -// once for const methods and once for non-const methods. -template -void dukglue_register_method(duk_context* ctx, typename std::conditional::type method, const char* name) -{ - using namespace dukglue::detail; - typedef MethodInfo MethodInfo; - - duk_c_function method_func = MethodInfo::MethodRuntime::call_native_method; - - ProtoManager::push_prototype(ctx); - - duk_push_c_function(ctx, method_func, sizeof...(Ts)); - - duk_push_pointer(ctx, new typename MethodInfo::MethodHolder{ method }); - duk_put_prop_string(ctx, -2, "\xFF" "method_holder"); // consumes raw method pointer - - // make sure we free the method_holder when this function is removed - duk_push_c_function(ctx, MethodInfo::MethodRuntime::finalize_method, 1); - duk_set_finalizer(ctx, -2); - - duk_put_prop_string(ctx, -2, name); // consumes method function - - duk_pop(ctx); // pop prototype -} - -// methods with a variable number of (script) arguments -template -inline void dukglue_register_method_varargs(duk_context* ctx, duk_ret_t(Cls::*method)(duk_context*), const char* name) -{ - dukglue_register_method_varargs(ctx, method, name); -} - -template -inline void dukglue_register_method_varargs(duk_context* ctx, duk_ret_t(Cls::*method)(duk_context*) const, const char* name) -{ - dukglue_register_method_varargs(ctx, method, name); -} - -template -void dukglue_register_method_varargs(duk_context* ctx, - typename std::conditional::type method, - const char* name) -{ - using namespace dukglue::detail; - typedef MethodVariadicRuntime MethodVariadicInfo; - - duk_c_function method_func = MethodVariadicInfo::call_native_method; - - ProtoManager::push_prototype(ctx); - - duk_push_c_function(ctx, method_func, DUK_VARARGS); - - duk_push_pointer(ctx, new typename MethodVariadicInfo::MethodHolderVariadic{ method }); - duk_put_prop_string(ctx, -2, "\xFF" "method_holder"); // consumes raw method pointer - - // make sure we free the method_holder when this function is removed - duk_push_c_function(ctx, MethodVariadicInfo::finalize_method, 1); - duk_set_finalizer(ctx, -2); - - duk_put_prop_string(ctx, -2, name); // consumes method function - - duk_pop(ctx); // pop prototype -} - -inline void dukglue_invalidate_object(duk_context* ctx, void* obj_ptr) -{ - dukglue::detail::RefManager::find_and_invalidate_native_object(ctx, obj_ptr); -} - -// register a deleter -template -void dukglue_register_delete(duk_context* ctx) -{ - duk_c_function delete_func = dukglue::detail::call_native_deleter; - - dukglue::detail::ProtoManager::push_prototype(ctx); - duk_push_c_function(ctx, delete_func, 0); - duk_put_prop_string(ctx, -2, "delete"); - duk_pop(ctx); // pop prototype -} \ No newline at end of file diff --git a/src/thirdparty/dukglue/register_function.h b/src/thirdparty/dukglue/register_function.h deleted file mode 100644 index 77e83aad95..0000000000 --- a/src/thirdparty/dukglue/register_function.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "detail_function.h" - -// Register a function, embedding the function address at compile time. -// According to benchmarks, there's really not much reason to do this -// (inconsistent 2-3% performance improvement for a 10,000 function call stress test averaged over 100 runs), -// since it has much uglier syntax and may bloat executable size if you have many functions with identical signatures. -template -void dukglue_register_function_compiletime(duk_context* ctx, RetType(*)(Ts...), const char* name) -{ - static_assert(std::is_same::value, - "Mismatching function pointer template parameter and function pointer argument types. " - "Try: dukglue_register_function(ctx, \"funcName\", func)"); - - duk_c_function evalFunc = dukglue::detail::FuncInfoHolder::template FuncActual::call_native_function; - - duk_push_c_function(ctx, evalFunc, sizeof...(Ts)); - duk_put_global_string(ctx, name); -} - -// Register a function. -template -void dukglue_register_function(duk_context* ctx, RetType(*funcToCall)(Ts...), const char* name) -{ - duk_c_function evalFunc = dukglue::detail::FuncInfoHolder::FuncRuntime::call_native_function; - - duk_push_c_function(ctx, evalFunc, sizeof...(Ts)); - - static_assert(sizeof(RetType(*)(Ts...)) == sizeof(void*), "Function pointer and data pointer are different sizes"); - duk_push_pointer(ctx, reinterpret_cast(funcToCall)); - duk_put_prop_string(ctx, -2, "\xFF" "func_ptr"); - - duk_put_global_string(ctx, name); -} diff --git a/src/thirdparty/dukglue/register_property.h b/src/thirdparty/dukglue/register_property.h deleted file mode 100644 index c71dc0a3ac..0000000000 --- a/src/thirdparty/dukglue/register_property.h +++ /dev/null @@ -1,124 +0,0 @@ -#pragma once - -#include "detail_method.h" - -// const getter, setter -template -void dukglue_register_property(duk_context* ctx, - RetT(Cls::*getter)() const, - void(Cls::*setter)(ArgT), - const char* name) -{ - dukglue_register_property(ctx, getter, setter, name); -} - -// const getter, no setter -template -void dukglue_register_property(duk_context* ctx, - RetT(Cls::*getter)() const, - std::nullptr_t setter, - const char* name) -{ - dukglue_register_property(ctx, getter, setter, name); -} - -// non-const getter, setter -template -void dukglue_register_property(duk_context* ctx, - RetT(Cls::*getter)(), - void(Cls::*setter)(ArgT), - const char* name) -{ - dukglue_register_property(ctx, getter, setter, name); -} - -// non-const getter, no setter -template -void dukglue_register_property(duk_context* ctx, - RetT(Cls::*getter)(), - std::nullptr_t setter, - const char* name) -{ - dukglue_register_property(ctx, getter, setter, name); -} - -// no getter, setter -template -void dukglue_register_property(duk_context* ctx, - std::nullptr_t getter, - void(Cls::*setter)(ArgT), - const char* name) -{ - dukglue_register_property(ctx, getter, setter, name); -} - -// no getter, no setter -template -void dukglue_register_property(duk_context* ctx, std::nullptr_t getter, std::nullptr_t setter, const char* name) -{ - // strictly speaking I think duktape can probably handle neither - // (according to the wonderful API docs), but I don't know why you - // would want to do this in the first place - static_assert(std::is_void::value, "Must have getter or setter"); -} - -inline duk_ret_t dukglue_throw_error(duk_context* ctx) -{ - duk_error(ctx, DUK_ERR_TYPE_ERROR, "Property does not have getter or setter."); -} - -template -void dukglue_register_property(duk_context* ctx, - typename std::conditional::type getter, - void(Cls::*setter)(ArgT), - const char* name) -{ - using namespace dukglue::detail; - typedef MethodInfo GetterMethodInfo; - typedef MethodInfo SetterMethodInfo; - - ProtoManager::push_prototype(ctx); - - // push key - duk_push_string(ctx, name); - - // push getter - if (getter != nullptr) { - duk_c_function method_func = GetterMethodInfo::MethodRuntime::call_native_method; - - duk_push_c_function(ctx, method_func, 0); - - duk_push_pointer(ctx, new typename GetterMethodInfo::MethodHolder{ getter }); - duk_put_prop_string(ctx, -2, "\xFF" "method_holder"); // consumes raw method pointer - - // make sure we free the method_holder when this function is removed - duk_push_c_function(ctx, GetterMethodInfo::MethodRuntime::finalize_method, 1); - duk_set_finalizer(ctx, -2); - } else { - duk_push_c_function(ctx, dukglue_throw_error, 1); - } - - if (setter != nullptr) { - duk_c_function method_func = SetterMethodInfo::MethodRuntime::call_native_method; - - duk_push_c_function(ctx, method_func, 1); - - duk_push_pointer(ctx, new typename SetterMethodInfo::MethodHolder{ setter }); - duk_put_prop_string(ctx, -2, "\xFF" "method_holder"); // consumes raw method pointer - - // make sure we free the method_holder when this function is removed - duk_push_c_function(ctx, SetterMethodInfo::MethodRuntime::finalize_method, 1); - duk_set_finalizer(ctx, -2); - } else { - duk_push_c_function(ctx, dukglue_throw_error, 1); - } - - duk_uint_t flags = DUK_DEFPROP_HAVE_GETTER - | DUK_DEFPROP_HAVE_SETTER - | DUK_DEFPROP_SET_ENUMERABLE - | DUK_DEFPROP_HAVE_CONFIGURABLE /* set not configurable (from JS) */ - | DUK_DEFPROP_FORCE /* allow overriding built-ins and previously defined properties */; - - duk_def_prop(ctx, -4, flags); - duk_pop(ctx); // pop prototype -} diff --git a/src/thirdparty/duktape/README.md b/src/thirdparty/duktape/README.md deleted file mode 100644 index 1eb700e796..0000000000 --- a/src/thirdparty/duktape/README.md +++ /dev/null @@ -1,23 +0,0 @@ -## Duktape - -[Duktape](https://duktape.org/) is geared around compile time switches to minimise code size and memory footprint. Because of this, we can not rely on shared libraries, as we have no control on what switches it may have been compiled with. It also can cause inconsistent behaviour of plugins across multiple platforms, depending on what version of Duktape is installed on the system. - -[#14853](https://github.com/OpenRCT2/OpenRCT2/issues/14853) is the primary issue that was solved by embedding our own copy of duktape. - -This directory contains a pre-configured copy of duktape v2.7.0 with the following switches enabled: -* `DUK_USE_CPP_EXCEPTIONS` for C++ exception handling. -* `DUK_USE_DATE_NOW_WINDOWS` for Windows Vista compatibility. -* `DUK_USE_INTERRUPT_COUNTER` for aborting long or non-terminating scripts. - -duktape is configured with a command such as: -``` -python2 duktape-2.7.0/tools/configure.py --output-directory src/thirdparty/duktape \ - -DDUK_USE_CPP_EXCEPTIONS \ - -DDUK_USE_DATE_NOW_WINDOWS \ - -DDUK_USE_INTERRUPT_COUNTER \ - -DDUK_USE_EXEC_TIMEOUT_CHECK=duk_exec_timeout_check -``` - -Then manually apply configuration fixup for `duk_exec_timeout_check` (e.g. compare repo version of `duk_config.h` with the one freshly generated) and rename resulting `duktape.c` file to `duktape.cpp` - -See [Configuring Duktape for build](https://wiki.duktape.org/configuring) for more information. diff --git a/src/thirdparty/duktape/duk_config.h b/src/thirdparty/duktape/duk_config.h deleted file mode 100644 index 4ac2e1d54d..0000000000 --- a/src/thirdparty/duktape/duk_config.h +++ /dev/null @@ -1,3213 +0,0 @@ -/* - * duk_config.h configuration header generated by genconfig.py. - * - * Git commit: external - * Git describe: external - * Git branch: external - * - * Supported platforms: - * - Mac OSX, iPhone, Darwin - * - Orbis - * - OpenBSD - * - Generic BSD - * - Atari ST TOS - * - AmigaOS - * - Durango (XboxOne) - * - Windows - * - Flashplayer (Crossbridge) - * - QNX - * - TI-Nspire - * - Emscripten - * - Android - * - Linux - * - Solaris - * - AIX - * - HPUX - * - Generic POSIX - * - Cygwin - * - Generic UNIX - * - Generic fallback - * - * Supported architectures: - * - x86 - * - x64 - * - x32 - * - ARM 32-bit - * - ARM 64-bit - * - MIPS 32-bit - * - MIPS 64-bit - * - PowerPC 32-bit - * - PowerPC 64-bit - * - SPARC 32-bit - * - SPARC 64-bit - * - RISC-V 32-bit - * - RISC-V 64-bit - * - SuperH - * - Motorola 68k - * - Emscripten - * - Generic - * - * Supported compilers: - * - Clang - * - GCC - * - MSVC - * - Emscripten - * - TinyC - * - VBCC - * - Bruce's C compiler - * - Generic - * - */ - -#if !defined(DUK_CONFIG_H_INCLUDED) -#define DUK_CONFIG_H_INCLUDED - -/* - * Intermediate helper defines - */ - -/* DLL build detection */ -/* not configured for DLL build */ -#undef DUK_F_DLL_BUILD - -/* Apple OSX, iOS */ -#if defined(__APPLE__) -#define DUK_F_APPLE -#endif - -/* FreeBSD */ -#if defined(__FreeBSD__) || defined(__FreeBSD) -#define DUK_F_FREEBSD -#endif - -/* Orbis (PS4) variant */ -#if defined(DUK_F_FREEBSD) && defined(__ORBIS__) -#define DUK_F_ORBIS -#endif - -/* OpenBSD */ -#if defined(__OpenBSD__) || defined(__OpenBSD) -#define DUK_F_OPENBSD -#endif - -/* NetBSD */ -#if defined(__NetBSD__) || defined(__NetBSD) -#define DUK_F_NETBSD -#endif - -/* BSD variant */ -#if defined(DUK_F_FREEBSD) || defined(DUK_F_NETBSD) || defined(DUK_F_OPENBSD) || \ - defined(__bsdi__) || defined(__DragonFly__) -#define DUK_F_BSD -#endif - -/* Atari ST TOS. __TOS__ defined by PureC. No platform define in VBCC - * apparently, so to use with VBCC user must define __TOS__ manually. - */ -#if defined(__TOS__) -#define DUK_F_TOS -#endif - -/* Motorola 68K. Not defined by VBCC, so user must define one of these - * manually when using VBCC. - */ -#if defined(__m68k__) || defined(M68000) || defined(__MC68K__) -#define DUK_F_M68K -#endif - -/* AmigaOS. Neither AMIGA nor __amigaos__ is defined on VBCC, so user must - * define 'AMIGA' manually when using VBCC. - */ -#if defined(AMIGA) || defined(__amigaos__) -#define DUK_F_AMIGAOS -#endif - -/* PowerPC */ -#if defined(__powerpc) || defined(__powerpc__) || defined(__PPC__) -#define DUK_F_PPC -#if defined(__PPC64__) || defined(__LP64__) || defined(_LP64) -#define DUK_F_PPC64 -#else -#define DUK_F_PPC32 -#endif -#endif - -/* Durango (Xbox One) */ -#if defined(_DURANGO) || defined(_XBOX_ONE) -#define DUK_F_DURANGO -#endif - -/* Windows, both 32-bit and 64-bit */ -#if defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64) || \ - defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__) -#define DUK_F_WINDOWS -#if defined(_WIN64) || defined(WIN64) -#define DUK_F_WIN64 -#else -#define DUK_F_WIN32 -#endif -#endif - -/* Flash player (e.g. Crossbridge) */ -#if defined(__FLASHPLAYER__) -#define DUK_F_FLASHPLAYER -#endif - -/* QNX */ -#if defined(__QNX__) -#define DUK_F_QNX -#endif - -/* TI-Nspire (using Ndless) */ -#if defined(_TINSPIRE) -#define DUK_F_TINSPIRE -#endif - -/* Emscripten (provided explicitly by user), improve if possible */ -#if defined(EMSCRIPTEN) -#define DUK_F_EMSCRIPTEN -#endif - -/* BCC (Bruce's C compiler): this is a "torture target" for compilation */ -#if defined(__BCC__) || defined(__BCC_VERSION__) -#define DUK_F_BCC -#endif - -#if defined(ANDROID) || defined(__ANDROID__) -#define DUK_F_ANDROID -#endif - -/* Linux */ -#if defined(__linux) || defined(__linux__) || defined(linux) -#define DUK_F_LINUX -#endif - -/* illumos / Solaris */ -#if defined(__sun) && defined(__SVR4) -#define DUK_F_SUN -#if defined(__SUNPRO_C) && (__SUNPRO_C < 0x550) -#define DUK_F_OLD_SOLARIS -/* Defines _ILP32 / _LP64 required by DUK_F_X86/DUK_F_X64. Platforms - * are processed before architectures, so this happens before the - * DUK_F_X86/DUK_F_X64 detection is emitted. - */ -#include -#endif -#endif - -/* AIX */ -#if defined(_AIX) -/* defined(__xlc__) || defined(__IBMC__): works but too wide */ -#define DUK_F_AIX -#endif - -/* HPUX */ -#if defined(__hpux) -#define DUK_F_HPUX -#if defined(__ia64) -#define DUK_F_HPUX_ITANIUM -#endif -#endif - -/* POSIX */ -#if defined(__posix) -#define DUK_F_POSIX -#endif - -/* Cygwin */ -#if defined(__CYGWIN__) -#define DUK_F_CYGWIN -#endif - -/* Generic Unix (includes Cygwin) */ -#if defined(__unix) || defined(__unix__) || defined(unix) || \ - defined(DUK_F_LINUX) || defined(DUK_F_BSD) -#define DUK_F_UNIX -#endif - -/* Intel x86 (32-bit), x64 (64-bit) or x32 (64-bit but 32-bit pointers), - * define only one of DUK_F_X86, DUK_F_X64, DUK_F_X32. - * https://sites.google.com/site/x32abi/ - * - * With DUK_F_OLD_SOLARIS the header must be included - * before this. - */ -#if defined(__amd64__) || defined(__amd64) || \ - defined(__x86_64__) || defined(__x86_64) || \ - defined(_M_X64) || defined(_M_AMD64) -#if defined(__ILP32__) || defined(_ILP32) -#define DUK_F_X32 -#else -#define DUK_F_X64 -#endif -#elif defined(i386) || defined(__i386) || defined(__i386__) || \ - defined(__i486__) || defined(__i586__) || defined(__i686__) || \ - defined(__IA32__) || defined(_M_IX86) || defined(__X86__) || \ - defined(_X86_) || defined(__THW_INTEL__) || defined(__I86__) -#if defined(__LP64__) || defined(_LP64) -/* This should not really happen, but would indicate x64. */ -#define DUK_F_X64 -#else -#define DUK_F_X86 -#endif -#endif - -/* ARM */ -#if defined(__arm__) || defined(__thumb__) || defined(_ARM) || defined(_M_ARM) || defined(_M_ARM64) || defined(__aarch64__) -#define DUK_F_ARM -#if defined(__LP64__) || defined(_LP64) || defined(__arm64) || defined(__arm64__) || defined(_M_ARM64) || defined(__aarch64__) -#define DUK_F_ARM64 -#else -#define DUK_F_ARM32 -#endif -#endif - -/* MIPS. Related defines: __MIPSEB__, __MIPSEL__, __mips_isa_rev, __LP64__ */ -#if defined(__mips__) || defined(mips) || defined(_MIPS_ISA) || \ - defined(_R3000) || defined(_R4000) || defined(_R5900) || \ - defined(_MIPS_ISA_MIPS1) || defined(_MIPS_ISA_MIPS2) || \ - defined(_MIPS_ISA_MIPS3) || defined(_MIPS_ISA_MIPS4) || \ - defined(__mips) || defined(__MIPS__) -#define DUK_F_MIPS -#if defined(__LP64__) || defined(_LP64) || defined(__mips64) || \ - defined(__mips64__) || defined(__mips_n64) -#define DUK_F_MIPS64 -#else -#define DUK_F_MIPS32 -#endif -#endif - -/* SPARC */ -#if defined(sparc) || defined(__sparc) || defined(__sparc__) -#define DUK_F_SPARC -#if defined(__LP64__) || defined(_LP64) -#define DUK_F_SPARC64 -#else -#define DUK_F_SPARC32 -#endif -#endif - -/* RISC-V, https://github.com/riscv/riscv-toolchain-conventions#cc-preprocessor-definitions */ -#if defined(__riscv) -#define DUK_F_RISCV -#if defined(__riscv_xlen) -#if (__riscv_xlen == 32) -#define DUK_F_RISCV32 -#elif (__riscv_xlen == 64) -#define DUK_F_RISCV64 -#else -#error __riscv_xlen has unsupported value (not 32 or 64) -#endif -#else -#error __riscv defined without __riscv_xlen -#endif -#endif /* __riscv */ - -/* SuperH */ -#if defined(__sh__) || \ - defined(__sh1__) || defined(__SH1__) || \ - defined(__sh2__) || defined(__SH2__) || \ - defined(__sh3__) || defined(__SH3__) || \ - defined(__sh4__) || defined(__SH4__) || \ - defined(__sh5__) || defined(__SH5__) -#define DUK_F_SUPERH -#endif - -/* Clang */ -#if defined(__clang__) -#define DUK_F_CLANG -#endif - -/* C++ */ -#undef DUK_F_CPP -#if defined(__cplusplus) -#define DUK_F_CPP -#endif - -/* C99 or above */ -#undef DUK_F_C99 -#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) -#define DUK_F_C99 -#endif - -/* C++11 or above */ -#undef DUK_F_CPP11 -#if defined(__cplusplus) && (__cplusplus >= 201103L) -#define DUK_F_CPP11 -#endif - -/* GCC. Clang also defines __GNUC__ so don't detect GCC if using Clang. */ -#if defined(__GNUC__) && !defined(__clang__) && !defined(DUK_F_CLANG) -#define DUK_F_GCC -#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) -/* Convenience, e.g. gcc 4.5.1 == 40501; http://stackoverflow.com/questions/6031819/emulating-gccs-builtin-unreachable */ -#define DUK_F_GCC_VERSION (__GNUC__ * 10000L + __GNUC_MINOR__ * 100L + __GNUC_PATCHLEVEL__) -#else -#error cannot figure out gcc version -#endif -#endif - -/* MinGW. Also GCC flags (DUK_F_GCC) are enabled now. */ -#if defined(__MINGW32__) || defined(__MINGW64__) -#define DUK_F_MINGW -#endif - -/* MSVC */ -#if defined(_MSC_VER) -/* MSVC preprocessor defines: http://msdn.microsoft.com/en-us/library/b0084kay.aspx - * _MSC_FULL_VER includes the build number, but it has at least two formats, see e.g. - * BOOST_MSVC_FULL_VER in http://www.boost.org/doc/libs/1_52_0/boost/config/compiler/visualc.hpp - */ -#define DUK_F_MSVC -#if defined(_MSC_FULL_VER) -#if (_MSC_FULL_VER > 100000000) -#define DUK_F_MSVC_FULL_VER _MSC_FULL_VER -#else -#define DUK_F_MSCV_FULL_VER (_MSC_FULL_VER * 10) -#endif -#endif -#endif /* _MSC_VER */ - -/* TinyC */ -#if defined(__TINYC__) -/* http://bellard.org/tcc/tcc-doc.html#SEC9 */ -#define DUK_F_TINYC -#endif - -/* VBCC */ -#if defined(__VBCC__) -#define DUK_F_VBCC -#endif - -/* Atari Mint */ -#if defined(__MINT__) -#define DUK_F_MINT -#endif - -/* - * Platform autodetection - */ - -/* Workaround for older C++ compilers before including , - * see e.g.: https://sourceware.org/bugzilla/show_bug.cgi?id=15366 - */ -#if defined(__cplusplus) && !defined(__STDC_LIMIT_MACROS) -#define __STDC_LIMIT_MACROS -#endif -#if defined(__cplusplus) && !defined(__STDC_CONSTANT_MACROS) -#define __STDC_CONSTANT_MACROS -#endif - -#if defined(DUK_F_APPLE) -/* --- Mac OSX, iPhone, Darwin --- */ -#define DUK_USE_DATE_NOW_GETTIMEOFDAY -#define DUK_USE_DATE_TZO_GMTIME_R -#define DUK_USE_DATE_PRS_STRPTIME -#define DUK_USE_DATE_FMT_STRFTIME -#include -#include -#include -#include -#include - -/* http://stackoverflow.com/questions/5919996/how-to-detect-reliably-mac-os-x-ios-linux-windows-in-c-preprocessor */ -#if TARGET_IPHONE_SIMULATOR -#define DUK_USE_OS_STRING "iphone-sim" -#elif TARGET_OS_IPHONE -#define DUK_USE_OS_STRING "iphone" -#elif TARGET_OS_MAC -#define DUK_USE_OS_STRING "osx" -#else -#define DUK_USE_OS_STRING "osx-unknown" -#endif - -/* Use _setjmp() on Apple by default, see GH-55. */ -#define DUK_JMPBUF_TYPE jmp_buf -#define DUK_SETJMP(jb) _setjmp((jb)) -#define DUK_LONGJMP(jb) _longjmp((jb), 1) -#elif defined(DUK_F_ORBIS) -/* --- Orbis --- */ -/* Orbis = PS4 */ -#define DUK_USE_DATE_NOW_GETTIMEOFDAY -#define DUK_USE_DATE_TZO_GMTIME_S -/* no parsing (not an error) */ -#define DUK_USE_DATE_FMT_STRFTIME -#include -#include -#include -#include -#include - -#define DUK_USE_OS_STRING "orbis" -#elif defined(DUK_F_OPENBSD) -/* --- OpenBSD --- */ -/* http://www.monkey.org/openbsd/archive/ports/0401/msg00089.html */ -#define DUK_USE_DATE_NOW_GETTIMEOFDAY -#define DUK_USE_DATE_TZO_GMTIME_R -#define DUK_USE_DATE_PRS_STRPTIME -#define DUK_USE_DATE_FMT_STRFTIME -#include -#include -#include -#include -#include - -#define DUK_USE_OS_STRING "openbsd" -#elif defined(DUK_F_BSD) -/* --- Generic BSD --- */ -#define DUK_USE_DATE_NOW_GETTIMEOFDAY -#define DUK_USE_DATE_TZO_GMTIME_R -#define DUK_USE_DATE_PRS_STRPTIME -#define DUK_USE_DATE_FMT_STRFTIME -#include -#include -#include -#include -#include - -#define DUK_USE_OS_STRING "bsd" -#elif defined(DUK_F_TOS) -/* --- Atari ST TOS --- */ -#define DUK_USE_DATE_NOW_TIME -#define DUK_USE_DATE_TZO_GMTIME -/* no parsing (not an error) */ -#define DUK_USE_DATE_FMT_STRFTIME -#include - -#define DUK_USE_OS_STRING "tos" - -/* TOS on M68K is always big endian. */ -#if !defined(DUK_USE_BYTEORDER) && defined(DUK_F_M68K) -#define DUK_USE_BYTEORDER 3 -#endif -#elif defined(DUK_F_AMIGAOS) -/* --- AmigaOS --- */ -#if defined(DUK_F_M68K) -/* AmigaOS on M68k */ -#define DUK_USE_DATE_NOW_TIME -#define DUK_USE_DATE_TZO_GMTIME -/* no parsing (not an error) */ -#define DUK_USE_DATE_FMT_STRFTIME -#include -#elif defined(DUK_F_PPC) -#define DUK_USE_DATE_NOW_GETTIMEOFDAY -#define DUK_USE_DATE_TZO_GMTIME_R -#define DUK_USE_DATE_PRS_STRPTIME -#define DUK_USE_DATE_FMT_STRFTIME -#include -#if !defined(UINTPTR_MAX) -#define UINTPTR_MAX UINT_MAX -#endif -#else -#error AmigaOS but not M68K/PPC, not supported now -#endif - -#define DUK_USE_OS_STRING "amigaos" - -/* AmigaOS on M68K or PPC is always big endian. */ -#if !defined(DUK_USE_BYTEORDER) && (defined(DUK_F_M68K) || defined(DUK_F_PPC)) -#define DUK_USE_BYTEORDER 3 -#endif -#elif defined(DUK_F_DURANGO) -/* --- Durango (XboxOne) --- */ -/* Durango = XboxOne - * Configuration is nearly identical to Windows, except for - * DUK_USE_DATE_TZO_WINDOWS. - */ - -/* Initial fix: disable secure CRT related warnings when compiling Duktape - * itself (must be defined before including Windows headers). Don't define - * for user code including duktape.h. - */ -#if defined(DUK_COMPILING_DUKTAPE) && !defined(_CRT_SECURE_NO_WARNINGS) -#define _CRT_SECURE_NO_WARNINGS -#endif - -/* MSVC does not have sys/param.h */ -#define DUK_USE_DATE_NOW_WINDOWS -#define DUK_USE_DATE_TZO_WINDOWS_NO_DST -/* Note: PRS and FMT are intentionally left undefined for now. This means - * there is no platform specific date parsing/formatting but there is still - * the ISO 8601 standard format. - */ -#if defined(DUK_COMPILING_DUKTAPE) -/* Only include when compiling Duktape to avoid polluting application build - * with a lot of unnecessary defines. - */ -#include -#endif - -#define DUK_USE_OS_STRING "durango" - -#if !defined(DUK_USE_BYTEORDER) -#define DUK_USE_BYTEORDER 1 -#endif -#elif defined(DUK_F_WINDOWS) -/* --- Windows --- */ -/* Windows version can't obviously be determined at compile time, - * but _WIN32_WINNT indicates the minimum version targeted: - * - https://msdn.microsoft.com/en-us/library/6sehtctf.aspx - */ - -/* Initial fix: disable secure CRT related warnings when compiling Duktape - * itself (must be defined before including Windows headers). Don't define - * for user code including duktape.h. - */ -#if defined(DUK_COMPILING_DUKTAPE) && !defined(_CRT_SECURE_NO_WARNINGS) -#define _CRT_SECURE_NO_WARNINGS -#endif - -/* Windows 32-bit and 64-bit are currently the same. */ -/* MSVC does not have sys/param.h */ - -#if defined(DUK_COMPILING_DUKTAPE) -/* Only include when compiling Duktape to avoid polluting application build - * with a lot of unnecessary defines. - */ -#include -#endif - -/* GetSystemTimePreciseAsFileTime() available from Windows 8: - * https://msdn.microsoft.com/en-us/library/windows/desktop/hh706895(v=vs.85).aspx - */ -#if defined(DUK_USE_DATE_NOW_WINDOWS_SUBMS) || defined(DUK_USE_DATE_NOW_WINDOWS) -/* User forced provider. */ -#else -#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0602) -#define DUK_USE_DATE_NOW_WINDOWS_SUBMS -#else -#define DUK_USE_DATE_NOW_WINDOWS -#endif -#endif - -#define DUK_USE_DATE_TZO_WINDOWS - -/* Note: PRS and FMT are intentionally left undefined for now. This means - * there is no platform specific date parsing/formatting but there is still - * the ISO 8601 standard format. - */ - -/* QueryPerformanceCounter() may go backwards in Windows XP, so enable for - * Vista and later: https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx#qpc_support_in_windows_versions - */ -#if !defined(DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC) && \ - defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600) -#define DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC -#endif - -#define DUK_USE_OS_STRING "windows" - -/* On Windows, assume we're little endian. Even Itanium which has a - * configurable endianness runs little endian in Windows. - */ -#if !defined(DUK_USE_BYTEORDER) -#define DUK_USE_BYTEORDER 1 -#endif -#elif defined(DUK_F_FLASHPLAYER) -/* --- Flashplayer (Crossbridge) --- */ -#define DUK_USE_DATE_NOW_GETTIMEOFDAY -#define DUK_USE_DATE_TZO_GMTIME_R -#define DUK_USE_DATE_PRS_STRPTIME -#define DUK_USE_DATE_FMT_STRFTIME -#include -#include -#include -#include - -#define DUK_USE_OS_STRING "flashplayer" - -#if !defined(DUK_USE_BYTEORDER) && defined(DUK_F_FLASHPLAYER) -#define DUK_USE_BYTEORDER 1 -#endif -#elif defined(DUK_F_QNX) -/* --- QNX --- */ -#if defined(DUK_F_QNX) && defined(DUK_COMPILING_DUKTAPE) -/* See: /opt/qnx650/target/qnx6/usr/include/sys/platform.h */ -#define _XOPEN_SOURCE 600 -#define _POSIX_C_SOURCE 200112L -#endif - -#define DUK_USE_DATE_NOW_GETTIMEOFDAY -#define DUK_USE_DATE_TZO_GMTIME_R -#define DUK_USE_DATE_PRS_STRPTIME -#define DUK_USE_DATE_FMT_STRFTIME -#include -#include -#include -#include - -#define DUK_USE_OS_STRING "qnx" -#elif defined(DUK_F_TINSPIRE) -/* --- TI-Nspire --- */ -#if defined(DUK_COMPILING_DUKTAPE) && !defined(_XOPEN_SOURCE) -#define _XOPEN_SOURCE /* e.g. strptime */ -#endif - -#define DUK_USE_DATE_NOW_GETTIMEOFDAY -#define DUK_USE_DATE_TZO_GMTIME_R -#define DUK_USE_DATE_PRS_STRPTIME -#define DUK_USE_DATE_FMT_STRFTIME -#include -#include -#include -#include - -#define DUK_USE_OS_STRING "tinspire" -#elif defined(DUK_F_EMSCRIPTEN) -/* --- Emscripten --- */ -#if defined(DUK_COMPILING_DUKTAPE) -#if !defined(_POSIX_C_SOURCE) -#define _POSIX_C_SOURCE 200809L -#endif -#if !defined(_GNU_SOURCE) -#define _GNU_SOURCE /* e.g. getdate_r */ -#endif -#if !defined(_XOPEN_SOURCE) -#define _XOPEN_SOURCE /* e.g. strptime */ -#endif -#endif /* DUK_COMPILING_DUKTAPE */ - -#include -#if defined(DUK_F_BCC) -/* no endian.h */ -#else -#include -#endif /* DUK_F_BCC */ -#include -#include -#include -#include - -#define DUK_USE_DATE_NOW_GETTIMEOFDAY -#define DUK_USE_DATE_TZO_GMTIME_R -#define DUK_USE_DATE_PRS_STRPTIME -#define DUK_USE_DATE_FMT_STRFTIME - -#define DUK_USE_OS_STRING "emscripten" -#elif defined(DUK_F_ANDROID) -/* --- Android --- */ -#if defined(DUK_COMPILING_DUKTAPE) -#if !defined(_POSIX_C_SOURCE) -#define _POSIX_C_SOURCE 200809L -#endif -#if !defined(_GNU_SOURCE) -#define _GNU_SOURCE /* e.g. getdate_r */ -#endif -#if !defined(_XOPEN_SOURCE) -#define _XOPEN_SOURCE /* e.g. strptime */ -#endif -#endif /* DUK_COMPILING_DUKTAPE */ - -#include -#if defined(DUK_F_BCC) -/* no endian.h or stdint.h */ -#else -#include -#include -#endif /* DUK_F_BCC */ -#include -#include -#include - -#define DUK_USE_DATE_NOW_GETTIMEOFDAY -#define DUK_USE_DATE_TZO_GMTIME_R -#define DUK_USE_DATE_PRS_STRPTIME -#define DUK_USE_DATE_FMT_STRFTIME - -#if 0 /* XXX: safe condition? */ -#define DUK_USE_GET_MONOTONIC_TIME_CLOCK_GETTIME -#endif - -#define DUK_USE_OS_STRING "android" -#elif defined(DUK_F_LINUX) -/* --- Linux --- */ -#if defined(DUK_COMPILING_DUKTAPE) -#if !defined(_POSIX_C_SOURCE) -#define _POSIX_C_SOURCE 200809L -#endif -#if !defined(_GNU_SOURCE) -#define _GNU_SOURCE /* e.g. getdate_r */ -#endif -#if !defined(_XOPEN_SOURCE) -#define _XOPEN_SOURCE /* e.g. strptime */ -#endif -#endif /* DUK_COMPILING_DUKTAPE */ - -#include -#if defined(DUK_F_BCC) -/* no endian.h or stdint.h */ -#else -#include -#include -#endif /* DUK_F_BCC */ -#include -#include -#include - -#define DUK_USE_DATE_NOW_GETTIMEOFDAY -#define DUK_USE_DATE_TZO_GMTIME_R -#define DUK_USE_DATE_PRS_STRPTIME -#define DUK_USE_DATE_FMT_STRFTIME - -#if 0 /* XXX: safe condition? */ -#define DUK_USE_GET_MONOTONIC_TIME_CLOCK_GETTIME -#endif - -#define DUK_USE_OS_STRING "linux" -#elif defined(DUK_F_SUN) -/* --- Solaris --- */ -#define DUK_USE_DATE_NOW_GETTIMEOFDAY -#define DUK_USE_DATE_TZO_GMTIME_R -#define DUK_USE_DATE_PRS_STRPTIME -#define DUK_USE_DATE_FMT_STRFTIME - -#include -#if defined(DUK_F_OLD_SOLARIS) -/* Old Solaris with no endian.h, stdint.h */ -#define DUK_F_NO_STDINT_H -#if !defined(DUK_USE_BYTEORDER) -#define DUK_USE_BYTEORDER 3 -#endif -#else /* DUK_F_OLD_SOLARIS */ -#include -#endif /* DUK_F_OLD_SOLARIS */ - -#include -#include -#include - -#define DUK_USE_OS_STRING "solaris" -#elif defined(DUK_F_AIX) -/* --- AIX --- */ -#if !defined(DUK_USE_BYTEORDER) -#define DUK_USE_BYTEORDER 3 -#endif -#define DUK_USE_DATE_NOW_GETTIMEOFDAY -#define DUK_USE_DATE_TZO_GMTIME_R -#define DUK_USE_DATE_PRS_STRPTIME -#define DUK_USE_DATE_FMT_STRFTIME -#include -#include -#include - -#define DUK_USE_OS_STRING "aix" -#elif defined(DUK_F_HPUX) -/* --- HPUX --- */ -#define DUK_F_NO_STDINT_H -#if !defined(DUK_USE_BYTEORDER) -#define DUK_USE_BYTEORDER 3 -#endif -#define DUK_USE_DATE_NOW_GETTIMEOFDAY -#define DUK_USE_DATE_TZO_GMTIME_R -#define DUK_USE_DATE_PRS_STRPTIME -#define DUK_USE_DATE_FMT_STRFTIME -#include -#include -#include - -#define DUK_USE_OS_STRING "hpux" -#elif defined(DUK_F_POSIX) -/* --- Generic POSIX --- */ -#define DUK_USE_DATE_NOW_GETTIMEOFDAY -#define DUK_USE_DATE_TZO_GMTIME_R -#define DUK_USE_DATE_PRS_STRPTIME -#define DUK_USE_DATE_FMT_STRFTIME -#include -#include -#include -#include -#include - -#define DUK_USE_OS_STRING "posix" -#elif defined(DUK_F_CYGWIN) -/* --- Cygwin --- */ -/* don't use strptime() for now */ -#define DUK_USE_DATE_NOW_GETTIMEOFDAY -#define DUK_USE_DATE_TZO_GMTIME_R -#define DUK_USE_DATE_FMT_STRFTIME -#include -#include -#include -#include -#include - -#define DUK_JMPBUF_TYPE jmp_buf -#define DUK_SETJMP(jb) _setjmp((jb)) -#define DUK_LONGJMP(jb) _longjmp((jb), 1) - -#define DUK_USE_OS_STRING "windows" -#elif defined(DUK_F_UNIX) -/* --- Generic UNIX --- */ -#define DUK_USE_DATE_NOW_GETTIMEOFDAY -#define DUK_USE_DATE_TZO_GMTIME_R -#define DUK_USE_DATE_PRS_STRPTIME -#define DUK_USE_DATE_FMT_STRFTIME -#include -#include -#define DUK_USE_OS_STRING "unknown" -#else -/* --- Generic fallback --- */ -/* The most portable current time provider is time(), but it only has a - * one second resolution. - */ -#define DUK_USE_DATE_NOW_TIME - -/* The most portable way to figure out local time offset is gmtime(), - * but it's not thread safe so use with caution. - */ -#define DUK_USE_DATE_TZO_GMTIME - -/* Avoid custom date parsing and formatting for portability. */ -#undef DUK_USE_DATE_PRS_STRPTIME -#undef DUK_USE_DATE_FMT_STRFTIME - -/* Rely on C89 headers only; time.h must be here. */ -#include - -#define DUK_USE_OS_STRING "unknown" -#endif /* autodetect platform */ - -/* Shared includes: C89 */ -#include -#include -#include -#include /* varargs */ -#include -#include /* e.g. ptrdiff_t */ -#include -#include - -/* date.h is omitted, and included per platform */ - -/* Shared includes: stdint.h is C99 */ -#if defined(DUK_F_NO_STDINT_H) -/* stdint.h not available */ -#else -/* Technically C99 (C++11) but found in many systems. On some systems - * __STDC_LIMIT_MACROS and __STDC_CONSTANT_MACROS must be defined before - * including stdint.h (see above). - */ -#include -#endif - -/* is only included if needed, based on DUK_USE_xxx flags. */ - -/* - * Architecture autodetection - */ - -#if defined(DUK_F_X86) -/* --- x86 --- */ -#define DUK_USE_ARCH_STRING "x86" -#if !defined(DUK_USE_BYTEORDER) -#define DUK_USE_BYTEORDER 1 -#endif - -#define DUK_USE_PACKED_TVAL - -/* FreeBSD, -m32, and clang prior to 5.0 has union aliasing issues which - * break duk_tval copying. Disable packed duk_tval automatically. - */ -#if defined(DUK_F_FREEBSD) && defined(DUK_F_X86) && \ - defined(__clang__) && defined(__clang_major__) && (__clang_major__ < 5) -#undef DUK_USE_PACKED_TVAL -#endif -#define DUK_F_PACKED_TVAL_PROVIDED -#elif defined(DUK_F_X64) -/* --- x64 --- */ -#define DUK_USE_ARCH_STRING "x64" -#if !defined(DUK_USE_BYTEORDER) -#define DUK_USE_BYTEORDER 1 -#endif -#undef DUK_USE_PACKED_TVAL -#define DUK_F_PACKED_TVAL_PROVIDED -#elif defined(DUK_F_X32) -/* --- x32 --- */ -#define DUK_USE_ARCH_STRING "x32" -#if !defined(DUK_USE_BYTEORDER) -#define DUK_USE_BYTEORDER 1 -#endif -#define DUK_USE_PACKED_TVAL -#define DUK_F_PACKED_TVAL_PROVIDED -#elif defined(DUK_F_ARM32) -/* --- ARM 32-bit --- */ -#define DUK_USE_ARCH_STRING "arm32" -/* Byte order varies, so rely on autodetect. */ -#define DUK_USE_PACKED_TVAL -#define DUK_F_PACKED_TVAL_PROVIDED -#elif defined(DUK_F_ARM64) -/* --- ARM 64-bit --- */ -#define DUK_USE_ARCH_STRING "arm64" -/* Byte order varies, so rely on autodetect. */ -#undef DUK_USE_PACKED_TVAL -#define DUK_F_PACKED_TVAL_PROVIDED -#elif defined(DUK_F_MIPS32) -/* --- MIPS 32-bit --- */ -#define DUK_USE_ARCH_STRING "mips32" -/* MIPS byte order varies so rely on autodetection. */ -#define DUK_USE_PACKED_TVAL -#define DUK_F_PACKED_TVAL_PROVIDED -#elif defined(DUK_F_MIPS64) -/* --- MIPS 64-bit --- */ -#define DUK_USE_ARCH_STRING "mips64" -/* MIPS byte order varies so rely on autodetection. */ -#undef DUK_USE_PACKED_TVAL -#define DUK_F_PACKED_TVAL_PROVIDED -#elif defined(DUK_F_PPC32) -/* --- PowerPC 32-bit --- */ -#define DUK_USE_ARCH_STRING "ppc32" -#if !defined(DUK_USE_BYTEORDER) -#define DUK_USE_BYTEORDER 3 -#endif -#define DUK_USE_PACKED_TVAL -#define DUK_F_PACKED_TVAL_PROVIDED -#elif defined(DUK_F_PPC64) -/* --- PowerPC 64-bit --- */ -#define DUK_USE_ARCH_STRING "ppc64" -/* No forced byteorder (both little and big endian are possible). */ -#undef DUK_USE_PACKED_TVAL -#define DUK_F_PACKED_TVAL_PROVIDED -#elif defined(DUK_F_SPARC32) -/* --- SPARC 32-bit --- */ -#define DUK_USE_ARCH_STRING "sparc32" -/* SPARC byte order varies so rely on autodetection. */ -#define DUK_USE_PACKED_TVAL -#define DUK_F_PACKED_TVAL_PROVIDED -#elif defined(DUK_F_SPARC64) -/* --- SPARC 64-bit --- */ -#define DUK_USE_ARCH_STRING "sparc64" -/* SPARC byte order varies so rely on autodetection. */ -#undef DUK_USE_PACKED_TVAL -#define DUK_F_PACKED_TVAL_PROVIDED -#elif defined(DUK_F_RISCV32) -/* --- RISC-V 32-bit --- */ -#define DUK_USE_ARCH_STRING "riscv32" -#define DUK_USE_BYTEORDER 1 -#define DUK_USE_PACKED_TVAL -#define DUK_F_PACKED_TVAL_PROVIDED -#elif defined(DUK_F_RISCV64) -/* --- RISC-V 64-bit --- */ -#define DUK_USE_ARCH_STRING "riscv64" -#define DUK_USE_BYTEORDER 1 -#undef DUK_USE_PACKED_TVAL -#define DUK_F_PACKED_TVAL_PROVIDED -#elif defined(DUK_F_SUPERH) -/* --- SuperH --- */ -#define DUK_USE_ARCH_STRING "sh" -/* Byte order varies, rely on autodetection. */ -#define DUK_USE_PACKED_TVAL -#define DUK_F_PACKED_TVAL_PROVIDED -#elif defined(DUK_F_M68K) -/* --- Motorola 68k --- */ -#define DUK_USE_ARCH_STRING "m68k" -#if !defined(DUK_USE_BYTEORDER) -#define DUK_USE_BYTEORDER 3 -#endif -#define DUK_USE_PACKED_TVAL -#define DUK_F_PACKED_TVAL_PROVIDED -#elif defined(DUK_F_EMSCRIPTEN) -/* --- Emscripten --- */ -#define DUK_USE_ARCH_STRING "emscripten" -#if !defined(DUK_USE_BYTEORDER) -#define DUK_USE_BYTEORDER 1 -#endif -#undef DUK_USE_PACKED_TVAL -#define DUK_F_PACKED_TVAL_PROVIDED -#else -/* --- Generic --- */ -/* These are necessary wild guesses. */ -#define DUK_USE_ARCH_STRING "generic" -/* Rely on autodetection for byte order, alignment, and packed tval. */ -#endif /* autodetect architecture */ - -/* - * Compiler autodetection - */ - -#if defined(DUK_F_CLANG) -/* --- Clang --- */ -#if defined(DUK_F_C99) || defined(DUK_F_CPP11) -/* C99 / C++11 and above: rely on va_copy() which is required. */ -#define DUK_VA_COPY(dest,src) va_copy(dest,src) -#else -/* Clang: assume we have __va_copy() in non-C99 mode. */ -#define DUK_VA_COPY(dest,src) __va_copy(dest,src) -#endif - -#define DUK_NORETURN(decl) decl __attribute__((noreturn)) - -#if defined(__clang__) && defined(__has_builtin) -#if __has_builtin(__builtin_unreachable) -#define DUK_UNREACHABLE() do { __builtin_unreachable(); } while (0) -#endif -#endif - -#define DUK_USE_BRANCH_HINTS -#define DUK_LIKELY(x) __builtin_expect((x), 1) -#define DUK_UNLIKELY(x) __builtin_expect((x), 0) -#if defined(__clang__) && defined(__has_builtin) -#if __has_builtin(__builtin_unpredictable) -#define DUK_UNPREDICTABLE(x) __builtin_unpredictable((x)) -#endif -#endif - -#if defined(DUK_F_C99) || defined(DUK_F_CPP11) -#define DUK_NOINLINE __attribute__((noinline)) -#define DUK_INLINE inline -#define DUK_ALWAYS_INLINE inline __attribute__((always_inline)) -#endif - -/* DUK_HOT */ -/* DUK_COLD */ - -#if defined(DUK_F_DLL_BUILD) && defined(DUK_F_WINDOWS) -/* MSVC dllexport/dllimport: appropriate __declspec depends on whether we're - * compiling Duktape or the application. - */ -#if defined(DUK_COMPILING_DUKTAPE) -#define DUK_EXTERNAL_DECL extern __declspec(dllexport) -#define DUK_EXTERNAL __declspec(dllexport) -#else -#define DUK_EXTERNAL_DECL extern __declspec(dllimport) -#define DUK_EXTERNAL should_not_happen -#endif -#if defined(DUK_SINGLE_FILE) -#define DUK_INTERNAL_DECL static -#define DUK_INTERNAL static -#else -#define DUK_INTERNAL_DECL extern -#define DUK_INTERNAL /*empty*/ -#endif -#define DUK_LOCAL_DECL static -#define DUK_LOCAL static -#else -#define DUK_EXTERNAL_DECL __attribute__ ((visibility("default"))) extern -#define DUK_EXTERNAL __attribute__ ((visibility("default"))) -#if defined(DUK_SINGLE_FILE) -#if (defined(DUK_F_GCC_VERSION) && DUK_F_GCC_VERSION >= 30101) || defined(DUK_F_CLANG) -/* Minimize warnings for unused internal functions with GCC >= 3.1.1 and - * Clang. Based on documentation it should suffice to have the attribute - * in the declaration only, but in practice some warnings are generated unless - * the attribute is also applied to the definition. - */ -#define DUK_INTERNAL_DECL static __attribute__ ((unused)) -#define DUK_INTERNAL static __attribute__ ((unused)) -#else -#define DUK_INTERNAL_DECL static -#define DUK_INTERNAL static -#endif -#else -#if (defined(DUK_F_GCC_VERSION) && DUK_F_GCC_VERSION >= 30101) || defined(DUK_F_CLANG) -#define DUK_INTERNAL_DECL __attribute__ ((visibility("hidden"))) __attribute__ ((unused)) extern -#define DUK_INTERNAL __attribute__ ((visibility("hidden"))) __attribute__ ((unused)) -#else -#define DUK_INTERNAL_DECL __attribute__ ((visibility("hidden"))) extern -#define DUK_INTERNAL __attribute__ ((visibility("hidden"))) -#endif -#endif -#define DUK_LOCAL_DECL static -#define DUK_LOCAL static -#endif - -#if defined(DUK_F_CPP) -#define DUK_USE_COMPILER_STRING "clang" -#else -#define DUK_USE_COMPILER_STRING "clang" -#endif - -#undef DUK_USE_VARIADIC_MACROS -#if defined(DUK_F_C99) || defined(DUK_F_CPP11) -#define DUK_USE_VARIADIC_MACROS -#endif - -#define DUK_USE_UNION_INITIALIZERS - -#undef DUK_USE_FLEX_C99 -#undef DUK_USE_FLEX_ZEROSIZE -#undef DUK_USE_FLEX_ONESIZE -#if defined(DUK_F_C99) -#define DUK_USE_FLEX_C99 -#else -#define DUK_USE_FLEX_ZEROSIZE -#endif - -#define DUK_USE_CLANG_PRAGMAS -#define DUK_USE_PACK_CLANG_ATTR - -#if defined(__clang__) && defined(__has_builtin) -#if __has_builtin(__builtin_bswap64) -#define DUK_BSWAP64(x) ((duk_uint64_t) __builtin_bswap64((duk_uint64_t) (x))) -#endif -#if __has_builtin(__builtin_bswap32) -#define DUK_BSWAP32(x) ((duk_uint32_t) __builtin_bswap32((duk_uint32_t) (x))) -#endif -#if __has_builtin(__builtin_bswap16) -#define DUK_BSWAP16(x) ((duk_uint16_t) __builtin_bswap16((duk_uint16_t) (x))) -#endif -#endif -#elif defined(DUK_F_GCC) -/* --- GCC --- */ -#if defined(DUK_F_C99) || defined(DUK_F_CPP11) -/* C99 / C++11 and above: rely on va_copy() which is required. */ -#define DUK_VA_COPY(dest,src) va_copy(dest,src) -#else -/* GCC: assume we have __va_copy() in non-C99 mode. */ -#define DUK_VA_COPY(dest,src) __va_copy(dest,src) -#endif - -#if defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 20500L) && (DUK_F_GCC_VERSION < 50000L) -/* Since gcc-2.5. - * - * Disabled temporarily in GCC 5+ because of an unresolved noreturn-related - * issue: https://github.com/svaarala/duktape/issues/2155. - */ -#define DUK_NORETURN(decl) decl __attribute__((noreturn)) -#endif - -#if defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 40500L) -/* Since gcc-4.5. */ -#define DUK_UNREACHABLE() do { __builtin_unreachable(); } while (0) -#endif - -#define DUK_USE_BRANCH_HINTS -#if defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 40500L) -/* GCC: test not very accurate; enable only in relatively recent builds - * because of bugs in gcc-4.4 (http://lists.debian.org/debian-gcc/2010/04/msg00000.html) - */ -#define DUK_LIKELY(x) __builtin_expect((x), 1) -#define DUK_UNLIKELY(x) __builtin_expect((x), 0) -#endif -/* XXX: equivalent of clang __builtin_unpredictable? */ - -#if (defined(DUK_F_C99) || defined(DUK_F_CPP11)) && \ - defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 30101) -#define DUK_NOINLINE __attribute__((noinline)) -#define DUK_INLINE inline -#define DUK_ALWAYS_INLINE inline __attribute__((always_inline)) -#endif - -#if (defined(DUK_F_C99) || defined(DUK_F_CPP11)) && \ - defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 40300) -#define DUK_HOT __attribute__((hot)) -#define DUK_COLD __attribute__((cold)) -#endif - -#if defined(DUK_F_DLL_BUILD) && defined(DUK_F_WINDOWS) -/* MSVC dllexport/dllimport: appropriate __declspec depends on whether we're - * compiling Duktape or the application. - */ -#if defined(DUK_COMPILING_DUKTAPE) -#define DUK_EXTERNAL_DECL extern __declspec(dllexport) -#define DUK_EXTERNAL __declspec(dllexport) -#else -#define DUK_EXTERNAL_DECL extern __declspec(dllimport) -#define DUK_EXTERNAL should_not_happen -#endif -#if defined(DUK_SINGLE_FILE) -#define DUK_INTERNAL_DECL static -#define DUK_INTERNAL static -#else -#define DUK_INTERNAL_DECL extern -#define DUK_INTERNAL /*empty*/ -#endif -#define DUK_LOCAL_DECL static -#define DUK_LOCAL static -#elif defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 40000) -#define DUK_EXTERNAL_DECL __attribute__ ((visibility("default"))) extern -#define DUK_EXTERNAL __attribute__ ((visibility("default"))) -#if defined(DUK_SINGLE_FILE) -#if (defined(DUK_F_GCC_VERSION) && DUK_F_GCC_VERSION >= 30101) || defined(DUK_F_CLANG) -/* Minimize warnings for unused internal functions with GCC >= 3.1.1 and - * Clang. Based on documentation it should suffice to have the attribute - * in the declaration only, but in practice some warnings are generated unless - * the attribute is also applied to the definition. - */ -#define DUK_INTERNAL_DECL static __attribute__ ((unused)) -#define DUK_INTERNAL static __attribute__ ((unused)) -#else -#define DUK_INTERNAL_DECL static -#define DUK_INTERNAL static -#endif -#else -#if (defined(DUK_F_GCC_VERSION) && DUK_F_GCC_VERSION >= 30101) || defined(DUK_F_CLANG) -#define DUK_INTERNAL_DECL __attribute__ ((visibility("hidden"))) __attribute__ ((unused)) extern -#define DUK_INTERNAL __attribute__ ((visibility("hidden"))) __attribute__ ((unused)) -#else -#define DUK_INTERNAL_DECL __attribute__ ((visibility("hidden"))) extern -#define DUK_INTERNAL __attribute__ ((visibility("hidden"))) -#endif -#endif -#define DUK_LOCAL_DECL static -#define DUK_LOCAL static -#endif - -#if defined(DUK_F_MINGW) -#if defined(DUK_F_CPP) -#define DUK_USE_COMPILER_STRING "mingw++" -#else -#define DUK_USE_COMPILER_STRING "mingw" -#endif -#else -#if defined(DUK_F_CPP) -#define DUK_USE_COMPILER_STRING "g++" -#else -#define DUK_USE_COMPILER_STRING "gcc" -#endif -#endif - -#undef DUK_USE_VARIADIC_MACROS -#if defined(DUK_F_C99) || (defined(DUK_F_CPP11) && defined(__GNUC__)) -#define DUK_USE_VARIADIC_MACROS -#endif - -#define DUK_USE_UNION_INITIALIZERS - -#undef DUK_USE_FLEX_C99 -#undef DUK_USE_FLEX_ZEROSIZE -#undef DUK_USE_FLEX_ONESIZE -#if defined(DUK_F_C99) -#define DUK_USE_FLEX_C99 -#else -#define DUK_USE_FLEX_ZEROSIZE -#endif - -/* Since 4.6 one can '#pragma GCC diagnostic push/pop'. */ -#if defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 40600) -#define DUK_USE_GCC_PRAGMAS -#else -#undef DUK_USE_GCC_PRAGMAS -#endif - -#define DUK_USE_PACK_GCC_ATTR - -/* Availability varies based on platform (between GCC 4.4 and 4.8), and there - * are apparently some bugs in GCC 4.x. Check for GCC 5.0 before enabling - * these to be safe. - */ -#if defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 50000L) -#define DUK_BSWAP64(x) ((duk_uint64_t) __builtin_bswap64((duk_uint64_t) (x))) -#define DUK_BSWAP32(x) ((duk_uint32_t) __builtin_bswap32((duk_uint32_t) (x))) -#define DUK_BSWAP16(x) ((duk_uint16_t) __builtin_bswap16((duk_uint16_t) (x))) -#endif -#elif defined(DUK_F_MSVC) -/* --- MSVC --- */ -/* http://msdn.microsoft.com/en-us/library/aa235362(VS.60).aspx */ -#define DUK_NORETURN(decl) __declspec(noreturn) decl - -/* XXX: DUK_UNREACHABLE for msvc? */ - -#undef DUK_USE_BRANCH_HINTS - -/* XXX: DUK_LIKELY, DUK_UNLIKELY for msvc? */ -/* XXX: DUK_NOINLINE, DUK_INLINE, DUK_ALWAYS_INLINE for msvc? */ - -#if defined(DUK_F_DLL_BUILD) && defined(DUK_F_WINDOWS) -/* MSVC dllexport/dllimport: appropriate __declspec depends on whether we're - * compiling Duktape or the application. - */ -#if defined(DUK_COMPILING_DUKTAPE) -#define DUK_EXTERNAL_DECL extern __declspec(dllexport) -#define DUK_EXTERNAL __declspec(dllexport) -#else -#define DUK_EXTERNAL_DECL extern __declspec(dllimport) -#define DUK_EXTERNAL should_not_happen -#endif -#if defined(DUK_SINGLE_FILE) -#define DUK_INTERNAL_DECL static -#define DUK_INTERNAL static -#else -#define DUK_INTERNAL_DECL extern -#define DUK_INTERNAL /*empty*/ -#endif -#define DUK_LOCAL_DECL static -#define DUK_LOCAL static -#endif - -#if defined(DUK_F_CPP) -#define DUK_USE_COMPILER_STRING "msvc++" -#else -#define DUK_USE_COMPILER_STRING "msvc" -#endif - -#undef DUK_USE_VARIADIC_MACROS -#if defined(DUK_F_C99) -#define DUK_USE_VARIADIC_MACROS -#elif defined(_MSC_VER) && (_MSC_VER >= 1400) -/* VS2005+ should have variadic macros even when they're not C99. */ -#define DUK_USE_VARIADIC_MACROS -#endif - -#undef DUK_USE_UNION_INITIALIZERS -#if defined(_MSC_VER) && (_MSC_VER >= 1800) -/* VS2013+ supports union initializers but there's a bug involving union-inside-struct: - * https://connect.microsoft.com/VisualStudio/feedback/details/805981 - * The bug was fixed (at least) in VS2015 so check for VS2015 for now: - * https://blogs.msdn.microsoft.com/vcblog/2015/07/01/c-compiler-front-end-fixes-in-vs2015/ - * Manually tested using VS2013, CL reports 18.00.31101, so enable for VS2013 too. - */ -#define DUK_USE_UNION_INITIALIZERS -#endif - -#undef DUK_USE_FLEX_C99 -#undef DUK_USE_FLEX_ZEROSIZE -#undef DUK_USE_FLEX_ONESIZE -#if defined(DUK_F_C99) -#define DUK_USE_FLEX_C99 -#else -#define DUK_USE_FLEX_ZEROSIZE -#endif - -#undef DUK_USE_GCC_PRAGMAS - -#define DUK_USE_PACK_MSVC_PRAGMA - -/* These have been tested from VS2008 onwards; may work in older VS versions - * too but not enabled by default. - */ -#if defined(_MSC_VER) && (_MSC_VER >= 1500) -#define DUK_NOINLINE __declspec(noinline) -#define DUK_INLINE __inline -#define DUK_ALWAYS_INLINE __forceinline -#endif - -#if defined(_MSC_VER) && (_MSC_VER >= 1900) -#define DUK_SNPRINTF snprintf -#define DUK_VSNPRINTF vsnprintf -#else -/* (v)snprintf() is missing before MSVC 2015. Note that _(v)snprintf() does - * NOT NUL terminate on truncation, but Duktape code never assumes that. - * http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 - */ -#define DUK_SNPRINTF _snprintf -#define DUK_VSNPRINTF _vsnprintf -#endif - -/* Avoid warning when doing DUK_UNREF(some_function). */ -#if defined(_MSC_VER) && (_MSC_VER < 1500) -#pragma warning(disable: 4100 4101 4550 4551) -#define DUK_UNREF(x) -#else -#define DUK_UNREF(x) do { __pragma(warning(suppress:4100 4101 4550 4551)) (x); } while (0) -#endif - -/* Older versions of MSVC don't support the LL/ULL suffix. */ -#define DUK_U64_CONSTANT(x) x##ui64 -#define DUK_I64_CONSTANT(x) x##i64 -#elif defined(DUK_F_EMSCRIPTEN) -/* --- Emscripten --- */ -#define DUK_NORETURN(decl) decl __attribute__((noreturn)) - -#if defined(__clang__) && defined(__has_builtin) -#if __has_builtin(__builtin_unreachable) -#define DUK_UNREACHABLE() do { __builtin_unreachable(); } while (0) -#endif -#endif - -#define DUK_USE_BRANCH_HINTS -#define DUK_LIKELY(x) __builtin_expect((x), 1) -#define DUK_UNLIKELY(x) __builtin_expect((x), 0) -#if defined(__clang__) && defined(__has_builtin) -#if __has_builtin(__builtin_unpredictable) -#define DUK_UNPREDICTABLE(x) __builtin_unpredictable((x)) -#endif -#endif - -#if defined(DUK_F_C99) || defined(DUK_F_CPP11) -#define DUK_NOINLINE __attribute__((noinline)) -#define DUK_INLINE inline -#define DUK_ALWAYS_INLINE inline __attribute__((always_inline)) -#endif - -#define DUK_EXTERNAL_DECL __attribute__ ((visibility("default"))) extern -#define DUK_EXTERNAL __attribute__ ((visibility("default"))) -#if defined(DUK_SINGLE_FILE) -#if (defined(DUK_F_GCC_VERSION) && DUK_F_GCC_VERSION >= 30101) || defined(DUK_F_CLANG) -/* Minimize warnings for unused internal functions with GCC >= 3.1.1 and - * Clang. Based on documentation it should suffice to have the attribute - * in the declaration only, but in practice some warnings are generated unless - * the attribute is also applied to the definition. - */ -#define DUK_INTERNAL_DECL static __attribute__ ((unused)) -#define DUK_INTERNAL static __attribute__ ((unused)) -#else -#define DUK_INTERNAL_DECL static -#define DUK_INTERNAL static -#endif -#else -#if (defined(DUK_F_GCC_VERSION) && DUK_F_GCC_VERSION >= 30101) || defined(DUK_F_CLANG) -#define DUK_INTERNAL_DECL __attribute__ ((visibility("hidden"))) __attribute__ ((unused)) extern -#define DUK_INTERNAL __attribute__ ((visibility("hidden"))) __attribute__ ((unused)) -#else -#define DUK_INTERNAL_DECL __attribute__ ((visibility("hidden"))) extern -#define DUK_INTERNAL __attribute__ ((visibility("hidden"))) -#endif -#endif -#define DUK_LOCAL_DECL static -#define DUK_LOCAL static - -#define DUK_USE_COMPILER_STRING "emscripten" - -#undef DUK_USE_VARIADIC_MACROS -#if defined(DUK_F_C99) || defined(DUK_F_CPP11) -#define DUK_USE_VARIADIC_MACROS -#endif - -#define DUK_USE_UNION_INITIALIZERS - -#undef DUK_USE_FLEX_C99 -#undef DUK_USE_FLEX_ZEROSIZE -#undef DUK_USE_FLEX_ONESIZE -#if defined(DUK_F_C99) -#define DUK_USE_FLEX_C99 -#else -#define DUK_USE_FLEX_ZEROSIZE -#endif - -#undef DUK_USE_GCC_PRAGMAS -#define DUK_USE_PACK_CLANG_ATTR -#elif defined(DUK_F_TINYC) -/* --- TinyC --- */ -#undef DUK_USE_BRANCH_HINTS - -#if defined(DUK_F_CPP) -#define DUK_USE_COMPILER_STRING "tinyc++" -#else -#define DUK_USE_COMPILER_STRING "tinyc" -#endif - -/* http://bellard.org/tcc/tcc-doc.html#SEC7 */ -#define DUK_USE_VARIADIC_MACROS - -#define DUK_USE_UNION_INITIALIZERS - -/* Most portable, wastes space */ -#define DUK_USE_FLEX_ONESIZE - -/* Most portable, potentially wastes space */ -#define DUK_USE_PACK_DUMMY_MEMBER -#elif defined(DUK_F_VBCC) -/* --- VBCC --- */ -#undef DUK_USE_BRANCH_HINTS - -#if defined(DUK_F_CPP) -#define DUK_USE_COMPILER_STRING "vbcc-c++" -#else -#define DUK_USE_COMPILER_STRING "vbcc" -#endif - -#undef DUK_USE_VARIADIC_MACROS -#if defined(DUK_F_C99) || defined(DUK_F_CPP11) -#define DUK_USE_VARIADIC_MACROS -#endif - -/* VBCC supports C99 so check only for C99 for union initializer support. - * Designated union initializers would possibly work even without a C99 check. - */ -#undef DUK_USE_UNION_INITIALIZERS -#if defined(DUK_F_C99) -#define DUK_USE_UNION_INITIALIZERS -#endif - -#define DUK_USE_FLEX_ZEROSIZE -#define DUK_USE_PACK_DUMMY_MEMBER -#elif defined(DUK_F_BCC) -/* --- Bruce's C compiler --- */ -#undef DUK_USE_BRANCH_HINTS - -#if defined(DUK_F_CPP) -#define DUK_USE_COMPILER_STRING "bcc++" -#else -#define DUK_USE_COMPILER_STRING "bcc" -#endif - -/* Most portable */ -#undef DUK_USE_VARIADIC_MACROS - -/* Most portable, wastes space */ -#undef DUK_USE_UNION_INITIALIZERS - -/* Most portable, wastes space */ -#define DUK_USE_FLEX_ONESIZE - -/* Most portable, potentially wastes space */ -#define DUK_USE_PACK_DUMMY_MEMBER - -/* BCC, assume we're on x86. */ -#if !defined(DUK_USE_BYTEORDER) -#define DUK_USE_BYTEORDER 1 -#endif -#else -/* --- Generic --- */ -#undef DUK_USE_BRANCH_HINTS - -#if defined(DUK_F_CPP) -#define DUK_USE_COMPILER_STRING "generic-c++" -#else -#define DUK_USE_COMPILER_STRING "generic" -#endif - -#undef DUK_USE_VARIADIC_MACROS -#if defined(DUK_F_C99) || defined(DUK_F_CPP11) -#define DUK_USE_VARIADIC_MACROS -#endif - -/* C++ doesn't have standard designated union initializers ({ .foo = 1 }). */ -#undef DUK_USE_UNION_INITIALIZERS -#if defined(DUK_F_C99) -#define DUK_USE_UNION_INITIALIZERS -#endif - -/* Most portable, wastes space */ -#define DUK_USE_FLEX_ONESIZE - -/* Most portable, potentially wastes space */ -#define DUK_USE_PACK_DUMMY_MEMBER -#endif /* autodetect compiler */ - -/* uclibc */ -#if defined(__UCLIBC__) -#define DUK_F_UCLIBC -#endif - -/* - * Wrapper typedefs and constants for integer types, also sanity check types. - * - * C99 typedefs are quite good but not always available, and we want to avoid - * forcibly redefining the C99 typedefs. So, there are Duktape wrappers for - * all C99 typedefs and Duktape code should only use these typedefs. Type - * detection when C99 is not supported is best effort and may end up detecting - * some types incorrectly. - * - * Pointer sizes are a portability problem: pointers to different types may - * have a different size and function pointers are very difficult to manage - * portably. - * - * http://en.wikipedia.org/wiki/C_data_types#Fixed-width_integer_types - * - * Note: there's an interesting corner case when trying to define minimum - * signed integer value constants which leads to the current workaround of - * defining e.g. -0x80000000 as (-0x7fffffffL - 1L). See doc/code-issues.txt - * for a longer discussion. - * - * Note: avoid typecasts and computations in macro integer constants as they - * can then no longer be used in macro relational expressions (such as - * #if DUK_SIZE_MAX < 0xffffffffUL). There is internal code which relies on - * being able to compare DUK_SIZE_MAX against a limit. - */ - -/* XXX: add feature options to force basic types from outside? */ - -#if !defined(INT_MAX) -#error INT_MAX not defined -#endif - -/* Check that architecture is two's complement, standard C allows e.g. - * INT_MIN to be -2**31+1 (instead of -2**31). - */ -#if defined(INT_MAX) && defined(INT_MIN) -#if INT_MAX != -(INT_MIN + 1) -#error platform does not seem complement of two -#endif -#else -#error cannot check complement of two -#endif - -/* Pointer size determination based on __WORDSIZE or architecture when - * that's not available. - */ -#if defined(DUK_F_X86) || defined(DUK_F_X32) || \ - defined(DUK_F_M68K) || defined(DUK_F_PPC32) || \ - defined(DUK_F_BCC) || \ - (defined(__WORDSIZE) && (__WORDSIZE == 32)) || \ - ((defined(DUK_F_OLD_SOLARIS) || defined(DUK_F_AIX) || \ - defined(DUK_F_HPUX)) && defined(_ILP32)) || \ - defined(DUK_F_ARM32) -#define DUK_F_32BIT_PTRS -#elif defined(DUK_F_X64) || \ - (defined(__WORDSIZE) && (__WORDSIZE == 64)) || \ - ((defined(DUK_F_OLD_SOLARIS) || defined(DUK_F_AIX) || \ - defined(DUK_F_HPUX)) && defined(_LP64)) || \ - defined(DUK_F_ARM64) -#define DUK_F_64BIT_PTRS -#else -/* not sure, not needed with C99 anyway */ -#endif - -/* Intermediate define for 'have inttypes.h' */ -#undef DUK_F_HAVE_INTTYPES -#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ - !(defined(DUK_F_AMIGAOS) && defined(DUK_F_VBCC)) -/* vbcc + AmigaOS has C99 but no inttypes.h */ -#define DUK_F_HAVE_INTTYPES -#elif defined(__cplusplus) && (__cplusplus >= 201103L) -/* C++11 apparently ratified stdint.h */ -#define DUK_F_HAVE_INTTYPES -#endif - -/* Basic integer typedefs and limits, preferably from inttypes.h, otherwise - * through automatic detection. - */ -#if defined(DUK_F_HAVE_INTTYPES) -/* C99 or compatible */ - -#define DUK_F_HAVE_64BIT -#include - -typedef uint8_t duk_uint8_t; -typedef int8_t duk_int8_t; -typedef uint16_t duk_uint16_t; -typedef int16_t duk_int16_t; -typedef uint32_t duk_uint32_t; -typedef int32_t duk_int32_t; -typedef uint64_t duk_uint64_t; -typedef int64_t duk_int64_t; -typedef uint_least8_t duk_uint_least8_t; -typedef int_least8_t duk_int_least8_t; -typedef uint_least16_t duk_uint_least16_t; -typedef int_least16_t duk_int_least16_t; -typedef uint_least32_t duk_uint_least32_t; -typedef int_least32_t duk_int_least32_t; -typedef uint_least64_t duk_uint_least64_t; -typedef int_least64_t duk_int_least64_t; -typedef uint_fast8_t duk_uint_fast8_t; -typedef int_fast8_t duk_int_fast8_t; -typedef uint_fast16_t duk_uint_fast16_t; -typedef int_fast16_t duk_int_fast16_t; -typedef uint_fast32_t duk_uint_fast32_t; -typedef int_fast32_t duk_int_fast32_t; -typedef uint_fast64_t duk_uint_fast64_t; -typedef int_fast64_t duk_int_fast64_t; -typedef uintptr_t duk_uintptr_t; -typedef intptr_t duk_intptr_t; -typedef uintmax_t duk_uintmax_t; -typedef intmax_t duk_intmax_t; - -#define DUK_UINT8_MIN 0 -#define DUK_UINT8_MAX UINT8_MAX -#define DUK_INT8_MIN INT8_MIN -#define DUK_INT8_MAX INT8_MAX -#define DUK_UINT_LEAST8_MIN 0 -#define DUK_UINT_LEAST8_MAX UINT_LEAST8_MAX -#define DUK_INT_LEAST8_MIN INT_LEAST8_MIN -#define DUK_INT_LEAST8_MAX INT_LEAST8_MAX -#define DUK_UINT_FAST8_MIN 0 -#define DUK_UINT_FAST8_MAX UINT_FAST8_MAX -#define DUK_INT_FAST8_MIN INT_FAST8_MIN -#define DUK_INT_FAST8_MAX INT_FAST8_MAX -#define DUK_UINT16_MIN 0 -#define DUK_UINT16_MAX UINT16_MAX -#define DUK_INT16_MIN INT16_MIN -#define DUK_INT16_MAX INT16_MAX -#define DUK_UINT_LEAST16_MIN 0 -#define DUK_UINT_LEAST16_MAX UINT_LEAST16_MAX -#define DUK_INT_LEAST16_MIN INT_LEAST16_MIN -#define DUK_INT_LEAST16_MAX INT_LEAST16_MAX -#define DUK_UINT_FAST16_MIN 0 -#define DUK_UINT_FAST16_MAX UINT_FAST16_MAX -#define DUK_INT_FAST16_MIN INT_FAST16_MIN -#define DUK_INT_FAST16_MAX INT_FAST16_MAX -#define DUK_UINT32_MIN 0 -#define DUK_UINT32_MAX UINT32_MAX -#define DUK_INT32_MIN INT32_MIN -#define DUK_INT32_MAX INT32_MAX -#define DUK_UINT_LEAST32_MIN 0 -#define DUK_UINT_LEAST32_MAX UINT_LEAST32_MAX -#define DUK_INT_LEAST32_MIN INT_LEAST32_MIN -#define DUK_INT_LEAST32_MAX INT_LEAST32_MAX -#define DUK_UINT_FAST32_MIN 0 -#define DUK_UINT_FAST32_MAX UINT_FAST32_MAX -#define DUK_INT_FAST32_MIN INT_FAST32_MIN -#define DUK_INT_FAST32_MAX INT_FAST32_MAX -#define DUK_UINT64_MIN 0 -#define DUK_UINT64_MAX UINT64_MAX -#define DUK_INT64_MIN INT64_MIN -#define DUK_INT64_MAX INT64_MAX -#define DUK_UINT_LEAST64_MIN 0 -#define DUK_UINT_LEAST64_MAX UINT_LEAST64_MAX -#define DUK_INT_LEAST64_MIN INT_LEAST64_MIN -#define DUK_INT_LEAST64_MAX INT_LEAST64_MAX -#define DUK_UINT_FAST64_MIN 0 -#define DUK_UINT_FAST64_MAX UINT_FAST64_MAX -#define DUK_INT_FAST64_MIN INT_FAST64_MIN -#define DUK_INT_FAST64_MAX INT_FAST64_MAX - -#define DUK_UINTPTR_MIN 0 -#define DUK_UINTPTR_MAX UINTPTR_MAX -#define DUK_INTPTR_MIN INTPTR_MIN -#define DUK_INTPTR_MAX INTPTR_MAX - -#define DUK_UINTMAX_MIN 0 -#define DUK_UINTMAX_MAX UINTMAX_MAX -#define DUK_INTMAX_MIN INTMAX_MIN -#define DUK_INTMAX_MAX INTMAX_MAX - -#define DUK_SIZE_MIN 0 -#define DUK_SIZE_MAX SIZE_MAX -#undef DUK_SIZE_MAX_COMPUTED - -#else /* C99 types */ - -/* When C99 types are not available, we use heuristic detection to get - * the basic 8, 16, 32, and (possibly) 64 bit types. The fast/least - * types are then assumed to be exactly the same for now: these could - * be improved per platform but C99 types are very often now available. - * 64-bit types are not available on all platforms; this is OK at least - * on 32-bit platforms. - * - * This detection code is necessarily a bit hacky and can provide typedefs - * and defines that won't work correctly on some exotic platform. - */ - -#if (defined(CHAR_BIT) && (CHAR_BIT == 8)) || \ - (defined(UCHAR_MAX) && (UCHAR_MAX == 255)) -typedef unsigned char duk_uint8_t; -typedef signed char duk_int8_t; -#else -#error cannot detect 8-bit type -#endif - -#if defined(USHRT_MAX) && (USHRT_MAX == 65535UL) -typedef unsigned short duk_uint16_t; -typedef signed short duk_int16_t; -#elif defined(UINT_MAX) && (UINT_MAX == 65535UL) -/* On some platforms int is 16-bit but long is 32-bit (e.g. PureC) */ -typedef unsigned int duk_uint16_t; -typedef signed int duk_int16_t; -#else -#error cannot detect 16-bit type -#endif - -#if defined(UINT_MAX) && (UINT_MAX == 4294967295UL) -typedef unsigned int duk_uint32_t; -typedef signed int duk_int32_t; -#elif defined(ULONG_MAX) && (ULONG_MAX == 4294967295UL) -/* On some platforms int is 16-bit but long is 32-bit (e.g. PureC) */ -typedef unsigned long duk_uint32_t; -typedef signed long duk_int32_t; -#else -#error cannot detect 32-bit type -#endif - -/* 64-bit type detection is a bit tricky. - * - * ULLONG_MAX is a standard define. __LONG_LONG_MAX__ and __ULONG_LONG_MAX__ - * are used by at least GCC (even if system headers don't provide ULLONG_MAX). - * Some GCC variants may provide __LONG_LONG_MAX__ but not __ULONG_LONG_MAX__. - * - * ULL / LL constants are rejected / warned about by some compilers, even if - * the compiler has a 64-bit type and the compiler/system headers provide an - * unsupported constant (ULL/LL)! Try to avoid using ULL / LL constants. - * As a side effect we can only check that e.g. ULONG_MAX is larger than 32 - * bits but can't be sure it is exactly 64 bits. Self tests will catch such - * cases. - */ -#undef DUK_F_HAVE_64BIT -#if !defined(DUK_F_HAVE_64BIT) && defined(ULONG_MAX) -#if (ULONG_MAX > 4294967295UL) -#define DUK_F_HAVE_64BIT -typedef unsigned long duk_uint64_t; -typedef signed long duk_int64_t; -#endif -#endif -#if !defined(DUK_F_HAVE_64BIT) && defined(ULLONG_MAX) -#if (ULLONG_MAX > 4294967295UL) -#define DUK_F_HAVE_64BIT -typedef unsigned long long duk_uint64_t; -typedef signed long long duk_int64_t; -#endif -#endif -#if !defined(DUK_F_HAVE_64BIT) && defined(__ULONG_LONG_MAX__) -#if (__ULONG_LONG_MAX__ > 4294967295UL) -#define DUK_F_HAVE_64BIT -typedef unsigned long long duk_uint64_t; -typedef signed long long duk_int64_t; -#endif -#endif -#if !defined(DUK_F_HAVE_64BIT) && defined(__LONG_LONG_MAX__) -#if (__LONG_LONG_MAX__ > 2147483647L) -#define DUK_F_HAVE_64BIT -typedef unsigned long long duk_uint64_t; -typedef signed long long duk_int64_t; -#endif -#endif -#if !defined(DUK_F_HAVE_64BIT) && defined(DUK_F_MINGW) -#define DUK_F_HAVE_64BIT -typedef unsigned long duk_uint64_t; -typedef signed long duk_int64_t; -#endif -#if !defined(DUK_F_HAVE_64BIT) && defined(DUK_F_MSVC) -#define DUK_F_HAVE_64BIT -typedef unsigned __int64 duk_uint64_t; -typedef signed __int64 duk_int64_t; -#endif -#if !defined(DUK_F_HAVE_64BIT) -/* cannot detect 64-bit type, not always needed so don't error */ -#endif - -typedef duk_uint8_t duk_uint_least8_t; -typedef duk_int8_t duk_int_least8_t; -typedef duk_uint16_t duk_uint_least16_t; -typedef duk_int16_t duk_int_least16_t; -typedef duk_uint32_t duk_uint_least32_t; -typedef duk_int32_t duk_int_least32_t; -typedef duk_uint8_t duk_uint_fast8_t; -typedef duk_int8_t duk_int_fast8_t; -typedef duk_uint16_t duk_uint_fast16_t; -typedef duk_int16_t duk_int_fast16_t; -typedef duk_uint32_t duk_uint_fast32_t; -typedef duk_int32_t duk_int_fast32_t; -#if defined(DUK_F_HAVE_64BIT) -typedef duk_uint64_t duk_uint_least64_t; -typedef duk_int64_t duk_int_least64_t; -typedef duk_uint64_t duk_uint_fast64_t; -typedef duk_int64_t duk_int_fast64_t; -#endif -#if defined(DUK_F_HAVE_64BIT) -typedef duk_uint64_t duk_uintmax_t; -typedef duk_int64_t duk_intmax_t; -#else -typedef duk_uint32_t duk_uintmax_t; -typedef duk_int32_t duk_intmax_t; -#endif - -/* Note: the funny looking computations for signed minimum 16-bit, 32-bit, and - * 64-bit values are intentional as the obvious forms (e.g. -0x80000000L) are - * -not- portable. See code-issues.txt for a detailed discussion. - */ -#define DUK_UINT8_MIN 0UL -#define DUK_UINT8_MAX 0xffUL -#define DUK_INT8_MIN (-0x80L) -#define DUK_INT8_MAX 0x7fL -#define DUK_UINT_LEAST8_MIN 0UL -#define DUK_UINT_LEAST8_MAX 0xffUL -#define DUK_INT_LEAST8_MIN (-0x80L) -#define DUK_INT_LEAST8_MAX 0x7fL -#define DUK_UINT_FAST8_MIN 0UL -#define DUK_UINT_FAST8_MAX 0xffUL -#define DUK_INT_FAST8_MIN (-0x80L) -#define DUK_INT_FAST8_MAX 0x7fL -#define DUK_UINT16_MIN 0UL -#define DUK_UINT16_MAX 0xffffUL -#define DUK_INT16_MIN (-0x7fffL - 1L) -#define DUK_INT16_MAX 0x7fffL -#define DUK_UINT_LEAST16_MIN 0UL -#define DUK_UINT_LEAST16_MAX 0xffffUL -#define DUK_INT_LEAST16_MIN (-0x7fffL - 1L) -#define DUK_INT_LEAST16_MAX 0x7fffL -#define DUK_UINT_FAST16_MIN 0UL -#define DUK_UINT_FAST16_MAX 0xffffUL -#define DUK_INT_FAST16_MIN (-0x7fffL - 1L) -#define DUK_INT_FAST16_MAX 0x7fffL -#define DUK_UINT32_MIN 0UL -#define DUK_UINT32_MAX 0xffffffffUL -#define DUK_INT32_MIN (-0x7fffffffL - 1L) -#define DUK_INT32_MAX 0x7fffffffL -#define DUK_UINT_LEAST32_MIN 0UL -#define DUK_UINT_LEAST32_MAX 0xffffffffUL -#define DUK_INT_LEAST32_MIN (-0x7fffffffL - 1L) -#define DUK_INT_LEAST32_MAX 0x7fffffffL -#define DUK_UINT_FAST32_MIN 0UL -#define DUK_UINT_FAST32_MAX 0xffffffffUL -#define DUK_INT_FAST32_MIN (-0x7fffffffL - 1L) -#define DUK_INT_FAST32_MAX 0x7fffffffL - -/* 64-bit constants. Since LL / ULL constants are not always available, - * use computed values. These values can't be used in preprocessor - * comparisons; flag them as such. - */ -#if defined(DUK_F_HAVE_64BIT) -#define DUK_UINT64_MIN ((duk_uint64_t) 0) -#define DUK_UINT64_MAX ((duk_uint64_t) -1) -#define DUK_INT64_MIN ((duk_int64_t) (~(DUK_UINT64_MAX >> 1))) -#define DUK_INT64_MAX ((duk_int64_t) (DUK_UINT64_MAX >> 1)) -#define DUK_UINT_LEAST64_MIN DUK_UINT64_MIN -#define DUK_UINT_LEAST64_MAX DUK_UINT64_MAX -#define DUK_INT_LEAST64_MIN DUK_INT64_MIN -#define DUK_INT_LEAST64_MAX DUK_INT64_MAX -#define DUK_UINT_FAST64_MIN DUK_UINT64_MIN -#define DUK_UINT_FAST64_MAX DUK_UINT64_MAX -#define DUK_INT_FAST64_MIN DUK_INT64_MIN -#define DUK_INT_FAST64_MAX DUK_INT64_MAX -#define DUK_UINT64_MIN_COMPUTED -#define DUK_UINT64_MAX_COMPUTED -#define DUK_INT64_MIN_COMPUTED -#define DUK_INT64_MAX_COMPUTED -#define DUK_UINT_LEAST64_MIN_COMPUTED -#define DUK_UINT_LEAST64_MAX_COMPUTED -#define DUK_INT_LEAST64_MIN_COMPUTED -#define DUK_INT_LEAST64_MAX_COMPUTED -#define DUK_UINT_FAST64_MIN_COMPUTED -#define DUK_UINT_FAST64_MAX_COMPUTED -#define DUK_INT_FAST64_MIN_COMPUTED -#define DUK_INT_FAST64_MAX_COMPUTED -#endif - -#if defined(DUK_F_HAVE_64BIT) -#define DUK_UINTMAX_MIN DUK_UINT64_MIN -#define DUK_UINTMAX_MAX DUK_UINT64_MAX -#define DUK_INTMAX_MIN DUK_INT64_MIN -#define DUK_INTMAX_MAX DUK_INT64_MAX -#define DUK_UINTMAX_MIN_COMPUTED -#define DUK_UINTMAX_MAX_COMPUTED -#define DUK_INTMAX_MIN_COMPUTED -#define DUK_INTMAX_MAX_COMPUTED -#else -#define DUK_UINTMAX_MIN 0UL -#define DUK_UINTMAX_MAX 0xffffffffUL -#define DUK_INTMAX_MIN (-0x7fffffffL - 1L) -#define DUK_INTMAX_MAX 0x7fffffffL -#endif - -/* This detection is not very reliable. */ -#if defined(DUK_F_32BIT_PTRS) -typedef duk_int32_t duk_intptr_t; -typedef duk_uint32_t duk_uintptr_t; -#define DUK_UINTPTR_MIN DUK_UINT32_MIN -#define DUK_UINTPTR_MAX DUK_UINT32_MAX -#define DUK_INTPTR_MIN DUK_INT32_MIN -#define DUK_INTPTR_MAX DUK_INT32_MAX -#elif defined(DUK_F_64BIT_PTRS) && defined(DUK_F_HAVE_64BIT) -typedef duk_int64_t duk_intptr_t; -typedef duk_uint64_t duk_uintptr_t; -#define DUK_UINTPTR_MIN DUK_UINT64_MIN -#define DUK_UINTPTR_MAX DUK_UINT64_MAX -#define DUK_INTPTR_MIN DUK_INT64_MIN -#define DUK_INTPTR_MAX DUK_INT64_MAX -#define DUK_UINTPTR_MIN_COMPUTED -#define DUK_UINTPTR_MAX_COMPUTED -#define DUK_INTPTR_MIN_COMPUTED -#define DUK_INTPTR_MAX_COMPUTED -#else -#error cannot determine intptr type -#endif - -/* SIZE_MAX may be missing so use an approximate value for it. */ -#undef DUK_SIZE_MAX_COMPUTED -#if !defined(SIZE_MAX) -#define DUK_SIZE_MAX_COMPUTED -#define SIZE_MAX ((size_t) (-1)) -#endif -#define DUK_SIZE_MIN 0 -#define DUK_SIZE_MAX SIZE_MAX - -#endif /* C99 types */ - -/* A few types are assumed to always exist. */ -typedef size_t duk_size_t; -typedef ptrdiff_t duk_ptrdiff_t; - -/* The best type for an "all around int" in Duktape internals is "at least - * 32 bit signed integer" which is most convenient. Same for unsigned type. - * Prefer 'int' when large enough, as it is almost always a convenient type. - */ -#if defined(UINT_MAX) && (UINT_MAX >= 0xffffffffUL) -typedef int duk_int_t; -typedef unsigned int duk_uint_t; -#define DUK_INT_MIN INT_MIN -#define DUK_INT_MAX INT_MAX -#define DUK_UINT_MIN 0 -#define DUK_UINT_MAX UINT_MAX -#else -typedef duk_int_fast32_t duk_int_t; -typedef duk_uint_fast32_t duk_uint_t; -#define DUK_INT_MIN DUK_INT_FAST32_MIN -#define DUK_INT_MAX DUK_INT_FAST32_MAX -#define DUK_UINT_MIN DUK_UINT_FAST32_MIN -#define DUK_UINT_MAX DUK_UINT_FAST32_MAX -#endif - -/* Same as 'duk_int_t' but guaranteed to be a 'fast' variant if this - * distinction matters for the CPU. These types are used mainly in the - * executor where it might really matter. - */ -typedef duk_int_fast32_t duk_int_fast_t; -typedef duk_uint_fast32_t duk_uint_fast_t; -#define DUK_INT_FAST_MIN DUK_INT_FAST32_MIN -#define DUK_INT_FAST_MAX DUK_INT_FAST32_MAX -#define DUK_UINT_FAST_MIN DUK_UINT_FAST32_MIN -#define DUK_UINT_FAST_MAX DUK_UINT_FAST32_MAX - -/* Small integers (16 bits or more) can fall back to the 'int' type, but - * have a typedef so they are marked "small" explicitly. - */ -typedef int duk_small_int_t; -typedef unsigned int duk_small_uint_t; -#define DUK_SMALL_INT_MIN INT_MIN -#define DUK_SMALL_INT_MAX INT_MAX -#define DUK_SMALL_UINT_MIN 0 -#define DUK_SMALL_UINT_MAX UINT_MAX - -/* Fast variants of small integers, again for really fast paths like the - * executor. - */ -typedef duk_int_fast16_t duk_small_int_fast_t; -typedef duk_uint_fast16_t duk_small_uint_fast_t; -#define DUK_SMALL_INT_FAST_MIN DUK_INT_FAST16_MIN -#define DUK_SMALL_INT_FAST_MAX DUK_INT_FAST16_MAX -#define DUK_SMALL_UINT_FAST_MIN DUK_UINT_FAST16_MIN -#define DUK_SMALL_UINT_FAST_MAX DUK_UINT_FAST16_MAX - -/* Boolean values are represented with the platform 'unsigned int'. */ -typedef duk_small_uint_t duk_bool_t; -#define DUK_BOOL_MIN DUK_SMALL_UINT_MIN -#define DUK_BOOL_MAX DUK_SMALL_UINT_MAX - -/* Index values must have at least 32-bit signed range. */ -typedef duk_int_t duk_idx_t; -#define DUK_IDX_MIN DUK_INT_MIN -#define DUK_IDX_MAX DUK_INT_MAX - -/* Unsigned index variant. */ -typedef duk_uint_t duk_uidx_t; -#define DUK_UIDX_MIN DUK_UINT_MIN -#define DUK_UIDX_MAX DUK_UINT_MAX - -/* Array index values, could be exact 32 bits. - * Currently no need for signed duk_arridx_t. - */ -typedef duk_uint_t duk_uarridx_t; -#define DUK_UARRIDX_MIN DUK_UINT_MIN -#define DUK_UARRIDX_MAX DUK_UINT_MAX - -/* Duktape/C function return value, platform int is enough for now to - * represent 0, 1, or negative error code. Must be compatible with - * assigning truth values (e.g. duk_ret_t rc = (foo == bar);). - */ -typedef duk_small_int_t duk_ret_t; -#define DUK_RET_MIN DUK_SMALL_INT_MIN -#define DUK_RET_MAX DUK_SMALL_INT_MAX - -/* Error codes are represented with platform int. High bits are used - * for flags and such, so 32 bits are needed. - */ -typedef duk_int_t duk_errcode_t; -#define DUK_ERRCODE_MIN DUK_INT_MIN -#define DUK_ERRCODE_MAX DUK_INT_MAX - -/* Codepoint type. Must be 32 bits or more because it is used also for - * internal codepoints. The type is signed because negative codepoints - * are used as internal markers (e.g. to mark EOF or missing argument). - * (X)UTF-8/CESU-8 encode/decode take and return an unsigned variant to - * ensure duk_uint32_t casts back and forth nicely. Almost everything - * else uses the signed one. - */ -typedef duk_int_t duk_codepoint_t; -typedef duk_uint_t duk_ucodepoint_t; -#define DUK_CODEPOINT_MIN DUK_INT_MIN -#define DUK_CODEPOINT_MAX DUK_INT_MAX -#define DUK_UCODEPOINT_MIN DUK_UINT_MIN -#define DUK_UCODEPOINT_MAX DUK_UINT_MAX - -/* IEEE float/double typedef. */ -typedef float duk_float_t; -typedef double duk_double_t; - -/* We're generally assuming that we're working on a platform with a 32-bit - * address space. If DUK_SIZE_MAX is a typecast value (which is necessary - * if SIZE_MAX is missing), the check must be avoided because the - * preprocessor can't do a comparison. - */ -#if !defined(DUK_SIZE_MAX) -#error DUK_SIZE_MAX is undefined, probably missing SIZE_MAX -#elif !defined(DUK_SIZE_MAX_COMPUTED) -#if DUK_SIZE_MAX < 0xffffffffUL -/* On some systems SIZE_MAX can be smaller than max unsigned 32-bit value - * which seems incorrect if size_t is (at least) an unsigned 32-bit type. - * However, it doesn't seem useful to error out compilation if this is the - * case. - */ -#endif -#endif - -/* Type used in public API declarations and user code. Typedef maps to - * 'struct duk_hthread' like the 'duk_hthread' typedef which is used - * exclusively in internals. - */ -typedef struct duk_hthread duk_context; - -/* Check whether we should use 64-bit integers or not. - * - * Quite incomplete now. Use 64-bit types if detected (C99 or other detection) - * unless they are known to be unreliable. For instance, 64-bit types are - * available on VBCC but seem to misbehave. - */ -#if defined(DUK_F_HAVE_64BIT) && !defined(DUK_F_VBCC) -#define DUK_USE_64BIT_OPS -#else -#undef DUK_USE_64BIT_OPS -#endif - -/* - * Fill-ins for platform, architecture, and compiler - */ - -/* An abort()-like primitive is needed by the default fatal error handler. */ -#if !defined(DUK_ABORT) -#define DUK_ABORT abort -#endif - -#if !defined(DUK_SETJMP) -#define DUK_JMPBUF_TYPE jmp_buf -#define DUK_SETJMP(jb) setjmp((jb)) -#define DUK_LONGJMP(jb) longjmp((jb), 1) -#endif - -#if 0 -/* sigsetjmp() alternative */ -#define DUK_JMPBUF_TYPE sigjmp_buf -#define DUK_SETJMP(jb) sigsetjmp((jb)) -#define DUK_LONGJMP(jb) siglongjmp((jb), 1) -#endif - -/* Special naming to avoid conflict with e.g. DUK_FREE() in duk_heap.h - * (which is unfortunately named). May sometimes need replacement, e.g. - * some compilers don't handle zero length or NULL correctly in realloc(). - */ -#if !defined(DUK_ANSI_MALLOC) -#define DUK_ANSI_MALLOC malloc -#endif -#if !defined(DUK_ANSI_REALLOC) -#define DUK_ANSI_REALLOC realloc -#endif -#if !defined(DUK_ANSI_CALLOC) -#define DUK_ANSI_CALLOC calloc -#endif -#if !defined(DUK_ANSI_FREE) -#define DUK_ANSI_FREE free -#endif - -/* ANSI C (various versions) and some implementations require that the - * pointer arguments to memset(), memcpy(), and memmove() be valid values - * even when byte size is 0 (even a NULL pointer is considered invalid in - * this context). Zero-size operations as such are allowed, as long as their - * pointer arguments point to a valid memory area. The DUK_MEMSET(), - * DUK_MEMCPY(), and DUK_MEMMOVE() macros require this same behavior, i.e.: - * (1) pointers must be valid and non-NULL, (2) zero size must otherwise be - * allowed. If these are not fulfilled, a macro wrapper is needed. - * - * http://stackoverflow.com/questions/5243012/is-it-guaranteed-to-be-safe-to-perform-memcpy0-0-0 - * http://lists.cs.uiuc.edu/pipermail/llvmdev/2007-October/011065.html - * - * Not sure what's the required behavior when a pointer points just past the - * end of a buffer, which often happens in practice (e.g. zero size memmoves). - * For example, if allocation size is 3, the following pointer would not - * technically point to a valid memory byte: - * - * <-- alloc --> - * | 0 | 1 | 2 | ..... - * ^-- p=3, points after last valid byte (2) - */ -#if !defined(DUK_MEMCPY) -#if defined(DUK_F_UCLIBC) -/* Old uclibcs have a broken memcpy so use memmove instead (this is overly wide - * now on purpose): http://lists.uclibc.org/pipermail/uclibc-cvs/2008-October/025511.html - */ -#define DUK_MEMCPY memmove -#else -#define DUK_MEMCPY memcpy -#endif -#endif -#if !defined(DUK_MEMMOVE) -#define DUK_MEMMOVE memmove -#endif -#if !defined(DUK_MEMCMP) -#define DUK_MEMCMP memcmp -#endif -#if !defined(DUK_MEMSET) -#define DUK_MEMSET memset -#endif -#if !defined(DUK_STRLEN) -#define DUK_STRLEN strlen -#endif -#if !defined(DUK_STRCMP) -#define DUK_STRCMP strcmp -#endif -#if !defined(DUK_STRNCMP) -#define DUK_STRNCMP strncmp -#endif -#if !defined(DUK_SPRINTF) -#define DUK_SPRINTF sprintf -#endif -#if !defined(DUK_SNPRINTF) -/* snprintf() is technically not part of C89 but usually available. */ -#define DUK_SNPRINTF snprintf -#endif -#if !defined(DUK_VSPRINTF) -#define DUK_VSPRINTF vsprintf -#endif -#if !defined(DUK_VSNPRINTF) -/* vsnprintf() is technically not part of C89 but usually available. */ -#define DUK_VSNPRINTF vsnprintf -#endif -#if !defined(DUK_SSCANF) -#define DUK_SSCANF sscanf -#endif -#if !defined(DUK_VSSCANF) -#define DUK_VSSCANF vsscanf -#endif -#if !defined(DUK_MEMZERO) -#define DUK_MEMZERO(p,n) DUK_MEMSET((p), 0, (n)) -#endif - -#if !defined(DUK_DOUBLE_INFINITY) -#undef DUK_USE_COMPUTED_INFINITY -#if defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION < 40600) -/* GCC older than 4.6: avoid overflow warnings related to using INFINITY */ -#define DUK_DOUBLE_INFINITY (__builtin_inf()) -#elif defined(INFINITY) -#define DUK_DOUBLE_INFINITY ((double) INFINITY) -#elif !defined(DUK_F_VBCC) && !defined(DUK_F_MSVC) && !defined(DUK_F_BCC) && \ - !defined(DUK_F_OLD_SOLARIS) && !defined(DUK_F_AIX) -#define DUK_DOUBLE_INFINITY (1.0 / 0.0) -#else -/* In VBCC (1.0 / 0.0) results in a warning and 0.0 instead of infinity. - * Use a computed infinity (initialized when a heap is created at the - * latest). - */ -#define DUK_USE_COMPUTED_INFINITY -#define DUK_DOUBLE_INFINITY duk_computed_infinity -#endif -#endif - -#if !defined(DUK_DOUBLE_NAN) -#undef DUK_USE_COMPUTED_NAN -#if defined(NAN) -#define DUK_DOUBLE_NAN NAN -#elif !defined(DUK_F_VBCC) && !defined(DUK_F_MSVC) && !defined(DUK_F_BCC) && \ - !defined(DUK_F_OLD_SOLARIS) && !defined(DUK_F_AIX) -#define DUK_DOUBLE_NAN (0.0 / 0.0) -#else -/* In VBCC (0.0 / 0.0) results in a warning and 0.0 instead of NaN. - * In MSVC (VS2010 Express) (0.0 / 0.0) results in a compile error. - * Use a computed NaN (initialized when a heap is created at the - * latest). - */ -#define DUK_USE_COMPUTED_NAN -#define DUK_DOUBLE_NAN duk_computed_nan -#endif -#endif - -/* Many platforms are missing fpclassify() and friends, so use replacements - * if necessary. The replacement constants (FP_NAN etc) can be anything but - * match Linux constants now. - */ -#undef DUK_USE_REPL_FPCLASSIFY -#undef DUK_USE_REPL_SIGNBIT -#undef DUK_USE_REPL_ISFINITE -#undef DUK_USE_REPL_ISNAN -#undef DUK_USE_REPL_ISINF - -/* Complex condition broken into separate parts. */ -#undef DUK_F_USE_REPL_ALL -#if !(defined(FP_NAN) && defined(FP_INFINITE) && defined(FP_ZERO) && \ - defined(FP_SUBNORMAL) && defined(FP_NORMAL)) -/* Missing some obvious constants. */ -#define DUK_F_USE_REPL_ALL -#elif defined(DUK_F_AMIGAOS) && defined(DUK_F_VBCC) -/* VBCC is missing the built-ins even in C99 mode (perhaps a header issue). */ -#define DUK_F_USE_REPL_ALL -#elif defined(DUK_F_AMIGAOS) && defined(DUK_F_M68K) -/* AmigaOS + M68K seems to have math issues even when using GCC cross - * compilation. Use replacements for all AmigaOS versions on M68K - * regardless of compiler. - */ -#define DUK_F_USE_REPL_ALL -#elif defined(DUK_F_FREEBSD) && defined(DUK_F_CLANG) -/* Placeholder fix for (detection is wider than necessary): - * http://llvm.org/bugs/show_bug.cgi?id=17788 - */ -#define DUK_F_USE_REPL_ALL -#elif defined(DUK_F_UCLIBC) -/* At least some uclibc versions have broken floating point math. For - * example, fpclassify() can incorrectly classify certain NaN formats. - * To be safe, use replacements. - */ -#define DUK_F_USE_REPL_ALL -#elif defined(DUK_F_AIX) -/* Older versions may be missing isnan(), etc. */ -#define DUK_F_USE_REPL_ALL -#endif - -#if defined(DUK_F_USE_REPL_ALL) -#define DUK_USE_REPL_FPCLASSIFY -#define DUK_USE_REPL_SIGNBIT -#define DUK_USE_REPL_ISFINITE -#define DUK_USE_REPL_ISNAN -#define DUK_USE_REPL_ISINF -#define DUK_FPCLASSIFY duk_repl_fpclassify -#define DUK_SIGNBIT duk_repl_signbit -#define DUK_ISFINITE duk_repl_isfinite -#define DUK_ISNAN duk_repl_isnan -#define DUK_ISINF duk_repl_isinf -#define DUK_FP_NAN 0 -#define DUK_FP_INFINITE 1 -#define DUK_FP_ZERO 2 -#define DUK_FP_SUBNORMAL 3 -#define DUK_FP_NORMAL 4 -#else -#define DUK_FPCLASSIFY fpclassify -#define DUK_SIGNBIT signbit -#define DUK_ISFINITE isfinite -#define DUK_ISNAN isnan -#define DUK_ISINF isinf -#define DUK_FP_NAN FP_NAN -#define DUK_FP_INFINITE FP_INFINITE -#define DUK_FP_ZERO FP_ZERO -#define DUK_FP_SUBNORMAL FP_SUBNORMAL -#define DUK_FP_NORMAL FP_NORMAL -#endif - -#if defined(DUK_F_USE_REPL_ALL) -#undef DUK_F_USE_REPL_ALL -#endif - -/* These functions don't currently need replacement but are wrapped for - * completeness. Because these are used as function pointers, they need - * to be defined as concrete C functions (not macros). - */ -#if !defined(DUK_FABS) -#define DUK_FABS fabs -#endif -#if !defined(DUK_FLOOR) -#define DUK_FLOOR floor -#endif -#if !defined(DUK_CEIL) -#define DUK_CEIL ceil -#endif -#if !defined(DUK_FMOD) -#define DUK_FMOD fmod -#endif -#if !defined(DUK_POW) -#define DUK_POW pow -#endif -#if !defined(DUK_ACOS) -#define DUK_ACOS acos -#endif -#if !defined(DUK_ASIN) -#define DUK_ASIN asin -#endif -#if !defined(DUK_ATAN) -#define DUK_ATAN atan -#endif -#if !defined(DUK_ATAN2) -#define DUK_ATAN2 atan2 -#endif -#if !defined(DUK_SIN) -#define DUK_SIN sin -#endif -#if !defined(DUK_COS) -#define DUK_COS cos -#endif -#if !defined(DUK_TAN) -#define DUK_TAN tan -#endif -#if !defined(DUK_EXP) -#define DUK_EXP exp -#endif -#if !defined(DUK_LOG) -#define DUK_LOG log -#endif -#if !defined(DUK_SQRT) -#define DUK_SQRT sqrt -#endif - -/* The functions below exist only in C99/C++11 or later and need a workaround - * for platforms that don't include them. MSVC isn't detected as C99, but - * these functions also exist in MSVC 2013 and later so include a clause for - * that too. Android doesn't have log2; disable all of these for Android. - */ -#if (defined(DUK_F_C99) || defined(DUK_F_CPP11) || (defined(_MSC_VER) && (_MSC_VER >= 1800))) && \ - !defined(DUK_F_ANDROID) && !defined(DUK_F_MINT) -#if !defined(DUK_CBRT) -#define DUK_CBRT cbrt -#endif -#if !defined(DUK_LOG2) -#define DUK_LOG2 log2 -#endif -#if !defined(DUK_LOG10) -#define DUK_LOG10 log10 -#endif -#if !defined(DUK_TRUNC) -#define DUK_TRUNC trunc -#endif -#endif /* DUK_F_C99 etc */ - -/* NetBSD 6.0 x86 (at least) has a few problems with pow() semantics, - * see test-bug-netbsd-math-pow.js. MinGW has similar (but different) - * issues, see test-bug-mingw-math-issues.js. Enable pow() workarounds - * for these targets. - */ -#undef DUK_USE_POW_WORKAROUNDS -#if defined(DUK_F_NETBSD) || defined(DUK_F_MINGW) -#define DUK_USE_POW_WORKAROUNDS -#endif - -/* Similar workarounds for atan2() semantics issues. MinGW issues are - * documented in test-bug-mingw-math-issues.js. - */ -#undef DUK_USE_ATAN2_WORKAROUNDS -#if defined(DUK_F_MINGW) -#define DUK_USE_ATAN2_WORKAROUNDS -#endif - -/* Rely as little as possible on compiler behavior for NaN comparison, - * signed zero handling, etc. Currently never activated but may be needed - * for broken compilers. - */ -#undef DUK_USE_PARANOID_MATH - -/* There was a curious bug where test-bi-date-canceling.js would fail e.g. - * on 64-bit Ubuntu, gcc-4.8.1, -m32, and no -std=c99. Some date computations - * using doubles would be optimized which then broke some corner case tests. - * The problem goes away by adding 'volatile' to the datetime computations. - * Not sure what the actual triggering conditions are, but using this on - * non-C99 systems solves the known issues and has relatively little cost - * on other platforms. - */ -#undef DUK_USE_PARANOID_DATE_COMPUTATION -#if !defined(DUK_F_C99) -#define DUK_USE_PARANOID_DATE_COMPUTATION -#endif - -/* - * Byte order and double memory layout detection - * - * Endianness detection is a major portability hassle because the macros - * and headers are not standardized. There's even variance across UNIX - * platforms. Even with "standard" headers, details like underscore count - * varies between platforms, e.g. both __BYTE_ORDER and _BYTE_ORDER are used - * (Crossbridge has a single underscore, for instance). - * - * The checks below are structured with this in mind: several approaches are - * used, and at the end we check if any of them worked. This allows generic - * approaches to be tried first, and platform/compiler specific hacks tried - * last. As a last resort, the user can force a specific endianness, as it's - * not likely that automatic detection will work on the most exotic platforms. - * - * Duktape supports little and big endian machines. There's also support - * for a hybrid used by some ARM machines where integers are little endian - * but IEEE double values use a mixed order (12345678 -> 43218765). This - * byte order for doubles is referred to as "mixed endian". - */ - -/* GCC and Clang provide endianness defines as built-in predefines, with - * leading and trailing double underscores (e.g. __BYTE_ORDER__). See - * output of "make gccpredefs" and "make clangpredefs". Clang doesn't - * seem to provide __FLOAT_WORD_ORDER__; assume not mixed endian for clang. - * http://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html - */ -#if !defined(DUK_USE_BYTEORDER) && defined(__BYTE_ORDER__) -#if defined(__ORDER_LITTLE_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) -#if defined(__FLOAT_WORD_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && (__FLOAT_WORD_ORDER__ == __ORDER_LITTLE_ENDIAN__) -#define DUK_USE_BYTEORDER 1 -#elif defined(__FLOAT_WORD_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && (__FLOAT_WORD_ORDER__ == __ORDER_BIG_ENDIAN__) -#define DUK_USE_BYTEORDER 2 -#elif !defined(__FLOAT_WORD_ORDER__) -/* Float word order not known, assume not a hybrid. */ -#define DUK_USE_BYTEORDER 1 -#else -/* Byte order is little endian but cannot determine IEEE double word order. */ -#endif /* float word order */ -#elif defined(__ORDER_BIG_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) -#if defined(__FLOAT_WORD_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && (__FLOAT_WORD_ORDER__ == __ORDER_BIG_ENDIAN__) -#define DUK_USE_BYTEORDER 3 -#elif !defined(__FLOAT_WORD_ORDER__) -/* Float word order not known, assume not a hybrid. */ -#define DUK_USE_BYTEORDER 3 -#else -/* Byte order is big endian but cannot determine IEEE double word order. */ -#endif /* float word order */ -#else -/* Cannot determine byte order; __ORDER_PDP_ENDIAN__ is related to 32-bit - * integer ordering and is not relevant. - */ -#endif /* integer byte order */ -#endif /* !defined(DUK_USE_BYTEORDER) && defined(__BYTE_ORDER__) */ - -/* More or less standard endianness predefines provided by header files. - * The ARM hybrid case is detected by assuming that __FLOAT_WORD_ORDER - * will be big endian, see: http://lists.mysql.com/internals/443. - * On some platforms some defines may be present with an empty value which - * causes comparisons to fail: https://github.com/svaarala/duktape/issues/453. - */ -#if !defined(DUK_USE_BYTEORDER) -#if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && (__BYTE_ORDER == __LITTLE_ENDIAN) || \ - defined(_BYTE_ORDER) && defined(_LITTLE_ENDIAN) && (_BYTE_ORDER == _LITTLE_ENDIAN) || \ - defined(__LITTLE_ENDIAN__) -#if defined(__FLOAT_WORD_ORDER) && defined(__LITTLE_ENDIAN) && (__FLOAT_WORD_ORDER == __LITTLE_ENDIAN) || \ - defined(_FLOAT_WORD_ORDER) && defined(_LITTLE_ENDIAN) && (_FLOAT_WORD_ORDER == _LITTLE_ENDIAN) -#define DUK_USE_BYTEORDER 1 -#elif defined(__FLOAT_WORD_ORDER) && defined(__BIG_ENDIAN) && (__FLOAT_WORD_ORDER == __BIG_ENDIAN) || \ - defined(_FLOAT_WORD_ORDER) && defined(_BIG_ENDIAN) && (_FLOAT_WORD_ORDER == _BIG_ENDIAN) -#define DUK_USE_BYTEORDER 2 -#elif !defined(__FLOAT_WORD_ORDER) && !defined(_FLOAT_WORD_ORDER) -/* Float word order not known, assume not a hybrid. */ -#define DUK_USE_BYTEORDER 1 -#else -/* Byte order is little endian but cannot determine IEEE double word order. */ -#endif /* float word order */ -#elif defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && (__BYTE_ORDER == __BIG_ENDIAN) || \ - defined(_BYTE_ORDER) && defined(_BIG_ENDIAN) && (_BYTE_ORDER == _BIG_ENDIAN) || \ - defined(__BIG_ENDIAN__) -#if defined(__FLOAT_WORD_ORDER) && defined(__BIG_ENDIAN) && (__FLOAT_WORD_ORDER == __BIG_ENDIAN) || \ - defined(_FLOAT_WORD_ORDER) && defined(_BIG_ENDIAN) && (_FLOAT_WORD_ORDER == _BIG_ENDIAN) -#define DUK_USE_BYTEORDER 3 -#elif !defined(__FLOAT_WORD_ORDER) && !defined(_FLOAT_WORD_ORDER) -/* Float word order not known, assume not a hybrid. */ -#define DUK_USE_BYTEORDER 3 -#else -/* Byte order is big endian but cannot determine IEEE double word order. */ -#endif /* float word order */ -#else -/* Cannot determine byte order. */ -#endif /* integer byte order */ -#endif /* !defined(DUK_USE_BYTEORDER) */ - -/* QNX gcc cross compiler seems to define e.g. __LITTLEENDIAN__ or __BIGENDIAN__: - * $ /opt/qnx650/host/linux/x86/usr/bin/i486-pc-nto-qnx6.5.0-gcc -dM -E - > 56U) | \ - ((((duk_uint64_t) (x)) >> 40U) & DUK_U64_CONSTANT(0xff00)) | \ - ((((duk_uint64_t) (x)) >> 24U) & DUK_U64_CONSTANT(0xff0000)) | \ - ((((duk_uint64_t) (x)) >> 8U) & DUK_U64_CONSTANT(0xff000000)) | \ - ((((duk_uint64_t) (x)) << 8U) & DUK_U64_CONSTANT(0xff00000000)) | \ - ((((duk_uint64_t) (x)) << 24U) & DUK_U64_CONSTANT(0xff0000000000)) | \ - ((((duk_uint64_t) (x)) << 40U) & DUK_U64_CONSTANT(0xff000000000000)) | \ - (((duk_uint64_t) (x)) << 56U)) -#endif -#endif -#if !defined(DUK_BSWAP32) -#define DUK_BSWAP32(x) \ - ((((duk_uint32_t) (x)) >> 24U) | \ - ((((duk_uint32_t) (x)) >> 8U) & 0xff00UL) | \ - ((((duk_uint32_t) (x)) << 8U) & 0xff0000UL) | \ - (((duk_uint32_t) (x)) << 24U)) -#endif -#if !defined(DUK_BSWAP16) -#define DUK_BSWAP16(x) \ - ((duk_uint16_t) (x) >> 8U) | \ - ((duk_uint16_t) (x) << 8U) -#endif - -/* DUK_USE_VARIADIC_MACROS: required from compilers, so no fill-in. */ -/* DUK_USE_UNION_INITIALIZERS: required from compilers, so no fill-in. */ - -#if !(defined(DUK_USE_FLEX_C99) || defined(DUK_USE_FLEX_ZEROSIZE) || defined(DUK_USE_FLEX_ONESIZE)) -#if defined(DUK_F_C99) -#define DUK_USE_FLEX_C99 -#else -#define DUK_USE_FLEX_ZEROSIZE /* Not standard but common enough */ -#endif -#endif - -#if !(defined(DUK_USE_PACK_GCC_ATTR) || defined(DUK_USE_PACK_CLANG_ATTR) || \ - defined(DUK_USE_PACK_MSVC_PRAGMA) || defined(DUK_USE_PACK_DUMMY_MEMBER)) -#define DUK_USE_PACK_DUMMY_MEMBER -#endif - -#if 0 /* not defined by default */ -#undef DUK_USE_GCC_PRAGMAS -#endif - -/* Workaround for GH-323: avoid inlining control when compiling from - * multiple sources, as it causes compiler portability trouble. - */ -#if !defined(DUK_SINGLE_FILE) -#undef DUK_NOINLINE -#undef DUK_INLINE -#undef DUK_ALWAYS_INLINE -#define DUK_NOINLINE /*nop*/ -#define DUK_INLINE /*nop*/ -#define DUK_ALWAYS_INLINE /*nop*/ -#endif - -/* - * Check whether or not a packed duk_tval representation is possible. - * What's basically required is that pointers are 32-bit values - * (sizeof(void *) == 4). Best effort check, not always accurate. - * If guess goes wrong, crashes may result; self tests also verify - * the guess. - */ - -/* Explicit marker needed; may be 'defined', 'undefined, 'or 'not provided'. */ -#if !defined(DUK_F_PACKED_TVAL_PROVIDED) -#undef DUK_F_PACKED_TVAL_POSSIBLE - -/* Strict C99 case: DUK_UINTPTR_MAX (= UINTPTR_MAX) should be very reliable */ -#if !defined(DUK_F_PACKED_TVAL_POSSIBLE) && defined(DUK_UINTPTR_MAX) -#if (DUK_UINTPTR_MAX <= 0xffffffffUL) -#define DUK_F_PACKED_TVAL_POSSIBLE -#endif -#endif - -/* Non-C99 case, still relying on DUK_UINTPTR_MAX, as long as it is not a computed value */ -#if !defined(DUK_F_PACKED_TVAL_POSSIBLE) && defined(DUK_UINTPTR_MAX) && !defined(DUK_UINTPTR_MAX_COMPUTED) -#if (DUK_UINTPTR_MAX <= 0xffffffffUL) -#define DUK_F_PACKED_TVAL_POSSIBLE -#endif -#endif - -/* DUK_SIZE_MAX (= SIZE_MAX) is often reliable */ -#if !defined(DUK_F_PACKED_TVAL_POSSIBLE) && defined(DUK_SIZE_MAX) && !defined(DUK_SIZE_MAX_COMPUTED) -#if (DUK_SIZE_MAX <= 0xffffffffUL) -#define DUK_F_PACKED_TVAL_POSSIBLE -#endif -#endif - -#undef DUK_USE_PACKED_TVAL -#if defined(DUK_F_PACKED_TVAL_POSSIBLE) -#define DUK_USE_PACKED_TVAL -#endif -#undef DUK_F_PACKED_TVAL_POSSIBLE - -#endif /* DUK_F_PACKED_TVAL_PROVIDED */ -/* Object property allocation layout has implications for memory and code - * footprint and generated code size/speed. The best layout also depends - * on whether the platform has alignment requirements or benefits from - * having mostly aligned accesses. - */ -#undef DUK_USE_HOBJECT_LAYOUT_1 -#undef DUK_USE_HOBJECT_LAYOUT_2 -#undef DUK_USE_HOBJECT_LAYOUT_3 -#if (DUK_USE_ALIGN_BY == 1) -/* On platforms without any alignment issues, layout 1 is preferable - * because it compiles to slightly less code and provides direct access - * to property keys. - */ -#define DUK_USE_HOBJECT_LAYOUT_1 -#else -/* On other platforms use layout 2, which requires some padding but - * is a bit more natural than layout 3 in ordering the entries. Layout - * 3 is currently not used. - */ -#define DUK_USE_HOBJECT_LAYOUT_2 -#endif - -/* GCC/clang inaccurate math would break compliance and probably duk_tval, - * so refuse to compile. Relax this if -ffast-math is tested to work. - */ -#if defined(__FAST_MATH__) -#error __FAST_MATH__ defined, refusing to compile -#endif - -/* - * Forced options - */ - -#define DUK_USE_CPP_EXCEPTIONS -#undef DUK_USE_DATE_NOW_WINDOWS -#ifdef _WIN32 -#define DUK_USE_DATE_NOW_WINDOWS -#endif -#define DUK_USE_EXEC_TIMEOUT_CHECK duk_exec_timeout_check -#define DUK_USE_INTERRUPT_COUNTER - -/* - * Autogenerated defaults - */ - -#undef DUK_USE_ALLOW_UNDEFINED_BEHAVIOR -#define DUK_USE_ARRAY_BUILTIN -#define DUK_USE_ARRAY_FASTPATH -#define DUK_USE_ARRAY_PROP_FASTPATH -#undef DUK_USE_ASSERTIONS -#define DUK_USE_AUGMENT_ERROR_CREATE -#define DUK_USE_AUGMENT_ERROR_THROW -#define DUK_USE_AVOID_PLATFORM_FUNCPTRS -#define DUK_USE_BASE64_FASTPATH -#define DUK_USE_BASE64_SUPPORT -#define DUK_USE_BOOLEAN_BUILTIN -#define DUK_USE_BUFFEROBJECT_SUPPORT -#undef DUK_USE_BUFLEN16 -#define DUK_USE_BYTECODE_DUMP_SUPPORT -#define DUK_USE_CACHE_ACTIVATION -#define DUK_USE_CACHE_CATCHER -#define DUK_USE_CALLSTACK_LIMIT 10000 -#define DUK_USE_CBOR_BUILTIN -#define DUK_USE_CBOR_DEC_RECLIMIT 1000 -#define DUK_USE_CBOR_ENC_RECLIMIT 1000 -#define DUK_USE_CBOR_SUPPORT -#define DUK_USE_COMPILER_RECLIMIT 2500 -#define DUK_USE_COROUTINE_SUPPORT -#undef DUK_USE_DATAPTR16 -#undef DUK_USE_DATAPTR_DEC16 -#undef DUK_USE_DATAPTR_ENC16 -#define DUK_USE_DATE_BUILTIN -#undef DUK_USE_DATE_FORMAT_STRING -#undef DUK_USE_DATE_GET_LOCAL_TZOFFSET -#undef DUK_USE_DATE_GET_NOW -#undef DUK_USE_DATE_PARSE_STRING -#undef DUK_USE_DATE_PRS_GETDATE -#undef DUK_USE_DEBUG -#undef DUK_USE_DEBUGGER_DUMPHEAP -#undef DUK_USE_DEBUGGER_INSPECT -#undef DUK_USE_DEBUGGER_PAUSE_UNCAUGHT -#undef DUK_USE_DEBUGGER_SUPPORT -#define DUK_USE_DEBUGGER_THROW_NOTIFY -#undef DUK_USE_DEBUGGER_TRANSPORT_TORTURE -#define DUK_USE_DEBUG_BUFSIZE 65536L -#define DUK_USE_DEBUG_LEVEL 0 -#undef DUK_USE_DEBUG_WRITE -#define DUK_USE_DOUBLE_LINKED_HEAP -#define DUK_USE_DUKTAPE_BUILTIN -#define DUK_USE_ENCODING_BUILTINS -#define DUK_USE_ERRCREATE -#define DUK_USE_ERRTHROW -#define DUK_USE_ES6 -#define DUK_USE_ES6_OBJECT_PROTO_PROPERTY -#define DUK_USE_ES6_OBJECT_SETPROTOTYPEOF -#define DUK_USE_ES6_PROXY -#define DUK_USE_ES6_REGEXP_SYNTAX -#define DUK_USE_ES6_UNICODE_ESCAPE -#define DUK_USE_ES7 -#define DUK_USE_ES7_EXP_OPERATOR -#define DUK_USE_ES8 -#define DUK_USE_ES9 -#define DUK_USE_ESBC_LIMITS -#define DUK_USE_ESBC_MAX_BYTES 2147418112L -#define DUK_USE_ESBC_MAX_LINENUMBER 2147418112L -#undef DUK_USE_EXEC_FUN_LOCAL -#undef DUK_USE_EXEC_INDIRECT_BOUND_CHECK -#undef DUK_USE_EXEC_PREFER_SIZE -#define DUK_USE_EXEC_REGCONST_OPTIMIZE -#undef DUK_USE_EXPLICIT_NULL_INIT -#undef DUK_USE_EXTSTR_FREE -#undef DUK_USE_EXTSTR_INTERN_CHECK -#undef DUK_USE_FASTINT -#define DUK_USE_FAST_REFCOUNT_DEFAULT -#undef DUK_USE_FATAL_HANDLER -#define DUK_USE_FATAL_MAXLEN 128 -#define DUK_USE_FINALIZER_SUPPORT -#undef DUK_USE_FINALIZER_TORTURE -#undef DUK_USE_FUNCPTR16 -#undef DUK_USE_FUNCPTR_DEC16 -#undef DUK_USE_FUNCPTR_ENC16 -#define DUK_USE_FUNCTION_BUILTIN -#define DUK_USE_FUNC_FILENAME_PROPERTY -#define DUK_USE_FUNC_NAME_PROPERTY -#undef DUK_USE_GC_TORTURE -#undef DUK_USE_GET_MONOTONIC_TIME -#undef DUK_USE_GET_RANDOM_DOUBLE -#define DUK_USE_GLOBAL_BINDING -#define DUK_USE_GLOBAL_BUILTIN -#undef DUK_USE_HEAPPTR16 -#undef DUK_USE_HEAPPTR_DEC16 -#undef DUK_USE_HEAPPTR_ENC16 -#define DUK_USE_HEX_FASTPATH -#define DUK_USE_HEX_SUPPORT -#define DUK_USE_HOBJECT_ARRAY_ABANDON_LIMIT 2 -#define DUK_USE_HOBJECT_ARRAY_ABANDON_MINSIZE 257 -#define DUK_USE_HOBJECT_ARRAY_FAST_RESIZE_LIMIT 9 -#define DUK_USE_HOBJECT_ARRAY_MINGROW_ADD 16 -#define DUK_USE_HOBJECT_ARRAY_MINGROW_DIVISOR 8 -#define DUK_USE_HOBJECT_ENTRY_MINGROW_ADD 16 -#define DUK_USE_HOBJECT_ENTRY_MINGROW_DIVISOR 8 -#define DUK_USE_HOBJECT_HASH_PART -#define DUK_USE_HOBJECT_HASH_PROP_LIMIT 8 -#define DUK_USE_HSTRING_ARRIDX -#define DUK_USE_HSTRING_CLEN -#undef DUK_USE_HSTRING_EXTDATA -#define DUK_USE_HSTRING_LAZY_CLEN -#define DUK_USE_HTML_COMMENTS -#define DUK_USE_IDCHAR_FASTPATH -#undef DUK_USE_INJECT_HEAP_ALLOC_ERROR -#undef DUK_USE_INTERRUPT_DEBUG_FIXUP -#define DUK_USE_JC -#define DUK_USE_JSON_BUILTIN -#define DUK_USE_JSON_DECNUMBER_FASTPATH -#define DUK_USE_JSON_DECSTRING_FASTPATH -#define DUK_USE_JSON_DEC_RECLIMIT 1000 -#define DUK_USE_JSON_EATWHITE_FASTPATH -#define DUK_USE_JSON_ENC_RECLIMIT 1000 -#define DUK_USE_JSON_QUOTESTRING_FASTPATH -#undef DUK_USE_JSON_STRINGIFY_FASTPATH -#define DUK_USE_JSON_SUPPORT -#define DUK_USE_JX -#define DUK_USE_LEXER_SLIDING_WINDOW -#undef DUK_USE_LIGHTFUNC_BUILTINS -#define DUK_USE_LITCACHE_SIZE 256 -#define DUK_USE_MARK_AND_SWEEP_RECLIMIT 256 -#define DUK_USE_MATH_BUILTIN -#define DUK_USE_NATIVE_CALL_RECLIMIT 1000 -#undef DUK_USE_NATIVE_STACK_CHECK -#define DUK_USE_NONSTD_ARRAY_SPLICE_DELCOUNT -#undef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY -#undef DUK_USE_NONSTD_FUNC_SOURCE_PROPERTY -#define DUK_USE_NONSTD_FUNC_STMT -#define DUK_USE_NONSTD_GETTER_KEY_ARGUMENT -#define DUK_USE_NONSTD_JSON_ESC_U2028_U2029 -#define DUK_USE_NONSTD_SETTER_KEY_ARGUMENT -#define DUK_USE_NONSTD_STRING_FROMCHARCODE_32BIT -#define DUK_USE_NUMBER_BUILTIN -#define DUK_USE_OBJECT_BUILTIN -#undef DUK_USE_OBJSIZES16 -#undef DUK_USE_PARANOID_ERRORS -#define DUK_USE_PC2LINE -#define DUK_USE_PERFORMANCE_BUILTIN -#undef DUK_USE_PREFER_SIZE -#undef DUK_USE_PROMISE_BUILTIN -#define DUK_USE_PROVIDE_DEFAULT_ALLOC_FUNCTIONS -#undef DUK_USE_REFCOUNT16 -#define DUK_USE_REFCOUNT32 -#define DUK_USE_REFERENCE_COUNTING -#define DUK_USE_REFLECT_BUILTIN -#define DUK_USE_REGEXP_CANON_BITMAP -#undef DUK_USE_REGEXP_CANON_WORKAROUND -#define DUK_USE_REGEXP_COMPILER_RECLIMIT 10000 -#define DUK_USE_REGEXP_EXECUTOR_RECLIMIT 10000 -#define DUK_USE_REGEXP_SUPPORT -#undef DUK_USE_ROM_GLOBAL_CLONE -#undef DUK_USE_ROM_GLOBAL_INHERIT -#undef DUK_USE_ROM_OBJECTS -#define DUK_USE_ROM_PTRCOMP_FIRST 63488L -#undef DUK_USE_ROM_STRINGS -#define DUK_USE_SECTION_B -#undef DUK_USE_SELF_TESTS -#define DUK_USE_SHEBANG_COMMENTS -#undef DUK_USE_SHUFFLE_TORTURE -#define DUK_USE_SOURCE_NONBMP -#undef DUK_USE_STRHASH16 -#undef DUK_USE_STRHASH_DENSE -#define DUK_USE_STRHASH_SKIP_SHIFT 5 -#define DUK_USE_STRICT_DECL -#undef DUK_USE_STRICT_UTF8_SOURCE -#define DUK_USE_STRING_BUILTIN -#undef DUK_USE_STRLEN16 -#define DUK_USE_STRTAB_GROW_LIMIT 17 -#define DUK_USE_STRTAB_MAXSIZE 268435456L -#define DUK_USE_STRTAB_MINSIZE 1024 -#undef DUK_USE_STRTAB_PTRCOMP -#define DUK_USE_STRTAB_RESIZE_CHECK_MASK 255 -#define DUK_USE_STRTAB_SHRINK_LIMIT 6 -#undef DUK_USE_STRTAB_TORTURE -#define DUK_USE_SYMBOL_BUILTIN -#define DUK_USE_TAILCALL -#define DUK_USE_TARGET_INFO "unknown" -#define DUK_USE_TRACEBACKS -#define DUK_USE_TRACEBACK_DEPTH 10 -#define DUK_USE_VALSTACK_GROW_SHIFT 2 -#define DUK_USE_VALSTACK_LIMIT 1000000L -#define DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT 2 -#define DUK_USE_VALSTACK_SHRINK_SLACK_SHIFT 4 -#undef DUK_USE_VALSTACK_UNSAFE -#define DUK_USE_VERBOSE_ERRORS -#define DUK_USE_VERBOSE_EXECUTOR_ERRORS -#define DUK_USE_VOLUNTARY_GC -#define DUK_USE_ZERO_BUFFER_DATA - -/* - * Fixups - */ - -extern duk_bool_t duk_exec_timeout_check(void*); - -/* - * You may add overriding #define/#undef directives below for - * customization. You of course cannot un-#include or un-typedef - * anything; these require direct changes above. - */ - -/* __OVERRIDE_DEFINES__ */ - -/* - * Conditional includes - */ - -#if defined(DUK_F_CPP) && defined(DUK_USE_CPP_EXCEPTIONS) -#include /* std::exception */ -#include /* std::runtime_error */ -#endif - -/* - * Date provider selection - * - * User may define DUK_USE_DATE_GET_NOW() etc directly, in which case we'll - * rely on an external provider. If this is not done, revert to previous - * behavior and use Unix/Windows built-in provider. - */ - -#if defined(DUK_COMPILING_DUKTAPE) - -#if defined(DUK_USE_DATE_GET_NOW) -/* External provider already defined. */ -#elif defined(DUK_USE_DATE_NOW_GETTIMEOFDAY) -#define DUK_USE_DATE_GET_NOW(ctx) duk_bi_date_get_now_gettimeofday() -#elif defined(DUK_USE_DATE_NOW_TIME) -#define DUK_USE_DATE_GET_NOW(ctx) duk_bi_date_get_now_time() -#elif defined(DUK_USE_DATE_NOW_WINDOWS) -#define DUK_USE_DATE_GET_NOW(ctx) duk_bi_date_get_now_windows() -#elif defined(DUK_USE_DATE_NOW_WINDOWS_SUBMS) -#define DUK_USE_DATE_GET_NOW(ctx) duk_bi_date_get_now_windows_subms() -#else -#error no provider for DUK_USE_DATE_GET_NOW() -#endif - -#if defined(DUK_USE_DATE_GET_LOCAL_TZOFFSET) -/* External provider already defined. */ -#elif defined(DUK_USE_DATE_TZO_GMTIME_R) || defined(DUK_USE_DATE_TZO_GMTIME_S) || defined(DUK_USE_DATE_TZO_GMTIME) -#define DUK_USE_DATE_GET_LOCAL_TZOFFSET(d) duk_bi_date_get_local_tzoffset_gmtime((d)) -#elif defined(DUK_USE_DATE_TZO_WINDOWS) -#define DUK_USE_DATE_GET_LOCAL_TZOFFSET(d) duk_bi_date_get_local_tzoffset_windows((d)) -#elif defined(DUK_USE_DATE_TZO_WINDOWS_NO_DST) -#define DUK_USE_DATE_GET_LOCAL_TZOFFSET(d) duk_bi_date_get_local_tzoffset_windows_no_dst((d)) -#else -#error no provider for DUK_USE_DATE_GET_LOCAL_TZOFFSET() -#endif - -#if defined(DUK_USE_DATE_PARSE_STRING) -/* External provider already defined. */ -#elif defined(DUK_USE_DATE_PRS_STRPTIME) -#define DUK_USE_DATE_PARSE_STRING(ctx,str) duk_bi_date_parse_string_strptime((ctx), (str)) -#elif defined(DUK_USE_DATE_PRS_GETDATE) -#define DUK_USE_DATE_PARSE_STRING(ctx,str) duk_bi_date_parse_string_getdate((ctx), (str)) -#else -/* No provider for DUK_USE_DATE_PARSE_STRING(), fall back to ISO 8601 only. */ -#endif - -#if defined(DUK_USE_DATE_FORMAT_STRING) -/* External provider already defined. */ -#elif defined(DUK_USE_DATE_FMT_STRFTIME) -#define DUK_USE_DATE_FORMAT_STRING(ctx,parts,tzoffset,flags) \ - duk_bi_date_format_parts_strftime((ctx), (parts), (tzoffset), (flags)) -#else -/* No provider for DUK_USE_DATE_FORMAT_STRING(), fall back to ISO 8601 only. */ -#endif - -#if defined(DUK_USE_GET_MONOTONIC_TIME) -/* External provider already defined. */ -#elif defined(DUK_USE_GET_MONOTONIC_TIME_CLOCK_GETTIME) -#define DUK_USE_GET_MONOTONIC_TIME(ctx) duk_bi_date_get_monotonic_time_clock_gettime() -#elif defined(DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC) -#define DUK_USE_GET_MONOTONIC_TIME(ctx) duk_bi_date_get_monotonic_time_windows_qpc() -#else -/* No provider for DUK_USE_GET_MONOTONIC_TIME(), fall back to DUK_USE_DATE_GET_NOW(). */ -#endif - -#endif /* DUK_COMPILING_DUKTAPE */ - -/* - * Convert DUK_USE_BYTEORDER, from whatever source, into currently used - * internal defines. If detection failed, #error out. - */ - -#if defined(DUK_USE_BYTEORDER) -#if (DUK_USE_BYTEORDER == 1) -#define DUK_USE_INTEGER_LE -#define DUK_USE_DOUBLE_LE -#elif (DUK_USE_BYTEORDER == 2) -#define DUK_USE_INTEGER_LE /* integer endianness is little on purpose */ -#define DUK_USE_DOUBLE_ME -#elif (DUK_USE_BYTEORDER == 3) -#define DUK_USE_INTEGER_BE -#define DUK_USE_DOUBLE_BE -#else -#error unsupported: byte order invalid -#endif /* byte order */ -#else -#error unsupported: byte order detection failed -#endif /* defined(DUK_USE_BYTEORDER) */ - -#endif /* DUK_CONFIG_H_INCLUDED */ diff --git a/src/thirdparty/duktape/duk_source_meta.json b/src/thirdparty/duktape/duk_source_meta.json deleted file mode 100644 index 8ab44ca3b9..0000000000 --- a/src/thirdparty/duktape/duk_source_meta.json +++ /dev/null @@ -1,1911 +0,0 @@ -{ - "comment": "Metadata for Duktape sources", - "duk_version_string": "2.7.0", - "type": "duk_source_meta", - "line_map": [ - { - "original_line": 1, - "combined_line": 161, - "original_file": "duk_replacements.c" - }, - { - "original_line": 1, - "combined_line": 170, - "original_file": "duk_internal.h" - }, - { - "original_line": 1, - "combined_line": 207, - "original_file": "duk_dblunion.h" - }, - { - "original_line": 1, - "combined_line": 627, - "original_file": "duk_fltunion.h" - }, - { - "original_line": 1, - "combined_line": 667, - "original_file": "duk_replacements.h" - }, - { - "original_line": 1, - "combined_line": 697, - "original_file": "duk_jmpbuf.h" - }, - { - "original_line": 1, - "combined_line": 722, - "original_file": "duk_exception.h" - }, - { - "original_line": 1, - "combined_line": 754, - "original_file": "duk_forwdecl.h" - }, - { - "original_line": 1, - "combined_line": 889, - "original_file": "duk_tval.h" - }, - { - "original_line": 1, - "combined_line": 1567, - "original_file": "duk_builtins.h" - }, - { - "original_line": 45, - "combined_line": 2358, - "original_file": "duk_internal.h" - }, - { - "original_line": 1, - "combined_line": 2360, - "original_file": "duk_util.h" - }, - { - "original_line": 1, - "combined_line": 3156, - "original_file": "duk_strings.h" - }, - { - "original_line": 1, - "combined_line": 3326, - "original_file": "duk_js_bytecode.h" - }, - { - "original_line": 1, - "combined_line": 3800, - "original_file": "duk_lexer.h" - }, - { - "original_line": 1, - "combined_line": 4236, - "original_file": "duk_js_compiler.h" - }, - { - "original_line": 1, - "combined_line": 4469, - "original_file": "duk_regexp.h" - }, - { - "original_line": 1, - "combined_line": 4554, - "original_file": "duk_heaphdr.h" - }, - { - "original_line": 1, - "combined_line": 4871, - "original_file": "duk_refcount.h" - }, - { - "original_line": 1, - "combined_line": 5888, - "original_file": "duk_api_internal.h" - }, - { - "original_line": 1, - "combined_line": 6282, - "original_file": "duk_hstring.h" - }, - { - "original_line": 1, - "combined_line": 6548, - "original_file": "duk_hobject.h" - }, - { - "original_line": 1, - "combined_line": 7538, - "original_file": "duk_hcompfunc.h" - }, - { - "original_line": 1, - "combined_line": 7798, - "original_file": "duk_hnatfunc.h" - }, - { - "original_line": 1, - "combined_line": 7843, - "original_file": "duk_hboundfunc.h" - }, - { - "original_line": 1, - "combined_line": 7886, - "original_file": "duk_hbufobj.h" - }, - { - "original_line": 1, - "combined_line": 8024, - "original_file": "duk_hthread.h" - }, - { - "original_line": 1, - "combined_line": 8446, - "original_file": "duk_harray.h" - }, - { - "original_line": 1, - "combined_line": 8506, - "original_file": "duk_henv.h" - }, - { - "original_line": 1, - "combined_line": 8562, - "original_file": "duk_hbuffer.h" - }, - { - "original_line": 1, - "combined_line": 8910, - "original_file": "duk_hproxy.h" - }, - { - "original_line": 1, - "combined_line": 8942, - "original_file": "duk_heap.h" - }, - { - "original_line": 1, - "combined_line": 9689, - "original_file": "duk_debugger.h" - }, - { - "original_line": 1, - "combined_line": 9841, - "original_file": "duk_debug.h" - }, - { - "original_line": 1, - "combined_line": 10038, - "original_file": "duk_error.h" - }, - { - "original_line": 1, - "combined_line": 10728, - "original_file": "duk_unicode.h" - }, - { - "original_line": 1, - "combined_line": 11028, - "original_file": "duk_json.h" - }, - { - "original_line": 1, - "combined_line": 11097, - "original_file": "duk_js.h" - }, - { - "original_line": 1, - "combined_line": 11231, - "original_file": "duk_numconv.h" - }, - { - "original_line": 1, - "combined_line": 11339, - "original_file": "duk_bi_protos.h" - }, - { - "original_line": 1, - "combined_line": 11421, - "original_file": "duk_selftest.h" - }, - { - "original_line": 76, - "combined_line": 11436, - "original_file": "duk_internal.h" - }, - { - "original_line": 10, - "combined_line": 11438, - "original_file": "duk_replacements.c" - }, - { - "original_line": 1, - "combined_line": 11511, - "original_file": "duk_debug_macros.c" - }, - { - "original_line": 1, - "combined_line": 11602, - "original_file": "duk_builtins.c" - }, - { - "original_line": 1, - "combined_line": 12466, - "original_file": "duk_error_macros.c" - }, - { - "original_line": 1, - "combined_line": 12653, - "original_file": "duk_unicode_support.c" - }, - { - "original_line": 1, - "combined_line": 13878, - "original_file": "duk_util_memrw.c" - }, - { - "original_line": 1, - "combined_line": 14026, - "original_file": "duk_util_misc.c" - }, - { - "original_line": 1, - "combined_line": 14191, - "original_file": "duk_hobject_class.c" - }, - { - "original_line": 1, - "combined_line": 14320, - "original_file": "duk_alloc_default.c" - }, - { - "original_line": 1, - "combined_line": 14352, - "original_file": "duk_api_buffer.c" - }, - { - "original_line": 1, - "combined_line": 14425, - "original_file": "duk_api_bytecode.c" - }, - { - "original_line": 1, - "combined_line": 15204, - "original_file": "duk_api_call.c" - }, - { - "original_line": 1, - "combined_line": 15722, - "original_file": "duk_api_codec.c" - }, - { - "original_line": 1, - "combined_line": 16653, - "original_file": "duk_api_compile.c" - }, - { - "original_line": 1, - "combined_line": 16827, - "original_file": "duk_api_debug.c" - }, - { - "original_line": 1, - "combined_line": 17088, - "original_file": "duk_api_heap.c" - }, - { - "original_line": 1, - "combined_line": 17297, - "original_file": "duk_api_inspect.c" - }, - { - "original_line": 1, - "combined_line": 17568, - "original_file": "duk_api_memory.c" - }, - { - "original_line": 1, - "combined_line": 17648, - "original_file": "duk_api_object.c" - }, - { - "original_line": 1, - "combined_line": 18686, - "original_file": "duk_api_random.c" - }, - { - "original_line": 1, - "combined_line": 18695, - "original_file": "duk_api_stack.c" - }, - { - "original_line": 1, - "combined_line": 25603, - "original_file": "duk_api_string.c" - }, - { - "original_line": 1, - "combined_line": 25982, - "original_file": "duk_api_time.c" - }, - { - "original_line": 1, - "combined_line": 26092, - "original_file": "duk_bi_array.c" - }, - { - "original_line": 1, - "combined_line": 27746, - "original_file": "duk_bi_boolean.c" - }, - { - "original_line": 1, - "combined_line": 27815, - "original_file": "duk_bi_buffer.c" - }, - { - "original_line": 1, - "combined_line": 30744, - "original_file": "duk_bi_cbor.c" - }, - { - "original_line": 1, - "combined_line": 32667, - "original_file": "duk_bi_date.c" - }, - { - "original_line": 1, - "combined_line": 34566, - "original_file": "duk_bi_date_unix.c" - }, - { - "original_line": 1, - "combined_line": 34914, - "original_file": "duk_bi_date_windows.c" - }, - { - "original_line": 1, - "combined_line": 35109, - "original_file": "duk_bi_duktape.c" - }, - { - "original_line": 1, - "combined_line": 35259, - "original_file": "duk_bi_encoding.c" - }, - { - "original_line": 1, - "combined_line": 35796, - "original_file": "duk_bi_error.c" - }, - { - "original_line": 1, - "combined_line": 36196, - "original_file": "duk_bi_function.c" - }, - { - "original_line": 1, - "combined_line": 36649, - "original_file": "duk_bi_global.c" - }, - { - "original_line": 1, - "combined_line": 37369, - "original_file": "duk_bi_json.c" - }, - { - "original_line": 1, - "combined_line": 40612, - "original_file": "duk_bi_math.c" - }, - { - "original_line": 1, - "combined_line": 41103, - "original_file": "duk_bi_number.c" - }, - { - "original_line": 1, - "combined_line": 41368, - "original_file": "duk_bi_object.c" - }, - { - "original_line": 1, - "combined_line": 42122, - "original_file": "duk_bi_performance.c" - }, - { - "original_line": 1, - "combined_line": 42153, - "original_file": "duk_bi_pointer.c" - }, - { - "original_line": 1, - "combined_line": 42227, - "original_file": "duk_bi_promise.c" - }, - { - "original_line": 1, - "combined_line": 42271, - "original_file": "duk_bi_proxy.c" - }, - { - "original_line": 1, - "combined_line": 42369, - "original_file": "duk_bi_reflect.c" - }, - { - "original_line": 1, - "combined_line": 42468, - "original_file": "duk_bi_regexp.c" - }, - { - "original_line": 1, - "combined_line": 42692, - "original_file": "duk_bi_string.c" - }, - { - "original_line": 1, - "combined_line": 44307, - "original_file": "duk_bi_symbol.c" - }, - { - "original_line": 1, - "combined_line": 44479, - "original_file": "duk_bi_thread.c" - }, - { - "original_line": 1, - "combined_line": 44803, - "original_file": "duk_bi_thrower.c" - }, - { - "original_line": 1, - "combined_line": 44812, - "original_file": "duk_debug_fixedbuffer.c" - }, - { - "original_line": 1, - "combined_line": 44881, - "original_file": "duk_debug_vsnprintf.c" - }, - { - "original_line": 1, - "combined_line": 46085, - "original_file": "duk_debugger.c" - }, - { - "original_line": 1, - "combined_line": 48984, - "original_file": "duk_error_augment.c" - }, - { - "original_line": 1, - "combined_line": 49586, - "original_file": "duk_error_longjmp.c" - }, - { - "original_line": 1, - "combined_line": 49693, - "original_file": "duk_error_misc.c" - }, - { - "original_line": 1, - "combined_line": 49866, - "original_file": "duk_error_throw.c" - }, - { - "original_line": 1, - "combined_line": 50025, - "original_file": "duk_hbuffer_alloc.c" - }, - { - "original_line": 1, - "combined_line": 50156, - "original_file": "duk_hbuffer_assert.c" - }, - { - "original_line": 1, - "combined_line": 50169, - "original_file": "duk_hbuffer_ops.c" - }, - { - "original_line": 2, - "combined_line": 50247, - "original_file": "duk_hbufobj_misc.c" - }, - { - "original_line": 1, - "combined_line": 50266, - "original_file": "duk_heap_alloc.c" - }, - { - "original_line": 1, - "combined_line": 51493, - "original_file": "duk_heap_finalize.c" - }, - { - "original_line": 1, - "combined_line": 51942, - "original_file": "duk_heap_hashstring.c" - }, - { - "original_line": 1, - "combined_line": 52063, - "original_file": "duk_heap_markandsweep.c" - }, - { - "original_line": 1, - "combined_line": 53571, - "original_file": "duk_heap_memory.c" - }, - { - "original_line": 1, - "combined_line": 53983, - "original_file": "duk_heap_misc.c" - }, - { - "original_line": 1, - "combined_line": 54170, - "original_file": "duk_heap_refcount.c" - }, - { - "original_line": 1, - "combined_line": 55029, - "original_file": "duk_heap_stringcache.c" - }, - { - "original_line": 1, - "combined_line": 55349, - "original_file": "duk_heap_stringtable.c" - }, - { - "original_line": 1, - "combined_line": 56419, - "original_file": "duk_heaphdr_assert.c" - }, - { - "original_line": 1, - "combined_line": 56497, - "original_file": "duk_hobject_alloc.c" - }, - { - "original_line": 1, - "combined_line": 56768, - "original_file": "duk_hobject_assert.c" - }, - { - "original_line": 1, - "combined_line": 56892, - "original_file": "duk_hobject_enum.c" - }, - { - "original_line": 1, - "combined_line": 57612, - "original_file": "duk_hobject_misc.c" - }, - { - "original_line": 1, - "combined_line": 57668, - "original_file": "duk_hobject_pc2line.c" - }, - { - "original_line": 1, - "combined_line": 57914, - "original_file": "duk_hobject_props.c" - }, - { - "original_line": 1, - "combined_line": 64276, - "original_file": "duk_hstring_assert.c" - }, - { - "original_line": 1, - "combined_line": 64289, - "original_file": "duk_hstring_misc.c" - }, - { - "original_line": 1, - "combined_line": 64485, - "original_file": "duk_hthread_alloc.c" - }, - { - "original_line": 1, - "combined_line": 64544, - "original_file": "duk_hthread_builtins.c" - }, - { - "original_line": 1, - "combined_line": 65445, - "original_file": "duk_hthread_misc.c" - }, - { - "original_line": 1, - "combined_line": 65542, - "original_file": "duk_hthread_stacks.c" - }, - { - "original_line": 1, - "combined_line": 65947, - "original_file": "duk_js_arith.c" - }, - { - "original_line": 1, - "combined_line": 66088, - "original_file": "duk_js_call.c" - }, - { - "original_line": 1, - "combined_line": 69036, - "original_file": "duk_js_compiler.c" - }, - { - "original_line": 1, - "combined_line": 77176, - "original_file": "duk_js_executor.c" - }, - { - "original_line": 1, - "combined_line": 82498, - "original_file": "duk_js_ops.c" - }, - { - "original_line": 1, - "combined_line": 83954, - "original_file": "duk_js_var.c" - }, - { - "original_line": 1, - "combined_line": 85745, - "original_file": "duk_lexer.c" - }, - { - "original_line": 1, - "combined_line": 88196, - "original_file": "duk_numconv.c" - }, - { - "original_line": 1, - "combined_line": 90538, - "original_file": "duk_regexp_compiler.c" - }, - { - "original_line": 1, - "combined_line": 91841, - "original_file": "duk_regexp_executor.c" - }, - { - "original_line": 1, - "combined_line": 92896, - "original_file": "duk_selftest.c" - }, - { - "original_line": 2, - "combined_line": 93643, - "original_file": "duk_tval.c" - }, - { - "original_line": 1, - "combined_line": 93794, - "original_file": "duk_unicode_tables.c" - }, - { - "original_line": 1, - "combined_line": 99969, - "original_file": "duk_util_bitdecoder.c" - }, - { - "original_line": 1, - "combined_line": 100133, - "original_file": "duk_util_bitencoder.c" - }, - { - "original_line": 1, - "combined_line": 100176, - "original_file": "duk_util_bufwriter.c" - }, - { - "original_line": 1, - "combined_line": 100462, - "original_file": "duk_util_cast.c" - }, - { - "original_line": 1, - "combined_line": 100634, - "original_file": "duk_util_double.c" - }, - { - "original_line": 1, - "combined_line": 100977, - "original_file": "duk_util_hashbytes.c" - }, - { - "original_line": 1, - "combined_line": 101039, - "original_file": "duk_util_memory.c" - }, - { - "original_line": 1, - "combined_line": 101075, - "original_file": "duk_util_tinyrandom.c" - } - ], - "duk_version": 20700, - "git_branch": "external", - "git_commit": "external", - "builtin_strings_info": [ - { - "plain": "Undefined", - "base64": "VW5kZWZpbmVk", - "define": "DUK_STRIDX_UC_UNDEFINED" - }, - { - "plain": "Null", - "base64": "TnVsbA==", - "define": "DUK_STRIDX_UC_NULL" - }, - { - "plain": "Symbol", - "base64": "U3ltYm9s", - "define": "DUK_STRIDX_UC_SYMBOL" - }, - { - "plain": "Arguments", - "base64": "QXJndW1lbnRz", - "define": "DUK_STRIDX_UC_ARGUMENTS" - }, - { - "plain": "Object", - "base64": "T2JqZWN0", - "define": "DUK_STRIDX_UC_OBJECT" - }, - { - "plain": "Function", - "base64": "RnVuY3Rpb24=", - "define": "DUK_STRIDX_UC_FUNCTION" - }, - { - "plain": "Array", - "base64": "QXJyYXk=", - "define": "DUK_STRIDX_UC_ARRAY" - }, - { - "plain": "String", - "base64": "U3RyaW5n", - "define": "DUK_STRIDX_UC_STRING" - }, - { - "plain": "Boolean", - "base64": "Qm9vbGVhbg==", - "define": "DUK_STRIDX_UC_BOOLEAN" - }, - { - "plain": "Number", - "base64": "TnVtYmVy", - "define": "DUK_STRIDX_UC_NUMBER" - }, - { - "plain": "Date", - "base64": "RGF0ZQ==", - "define": "DUK_STRIDX_UC_DATE" - }, - { - "plain": "RegExp", - "base64": "UmVnRXhw", - "define": "DUK_STRIDX_REG_EXP" - }, - { - "plain": "Error", - "base64": "RXJyb3I=", - "define": "DUK_STRIDX_UC_ERROR" - }, - { - "plain": "Math", - "base64": "TWF0aA==", - "define": "DUK_STRIDX_MATH" - }, - { - "plain": "JSON", - "base64": "SlNPTg==", - "define": "DUK_STRIDX_JSON" - }, - { - "plain": "", - "base64": "", - "define": "DUK_STRIDX_EMPTY_STRING" - }, - { - "plain": "ArrayBuffer", - "base64": "QXJyYXlCdWZmZXI=", - "define": "DUK_STRIDX_ARRAY_BUFFER" - }, - { - "plain": "DataView", - "base64": "RGF0YVZpZXc=", - "define": "DUK_STRIDX_DATA_VIEW" - }, - { - "plain": "Int8Array", - "base64": "SW50OEFycmF5", - "define": "DUK_STRIDX_INT8_ARRAY" - }, - { - "plain": "Uint8Array", - "base64": "VWludDhBcnJheQ==", - "define": "DUK_STRIDX_UINT8_ARRAY" - }, - { - "plain": "Uint8ClampedArray", - "base64": "VWludDhDbGFtcGVkQXJyYXk=", - "define": "DUK_STRIDX_UINT8_CLAMPED_ARRAY" - }, - { - "plain": "Int16Array", - "base64": "SW50MTZBcnJheQ==", - "define": "DUK_STRIDX_INT16_ARRAY" - }, - { - "plain": "Uint16Array", - "base64": "VWludDE2QXJyYXk=", - "define": "DUK_STRIDX_UINT16_ARRAY" - }, - { - "plain": "Int32Array", - "base64": "SW50MzJBcnJheQ==", - "define": "DUK_STRIDX_INT32_ARRAY" - }, - { - "plain": "Uint32Array", - "base64": "VWludDMyQXJyYXk=", - "define": "DUK_STRIDX_UINT32_ARRAY" - }, - { - "plain": "Float32Array", - "base64": "RmxvYXQzMkFycmF5", - "define": "DUK_STRIDX_FLOAT32_ARRAY" - }, - { - "plain": "Float64Array", - "base64": "RmxvYXQ2NEFycmF5", - "define": "DUK_STRIDX_FLOAT64_ARRAY" - }, - { - "plain": "global", - "base64": "Z2xvYmFs", - "define": "DUK_STRIDX_GLOBAL" - }, - { - "plain": "ObjEnv", - "base64": "T2JqRW52", - "define": "DUK_STRIDX_OBJ_ENV" - }, - { - "plain": "DecEnv", - "base64": "RGVjRW52", - "define": "DUK_STRIDX_DEC_ENV" - }, - { - "plain": "Buffer", - "base64": "QnVmZmVy", - "define": "DUK_STRIDX_UC_BUFFER" - }, - { - "plain": "Pointer", - "base64": "UG9pbnRlcg==", - "define": "DUK_STRIDX_UC_POINTER" - }, - { - "plain": "Thread", - "base64": "VGhyZWFk", - "define": "DUK_STRIDX_UC_THREAD" - }, - { - "plain": "eval", - "base64": "ZXZhbA==", - "define": "DUK_STRIDX_EVAL" - }, - { - "plain": "value", - "base64": "dmFsdWU=", - "define": "DUK_STRIDX_VALUE" - }, - { - "plain": "writable", - "base64": "d3JpdGFibGU=", - "define": "DUK_STRIDX_WRITABLE" - }, - { - "plain": "configurable", - "base64": "Y29uZmlndXJhYmxl", - "define": "DUK_STRIDX_CONFIGURABLE" - }, - { - "plain": "enumerable", - "base64": "ZW51bWVyYWJsZQ==", - "define": "DUK_STRIDX_ENUMERABLE" - }, - { - "plain": "join", - "base64": "am9pbg==", - "define": "DUK_STRIDX_JOIN" - }, - { - "plain": "toLocaleString", - "base64": "dG9Mb2NhbGVTdHJpbmc=", - "define": "DUK_STRIDX_TO_LOCALE_STRING" - }, - { - "plain": "valueOf", - "base64": "dmFsdWVPZg==", - "define": "DUK_STRIDX_VALUE_OF" - }, - { - "plain": "toUTCString", - "base64": "dG9VVENTdHJpbmc=", - "define": "DUK_STRIDX_TO_UTC_STRING" - }, - { - "plain": "toISOString", - "base64": "dG9JU09TdHJpbmc=", - "define": "DUK_STRIDX_TO_ISO_STRING" - }, - { - "plain": "toGMTString", - "base64": "dG9HTVRTdHJpbmc=", - "define": "DUK_STRIDX_TO_GMT_STRING" - }, - { - "plain": "source", - "base64": "c291cmNl", - "define": "DUK_STRIDX_SOURCE" - }, - { - "plain": "ignoreCase", - "base64": "aWdub3JlQ2FzZQ==", - "define": "DUK_STRIDX_IGNORE_CASE" - }, - { - "plain": "multiline", - "base64": "bXVsdGlsaW5l", - "define": "DUK_STRIDX_MULTILINE" - }, - { - "plain": "lastIndex", - "base64": "bGFzdEluZGV4", - "define": "DUK_STRIDX_LAST_INDEX" - }, - { - "plain": "flags", - "base64": "ZmxhZ3M=", - "define": "DUK_STRIDX_FLAGS" - }, - { - "plain": "index", - "base64": "aW5kZXg=", - "define": "DUK_STRIDX_INDEX" - }, - { - "plain": "prototype", - "base64": "cHJvdG90eXBl", - "define": "DUK_STRIDX_PROTOTYPE" - }, - { - "plain": "constructor", - "base64": "Y29uc3RydWN0b3I=", - "define": "DUK_STRIDX_CONSTRUCTOR" - }, - { - "plain": "message", - "base64": "bWVzc2FnZQ==", - "define": "DUK_STRIDX_MESSAGE" - }, - { - "plain": "boolean", - "base64": "Ym9vbGVhbg==", - "define": "DUK_STRIDX_LC_BOOLEAN" - }, - { - "plain": "number", - "base64": "bnVtYmVy", - "define": "DUK_STRIDX_LC_NUMBER" - }, - { - "plain": "string", - "base64": "c3RyaW5n", - "define": "DUK_STRIDX_LC_STRING" - }, - { - "plain": "symbol", - "base64": "c3ltYm9s", - "define": "DUK_STRIDX_LC_SYMBOL" - }, - { - "plain": "object", - "base64": "b2JqZWN0", - "define": "DUK_STRIDX_LC_OBJECT" - }, - { - "plain": "undefined", - "base64": "dW5kZWZpbmVk", - "define": "DUK_STRIDX_LC_UNDEFINED" - }, - { - "plain": "NaN", - "base64": "TmFO", - "define": "DUK_STRIDX_NAN" - }, - { - "plain": "Infinity", - "base64": "SW5maW5pdHk=", - "define": "DUK_STRIDX_INFINITY" - }, - { - "plain": "-Infinity", - "base64": "LUluZmluaXR5", - "define": "DUK_STRIDX_MINUS_INFINITY" - }, - { - "plain": "-0", - "base64": "LTA=", - "define": "DUK_STRIDX_MINUS_ZERO" - }, - { - "plain": ",", - "base64": "LA==", - "define": "DUK_STRIDX_COMMA" - }, - { - "plain": "\n ", - "base64": "CiAgICA=", - "define": "DUK_STRIDX_NEWLINE_4SPACE" - }, - { - "plain": "[...]", - "base64": "Wy4uLl0=", - "define": "DUK_STRIDX_BRACKETED_ELLIPSIS" - }, - { - "plain": "Invalid Date", - "base64": "SW52YWxpZCBEYXRl", - "define": "DUK_STRIDX_INVALID_DATE" - }, - { - "plain": "arguments", - "base64": "YXJndW1lbnRz", - "define": "DUK_STRIDX_LC_ARGUMENTS" - }, - { - "plain": "callee", - "base64": "Y2FsbGVl", - "define": "DUK_STRIDX_CALLEE" - }, - { - "plain": "caller", - "base64": "Y2FsbGVy", - "define": "DUK_STRIDX_CALLER" - }, - { - "plain": "apply", - "base64": "YXBwbHk=", - "define": "DUK_STRIDX_APPLY" - }, - { - "plain": "construct", - "base64": "Y29uc3RydWN0", - "define": "DUK_STRIDX_CONSTRUCT" - }, - { - "plain": "deleteProperty", - "base64": "ZGVsZXRlUHJvcGVydHk=", - "define": "DUK_STRIDX_DELETE_PROPERTY" - }, - { - "plain": "get", - "base64": "Z2V0", - "define": "DUK_STRIDX_GET" - }, - { - "plain": "has", - "base64": "aGFz", - "define": "DUK_STRIDX_HAS" - }, - { - "plain": "ownKeys", - "base64": "b3duS2V5cw==", - "define": "DUK_STRIDX_OWN_KEYS" - }, - { - "plain": "\u0081Symbol.toPrimitive\u00ff", - "base64": "gVN5bWJvbC50b1ByaW1pdGl2Zf8=", - "define": "DUK_STRIDX_WELLKNOWN_SYMBOL_TO_PRIMITIVE" - }, - { - "plain": "\u0081Symbol.hasInstance\u00ff", - "base64": "gVN5bWJvbC5oYXNJbnN0YW5jZf8=", - "define": "DUK_STRIDX_WELLKNOWN_SYMBOL_HAS_INSTANCE" - }, - { - "plain": "\u0081Symbol.toStringTag\u00ff", - "base64": "gVN5bWJvbC50b1N0cmluZ1RhZ/8=", - "define": "DUK_STRIDX_WELLKNOWN_SYMBOL_TO_STRING_TAG" - }, - { - "plain": "\u0081Symbol.isConcatSpreadable\u00ff", - "base64": "gVN5bWJvbC5pc0NvbmNhdFNwcmVhZGFibGX/", - "define": "DUK_STRIDX_WELLKNOWN_SYMBOL_IS_CONCAT_SPREADABLE" - }, - { - "plain": "setPrototypeOf", - "base64": "c2V0UHJvdG90eXBlT2Y=", - "define": "DUK_STRIDX_SET_PROTOTYPE_OF" - }, - { - "plain": "__proto__", - "base64": "X19wcm90b19f", - "define": "DUK_STRIDX___PROTO__" - }, - { - "plain": "toString", - "base64": "dG9TdHJpbmc=", - "define": "DUK_STRIDX_TO_STRING" - }, - { - "plain": "toJSON", - "base64": "dG9KU09O", - "define": "DUK_STRIDX_TO_JSON" - }, - { - "plain": "type", - "base64": "dHlwZQ==", - "define": "DUK_STRIDX_TYPE" - }, - { - "plain": "data", - "base64": "ZGF0YQ==", - "define": "DUK_STRIDX_DATA" - }, - { - "plain": "buffer", - "base64": "YnVmZmVy", - "define": "DUK_STRIDX_LC_BUFFER" - }, - { - "plain": "length", - "base64": "bGVuZ3Ro", - "define": "DUK_STRIDX_LENGTH" - }, - { - "plain": "set", - "base64": "c2V0", - "define": "DUK_STRIDX_SET" - }, - { - "plain": "stack", - "base64": "c3RhY2s=", - "define": "DUK_STRIDX_STACK" - }, - { - "plain": "pc", - "base64": "cGM=", - "define": "DUK_STRIDX_PC" - }, - { - "plain": "lineNumber", - "base64": "bGluZU51bWJlcg==", - "define": "DUK_STRIDX_LINE_NUMBER" - }, - { - "plain": "\u0082Tracedata", - "base64": "glRyYWNlZGF0YQ==", - "define": "DUK_STRIDX_INT_TRACEDATA" - }, - { - "plain": "name", - "base64": "bmFtZQ==", - "define": "DUK_STRIDX_NAME" - }, - { - "plain": "fileName", - "base64": "ZmlsZU5hbWU=", - "define": "DUK_STRIDX_FILE_NAME" - }, - { - "plain": "pointer", - "base64": "cG9pbnRlcg==", - "define": "DUK_STRIDX_LC_POINTER" - }, - { - "plain": "\u0082Target", - "base64": "glRhcmdldA==", - "define": "DUK_STRIDX_INT_TARGET" - }, - { - "plain": "\u0082Next", - "base64": "gk5leHQ=", - "define": "DUK_STRIDX_INT_NEXT" - }, - { - "plain": "\u0082Bytecode", - "base64": "gkJ5dGVjb2Rl", - "define": "DUK_STRIDX_INT_BYTECODE" - }, - { - "plain": "\u0082Formals", - "base64": "gkZvcm1hbHM=", - "define": "DUK_STRIDX_INT_FORMALS" - }, - { - "plain": "\u0082Varmap", - "base64": "glZhcm1hcA==", - "define": "DUK_STRIDX_INT_VARMAP" - }, - { - "plain": "\u0082Source", - "base64": "glNvdXJjZQ==", - "define": "DUK_STRIDX_INT_SOURCE" - }, - { - "plain": "\u0082Pc2line", - "base64": "glBjMmxpbmU=", - "define": "DUK_STRIDX_INT_PC2LINE" - }, - { - "plain": "\u0082Map", - "base64": "gk1hcA==", - "define": "DUK_STRIDX_INT_MAP" - }, - { - "plain": "\u0082Varenv", - "base64": "glZhcmVudg==", - "define": "DUK_STRIDX_INT_VARENV" - }, - { - "plain": "\u0082Finalizer", - "base64": "gkZpbmFsaXplcg==", - "define": "DUK_STRIDX_INT_FINALIZER" - }, - { - "plain": "\u0082Value", - "base64": "glZhbHVl", - "define": "DUK_STRIDX_INT_VALUE" - }, - { - "plain": "compile", - "base64": "Y29tcGlsZQ==", - "define": "DUK_STRIDX_COMPILE" - }, - { - "plain": "input", - "base64": "aW5wdXQ=", - "define": "DUK_STRIDX_INPUT" - }, - { - "plain": "errCreate", - "base64": "ZXJyQ3JlYXRl", - "define": "DUK_STRIDX_ERR_CREATE" - }, - { - "plain": "errThrow", - "base64": "ZXJyVGhyb3c=", - "define": "DUK_STRIDX_ERR_THROW" - }, - { - "plain": "env", - "base64": "ZW52", - "define": "DUK_STRIDX_ENV" - }, - { - "plain": "hex", - "base64": "aGV4", - "define": "DUK_STRIDX_HEX" - }, - { - "plain": "base64", - "base64": "YmFzZTY0", - "define": "DUK_STRIDX_BASE64" - }, - { - "plain": "jx", - "base64": "ang=", - "define": "DUK_STRIDX_JX" - }, - { - "plain": "jc", - "base64": "amM=", - "define": "DUK_STRIDX_JC" - }, - { - "plain": "{\"_undef\":true}", - "base64": "eyJfdW5kZWYiOnRydWV9", - "define": "DUK_STRIDX_JSON_EXT_UNDEFINED" - }, - { - "plain": "{\"_nan\":true}", - "base64": "eyJfbmFuIjp0cnVlfQ==", - "define": "DUK_STRIDX_JSON_EXT_NAN" - }, - { - "plain": "{\"_inf\":true}", - "base64": "eyJfaW5mIjp0cnVlfQ==", - "define": "DUK_STRIDX_JSON_EXT_POSINF" - }, - { - "plain": "{\"_ninf\":true}", - "base64": "eyJfbmluZiI6dHJ1ZX0=", - "define": "DUK_STRIDX_JSON_EXT_NEGINF" - }, - { - "plain": "{\"_func\":true}", - "base64": "eyJfZnVuYyI6dHJ1ZX0=", - "define": "DUK_STRIDX_JSON_EXT_FUNCTION1" - }, - { - "plain": "{_func:true}", - "base64": "e19mdW5jOnRydWV9", - "define": "DUK_STRIDX_JSON_EXT_FUNCTION2" - }, - { - "plain": "break", - "base64": "YnJlYWs=", - "define": "DUK_STRIDX_BREAK" - }, - { - "plain": "case", - "base64": "Y2FzZQ==", - "define": "DUK_STRIDX_CASE" - }, - { - "plain": "catch", - "base64": "Y2F0Y2g=", - "define": "DUK_STRIDX_CATCH" - }, - { - "plain": "continue", - "base64": "Y29udGludWU=", - "define": "DUK_STRIDX_CONTINUE" - }, - { - "plain": "debugger", - "base64": "ZGVidWdnZXI=", - "define": "DUK_STRIDX_DEBUGGER" - }, - { - "plain": "default", - "base64": "ZGVmYXVsdA==", - "define": "DUK_STRIDX_DEFAULT" - }, - { - "plain": "delete", - "base64": "ZGVsZXRl", - "define": "DUK_STRIDX_DELETE" - }, - { - "plain": "do", - "base64": "ZG8=", - "define": "DUK_STRIDX_DO" - }, - { - "plain": "else", - "base64": "ZWxzZQ==", - "define": "DUK_STRIDX_ELSE" - }, - { - "plain": "finally", - "base64": "ZmluYWxseQ==", - "define": "DUK_STRIDX_FINALLY" - }, - { - "plain": "for", - "base64": "Zm9y", - "define": "DUK_STRIDX_FOR" - }, - { - "plain": "function", - "base64": "ZnVuY3Rpb24=", - "define": "DUK_STRIDX_LC_FUNCTION" - }, - { - "plain": "if", - "base64": "aWY=", - "define": "DUK_STRIDX_IF" - }, - { - "plain": "in", - "base64": "aW4=", - "define": "DUK_STRIDX_IN" - }, - { - "plain": "instanceof", - "base64": "aW5zdGFuY2VvZg==", - "define": "DUK_STRIDX_INSTANCEOF" - }, - { - "plain": "new", - "base64": "bmV3", - "define": "DUK_STRIDX_NEW" - }, - { - "plain": "return", - "base64": "cmV0dXJu", - "define": "DUK_STRIDX_RETURN" - }, - { - "plain": "switch", - "base64": "c3dpdGNo", - "define": "DUK_STRIDX_SWITCH" - }, - { - "plain": "this", - "base64": "dGhpcw==", - "define": "DUK_STRIDX_THIS" - }, - { - "plain": "throw", - "base64": "dGhyb3c=", - "define": "DUK_STRIDX_THROW" - }, - { - "plain": "try", - "base64": "dHJ5", - "define": "DUK_STRIDX_TRY" - }, - { - "plain": "typeof", - "base64": "dHlwZW9m", - "define": "DUK_STRIDX_TYPEOF" - }, - { - "plain": "var", - "base64": "dmFy", - "define": "DUK_STRIDX_VAR" - }, - { - "plain": "const", - "base64": "Y29uc3Q=", - "define": "DUK_STRIDX_CONST" - }, - { - "plain": "void", - "base64": "dm9pZA==", - "define": "DUK_STRIDX_VOID" - }, - { - "plain": "while", - "base64": "d2hpbGU=", - "define": "DUK_STRIDX_WHILE" - }, - { - "plain": "with", - "base64": "d2l0aA==", - "define": "DUK_STRIDX_WITH" - }, - { - "plain": "class", - "base64": "Y2xhc3M=", - "define": "DUK_STRIDX_CLASS" - }, - { - "plain": "enum", - "base64": "ZW51bQ==", - "define": "DUK_STRIDX_ENUM" - }, - { - "plain": "export", - "base64": "ZXhwb3J0", - "define": "DUK_STRIDX_EXPORT" - }, - { - "plain": "extends", - "base64": "ZXh0ZW5kcw==", - "define": "DUK_STRIDX_EXTENDS" - }, - { - "plain": "import", - "base64": "aW1wb3J0", - "define": "DUK_STRIDX_IMPORT" - }, - { - "plain": "super", - "base64": "c3VwZXI=", - "define": "DUK_STRIDX_SUPER" - }, - { - "plain": "null", - "base64": "bnVsbA==", - "define": "DUK_STRIDX_LC_NULL" - }, - { - "plain": "true", - "base64": "dHJ1ZQ==", - "define": "DUK_STRIDX_TRUE" - }, - { - "plain": "false", - "base64": "ZmFsc2U=", - "define": "DUK_STRIDX_FALSE" - }, - { - "plain": "implements", - "base64": "aW1wbGVtZW50cw==", - "define": "DUK_STRIDX_IMPLEMENTS" - }, - { - "plain": "interface", - "base64": "aW50ZXJmYWNl", - "define": "DUK_STRIDX_INTERFACE" - }, - { - "plain": "let", - "base64": "bGV0", - "define": "DUK_STRIDX_LET" - }, - { - "plain": "package", - "base64": "cGFja2FnZQ==", - "define": "DUK_STRIDX_PACKAGE" - }, - { - "plain": "private", - "base64": "cHJpdmF0ZQ==", - "define": "DUK_STRIDX_PRIVATE" - }, - { - "plain": "protected", - "base64": "cHJvdGVjdGVk", - "define": "DUK_STRIDX_PROTECTED" - }, - { - "plain": "public", - "base64": "cHVibGlj", - "define": "DUK_STRIDX_PUBLIC" - }, - { - "plain": "static", - "base64": "c3RhdGlj", - "define": "DUK_STRIDX_STATIC" - }, - { - "plain": "yield", - "base64": "eWllbGQ=", - "define": "DUK_STRIDX_YIELD" - } - ], - "builtin_strings_base64": [ - "VW5kZWZpbmVk", - "TnVsbA==", - "U3ltYm9s", - "QXJndW1lbnRz", - "T2JqZWN0", - "RnVuY3Rpb24=", - "QXJyYXk=", - "U3RyaW5n", - "Qm9vbGVhbg==", - "TnVtYmVy", - "RGF0ZQ==", - "UmVnRXhw", - "RXJyb3I=", - "TWF0aA==", - "SlNPTg==", - "", - "QXJyYXlCdWZmZXI=", - "RGF0YVZpZXc=", - "SW50OEFycmF5", - "VWludDhBcnJheQ==", - "VWludDhDbGFtcGVkQXJyYXk=", - "SW50MTZBcnJheQ==", - "VWludDE2QXJyYXk=", - "SW50MzJBcnJheQ==", - "VWludDMyQXJyYXk=", - "RmxvYXQzMkFycmF5", - "RmxvYXQ2NEFycmF5", - "Z2xvYmFs", - "T2JqRW52", - "RGVjRW52", - "QnVmZmVy", - "UG9pbnRlcg==", - "VGhyZWFk", - "ZXZhbA==", - "dmFsdWU=", - "d3JpdGFibGU=", - "Y29uZmlndXJhYmxl", - "ZW51bWVyYWJsZQ==", - "am9pbg==", - "dG9Mb2NhbGVTdHJpbmc=", - "dmFsdWVPZg==", - "dG9VVENTdHJpbmc=", - "dG9JU09TdHJpbmc=", - "dG9HTVRTdHJpbmc=", - "c291cmNl", - "aWdub3JlQ2FzZQ==", - "bXVsdGlsaW5l", - "bGFzdEluZGV4", - "ZmxhZ3M=", - "aW5kZXg=", - "cHJvdG90eXBl", - "Y29uc3RydWN0b3I=", - "bWVzc2FnZQ==", - "Ym9vbGVhbg==", - "bnVtYmVy", - "c3RyaW5n", - "c3ltYm9s", - "b2JqZWN0", - "dW5kZWZpbmVk", - "TmFO", - "SW5maW5pdHk=", - "LUluZmluaXR5", - "LTA=", - "LA==", - "CiAgICA=", - "Wy4uLl0=", - "SW52YWxpZCBEYXRl", - "YXJndW1lbnRz", - "Y2FsbGVl", - "Y2FsbGVy", - "YXBwbHk=", - "Y29uc3RydWN0", - "ZGVsZXRlUHJvcGVydHk=", - "Z2V0", - "aGFz", - "b3duS2V5cw==", - "gVN5bWJvbC50b1ByaW1pdGl2Zf8=", - "gVN5bWJvbC5oYXNJbnN0YW5jZf8=", - "gVN5bWJvbC50b1N0cmluZ1RhZ/8=", - "gVN5bWJvbC5pc0NvbmNhdFNwcmVhZGFibGX/", - "c2V0UHJvdG90eXBlT2Y=", - "X19wcm90b19f", - "dG9TdHJpbmc=", - "dG9KU09O", - "dHlwZQ==", - "ZGF0YQ==", - "YnVmZmVy", - "bGVuZ3Ro", - "c2V0", - "c3RhY2s=", - "cGM=", - "bGluZU51bWJlcg==", - "glRyYWNlZGF0YQ==", - "bmFtZQ==", - "ZmlsZU5hbWU=", - "cG9pbnRlcg==", - "glRhcmdldA==", - "gk5leHQ=", - "gkJ5dGVjb2Rl", - "gkZvcm1hbHM=", - "glZhcm1hcA==", - "glNvdXJjZQ==", - "glBjMmxpbmU=", - "gk1hcA==", - "glZhcmVudg==", - "gkZpbmFsaXplcg==", - "glZhbHVl", - "Y29tcGlsZQ==", - "aW5wdXQ=", - "ZXJyQ3JlYXRl", - "ZXJyVGhyb3c=", - "ZW52", - "aGV4", - "YmFzZTY0", - "ang=", - "amM=", - "eyJfdW5kZWYiOnRydWV9", - "eyJfbmFuIjp0cnVlfQ==", - "eyJfaW5mIjp0cnVlfQ==", - "eyJfbmluZiI6dHJ1ZX0=", - "eyJfZnVuYyI6dHJ1ZX0=", - "e19mdW5jOnRydWV9", - "YnJlYWs=", - "Y2FzZQ==", - "Y2F0Y2g=", - "Y29udGludWU=", - "ZGVidWdnZXI=", - "ZGVmYXVsdA==", - "ZGVsZXRl", - "ZG8=", - "ZWxzZQ==", - "ZmluYWxseQ==", - "Zm9y", - "ZnVuY3Rpb24=", - "aWY=", - "aW4=", - "aW5zdGFuY2VvZg==", - "bmV3", - "cmV0dXJu", - "c3dpdGNo", - "dGhpcw==", - "dGhyb3c=", - "dHJ5", - "dHlwZW9m", - "dmFy", - "Y29uc3Q=", - "dm9pZA==", - "d2hpbGU=", - "d2l0aA==", - "Y2xhc3M=", - "ZW51bQ==", - "ZXhwb3J0", - "ZXh0ZW5kcw==", - "aW1wb3J0", - "c3VwZXI=", - "bnVsbA==", - "dHJ1ZQ==", - "ZmFsc2U=", - "aW1wbGVtZW50cw==", - "aW50ZXJmYWNl", - "bGV0", - "cGFja2FnZQ==", - "cHJpdmF0ZQ==", - "cHJvdGVjdGVk", - "cHVibGlj", - "c3RhdGlj", - "eWllbGQ=" - ], - "git_describe": "external", - "builtin_strings": [ - "Undefined", - "Null", - "Symbol", - "Arguments", - "Object", - "Function", - "Array", - "String", - "Boolean", - "Number", - "Date", - "RegExp", - "Error", - "Math", - "JSON", - "", - "ArrayBuffer", - "DataView", - "Int8Array", - "Uint8Array", - "Uint8ClampedArray", - "Int16Array", - "Uint16Array", - "Int32Array", - "Uint32Array", - "Float32Array", - "Float64Array", - "global", - "ObjEnv", - "DecEnv", - "Buffer", - "Pointer", - "Thread", - "eval", - "value", - "writable", - "configurable", - "enumerable", - "join", - "toLocaleString", - "valueOf", - "toUTCString", - "toISOString", - "toGMTString", - "source", - "ignoreCase", - "multiline", - "lastIndex", - "flags", - "index", - "prototype", - "constructor", - "message", - "boolean", - "number", - "string", - "symbol", - "object", - "undefined", - "NaN", - "Infinity", - "-Infinity", - "-0", - ",", - "\n ", - "[...]", - "Invalid Date", - "arguments", - "callee", - "caller", - "apply", - "construct", - "deleteProperty", - "get", - "has", - "ownKeys", - "\u0081Symbol.toPrimitive\u00ff", - "\u0081Symbol.hasInstance\u00ff", - "\u0081Symbol.toStringTag\u00ff", - "\u0081Symbol.isConcatSpreadable\u00ff", - "setPrototypeOf", - "__proto__", - "toString", - "toJSON", - "type", - "data", - "buffer", - "length", - "set", - "stack", - "pc", - "lineNumber", - "\u0082Tracedata", - "name", - "fileName", - "pointer", - "\u0082Target", - "\u0082Next", - "\u0082Bytecode", - "\u0082Formals", - "\u0082Varmap", - "\u0082Source", - "\u0082Pc2line", - "\u0082Map", - "\u0082Varenv", - "\u0082Finalizer", - "\u0082Value", - "compile", - "input", - "errCreate", - "errThrow", - "env", - "hex", - "base64", - "jx", - "jc", - "{\"_undef\":true}", - "{\"_nan\":true}", - "{\"_inf\":true}", - "{\"_ninf\":true}", - "{\"_func\":true}", - "{_func:true}", - "break", - "case", - "catch", - "continue", - "debugger", - "default", - "delete", - "do", - "else", - "finally", - "for", - "function", - "if", - "in", - "instanceof", - "new", - "return", - "switch", - "this", - "throw", - "try", - "typeof", - "var", - "const", - "void", - "while", - "with", - "class", - "enum", - "export", - "extends", - "import", - "super", - "null", - "true", - "false", - "implements", - "interface", - "let", - "package", - "private", - "protected", - "public", - "static", - "yield" - ] -} \ No newline at end of file diff --git a/src/thirdparty/duktape/duktape.cpp b/src/thirdparty/duktape/duktape.cpp deleted file mode 100644 index 1a27b650a8..0000000000 --- a/src/thirdparty/duktape/duktape.cpp +++ /dev/null @@ -1,101206 +0,0 @@ -/* - * Single source autogenerated distributable for Duktape 2.7.0. - * - * Git commit external (external). - * Git branch external. - * - * See Duktape AUTHORS.rst and LICENSE.txt for copyright and - * licensing information. - */ - -/* LICENSE.txt */ -/* -* =============== -* Duktape license -* =============== -* -* (http://opensource.org/licenses/MIT) -* -* Copyright (c) 2013-present by Duktape authors (see AUTHORS.rst) -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. -*/ - -/* AUTHORS.rst */ -/* -* =============== -* Duktape authors -* =============== -* -* Copyright -* ========= -* -* Duktape copyrights are held by its authors. Each author has a copyright -* to their contribution, and agrees to irrevocably license the contribution -* under the Duktape ``LICENSE.txt``. -* -* Authors -* ======= -* -* Please include an e-mail address, a link to your GitHub profile, or something -* similar to allow your contribution to be identified accurately. -* -* The following people have contributed code, website contents, or Wiki contents, -* and agreed to irrevocably license their contributions under the Duktape -* ``LICENSE.txt`` (in order of appearance): -* -* * Sami Vaarala -* * Niki Dobrev -* * Andreas \u00d6man -* * L\u00e1szl\u00f3 Lang\u00f3 -* * Legimet -* * Karl Skomski -* * Bruce Pascoe -* * Ren\u00e9 Hollander -* * Julien Hamaide (https://github.com/crazyjul) -* * Sebastian G\u00f6tte (https://github.com/jaseg) -* * Tomasz Magulski (https://github.com/magul) -* * \D. Bohdan (https://github.com/dbohdan) -* * Ond\u0159ej Jirman (https://github.com/megous) -* * Sa\u00fal Ibarra Corretg\u00e9 -* * Jeremy HU -* * Ole Andr\u00e9 Vadla Ravn\u00e5s (https://github.com/oleavr) -* * Harold Brenes (https://github.com/harold-b) -* * Oliver Crow (https://github.com/ocrow) -* * Jakub Ch\u0142api\u0144ski (https://github.com/jchlapinski) -* * Brett Vickers (https://github.com/beevik) -* * Dominik Okwieka (https://github.com/okitec) -* * Remko Tron\u00e7on (https://el-tramo.be) -* * Romero Malaquias (rbsm@ic.ufal.br) -* * Michael Drake -* * Steven Don (https://github.com/shdon) -* * Simon Stone (https://github.com/sstone1) -* * \J. McC. (https://github.com/jmhmccr) -* * Jakub Nowakowski (https://github.com/jimvonmoon) -* * Tommy Nguyen (https://github.com/tn0502) -* * Fabrice Fontaine (https://github.com/ffontaine) -* * Christopher Hiller (https://github.com/boneskull) -* * Gonzalo Diethelm (https://github.com/gonzus) -* * Michal Kasperek (https://github.com/michalkas) -* * Andrew Janke (https://github.com/apjanke) -* * Steve Fan (https://github.com/stevefan1999) -* * Edward Betts (https://github.com/edwardbetts) -* * Ozhan Duz (https://github.com/webfolderio) -* * Akos Kiss (https://github.com/akosthekiss) -* * TheBrokenRail (https://github.com/TheBrokenRail) -* * Jesse Doyle (https://github.com/jessedoyle) -* * Gero Kuehn (https://github.com/dc6jgk) -* * James Swift (https://github.com/phraemer) -* * Luis de Bethencourt (https://github.com/luisbg) -* * Ian Whyman (https://github.com/v00d00) -* * Rick Sayre (https://github.com/whorfin) -* * Craig Leres (https://github.com/leres) -* * Maurici Abad (https://github.com/mauriciabad) -* * Nancy Li (https://github.com/NancyLi1013) -* * William Parks (https://github.com/WilliamParks) -* * Sam Hellawell (https://github.com/samhellawell) -* * Vladislavs Sokurenko (https://github.com/sokurenko) -* -* Other contributions -* =================== -* -* The following people have contributed something other than code (e.g. reported -* bugs, provided ideas, etc; roughly in order of appearance): -* -* * Greg Burns -* * Anthony Rabine -* * Carlos Costa -* * Aur\u00e9lien Bouilland -* * Preet Desai (Pris Matic) -* * judofyr (http://www.reddit.com/user/judofyr) -* * Jason Woofenden -* * Micha\u0142 Przyby\u015b -* * Anthony Howe -* * Conrad Pankoff -* * Jim Schimpf -* * Rajaran Gaunker (https://github.com/zimbabao) -* * Andreas \u00d6man -* * Doug Sanden -* * Josh Engebretson (https://github.com/JoshEngebretson) -* * Remo Eichenberger (https://github.com/remoe) -* * Mamod Mehyar (https://github.com/mamod) -* * David Demelier (https://github.com/markand) -* * Tim Caswell (https://github.com/creationix) -* * Mitchell Blank Jr (https://github.com/mitchblank) -* * https://github.com/yushli -* * Seo Sanghyeon (https://github.com/sanxiyn) -* * Han ChoongWoo (https://github.com/tunz) -* * Joshua Peek (https://github.com/josh) -* * Bruce E. Pascoe (https://github.com/fatcerberus) -* * https://github.com/Kelledin -* * https://github.com/sstruchtrup -* * Michael Drake (https://github.com/tlsa) -* * https://github.com/chris-y -* * Laurent Zubiaur (https://github.com/lzubiaur) -* * Neil Kolban (https://github.com/nkolban) -* * Wilhelm Wanecek (https://github.com/wanecek) -* * Andrew Janke (https://github.com/apjanke) -* * Unamer (https://github.com/unamer) -* * Karl Dahlke (eklhad@gmail.com) -* -* If you are accidentally missing from this list, send me an e-mail -* (``sami.vaarala@iki.fi``) and I'll fix the omission. -*/ - -/* - * Replacements for missing platform functions. - * - * Unlike the originals, fpclassify() and signbit() replacements don't - * work on any floating point types, only doubles. The C typing here - * mimics the standard prototypes. - */ - -/* #include duk_internal.h */ -/* - * Top-level include file to be used for all (internal) source files. - * - * Source files should not include individual header files, as they - * have not been designed to be individually included. - */ - -#if !defined(DUK_INTERNAL_H_INCLUDED) -#define DUK_INTERNAL_H_INCLUDED - -/* - * The 'duktape.h' header provides the public API, but also handles all - * compiler and platform specific feature detection, Duktape feature - * resolution, inclusion of system headers, etc. These have been merged - * because the public API is also dependent on e.g. detecting appropriate - * C types which is quite platform/compiler specific especially for a non-C99 - * build. The public API is also dependent on the resolved feature set. - * - * Some actions taken by the merged header (such as including system headers) - * are not appropriate for building a user application. The define - * DUK_COMPILING_DUKTAPE allows the merged header to skip/include some - * sections depending on what is being built. - */ - -#define DUK_COMPILING_DUKTAPE -#include "duktape.h" - -/* - * Duktape includes (other than duk_features.h) - * - * The header files expect to be included in an order which satisfies header - * dependencies correctly (the headers themselves don't include any other - * includes). Forward declarations are used to break circular struct/typedef - * dependencies. - */ - -/* #include duk_dblunion.h */ -/* - * Union to access IEEE double memory representation, indexes for double - * memory representation, and some macros for double manipulation. - * - * Also used by packed duk_tval. Use a union for bit manipulation to - * minimize aliasing issues in practice. The C99 standard does not - * guarantee that this should work, but it's a very widely supported - * practice for low level manipulation. - * - * IEEE double format summary: - * - * seeeeeee eeeeffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff - * A B C D E F G H - * - * s sign bit - * eee... exponent field - * fff... fraction - * - * See http://en.wikipedia.org/wiki/Double_precision_floating-point_format. - * - * NaNs are represented as exponent 0x7ff and mantissa != 0. The NaN is a - * signaling NaN when the highest bit of the mantissa is zero, and a quiet - * NaN when the highest bit is set. - * - * At least three memory layouts are relevant here: - * - * A B C D E F G H Big endian (e.g. 68k) DUK_USE_DOUBLE_BE - * H G F E D C B A Little endian (e.g. x86) DUK_USE_DOUBLE_LE - * D C B A H G F E Mixed endian (e.g. ARM FPA) DUK_USE_DOUBLE_ME - * - * Legacy ARM (FPA) is a special case: ARM double values are in mixed - * endian format while ARM duk_uint64_t values are in standard little endian - * format (H G F E D C B A). When a double is read as a duk_uint64_t - * from memory, the register will contain the (logical) value - * E F G H A B C D. This requires some special handling below. - * See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0056d/Bcfhgcgd.html. - * - * Indexes of various types (8-bit, 16-bit, 32-bit) in memory relative to - * the logical (big endian) order: - * - * byte order duk_uint8_t duk_uint16_t duk_uint32_t - * BE 01234567 0123 01 - * LE 76543210 3210 10 - * ME (ARM) 32107654 1032 01 - * - * Some processors may alter NaN values in a floating point load+store. - * For instance, on X86 a FLD + FSTP may convert a signaling NaN to a - * quiet one. This is catastrophic when NaN space is used in packed - * duk_tval values. See: misc/clang_aliasing.c. - */ - -#if !defined(DUK_DBLUNION_H_INCLUDED) -#define DUK_DBLUNION_H_INCLUDED - -/* - * Union for accessing double parts, also serves as packed duk_tval - */ - -union duk_double_union { - double d; - float f[2]; -#if defined(DUK_USE_64BIT_OPS) - duk_uint64_t ull[1]; -#endif - duk_uint32_t ui[2]; - duk_uint16_t us[4]; - duk_uint8_t uc[8]; -#if defined(DUK_USE_PACKED_TVAL) - void *vp[2]; /* used by packed duk_tval, assumes sizeof(void *) == 4 */ -#endif -}; - -typedef union duk_double_union duk_double_union; - -/* - * Indexes of various types with respect to big endian (logical) layout - */ - -#if defined(DUK_USE_DOUBLE_LE) -#if defined(DUK_USE_64BIT_OPS) -#define DUK_DBL_IDX_ULL0 0 -#endif -#define DUK_DBL_IDX_UI0 1 -#define DUK_DBL_IDX_UI1 0 -#define DUK_DBL_IDX_US0 3 -#define DUK_DBL_IDX_US1 2 -#define DUK_DBL_IDX_US2 1 -#define DUK_DBL_IDX_US3 0 -#define DUK_DBL_IDX_UC0 7 -#define DUK_DBL_IDX_UC1 6 -#define DUK_DBL_IDX_UC2 5 -#define DUK_DBL_IDX_UC3 4 -#define DUK_DBL_IDX_UC4 3 -#define DUK_DBL_IDX_UC5 2 -#define DUK_DBL_IDX_UC6 1 -#define DUK_DBL_IDX_UC7 0 -#define DUK_DBL_IDX_VP0 DUK_DBL_IDX_UI0 /* packed tval */ -#define DUK_DBL_IDX_VP1 DUK_DBL_IDX_UI1 /* packed tval */ -#elif defined(DUK_USE_DOUBLE_BE) -#if defined(DUK_USE_64BIT_OPS) -#define DUK_DBL_IDX_ULL0 0 -#endif -#define DUK_DBL_IDX_UI0 0 -#define DUK_DBL_IDX_UI1 1 -#define DUK_DBL_IDX_US0 0 -#define DUK_DBL_IDX_US1 1 -#define DUK_DBL_IDX_US2 2 -#define DUK_DBL_IDX_US3 3 -#define DUK_DBL_IDX_UC0 0 -#define DUK_DBL_IDX_UC1 1 -#define DUK_DBL_IDX_UC2 2 -#define DUK_DBL_IDX_UC3 3 -#define DUK_DBL_IDX_UC4 4 -#define DUK_DBL_IDX_UC5 5 -#define DUK_DBL_IDX_UC6 6 -#define DUK_DBL_IDX_UC7 7 -#define DUK_DBL_IDX_VP0 DUK_DBL_IDX_UI0 /* packed tval */ -#define DUK_DBL_IDX_VP1 DUK_DBL_IDX_UI1 /* packed tval */ -#elif defined(DUK_USE_DOUBLE_ME) -#if defined(DUK_USE_64BIT_OPS) -#define DUK_DBL_IDX_ULL0 0 /* not directly applicable, byte order differs from a double */ -#endif -#define DUK_DBL_IDX_UI0 0 -#define DUK_DBL_IDX_UI1 1 -#define DUK_DBL_IDX_US0 1 -#define DUK_DBL_IDX_US1 0 -#define DUK_DBL_IDX_US2 3 -#define DUK_DBL_IDX_US3 2 -#define DUK_DBL_IDX_UC0 3 -#define DUK_DBL_IDX_UC1 2 -#define DUK_DBL_IDX_UC2 1 -#define DUK_DBL_IDX_UC3 0 -#define DUK_DBL_IDX_UC4 7 -#define DUK_DBL_IDX_UC5 6 -#define DUK_DBL_IDX_UC6 5 -#define DUK_DBL_IDX_UC7 4 -#define DUK_DBL_IDX_VP0 DUK_DBL_IDX_UI0 /* packed tval */ -#define DUK_DBL_IDX_VP1 DUK_DBL_IDX_UI1 /* packed tval */ -#else -#error internal error -#endif - -/* - * Helper macros for reading/writing memory representation parts, used - * by duk_numconv.c and duk_tval.h. - */ - -#define DUK_DBLUNION_SET_DOUBLE(u, v) \ - do { \ - (u)->d = (v); \ - } while (0) - -#define DUK_DBLUNION_SET_HIGH32(u, v) \ - do { \ - (u)->ui[DUK_DBL_IDX_UI0] = (duk_uint32_t) (v); \ - } while (0) - -#if defined(DUK_USE_64BIT_OPS) -#if defined(DUK_USE_DOUBLE_ME) -#define DUK_DBLUNION_SET_HIGH32_ZERO_LOW32(u, v) \ - do { \ - (u)->ull[DUK_DBL_IDX_ULL0] = (duk_uint64_t) (v); \ - } while (0) -#else -#define DUK_DBLUNION_SET_HIGH32_ZERO_LOW32(u, v) \ - do { \ - (u)->ull[DUK_DBL_IDX_ULL0] = ((duk_uint64_t) (v)) << 32; \ - } while (0) -#endif -#else /* DUK_USE_64BIT_OPS */ -#define DUK_DBLUNION_SET_HIGH32_ZERO_LOW32(u, v) \ - do { \ - (u)->ui[DUK_DBL_IDX_UI0] = (duk_uint32_t) (v); \ - (u)->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) 0; \ - } while (0) -#endif /* DUK_USE_64BIT_OPS */ - -#define DUK_DBLUNION_SET_LOW32(u, v) \ - do { \ - (u)->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) (v); \ - } while (0) - -#define DUK_DBLUNION_GET_DOUBLE(u) ((u)->d) -#define DUK_DBLUNION_GET_HIGH32(u) ((u)->ui[DUK_DBL_IDX_UI0]) -#define DUK_DBLUNION_GET_LOW32(u) ((u)->ui[DUK_DBL_IDX_UI1]) - -#if defined(DUK_USE_64BIT_OPS) -#if defined(DUK_USE_DOUBLE_ME) -#define DUK_DBLUNION_SET_UINT64(u, v) \ - do { \ - (u)->ui[DUK_DBL_IDX_UI0] = (duk_uint32_t) ((v) >> 32); \ - (u)->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) (v); \ - } while (0) -#define DUK_DBLUNION_GET_UINT64(u) ((((duk_uint64_t) (u)->ui[DUK_DBL_IDX_UI0]) << 32) | ((duk_uint64_t) (u)->ui[DUK_DBL_IDX_UI1])) -#else -#define DUK_DBLUNION_SET_UINT64(u, v) \ - do { \ - (u)->ull[DUK_DBL_IDX_ULL0] = (duk_uint64_t) (v); \ - } while (0) -#define DUK_DBLUNION_GET_UINT64(u) ((u)->ull[DUK_DBL_IDX_ULL0]) -#endif -#define DUK_DBLUNION_SET_INT64(u, v) DUK_DBLUNION_SET_UINT64((u), (duk_uint64_t) (v)) -#define DUK_DBLUNION_GET_INT64(u) ((duk_int64_t) DUK_DBLUNION_GET_UINT64((u))) -#endif /* DUK_USE_64BIT_OPS */ - -/* - * Double NaN manipulation macros related to NaN normalization needed when - * using the packed duk_tval representation. NaN normalization is necessary - * to keep double values compatible with the duk_tval format. - * - * When packed duk_tval is used, the NaN space is used to store pointers - * and other tagged values in addition to NaNs. Actual NaNs are normalized - * to a specific quiet NaN. The macros below are used by the implementation - * to check and normalize NaN values when they might be created. The macros - * are essentially NOPs when the non-packed duk_tval representation is used. - * - * A FULL check is exact and checks all bits. A NOTFULL check is used by - * the packed duk_tval and works correctly for all NaNs except those that - * begin with 0x7ff0. Since the 'normalized NaN' values used with packed - * duk_tval begin with 0x7ff8, the partial check is reliable when packed - * duk_tval is used. The 0x7ff8 prefix means the normalized NaN will be a - * quiet NaN regardless of its remaining lower bits. - * - * The ME variant below is specifically for ARM byte order, which has the - * feature that while doubles have a mixed byte order (32107654), unsigned - * long long values has a little endian byte order (76543210). When writing - * a logical double value through a ULL pointer, the 32-bit words need to be - * swapped; hence the #if defined()s below for ULL writes with DUK_USE_DOUBLE_ME. - * This is not full ARM support but suffices for some environments. - */ - -#if defined(DUK_USE_64BIT_OPS) -#if defined(DUK_USE_DOUBLE_ME) -/* Macros for 64-bit ops + mixed endian doubles. */ -#define DUK__DBLUNION_SET_NAN_FULL(u) \ - do { \ - (u)->ull[DUK_DBL_IDX_ULL0] = DUK_U64_CONSTANT(0x000000007ff80000); \ - } while (0) -#define DUK__DBLUNION_IS_NAN_FULL(u) \ - ((((u)->ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x000000007ff00000)) == DUK_U64_CONSTANT(0x000000007ff00000)) && \ - ((((u)->ull[DUK_DBL_IDX_ULL0]) & DUK_U64_CONSTANT(0xffffffff000fffff)) != 0)) -#define DUK__DBLUNION_IS_NORMALIZED_NAN_FULL(u) ((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x000000007ff80000)) -#define DUK__DBLUNION_IS_ANYINF(u) \ - (((u)->ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0xffffffff7fffffff)) == DUK_U64_CONSTANT(0x000000007ff00000)) -#define DUK__DBLUNION_IS_POSINF(u) ((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x000000007ff00000)) -#define DUK__DBLUNION_IS_NEGINF(u) ((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x00000000fff00000)) -#define DUK__DBLUNION_IS_ANYZERO(u) \ - (((u)->ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0xffffffff7fffffff)) == DUK_U64_CONSTANT(0x0000000000000000)) -#define DUK__DBLUNION_IS_POSZERO(u) ((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x0000000000000000)) -#define DUK__DBLUNION_IS_NEGZERO(u) ((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x0000000080000000)) -#else -/* Macros for 64-bit ops + big/little endian doubles. */ -#define DUK__DBLUNION_SET_NAN_FULL(u) \ - do { \ - (u)->ull[DUK_DBL_IDX_ULL0] = DUK_U64_CONSTANT(0x7ff8000000000000); \ - } while (0) -#define DUK__DBLUNION_IS_NAN_FULL(u) \ - ((((u)->ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x7ff0000000000000)) == DUK_U64_CONSTANT(0x7ff0000000000000)) && \ - ((((u)->ull[DUK_DBL_IDX_ULL0]) & DUK_U64_CONSTANT(0x000fffffffffffff)) != 0)) -#define DUK__DBLUNION_IS_NORMALIZED_NAN_FULL(u) ((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x7ff8000000000000)) -#define DUK__DBLUNION_IS_ANYINF(u) \ - (((u)->ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x7fffffffffffffff)) == DUK_U64_CONSTANT(0x7ff0000000000000)) -#define DUK__DBLUNION_IS_POSINF(u) ((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x7ff0000000000000)) -#define DUK__DBLUNION_IS_NEGINF(u) ((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0xfff0000000000000)) -#define DUK__DBLUNION_IS_ANYZERO(u) \ - (((u)->ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x7fffffffffffffff)) == DUK_U64_CONSTANT(0x0000000000000000)) -#define DUK__DBLUNION_IS_POSZERO(u) ((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x0000000000000000)) -#define DUK__DBLUNION_IS_NEGZERO(u) ((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x8000000000000000)) -#endif -#else /* DUK_USE_64BIT_OPS */ -/* Macros for no 64-bit ops, any endianness. */ -#define DUK__DBLUNION_SET_NAN_FULL(u) \ - do { \ - (u)->ui[DUK_DBL_IDX_UI0] = (duk_uint32_t) 0x7ff80000UL; \ - (u)->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) 0x00000000UL; \ - } while (0) -#define DUK__DBLUNION_IS_NAN_FULL(u) \ - ((((u)->ui[DUK_DBL_IDX_UI0] & 0x7ff00000UL) == 0x7ff00000UL) && \ - (((u)->ui[DUK_DBL_IDX_UI0] & 0x000fffffUL) != 0 || (u)->ui[DUK_DBL_IDX_UI1] != 0)) -#define DUK__DBLUNION_IS_NORMALIZED_NAN_FULL(u) \ - (((u)->ui[DUK_DBL_IDX_UI0] == 0x7ff80000UL) && ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL)) -#define DUK__DBLUNION_IS_ANYINF(u) \ - ((((u)->ui[DUK_DBL_IDX_UI0] & 0x7fffffffUL) == 0x7ff00000UL) && ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL)) -#define DUK__DBLUNION_IS_POSINF(u) (((u)->ui[DUK_DBL_IDX_UI0] == 0x7ff00000UL) && ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL)) -#define DUK__DBLUNION_IS_NEGINF(u) (((u)->ui[DUK_DBL_IDX_UI0] == 0xfff00000UL) && ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL)) -#define DUK__DBLUNION_IS_ANYZERO(u) \ - ((((u)->ui[DUK_DBL_IDX_UI0] & 0x7fffffffUL) == 0x00000000UL) && ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL)) -#define DUK__DBLUNION_IS_POSZERO(u) (((u)->ui[DUK_DBL_IDX_UI0] == 0x00000000UL) && ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL)) -#define DUK__DBLUNION_IS_NEGZERO(u) (((u)->ui[DUK_DBL_IDX_UI0] == 0x80000000UL) && ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL)) -#endif /* DUK_USE_64BIT_OPS */ - -#define DUK__DBLUNION_SET_NAN_NOTFULL(u) \ - do { \ - (u)->us[DUK_DBL_IDX_US0] = 0x7ff8UL; \ - } while (0) - -#define DUK__DBLUNION_IS_NAN_NOTFULL(u) \ - /* E == 0x7ff, topmost four bits of F != 0 => assume NaN */ \ - ((((u)->us[DUK_DBL_IDX_US0] & 0x7ff0UL) == 0x7ff0UL) && (((u)->us[DUK_DBL_IDX_US0] & 0x000fUL) != 0x0000UL)) - -#define DUK__DBLUNION_IS_NORMALIZED_NAN_NOTFULL(u) \ - /* E == 0x7ff, F == 8 => normalized NaN */ \ - ((u)->us[DUK_DBL_IDX_US0] == 0x7ff8UL) - -#define DUK__DBLUNION_NORMALIZE_NAN_CHECK_FULL(u) \ - do { \ - if (DUK__DBLUNION_IS_NAN_FULL((u))) { \ - DUK__DBLUNION_SET_NAN_FULL((u)); \ - } \ - } while (0) - -#define DUK__DBLUNION_NORMALIZE_NAN_CHECK_NOTFULL(u) \ - do { \ - /* Check must be full. */ \ - if (DUK__DBLUNION_IS_NAN_FULL((u))) { \ - DUK__DBLUNION_SET_NAN_NOTFULL((u)); \ - } \ - } while (0) - -/* Concrete macros for NaN handling used by the implementation internals. - * Chosen so that they match the duk_tval representation: with a packed - * duk_tval, ensure NaNs are properly normalized; with a non-packed duk_tval - * these are essentially NOPs. - */ - -#if defined(DUK_USE_PACKED_TVAL) -#define DUK_DBLUNION_NORMALIZE_NAN_CHECK(u) DUK__DBLUNION_NORMALIZE_NAN_CHECK_FULL((u)) -#define DUK_DBLUNION_IS_NAN(u) DUK__DBLUNION_IS_NAN_FULL((u)) -#define DUK_DBLUNION_IS_NORMALIZED_NAN(u) DUK__DBLUNION_IS_NORMALIZED_NAN_FULL((u)) -#define DUK_DBLUNION_SET_NAN(d) DUK__DBLUNION_SET_NAN_FULL((d)) -#if 0 -#define DUK_DBLUNION_NORMALIZE_NAN_CHECK(u) DUK__DBLUNION_NORMALIZE_NAN_CHECK_NOTFULL((u)) -#define DUK_DBLUNION_IS_NAN(u) DUK__DBLUNION_IS_NAN_NOTFULL((u)) -#define DUK_DBLUNION_IS_NORMALIZED_NAN(u) DUK__DBLUNION_IS_NORMALIZED_NAN_NOTFULL((u)) -#define DUK_DBLUNION_SET_NAN(d) DUK__DBLUNION_SET_NAN_NOTFULL((d)) -#endif -#define DUK_DBLUNION_IS_NORMALIZED(u) \ - (!DUK_DBLUNION_IS_NAN((u)) || /* either not a NaN */ \ - DUK_DBLUNION_IS_NORMALIZED_NAN((u))) /* or is a normalized NaN */ -#else /* DUK_USE_PACKED_TVAL */ -#define DUK_DBLUNION_NORMALIZE_NAN_CHECK(u) /* nop: no need to normalize */ -#define DUK_DBLUNION_IS_NAN(u) DUK__DBLUNION_IS_NAN_FULL((u)) /* (DUK_ISNAN((u)->d)) */ -#define DUK_DBLUNION_IS_NORMALIZED_NAN(u) DUK__DBLUNION_IS_NAN_FULL((u)) /* (DUK_ISNAN((u)->d)) */ -#define DUK_DBLUNION_IS_NORMALIZED(u) 1 /* all doubles are considered normalized */ -#define DUK_DBLUNION_SET_NAN(u) \ - do { \ - /* in non-packed representation we don't care about which NaN is used */ \ - (u)->d = DUK_DOUBLE_NAN; \ - } while (0) -#endif /* DUK_USE_PACKED_TVAL */ - -#define DUK_DBLUNION_IS_ANYINF(u) DUK__DBLUNION_IS_ANYINF((u)) -#define DUK_DBLUNION_IS_POSINF(u) DUK__DBLUNION_IS_POSINF((u)) -#define DUK_DBLUNION_IS_NEGINF(u) DUK__DBLUNION_IS_NEGINF((u)) - -#define DUK_DBLUNION_IS_ANYZERO(u) DUK__DBLUNION_IS_ANYZERO((u)) -#define DUK_DBLUNION_IS_POSZERO(u) DUK__DBLUNION_IS_POSZERO((u)) -#define DUK_DBLUNION_IS_NEGZERO(u) DUK__DBLUNION_IS_NEGZERO((u)) - -/* XXX: native 64-bit byteswaps when available */ - -/* 64-bit byteswap, same operation independent of target endianness. */ -#define DUK_DBLUNION_BSWAP64(u) \ - do { \ - duk_uint32_t duk__bswaptmp1, duk__bswaptmp2; \ - duk__bswaptmp1 = (u)->ui[0]; \ - duk__bswaptmp2 = (u)->ui[1]; \ - duk__bswaptmp1 = DUK_BSWAP32(duk__bswaptmp1); \ - duk__bswaptmp2 = DUK_BSWAP32(duk__bswaptmp2); \ - (u)->ui[0] = duk__bswaptmp2; \ - (u)->ui[1] = duk__bswaptmp1; \ - } while (0) - -/* Byteswap an IEEE double in the duk_double_union from host to network - * order. For a big endian target this is a no-op. - */ -#if defined(DUK_USE_DOUBLE_LE) -#define DUK_DBLUNION_DOUBLE_HTON(u) \ - do { \ - duk_uint32_t duk__bswaptmp1, duk__bswaptmp2; \ - duk__bswaptmp1 = (u)->ui[0]; \ - duk__bswaptmp2 = (u)->ui[1]; \ - duk__bswaptmp1 = DUK_BSWAP32(duk__bswaptmp1); \ - duk__bswaptmp2 = DUK_BSWAP32(duk__bswaptmp2); \ - (u)->ui[0] = duk__bswaptmp2; \ - (u)->ui[1] = duk__bswaptmp1; \ - } while (0) -#elif defined(DUK_USE_DOUBLE_ME) -#define DUK_DBLUNION_DOUBLE_HTON(u) \ - do { \ - duk_uint32_t duk__bswaptmp1, duk__bswaptmp2; \ - duk__bswaptmp1 = (u)->ui[0]; \ - duk__bswaptmp2 = (u)->ui[1]; \ - duk__bswaptmp1 = DUK_BSWAP32(duk__bswaptmp1); \ - duk__bswaptmp2 = DUK_BSWAP32(duk__bswaptmp2); \ - (u)->ui[0] = duk__bswaptmp1; \ - (u)->ui[1] = duk__bswaptmp2; \ - } while (0) -#elif defined(DUK_USE_DOUBLE_BE) -#define DUK_DBLUNION_DOUBLE_HTON(u) \ - do { \ - } while (0) -#else -#error internal error, double endianness insane -#endif - -/* Reverse operation is the same. */ -#define DUK_DBLUNION_DOUBLE_NTOH(u) DUK_DBLUNION_DOUBLE_HTON((u)) - -/* Some sign bit helpers. */ -#if defined(DUK_USE_64BIT_OPS) -#define DUK_DBLUNION_HAS_SIGNBIT(u) (((u)->ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x8000000000000000)) != 0) -#define DUK_DBLUNION_GET_SIGNBIT(u) (((u)->ull[DUK_DBL_IDX_ULL0] >> 63U)) -#else -#define DUK_DBLUNION_HAS_SIGNBIT(u) (((u)->ui[DUK_DBL_IDX_UI0] & 0x80000000UL) != 0) -#define DUK_DBLUNION_GET_SIGNBIT(u) (((u)->ui[DUK_DBL_IDX_UI0] >> 31U)) -#endif - -#endif /* DUK_DBLUNION_H_INCLUDED */ -/* #include duk_fltunion.h */ -/* - * Union to access IEEE float memory representation. - */ - -#if !defined(DUK_FLTUNION_H_INCLUDED) -#define DUK_FLTUNION_H_INCLUDED - -/* #include duk_internal.h -> already included */ - -union duk_float_union { - float f; - duk_uint32_t ui[1]; - duk_uint16_t us[2]; - duk_uint8_t uc[4]; -}; - -typedef union duk_float_union duk_float_union; - -#if defined(DUK_USE_DOUBLE_LE) || defined(DUK_USE_DOUBLE_ME) -#define DUK_FLT_IDX_UI0 0 -#define DUK_FLT_IDX_US0 1 -#define DUK_FLT_IDX_US1 0 -#define DUK_FLT_IDX_UC0 3 -#define DUK_FLT_IDX_UC1 2 -#define DUK_FLT_IDX_UC2 1 -#define DUK_FLT_IDX_UC3 0 -#elif defined(DUK_USE_DOUBLE_BE) -#define DUK_FLT_IDX_UI0 0 -#define DUK_FLT_IDX_US0 0 -#define DUK_FLT_IDX_US1 1 -#define DUK_FLT_IDX_UC0 0 -#define DUK_FLT_IDX_UC1 1 -#define DUK_FLT_IDX_UC2 2 -#define DUK_FLT_IDX_UC3 3 -#else -#error internal error -#endif - -#endif /* DUK_FLTUNION_H_INCLUDED */ -/* #include duk_replacements.h */ -#if !defined(DUK_REPLACEMENTS_H_INCLUDED) -#define DUK_REPLACEMENTS_H_INCLUDED - -#if !defined(DUK_SINGLE_FILE) -#if defined(DUK_USE_COMPUTED_INFINITY) -DUK_INTERNAL_DECL double duk_computed_infinity; -#endif -#if defined(DUK_USE_COMPUTED_NAN) -DUK_INTERNAL_DECL double duk_computed_nan; -#endif -#endif /* !DUK_SINGLE_FILE */ - -#if defined(DUK_USE_REPL_FPCLASSIFY) -DUK_INTERNAL_DECL int duk_repl_fpclassify(double x); -#endif -#if defined(DUK_USE_REPL_SIGNBIT) -DUK_INTERNAL_DECL int duk_repl_signbit(double x); -#endif -#if defined(DUK_USE_REPL_ISFINITE) -DUK_INTERNAL_DECL int duk_repl_isfinite(double x); -#endif -#if defined(DUK_USE_REPL_ISNAN) -DUK_INTERNAL_DECL int duk_repl_isnan(double x); -#endif -#if defined(DUK_USE_REPL_ISINF) -DUK_INTERNAL_DECL int duk_repl_isinf(double x); -#endif - -#endif /* DUK_REPLACEMENTS_H_INCLUDED */ -/* #include duk_jmpbuf.h */ -/* - * Wrapper for jmp_buf. - * - * This is used because jmp_buf is an array type for backward compatibility. - * Wrapping jmp_buf in a struct makes pointer references, sizeof, etc, - * behave more intuitively. - * - * http://en.wikipedia.org/wiki/Setjmp.h#Member_types - */ - -#if !defined(DUK_JMPBUF_H_INCLUDED) -#define DUK_JMPBUF_H_INCLUDED - -#if defined(DUK_USE_CPP_EXCEPTIONS) -struct duk_jmpbuf { - duk_small_int_t dummy; /* unused */ -}; -#else -struct duk_jmpbuf { - DUK_JMPBUF_TYPE jb; -}; -#endif - -#endif /* DUK_JMPBUF_H_INCLUDED */ -/* #include duk_exception.h */ -/* - * Exceptions for Duktape internal throws when C++ exceptions are used - * for long control transfers. - */ - -#if !defined(DUK_EXCEPTION_H_INCLUDED) -#define DUK_EXCEPTION_H_INCLUDED - -#if defined(DUK_USE_CPP_EXCEPTIONS) -/* Internal exception used as a setjmp-longjmp replacement. User code should - * NEVER see or catch this exception, so it doesn't inherit from any base - * class which should minimize the chance of user code accidentally catching - * the exception. - */ -class duk_internal_exception { - /* intentionally empty */ -}; - -/* Fatal error, thrown as a specific C++ exception with C++ exceptions - * enabled. It is unsafe to continue; doing so may cause crashes or memory - * leaks. This is intended to be either uncaught, or caught by user code - * aware of the "unsafe to continue" semantics. - */ -class duk_fatal_exception : public virtual std::runtime_error { - public: - duk_fatal_exception(const char *message) : std::runtime_error(message) { - } -}; -#endif - -#endif /* DUK_EXCEPTION_H_INCLUDED */ -/* #include duk_forwdecl.h */ -/* - * Forward declarations for all Duktape structures. - */ - -#if !defined(DUK_FORWDECL_H_INCLUDED) -#define DUK_FORWDECL_H_INCLUDED - -/* - * Forward declarations - */ - -#if defined(DUK_USE_CPP_EXCEPTIONS) -class duk_internal_exception; -#else -struct duk_jmpbuf; -#endif - -/* duk_tval intentionally skipped */ -struct duk_heaphdr; -struct duk_heaphdr_string; -struct duk_harray; -struct duk_hstring; -struct duk_hstring_external; -struct duk_hobject; -struct duk_hcompfunc; -struct duk_hnatfunc; -struct duk_hboundfunc; -struct duk_hthread; -struct duk_hbufobj; -struct duk_hdecenv; -struct duk_hobjenv; -struct duk_hproxy; -struct duk_hbuffer; -struct duk_hbuffer_fixed; -struct duk_hbuffer_dynamic; -struct duk_hbuffer_external; - -struct duk_propaccessor; -union duk_propvalue; -struct duk_propdesc; - -struct duk_heap; -struct duk_breakpoint; - -struct duk_activation; -struct duk_catcher; -struct duk_ljstate; -struct duk_strcache_entry; -struct duk_litcache_entry; -struct duk_strtab_entry; - -#if defined(DUK_USE_DEBUG) -struct duk_fixedbuffer; -#endif - -struct duk_bitdecoder_ctx; -struct duk_bitencoder_ctx; -struct duk_bufwriter_ctx; - -struct duk_token; -struct duk_re_token; -struct duk_lexer_point; -struct duk_lexer_ctx; -struct duk_lexer_codepoint; - -struct duk_compiler_instr; -struct duk_compiler_func; -struct duk_compiler_ctx; - -struct duk_re_matcher_ctx; -struct duk_re_compiler_ctx; - -#if defined(DUK_USE_CPP_EXCEPTIONS) -/* no typedef */ -#else -typedef struct duk_jmpbuf duk_jmpbuf; -#endif - -/* duk_tval intentionally skipped */ -typedef struct duk_heaphdr duk_heaphdr; -typedef struct duk_heaphdr_string duk_heaphdr_string; -typedef struct duk_harray duk_harray; -typedef struct duk_hstring duk_hstring; -typedef struct duk_hstring_external duk_hstring_external; -typedef struct duk_hobject duk_hobject; -typedef struct duk_hcompfunc duk_hcompfunc; -typedef struct duk_hnatfunc duk_hnatfunc; -typedef struct duk_hboundfunc duk_hboundfunc; -typedef struct duk_hthread duk_hthread; -typedef struct duk_hbufobj duk_hbufobj; -typedef struct duk_hdecenv duk_hdecenv; -typedef struct duk_hobjenv duk_hobjenv; -typedef struct duk_hproxy duk_hproxy; -typedef struct duk_hbuffer duk_hbuffer; -typedef struct duk_hbuffer_fixed duk_hbuffer_fixed; -typedef struct duk_hbuffer_dynamic duk_hbuffer_dynamic; -typedef struct duk_hbuffer_external duk_hbuffer_external; - -typedef struct duk_propaccessor duk_propaccessor; -typedef union duk_propvalue duk_propvalue; -typedef struct duk_propdesc duk_propdesc; - -typedef struct duk_heap duk_heap; -typedef struct duk_breakpoint duk_breakpoint; - -typedef struct duk_activation duk_activation; -typedef struct duk_catcher duk_catcher; -typedef struct duk_ljstate duk_ljstate; -typedef struct duk_strcache_entry duk_strcache_entry; -typedef struct duk_litcache_entry duk_litcache_entry; -typedef struct duk_strtab_entry duk_strtab_entry; - -#if defined(DUK_USE_DEBUG) -typedef struct duk_fixedbuffer duk_fixedbuffer; -#endif - -typedef struct duk_bitdecoder_ctx duk_bitdecoder_ctx; -typedef struct duk_bitencoder_ctx duk_bitencoder_ctx; -typedef struct duk_bufwriter_ctx duk_bufwriter_ctx; - -typedef struct duk_token duk_token; -typedef struct duk_re_token duk_re_token; -typedef struct duk_lexer_point duk_lexer_point; -typedef struct duk_lexer_ctx duk_lexer_ctx; -typedef struct duk_lexer_codepoint duk_lexer_codepoint; - -typedef struct duk_compiler_instr duk_compiler_instr; -typedef struct duk_compiler_func duk_compiler_func; -typedef struct duk_compiler_ctx duk_compiler_ctx; - -typedef struct duk_re_matcher_ctx duk_re_matcher_ctx; -typedef struct duk_re_compiler_ctx duk_re_compiler_ctx; - -#endif /* DUK_FORWDECL_H_INCLUDED */ -/* #include duk_tval.h */ -/* - * Tagged type definition (duk_tval) and accessor macros. - * - * Access all fields through the accessor macros, as the representation - * is quite tricky. - * - * There are two packed type alternatives: an 8-byte representation - * based on an IEEE double (preferred for compactness), and a 12-byte - * representation (portability). The latter is needed also in e.g. - * 64-bit environments (it usually pads to 16 bytes per value). - * - * Selecting the tagged type format involves many trade-offs (memory - * use, size and performance of generated code, portability, etc). - * - * NB: because macro arguments are often expressions, macros should - * avoid evaluating their argument more than once. - */ - -#if !defined(DUK_TVAL_H_INCLUDED) -#define DUK_TVAL_H_INCLUDED - -/* sanity */ -#if !defined(DUK_USE_DOUBLE_LE) && !defined(DUK_USE_DOUBLE_ME) && !defined(DUK_USE_DOUBLE_BE) -#error unsupported: cannot determine byte order variant -#endif - -#if defined(DUK_USE_PACKED_TVAL) -/* ======================================================================== */ - -/* - * Packed 8-byte representation - */ - -/* use duk_double_union as duk_tval directly */ -typedef union duk_double_union duk_tval; -typedef struct { - duk_uint16_t a; - duk_uint16_t b; - duk_uint16_t c; - duk_uint16_t d; -} duk_tval_unused; - -/* tags */ -#define DUK_TAG_NORMALIZED_NAN 0x7ff8UL /* the NaN variant we use */ -/* avoid tag 0xfff0, no risk of confusion with negative infinity */ -#define DUK_TAG_MIN 0xfff1UL -#if defined(DUK_USE_FASTINT) -#define DUK_TAG_FASTINT 0xfff1UL /* embed: integer value */ -#endif -#define DUK_TAG_UNUSED 0xfff2UL /* marker; not actual tagged value */ -#define DUK_TAG_UNDEFINED 0xfff3UL /* embed: nothing */ -#define DUK_TAG_NULL 0xfff4UL /* embed: nothing */ -#define DUK_TAG_BOOLEAN 0xfff5UL /* embed: 0 or 1 (false or true) */ -/* DUK_TAG_NUMBER would logically go here, but it has multiple 'tags' */ -#define DUK_TAG_POINTER 0xfff6UL /* embed: void ptr */ -#define DUK_TAG_LIGHTFUNC 0xfff7UL /* embed: func ptr */ -#define DUK_TAG_STRING 0xfff8UL /* embed: duk_hstring ptr */ -#define DUK_TAG_OBJECT 0xfff9UL /* embed: duk_hobject ptr */ -#define DUK_TAG_BUFFER 0xfffaUL /* embed: duk_hbuffer ptr */ -#define DUK_TAG_MAX 0xfffaUL - -/* for convenience */ -#define DUK_XTAG_BOOLEAN_FALSE 0xfff50000UL -#define DUK_XTAG_BOOLEAN_TRUE 0xfff50001UL - -#define DUK_TVAL_IS_VALID_TAG(tv) (DUK_TVAL_GET_TAG((tv)) - DUK_TAG_MIN <= DUK_TAG_MAX - DUK_TAG_MIN) - -/* DUK_TVAL_UNUSED initializer for duk_tval_unused, works for any endianness. */ -#define DUK_TVAL_UNUSED_INITIALIZER() \ - { DUK_TAG_UNUSED, DUK_TAG_UNUSED, DUK_TAG_UNUSED, DUK_TAG_UNUSED } - -/* two casts to avoid gcc warning: "warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]" */ -#if defined(DUK_USE_64BIT_OPS) -#if defined(DUK_USE_DOUBLE_ME) -#define DUK__TVAL_SET_TAGGEDPOINTER(tv, h, tag) \ - do { \ - (tv)->ull[DUK_DBL_IDX_ULL0] = (((duk_uint64_t) (tag)) << 16) | (((duk_uint64_t) (duk_uint32_t) (h)) << 32); \ - } while (0) -#else -#define DUK__TVAL_SET_TAGGEDPOINTER(tv, h, tag) \ - do { \ - (tv)->ull[DUK_DBL_IDX_ULL0] = (((duk_uint64_t) (tag)) << 48) | ((duk_uint64_t) (duk_uint32_t) (h)); \ - } while (0) -#endif -#else /* DUK_USE_64BIT_OPS */ -#define DUK__TVAL_SET_TAGGEDPOINTER(tv, h, tag) \ - do { \ - duk_tval *duk__tv; \ - duk__tv = (tv); \ - duk__tv->ui[DUK_DBL_IDX_UI0] = ((duk_uint32_t) (tag)) << 16; \ - duk__tv->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) (h); \ - } while (0) -#endif /* DUK_USE_64BIT_OPS */ - -#if defined(DUK_USE_64BIT_OPS) -/* Double casting for pointer to avoid gcc warning (cast from pointer to integer of different size) */ -#if defined(DUK_USE_DOUBLE_ME) -#define DUK__TVAL_SET_LIGHTFUNC(tv, fp, flags) \ - do { \ - (tv)->ull[DUK_DBL_IDX_ULL0] = (((duk_uint64_t) DUK_TAG_LIGHTFUNC) << 16) | ((duk_uint64_t) (flags)) | \ - (((duk_uint64_t) (duk_uint32_t) (fp)) << 32); \ - } while (0) -#else -#define DUK__TVAL_SET_LIGHTFUNC(tv, fp, flags) \ - do { \ - (tv)->ull[DUK_DBL_IDX_ULL0] = (((duk_uint64_t) DUK_TAG_LIGHTFUNC) << 48) | (((duk_uint64_t) (flags)) << 32) | \ - ((duk_uint64_t) (duk_uint32_t) (fp)); \ - } while (0) -#endif -#else /* DUK_USE_64BIT_OPS */ -#define DUK__TVAL_SET_LIGHTFUNC(tv, fp, flags) \ - do { \ - duk_tval *duk__tv; \ - duk__tv = (tv); \ - duk__tv->ui[DUK_DBL_IDX_UI0] = (((duk_uint32_t) DUK_TAG_LIGHTFUNC) << 16) | ((duk_uint32_t) (flags)); \ - duk__tv->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) (fp); \ - } while (0) -#endif /* DUK_USE_64BIT_OPS */ - -#if defined(DUK_USE_FASTINT) -/* Note: masking is done for 'i' to deal with negative numbers correctly */ -#if defined(DUK_USE_DOUBLE_ME) -#define DUK__TVAL_SET_I48(tv, i) \ - do { \ - duk_tval *duk__tv; \ - duk__tv = (tv); \ - duk__tv->ui[DUK_DBL_IDX_UI0] = \ - ((duk_uint32_t) DUK_TAG_FASTINT) << 16 | (((duk_uint32_t) ((i) >> 32)) & 0x0000ffffUL); \ - duk__tv->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) (i); \ - } while (0) -#define DUK__TVAL_SET_U32(tv, i) \ - do { \ - duk_tval *duk__tv; \ - duk__tv = (tv); \ - duk__tv->ui[DUK_DBL_IDX_UI0] = ((duk_uint32_t) DUK_TAG_FASTINT) << 16; \ - duk__tv->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) (i); \ - } while (0) -#else -#define DUK__TVAL_SET_I48(tv, i) \ - do { \ - (tv)->ull[DUK_DBL_IDX_ULL0] = \ - (((duk_uint64_t) DUK_TAG_FASTINT) << 48) | (((duk_uint64_t) (i)) & DUK_U64_CONSTANT(0x0000ffffffffffff)); \ - } while (0) -#define DUK__TVAL_SET_U32(tv, i) \ - do { \ - (tv)->ull[DUK_DBL_IDX_ULL0] = (((duk_uint64_t) DUK_TAG_FASTINT) << 48) | (duk_uint64_t) (i); \ - } while (0) -#endif - -/* This needs to go through a cast because sign extension is needed. */ -#define DUK__TVAL_SET_I32(tv, i) \ - do { \ - duk_int64_t duk__tmp = (duk_int64_t) (i); \ - DUK_TVAL_SET_I48((tv), duk__tmp); \ - } while (0) - -/* XXX: Clumsy sign extend and masking of 16 topmost bits. */ -#if defined(DUK_USE_DOUBLE_ME) -#define DUK__TVAL_GET_FASTINT(tv) \ - (((duk_int64_t) ((((duk_uint64_t) (tv)->ui[DUK_DBL_IDX_UI0]) << 32) | ((duk_uint64_t) (tv)->ui[DUK_DBL_IDX_UI1]))) \ - << 16 >> \ - 16) -#else -#define DUK__TVAL_GET_FASTINT(tv) ((((duk_int64_t) (tv)->ull[DUK_DBL_IDX_ULL0]) << 16) >> 16) -#endif -#define DUK__TVAL_GET_FASTINT_U32(tv) ((tv)->ui[DUK_DBL_IDX_UI1]) -#define DUK__TVAL_GET_FASTINT_I32(tv) ((duk_int32_t) (tv)->ui[DUK_DBL_IDX_UI1]) -#endif /* DUK_USE_FASTINT */ - -#define DUK_TVAL_SET_UNDEFINED(tv) \ - do { \ - (tv)->us[DUK_DBL_IDX_US0] = (duk_uint16_t) DUK_TAG_UNDEFINED; \ - } while (0) -#define DUK_TVAL_SET_UNUSED(tv) \ - do { \ - (tv)->us[DUK_DBL_IDX_US0] = (duk_uint16_t) DUK_TAG_UNUSED; \ - } while (0) -#define DUK_TVAL_SET_NULL(tv) \ - do { \ - (tv)->us[DUK_DBL_IDX_US0] = (duk_uint16_t) DUK_TAG_NULL; \ - } while (0) - -#define DUK_TVAL_SET_BOOLEAN(tv, val) \ - DUK_DBLUNION_SET_HIGH32((tv), (((duk_uint32_t) DUK_TAG_BOOLEAN) << 16) | ((duk_uint32_t) (val))) - -#define DUK_TVAL_SET_NAN(tv) DUK_DBLUNION_SET_NAN_FULL((tv)) - -/* Assumes that caller has normalized NaNs, otherwise trouble ahead. */ -#if defined(DUK_USE_FASTINT) -#define DUK_TVAL_SET_DOUBLE(tv, d) \ - do { \ - duk_double_t duk__dblval; \ - duk__dblval = (d); \ - DUK_ASSERT_DOUBLE_IS_NORMALIZED(duk__dblval); \ - DUK_DBLUNION_SET_DOUBLE((tv), duk__dblval); \ - } while (0) -#define DUK_TVAL_SET_I48(tv, i) DUK__TVAL_SET_I48((tv), (i)) -#define DUK_TVAL_SET_I32(tv, i) DUK__TVAL_SET_I32((tv), (i)) -#define DUK_TVAL_SET_U32(tv, i) DUK__TVAL_SET_U32((tv), (i)) -#define DUK_TVAL_SET_NUMBER_CHKFAST_FAST(tv, d) duk_tval_set_number_chkfast_fast((tv), (d)) -#define DUK_TVAL_SET_NUMBER_CHKFAST_SLOW(tv, d) duk_tval_set_number_chkfast_slow((tv), (d)) -#define DUK_TVAL_SET_NUMBER(tv, d) DUK_TVAL_SET_DOUBLE((tv), (d)) -#define DUK_TVAL_CHKFAST_INPLACE_FAST(tv) \ - do { \ - duk_tval *duk__tv; \ - duk_double_t duk__d; \ - duk__tv = (tv); \ - if (DUK_TVAL_IS_DOUBLE(duk__tv)) { \ - duk__d = DUK_TVAL_GET_DOUBLE(duk__tv); \ - DUK_TVAL_SET_NUMBER_CHKFAST_FAST(duk__tv, duk__d); \ - } \ - } while (0) -#define DUK_TVAL_CHKFAST_INPLACE_SLOW(tv) \ - do { \ - duk_tval *duk__tv; \ - duk_double_t duk__d; \ - duk__tv = (tv); \ - if (DUK_TVAL_IS_DOUBLE(duk__tv)) { \ - duk__d = DUK_TVAL_GET_DOUBLE(duk__tv); \ - DUK_TVAL_SET_NUMBER_CHKFAST_SLOW(duk__tv, duk__d); \ - } \ - } while (0) -#else /* DUK_USE_FASTINT */ -#define DUK_TVAL_SET_DOUBLE(tv, d) \ - do { \ - duk_double_t duk__dblval; \ - duk__dblval = (d); \ - DUK_ASSERT_DOUBLE_IS_NORMALIZED(duk__dblval); \ - DUK_DBLUNION_SET_DOUBLE((tv), duk__dblval); \ - } while (0) -#define DUK_TVAL_SET_I48(tv, i) DUK_TVAL_SET_DOUBLE((tv), (duk_double_t) (i)) /* XXX: fast int-to-double */ -#define DUK_TVAL_SET_I32(tv, i) DUK_TVAL_SET_DOUBLE((tv), (duk_double_t) (i)) -#define DUK_TVAL_SET_U32(tv, i) DUK_TVAL_SET_DOUBLE((tv), (duk_double_t) (i)) -#define DUK_TVAL_SET_NUMBER_CHKFAST_FAST(tv, d) DUK_TVAL_SET_DOUBLE((tv), (d)) -#define DUK_TVAL_SET_NUMBER_CHKFAST_SLOW(tv, d) DUK_TVAL_SET_DOUBLE((tv), (d)) -#define DUK_TVAL_SET_NUMBER(tv, d) DUK_TVAL_SET_DOUBLE((tv), (d)) -#define DUK_TVAL_CHKFAST_INPLACE_FAST(tv) \ - do { \ - } while (0) -#define DUK_TVAL_CHKFAST_INPLACE_SLOW(tv) \ - do { \ - } while (0) -#endif /* DUK_USE_FASTINT */ - -#define DUK_TVAL_SET_FASTINT(tv, i) DUK_TVAL_SET_I48((tv), (i)) /* alias */ - -#define DUK_TVAL_SET_LIGHTFUNC(tv, fp, flags) DUK__TVAL_SET_LIGHTFUNC((tv), (fp), (flags)) -#define DUK_TVAL_SET_STRING(tv, h) DUK__TVAL_SET_TAGGEDPOINTER((tv), (h), DUK_TAG_STRING) -#define DUK_TVAL_SET_OBJECT(tv, h) DUK__TVAL_SET_TAGGEDPOINTER((tv), (h), DUK_TAG_OBJECT) -#define DUK_TVAL_SET_BUFFER(tv, h) DUK__TVAL_SET_TAGGEDPOINTER((tv), (h), DUK_TAG_BUFFER) -#define DUK_TVAL_SET_POINTER(tv, p) DUK__TVAL_SET_TAGGEDPOINTER((tv), (p), DUK_TAG_POINTER) - -#define DUK_TVAL_SET_TVAL(tv, x) \ - do { \ - *(tv) = *(x); \ - } while (0) - -/* getters */ -#define DUK_TVAL_GET_BOOLEAN(tv) ((duk_small_uint_t) (tv)->us[DUK_DBL_IDX_US1]) -#if defined(DUK_USE_FASTINT) -#define DUK_TVAL_GET_DOUBLE(tv) ((tv)->d) -#define DUK_TVAL_GET_FASTINT(tv) DUK__TVAL_GET_FASTINT((tv)) -#define DUK_TVAL_GET_FASTINT_U32(tv) DUK__TVAL_GET_FASTINT_U32((tv)) -#define DUK_TVAL_GET_FASTINT_I32(tv) DUK__TVAL_GET_FASTINT_I32((tv)) -#define DUK_TVAL_GET_NUMBER(tv) duk_tval_get_number_packed((tv)) -#else -#define DUK_TVAL_GET_NUMBER(tv) ((tv)->d) -#define DUK_TVAL_GET_DOUBLE(tv) ((tv)->d) -#endif -#define DUK_TVAL_GET_LIGHTFUNC(tv, out_fp, out_flags) \ - do { \ - (out_flags) = (tv)->ui[DUK_DBL_IDX_UI0] & 0xffffUL; \ - (out_fp) = (duk_c_function) (tv)->ui[DUK_DBL_IDX_UI1]; \ - } while (0) -#define DUK_TVAL_GET_LIGHTFUNC_FUNCPTR(tv) ((duk_c_function) ((tv)->ui[DUK_DBL_IDX_UI1])) -#define DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv) (((duk_small_uint_t) (tv)->ui[DUK_DBL_IDX_UI0]) & 0xffffUL) -#define DUK_TVAL_GET_STRING(tv) ((duk_hstring *) (tv)->vp[DUK_DBL_IDX_VP1]) -#define DUK_TVAL_GET_OBJECT(tv) ((duk_hobject *) (tv)->vp[DUK_DBL_IDX_VP1]) -#define DUK_TVAL_GET_BUFFER(tv) ((duk_hbuffer *) (tv)->vp[DUK_DBL_IDX_VP1]) -#define DUK_TVAL_GET_POINTER(tv) ((void *) (tv)->vp[DUK_DBL_IDX_VP1]) -#define DUK_TVAL_GET_HEAPHDR(tv) ((duk_heaphdr *) (tv)->vp[DUK_DBL_IDX_VP1]) - -/* decoding */ -#define DUK_TVAL_GET_TAG(tv) ((duk_small_uint_t) (tv)->us[DUK_DBL_IDX_US0]) - -#define DUK_TVAL_IS_UNDEFINED(tv) (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_UNDEFINED) -#define DUK_TVAL_IS_UNUSED(tv) (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_UNUSED) -#define DUK_TVAL_IS_NULL(tv) (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_NULL) -#define DUK_TVAL_IS_BOOLEAN(tv) (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_BOOLEAN) -#define DUK_TVAL_IS_BOOLEAN_TRUE(tv) ((tv)->ui[DUK_DBL_IDX_UI0] == DUK_XTAG_BOOLEAN_TRUE) -#define DUK_TVAL_IS_BOOLEAN_FALSE(tv) ((tv)->ui[DUK_DBL_IDX_UI0] == DUK_XTAG_BOOLEAN_FALSE) -#define DUK_TVAL_IS_LIGHTFUNC(tv) (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_LIGHTFUNC) -#define DUK_TVAL_IS_STRING(tv) (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_STRING) -#define DUK_TVAL_IS_OBJECT(tv) (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_OBJECT) -#define DUK_TVAL_IS_BUFFER(tv) (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_BUFFER) -#define DUK_TVAL_IS_POINTER(tv) (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_POINTER) -#if defined(DUK_USE_FASTINT) -/* 0xfff0 is -Infinity */ -#define DUK_TVAL_IS_DOUBLE(tv) (DUK_TVAL_GET_TAG((tv)) <= 0xfff0UL) -#define DUK_TVAL_IS_FASTINT(tv) (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_FASTINT) -#define DUK_TVAL_IS_NUMBER(tv) (DUK_TVAL_GET_TAG((tv)) <= 0xfff1UL) -#else -#define DUK_TVAL_IS_NUMBER(tv) (DUK_TVAL_GET_TAG((tv)) <= 0xfff0UL) -#define DUK_TVAL_IS_DOUBLE(tv) DUK_TVAL_IS_NUMBER((tv)) -#endif - -/* This is performance critical because it appears in every DECREF. */ -#define DUK_TVAL_IS_HEAP_ALLOCATED(tv) (DUK_TVAL_GET_TAG((tv)) >= DUK_TAG_STRING) - -#if defined(DUK_USE_FASTINT) -DUK_INTERNAL_DECL duk_double_t duk_tval_get_number_packed(duk_tval *tv); -#endif - -#else /* DUK_USE_PACKED_TVAL */ -/* ======================================================================== */ - -/* - * Portable 12-byte representation - */ - -/* Note: not initializing all bytes is normally not an issue: Duktape won't - * read or use the uninitialized bytes so valgrind won't issue warnings. - * In some special cases a harmless valgrind warning may be issued though. - * For example, the DumpHeap debugger command writes out a compiled function's - * 'data' area as is, including any uninitialized bytes, which causes a - * valgrind warning. - */ - -typedef struct duk_tval_struct duk_tval; - -struct duk_tval_struct { - duk_small_uint_t t; - duk_small_uint_t v_extra; - union { - duk_double_t d; - duk_small_int_t i; -#if defined(DUK_USE_FASTINT) - duk_int64_t fi; /* if present, forces 16-byte duk_tval */ -#endif - void *voidptr; - duk_hstring *hstring; - duk_hobject *hobject; - duk_hcompfunc *hcompfunc; - duk_hnatfunc *hnatfunc; - duk_hthread *hthread; - duk_hbuffer *hbuffer; - duk_heaphdr *heaphdr; - duk_c_function lightfunc; - } v; -}; - -typedef struct { - duk_small_uint_t t; - duk_small_uint_t v_extra; - /* The rest of the fields don't matter except for debug dumps and such - * for which a partial initializer may trigger out-ot-bounds memory - * reads. Include a double field which is usually as large or larger - * than pointers (not always however). - */ - duk_double_t d; -} duk_tval_unused; - -#define DUK_TVAL_UNUSED_INITIALIZER() \ - { DUK_TAG_UNUSED, 0, 0.0 } - -#define DUK_TAG_MIN 0 -#define DUK_TAG_NUMBER 0 /* DUK_TAG_NUMBER only defined for non-packed duk_tval */ -#if defined(DUK_USE_FASTINT) -#define DUK_TAG_FASTINT 1 -#endif -#define DUK_TAG_UNDEFINED 2 -#define DUK_TAG_NULL 3 -#define DUK_TAG_BOOLEAN 4 -#define DUK_TAG_POINTER 5 -#define DUK_TAG_LIGHTFUNC 6 -#define DUK_TAG_UNUSED 7 /* marker; not actual tagged type */ -#define DUK_TAG_STRING 8 /* first heap allocated, match bit boundary */ -#define DUK_TAG_OBJECT 9 -#define DUK_TAG_BUFFER 10 -#define DUK_TAG_MAX 10 - -#define DUK_TVAL_IS_VALID_TAG(tv) (DUK_TVAL_GET_TAG((tv)) - DUK_TAG_MIN <= DUK_TAG_MAX - DUK_TAG_MIN) - -/* DUK_TAG_NUMBER is intentionally first, as it is the default clause in code - * to support the 8-byte representation. Further, it is a non-heap-allocated - * type so it should come before DUK_TAG_STRING. Finally, it should not break - * the tag value ranges covered by case-clauses in a switch-case. - */ - -/* setters */ -#define DUK_TVAL_SET_UNDEFINED(tv) \ - do { \ - duk_tval *duk__tv; \ - duk__tv = (tv); \ - duk__tv->t = DUK_TAG_UNDEFINED; \ - } while (0) - -#define DUK_TVAL_SET_UNUSED(tv) \ - do { \ - duk_tval *duk__tv; \ - duk__tv = (tv); \ - duk__tv->t = DUK_TAG_UNUSED; \ - } while (0) - -#define DUK_TVAL_SET_NULL(tv) \ - do { \ - duk_tval *duk__tv; \ - duk__tv = (tv); \ - duk__tv->t = DUK_TAG_NULL; \ - } while (0) - -#define DUK_TVAL_SET_BOOLEAN(tv, val) \ - do { \ - duk_tval *duk__tv; \ - duk__tv = (tv); \ - duk__tv->t = DUK_TAG_BOOLEAN; \ - duk__tv->v.i = (duk_small_int_t) (val); \ - } while (0) - -#if defined(DUK_USE_FASTINT) -#define DUK_TVAL_SET_DOUBLE(tv, val) \ - do { \ - duk_tval *duk__tv; \ - duk_double_t duk__dblval; \ - duk__dblval = (val); \ - DUK_ASSERT_DOUBLE_IS_NORMALIZED(duk__dblval); /* nop for unpacked duk_tval */ \ - duk__tv = (tv); \ - duk__tv->t = DUK_TAG_NUMBER; \ - duk__tv->v.d = duk__dblval; \ - } while (0) -#define DUK_TVAL_SET_I48(tv, val) \ - do { \ - duk_tval *duk__tv; \ - duk__tv = (tv); \ - duk__tv->t = DUK_TAG_FASTINT; \ - duk__tv->v.fi = (val); \ - } while (0) -#define DUK_TVAL_SET_U32(tv, val) \ - do { \ - duk_tval *duk__tv; \ - duk__tv = (tv); \ - duk__tv->t = DUK_TAG_FASTINT; \ - duk__tv->v.fi = (duk_int64_t) (val); \ - } while (0) -#define DUK_TVAL_SET_I32(tv, val) \ - do { \ - duk_tval *duk__tv; \ - duk__tv = (tv); \ - duk__tv->t = DUK_TAG_FASTINT; \ - duk__tv->v.fi = (duk_int64_t) (val); \ - } while (0) -#define DUK_TVAL_SET_NUMBER_CHKFAST_FAST(tv, d) duk_tval_set_number_chkfast_fast((tv), (d)) -#define DUK_TVAL_SET_NUMBER_CHKFAST_SLOW(tv, d) duk_tval_set_number_chkfast_slow((tv), (d)) -#define DUK_TVAL_SET_NUMBER(tv, val) DUK_TVAL_SET_DOUBLE((tv), (val)) -#define DUK_TVAL_CHKFAST_INPLACE_FAST(tv) \ - do { \ - duk_tval *duk__tv; \ - duk_double_t duk__d; \ - duk__tv = (tv); \ - if (DUK_TVAL_IS_DOUBLE(duk__tv)) { \ - duk__d = DUK_TVAL_GET_DOUBLE(duk__tv); \ - DUK_TVAL_SET_NUMBER_CHKFAST_FAST(duk__tv, duk__d); \ - } \ - } while (0) -#define DUK_TVAL_CHKFAST_INPLACE_SLOW(tv) \ - do { \ - duk_tval *duk__tv; \ - duk_double_t duk__d; \ - duk__tv = (tv); \ - if (DUK_TVAL_IS_DOUBLE(duk__tv)) { \ - duk__d = DUK_TVAL_GET_DOUBLE(duk__tv); \ - DUK_TVAL_SET_NUMBER_CHKFAST_SLOW(duk__tv, duk__d); \ - } \ - } while (0) -#else /* DUK_USE_FASTINT */ -#define DUK_TVAL_SET_DOUBLE(tv, d) DUK_TVAL_SET_NUMBER((tv), (d)) -#define DUK_TVAL_SET_I48(tv, val) DUK_TVAL_SET_NUMBER((tv), (duk_double_t) (val)) /* XXX: fast int-to-double */ -#define DUK_TVAL_SET_U32(tv, val) DUK_TVAL_SET_NUMBER((tv), (duk_double_t) (val)) -#define DUK_TVAL_SET_I32(tv, val) DUK_TVAL_SET_NUMBER((tv), (duk_double_t) (val)) -#define DUK_TVAL_SET_NUMBER(tv, val) \ - do { \ - duk_tval *duk__tv; \ - duk_double_t duk__dblval; \ - duk__dblval = (val); \ - DUK_ASSERT_DOUBLE_IS_NORMALIZED(duk__dblval); /* nop for unpacked duk_tval */ \ - duk__tv = (tv); \ - duk__tv->t = DUK_TAG_NUMBER; \ - duk__tv->v.d = duk__dblval; \ - } while (0) -#define DUK_TVAL_SET_NUMBER_CHKFAST_FAST(tv, d) DUK_TVAL_SET_NUMBER((tv), (d)) -#define DUK_TVAL_SET_NUMBER_CHKFAST_SLOW(tv, d) DUK_TVAL_SET_NUMBER((tv), (d)) -#define DUK_TVAL_CHKFAST_INPLACE_FAST(tv) \ - do { \ - } while (0) -#define DUK_TVAL_CHKFAST_INPLACE_SLOW(tv) \ - do { \ - } while (0) -#endif /* DUK_USE_FASTINT */ - -#define DUK_TVAL_SET_FASTINT(tv, i) DUK_TVAL_SET_I48((tv), (i)) /* alias */ - -#define DUK_TVAL_SET_POINTER(tv, hptr) \ - do { \ - duk_tval *duk__tv; \ - duk__tv = (tv); \ - duk__tv->t = DUK_TAG_POINTER; \ - duk__tv->v.voidptr = (hptr); \ - } while (0) - -#define DUK_TVAL_SET_LIGHTFUNC(tv, fp, flags) \ - do { \ - duk_tval *duk__tv; \ - duk__tv = (tv); \ - duk__tv->t = DUK_TAG_LIGHTFUNC; \ - duk__tv->v_extra = (flags); \ - duk__tv->v.lightfunc = (duk_c_function) (fp); \ - } while (0) - -#define DUK_TVAL_SET_STRING(tv, hptr) \ - do { \ - duk_tval *duk__tv; \ - duk__tv = (tv); \ - duk__tv->t = DUK_TAG_STRING; \ - duk__tv->v.hstring = (hptr); \ - } while (0) - -#define DUK_TVAL_SET_OBJECT(tv, hptr) \ - do { \ - duk_tval *duk__tv; \ - duk__tv = (tv); \ - duk__tv->t = DUK_TAG_OBJECT; \ - duk__tv->v.hobject = (hptr); \ - } while (0) - -#define DUK_TVAL_SET_BUFFER(tv, hptr) \ - do { \ - duk_tval *duk__tv; \ - duk__tv = (tv); \ - duk__tv->t = DUK_TAG_BUFFER; \ - duk__tv->v.hbuffer = (hptr); \ - } while (0) - -#define DUK_TVAL_SET_NAN(tv) \ - do { \ - /* in non-packed representation we don't care about which NaN is used */ \ - duk_tval *duk__tv; \ - duk__tv = (tv); \ - duk__tv->t = DUK_TAG_NUMBER; \ - duk__tv->v.d = DUK_DOUBLE_NAN; \ - } while (0) - -#define DUK_TVAL_SET_TVAL(tv, x) \ - do { \ - *(tv) = *(x); \ - } while (0) - -/* getters */ -#define DUK_TVAL_GET_BOOLEAN(tv) ((duk_small_uint_t) (tv)->v.i) -#if defined(DUK_USE_FASTINT) -#define DUK_TVAL_GET_DOUBLE(tv) ((tv)->v.d) -#define DUK_TVAL_GET_FASTINT(tv) ((tv)->v.fi) -#define DUK_TVAL_GET_FASTINT_U32(tv) ((duk_uint32_t) ((tv)->v.fi)) -#define DUK_TVAL_GET_FASTINT_I32(tv) ((duk_int32_t) ((tv)->v.fi)) -#if 0 -#define DUK_TVAL_GET_NUMBER(tv) (DUK_TVAL_IS_FASTINT((tv)) ? (duk_double_t) DUK_TVAL_GET_FASTINT((tv)) : DUK_TVAL_GET_DOUBLE((tv))) -#define DUK_TVAL_GET_NUMBER(tv) duk_tval_get_number_unpacked((tv)) -#else -/* This seems reasonable overall. */ -#define DUK_TVAL_GET_NUMBER(tv) (DUK_TVAL_IS_FASTINT((tv)) ? duk_tval_get_number_unpacked_fastint((tv)) : DUK_TVAL_GET_DOUBLE((tv))) -#endif -#else -#define DUK_TVAL_GET_NUMBER(tv) ((tv)->v.d) -#define DUK_TVAL_GET_DOUBLE(tv) ((tv)->v.d) -#endif /* DUK_USE_FASTINT */ -#define DUK_TVAL_GET_POINTER(tv) ((tv)->v.voidptr) -#define DUK_TVAL_GET_LIGHTFUNC(tv, out_fp, out_flags) \ - do { \ - (out_flags) = (duk_uint32_t) (tv)->v_extra; \ - (out_fp) = (tv)->v.lightfunc; \ - } while (0) -#define DUK_TVAL_GET_LIGHTFUNC_FUNCPTR(tv) ((tv)->v.lightfunc) -#define DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv) ((duk_small_uint_t) ((tv)->v_extra)) -#define DUK_TVAL_GET_STRING(tv) ((tv)->v.hstring) -#define DUK_TVAL_GET_OBJECT(tv) ((tv)->v.hobject) -#define DUK_TVAL_GET_BUFFER(tv) ((tv)->v.hbuffer) -#define DUK_TVAL_GET_HEAPHDR(tv) ((tv)->v.heaphdr) - -/* decoding */ -#define DUK_TVAL_GET_TAG(tv) ((tv)->t) -#define DUK_TVAL_IS_UNDEFINED(tv) ((tv)->t == DUK_TAG_UNDEFINED) -#define DUK_TVAL_IS_UNUSED(tv) ((tv)->t == DUK_TAG_UNUSED) -#define DUK_TVAL_IS_NULL(tv) ((tv)->t == DUK_TAG_NULL) -#define DUK_TVAL_IS_BOOLEAN(tv) ((tv)->t == DUK_TAG_BOOLEAN) -#define DUK_TVAL_IS_BOOLEAN_TRUE(tv) (((tv)->t == DUK_TAG_BOOLEAN) && ((tv)->v.i != 0)) -#define DUK_TVAL_IS_BOOLEAN_FALSE(tv) (((tv)->t == DUK_TAG_BOOLEAN) && ((tv)->v.i == 0)) -#if defined(DUK_USE_FASTINT) -#define DUK_TVAL_IS_DOUBLE(tv) ((tv)->t == DUK_TAG_NUMBER) -#define DUK_TVAL_IS_FASTINT(tv) ((tv)->t == DUK_TAG_FASTINT) -#define DUK_TVAL_IS_NUMBER(tv) ((tv)->t == DUK_TAG_NUMBER || (tv)->t == DUK_TAG_FASTINT) -#else -#define DUK_TVAL_IS_NUMBER(tv) ((tv)->t == DUK_TAG_NUMBER) -#define DUK_TVAL_IS_DOUBLE(tv) DUK_TVAL_IS_NUMBER((tv)) -#endif /* DUK_USE_FASTINT */ -#define DUK_TVAL_IS_POINTER(tv) ((tv)->t == DUK_TAG_POINTER) -#define DUK_TVAL_IS_LIGHTFUNC(tv) ((tv)->t == DUK_TAG_LIGHTFUNC) -#define DUK_TVAL_IS_STRING(tv) ((tv)->t == DUK_TAG_STRING) -#define DUK_TVAL_IS_OBJECT(tv) ((tv)->t == DUK_TAG_OBJECT) -#define DUK_TVAL_IS_BUFFER(tv) ((tv)->t == DUK_TAG_BUFFER) - -/* This is performance critical because it's needed for every DECREF. - * Take advantage of the fact that the first heap allocated tag is 8, - * so that bit 3 is set for all heap allocated tags (and never set for - * non-heap-allocated tags). - */ -#if 0 -#define DUK_TVAL_IS_HEAP_ALLOCATED(tv) ((tv)->t >= DUK_TAG_STRING) -#endif -#define DUK_TVAL_IS_HEAP_ALLOCATED(tv) ((tv)->t & 0x08) - -#if defined(DUK_USE_FASTINT) -#if 0 -DUK_INTERNAL_DECL duk_double_t duk_tval_get_number_unpacked(duk_tval *tv); -#endif -DUK_INTERNAL_DECL duk_double_t duk_tval_get_number_unpacked_fastint(duk_tval *tv); -#endif - -#endif /* DUK_USE_PACKED_TVAL */ - -/* - * Convenience (independent of representation) - */ - -#define DUK_TVAL_SET_BOOLEAN_TRUE(tv) DUK_TVAL_SET_BOOLEAN((tv), 1) -#define DUK_TVAL_SET_BOOLEAN_FALSE(tv) DUK_TVAL_SET_BOOLEAN((tv), 0) - -#define DUK_TVAL_STRING_IS_SYMBOL(tv) DUK_HSTRING_HAS_SYMBOL(DUK_TVAL_GET_STRING((tv))) - -/* Lightfunc flags packing and unpacking. */ -/* Sign extend: 0x0000##00 -> 0x##000000 -> sign extend to 0xssssss##. - * Avoid signed shifts due to portability limitations. - */ -#define DUK_LFUNC_FLAGS_GET_MAGIC(lf_flags) ((duk_int32_t) (duk_int8_t) (((duk_uint16_t) (lf_flags)) >> 8)) -#define DUK_LFUNC_FLAGS_GET_LENGTH(lf_flags) (((lf_flags) >> 4) & 0x0fU) -#define DUK_LFUNC_FLAGS_GET_NARGS(lf_flags) ((lf_flags) &0x0fU) -#define DUK_LFUNC_FLAGS_PACK(magic, length, nargs) ((((duk_small_uint_t) (magic)) & 0xffU) << 8) | ((length) << 4) | (nargs) - -#define DUK_LFUNC_NARGS_VARARGS 0x0f /* varargs marker */ -#define DUK_LFUNC_NARGS_MIN 0x00 -#define DUK_LFUNC_NARGS_MAX 0x0e /* max, excl. varargs marker */ -#define DUK_LFUNC_LENGTH_MIN 0x00 -#define DUK_LFUNC_LENGTH_MAX 0x0f -#define DUK_LFUNC_MAGIC_MIN (-0x80) -#define DUK_LFUNC_MAGIC_MAX 0x7f - -/* fastint constants etc */ -#if defined(DUK_USE_FASTINT) -#define DUK_FASTINT_MIN (DUK_I64_CONSTANT(-0x800000000000)) -#define DUK_FASTINT_MAX (DUK_I64_CONSTANT(0x7fffffffffff)) -#define DUK_FASTINT_BITS 48 - -DUK_INTERNAL_DECL void duk_tval_set_number_chkfast_fast(duk_tval *tv, duk_double_t x); -DUK_INTERNAL_DECL void duk_tval_set_number_chkfast_slow(duk_tval *tv, duk_double_t x); -#endif - -#if defined(DUK_USE_ASSERTIONS) -DUK_INTERNAL_DECL void duk_tval_assert_valid(duk_tval *tv); -#define DUK_TVAL_ASSERT_VALID(tv) \ - do { \ - duk_tval_assert_valid((tv)); \ - } while (0) -#else -#define DUK_TVAL_ASSERT_VALID(tv) \ - do { \ - } while (0) -#endif - -#endif /* DUK_TVAL_H_INCLUDED */ -/* #include duk_builtins.h */ -/* - * Automatically generated by genbuiltins.py, do not edit! - */ - -#if !defined(DUK_BUILTINS_H_INCLUDED) -#define DUK_BUILTINS_H_INCLUDED - -#if defined(DUK_USE_ROM_STRINGS) -#error ROM support not enabled, rerun configure.py with --rom-support -#else /* DUK_USE_ROM_STRINGS */ -#define DUK_STRIDX_UC_UNDEFINED 0 /* 'Undefined' */ -#define DUK_HEAP_STRING_UC_UNDEFINED(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_UNDEFINED) -#define DUK_HTHREAD_STRING_UC_UNDEFINED(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_UNDEFINED) -#define DUK_STRIDX_UC_NULL 1 /* 'Null' */ -#define DUK_HEAP_STRING_UC_NULL(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_NULL) -#define DUK_HTHREAD_STRING_UC_NULL(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_NULL) -#define DUK_STRIDX_UC_SYMBOL 2 /* 'Symbol' */ -#define DUK_HEAP_STRING_UC_SYMBOL(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_SYMBOL) -#define DUK_HTHREAD_STRING_UC_SYMBOL(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_SYMBOL) -#define DUK_STRIDX_UC_ARGUMENTS 3 /* 'Arguments' */ -#define DUK_HEAP_STRING_UC_ARGUMENTS(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_ARGUMENTS) -#define DUK_HTHREAD_STRING_UC_ARGUMENTS(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_ARGUMENTS) -#define DUK_STRIDX_UC_OBJECT 4 /* 'Object' */ -#define DUK_HEAP_STRING_UC_OBJECT(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_OBJECT) -#define DUK_HTHREAD_STRING_UC_OBJECT(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_OBJECT) -#define DUK_STRIDX_UC_FUNCTION 5 /* 'Function' */ -#define DUK_HEAP_STRING_UC_FUNCTION(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_FUNCTION) -#define DUK_HTHREAD_STRING_UC_FUNCTION(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_FUNCTION) -#define DUK_STRIDX_UC_ARRAY 6 /* 'Array' */ -#define DUK_HEAP_STRING_UC_ARRAY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_ARRAY) -#define DUK_HTHREAD_STRING_UC_ARRAY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_ARRAY) -#define DUK_STRIDX_UC_STRING 7 /* 'String' */ -#define DUK_HEAP_STRING_UC_STRING(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_STRING) -#define DUK_HTHREAD_STRING_UC_STRING(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_STRING) -#define DUK_STRIDX_UC_BOOLEAN 8 /* 'Boolean' */ -#define DUK_HEAP_STRING_UC_BOOLEAN(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_BOOLEAN) -#define DUK_HTHREAD_STRING_UC_BOOLEAN(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_BOOLEAN) -#define DUK_STRIDX_UC_NUMBER 9 /* 'Number' */ -#define DUK_HEAP_STRING_UC_NUMBER(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_NUMBER) -#define DUK_HTHREAD_STRING_UC_NUMBER(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_NUMBER) -#define DUK_STRIDX_UC_DATE 10 /* 'Date' */ -#define DUK_HEAP_STRING_UC_DATE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_DATE) -#define DUK_HTHREAD_STRING_UC_DATE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_DATE) -#define DUK_STRIDX_REG_EXP 11 /* 'RegExp' */ -#define DUK_HEAP_STRING_REG_EXP(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_REG_EXP) -#define DUK_HTHREAD_STRING_REG_EXP(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_REG_EXP) -#define DUK_STRIDX_UC_ERROR 12 /* 'Error' */ -#define DUK_HEAP_STRING_UC_ERROR(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_ERROR) -#define DUK_HTHREAD_STRING_UC_ERROR(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_ERROR) -#define DUK_STRIDX_MATH 13 /* 'Math' */ -#define DUK_HEAP_STRING_MATH(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_MATH) -#define DUK_HTHREAD_STRING_MATH(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_MATH) -#define DUK_STRIDX_JSON 14 /* 'JSON' */ -#define DUK_HEAP_STRING_JSON(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JSON) -#define DUK_HTHREAD_STRING_JSON(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JSON) -#define DUK_STRIDX_EMPTY_STRING 15 /* '' */ -#define DUK_HEAP_STRING_EMPTY_STRING(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_EMPTY_STRING) -#define DUK_HTHREAD_STRING_EMPTY_STRING(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_EMPTY_STRING) -#define DUK_STRIDX_ARRAY_BUFFER 16 /* 'ArrayBuffer' */ -#define DUK_HEAP_STRING_ARRAY_BUFFER(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_ARRAY_BUFFER) -#define DUK_HTHREAD_STRING_ARRAY_BUFFER(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_ARRAY_BUFFER) -#define DUK_STRIDX_DATA_VIEW 17 /* 'DataView' */ -#define DUK_HEAP_STRING_DATA_VIEW(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DATA_VIEW) -#define DUK_HTHREAD_STRING_DATA_VIEW(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DATA_VIEW) -#define DUK_STRIDX_INT8_ARRAY 18 /* 'Int8Array' */ -#define DUK_HEAP_STRING_INT8_ARRAY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT8_ARRAY) -#define DUK_HTHREAD_STRING_INT8_ARRAY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT8_ARRAY) -#define DUK_STRIDX_UINT8_ARRAY 19 /* 'Uint8Array' */ -#define DUK_HEAP_STRING_UINT8_ARRAY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UINT8_ARRAY) -#define DUK_HTHREAD_STRING_UINT8_ARRAY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UINT8_ARRAY) -#define DUK_STRIDX_UINT8_CLAMPED_ARRAY 20 /* 'Uint8ClampedArray' */ -#define DUK_HEAP_STRING_UINT8_CLAMPED_ARRAY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UINT8_CLAMPED_ARRAY) -#define DUK_HTHREAD_STRING_UINT8_CLAMPED_ARRAY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UINT8_CLAMPED_ARRAY) -#define DUK_STRIDX_INT16_ARRAY 21 /* 'Int16Array' */ -#define DUK_HEAP_STRING_INT16_ARRAY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT16_ARRAY) -#define DUK_HTHREAD_STRING_INT16_ARRAY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT16_ARRAY) -#define DUK_STRIDX_UINT16_ARRAY 22 /* 'Uint16Array' */ -#define DUK_HEAP_STRING_UINT16_ARRAY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UINT16_ARRAY) -#define DUK_HTHREAD_STRING_UINT16_ARRAY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UINT16_ARRAY) -#define DUK_STRIDX_INT32_ARRAY 23 /* 'Int32Array' */ -#define DUK_HEAP_STRING_INT32_ARRAY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT32_ARRAY) -#define DUK_HTHREAD_STRING_INT32_ARRAY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT32_ARRAY) -#define DUK_STRIDX_UINT32_ARRAY 24 /* 'Uint32Array' */ -#define DUK_HEAP_STRING_UINT32_ARRAY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UINT32_ARRAY) -#define DUK_HTHREAD_STRING_UINT32_ARRAY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UINT32_ARRAY) -#define DUK_STRIDX_FLOAT32_ARRAY 25 /* 'Float32Array' */ -#define DUK_HEAP_STRING_FLOAT32_ARRAY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_FLOAT32_ARRAY) -#define DUK_HTHREAD_STRING_FLOAT32_ARRAY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_FLOAT32_ARRAY) -#define DUK_STRIDX_FLOAT64_ARRAY 26 /* 'Float64Array' */ -#define DUK_HEAP_STRING_FLOAT64_ARRAY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_FLOAT64_ARRAY) -#define DUK_HTHREAD_STRING_FLOAT64_ARRAY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_FLOAT64_ARRAY) -#define DUK_STRIDX_GLOBAL 27 /* 'global' */ -#define DUK_HEAP_STRING_GLOBAL(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_GLOBAL) -#define DUK_HTHREAD_STRING_GLOBAL(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_GLOBAL) -#define DUK_STRIDX_OBJ_ENV 28 /* 'ObjEnv' */ -#define DUK_HEAP_STRING_OBJ_ENV(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_OBJ_ENV) -#define DUK_HTHREAD_STRING_OBJ_ENV(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_OBJ_ENV) -#define DUK_STRIDX_DEC_ENV 29 /* 'DecEnv' */ -#define DUK_HEAP_STRING_DEC_ENV(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DEC_ENV) -#define DUK_HTHREAD_STRING_DEC_ENV(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DEC_ENV) -#define DUK_STRIDX_UC_BUFFER 30 /* 'Buffer' */ -#define DUK_HEAP_STRING_UC_BUFFER(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_BUFFER) -#define DUK_HTHREAD_STRING_UC_BUFFER(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_BUFFER) -#define DUK_STRIDX_UC_POINTER 31 /* 'Pointer' */ -#define DUK_HEAP_STRING_UC_POINTER(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_POINTER) -#define DUK_HTHREAD_STRING_UC_POINTER(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_POINTER) -#define DUK_STRIDX_UC_THREAD 32 /* 'Thread' */ -#define DUK_HEAP_STRING_UC_THREAD(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_THREAD) -#define DUK_HTHREAD_STRING_UC_THREAD(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_THREAD) -#define DUK_STRIDX_EVAL 33 /* 'eval' */ -#define DUK_HEAP_STRING_EVAL(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_EVAL) -#define DUK_HTHREAD_STRING_EVAL(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_EVAL) -#define DUK_STRIDX_VALUE 34 /* 'value' */ -#define DUK_HEAP_STRING_VALUE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_VALUE) -#define DUK_HTHREAD_STRING_VALUE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_VALUE) -#define DUK_STRIDX_WRITABLE 35 /* 'writable' */ -#define DUK_HEAP_STRING_WRITABLE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_WRITABLE) -#define DUK_HTHREAD_STRING_WRITABLE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_WRITABLE) -#define DUK_STRIDX_CONFIGURABLE 36 /* 'configurable' */ -#define DUK_HEAP_STRING_CONFIGURABLE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CONFIGURABLE) -#define DUK_HTHREAD_STRING_CONFIGURABLE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CONFIGURABLE) -#define DUK_STRIDX_ENUMERABLE 37 /* 'enumerable' */ -#define DUK_HEAP_STRING_ENUMERABLE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_ENUMERABLE) -#define DUK_HTHREAD_STRING_ENUMERABLE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_ENUMERABLE) -#define DUK_STRIDX_JOIN 38 /* 'join' */ -#define DUK_HEAP_STRING_JOIN(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JOIN) -#define DUK_HTHREAD_STRING_JOIN(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JOIN) -#define DUK_STRIDX_TO_LOCALE_STRING 39 /* 'toLocaleString' */ -#define DUK_HEAP_STRING_TO_LOCALE_STRING(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TO_LOCALE_STRING) -#define DUK_HTHREAD_STRING_TO_LOCALE_STRING(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TO_LOCALE_STRING) -#define DUK_STRIDX_VALUE_OF 40 /* 'valueOf' */ -#define DUK_HEAP_STRING_VALUE_OF(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_VALUE_OF) -#define DUK_HTHREAD_STRING_VALUE_OF(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_VALUE_OF) -#define DUK_STRIDX_TO_UTC_STRING 41 /* 'toUTCString' */ -#define DUK_HEAP_STRING_TO_UTC_STRING(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TO_UTC_STRING) -#define DUK_HTHREAD_STRING_TO_UTC_STRING(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TO_UTC_STRING) -#define DUK_STRIDX_TO_ISO_STRING 42 /* 'toISOString' */ -#define DUK_HEAP_STRING_TO_ISO_STRING(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TO_ISO_STRING) -#define DUK_HTHREAD_STRING_TO_ISO_STRING(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TO_ISO_STRING) -#define DUK_STRIDX_TO_GMT_STRING 43 /* 'toGMTString' */ -#define DUK_HEAP_STRING_TO_GMT_STRING(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TO_GMT_STRING) -#define DUK_HTHREAD_STRING_TO_GMT_STRING(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TO_GMT_STRING) -#define DUK_STRIDX_SOURCE 44 /* 'source' */ -#define DUK_HEAP_STRING_SOURCE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_SOURCE) -#define DUK_HTHREAD_STRING_SOURCE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_SOURCE) -#define DUK_STRIDX_IGNORE_CASE 45 /* 'ignoreCase' */ -#define DUK_HEAP_STRING_IGNORE_CASE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_IGNORE_CASE) -#define DUK_HTHREAD_STRING_IGNORE_CASE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_IGNORE_CASE) -#define DUK_STRIDX_MULTILINE 46 /* 'multiline' */ -#define DUK_HEAP_STRING_MULTILINE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_MULTILINE) -#define DUK_HTHREAD_STRING_MULTILINE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_MULTILINE) -#define DUK_STRIDX_LAST_INDEX 47 /* 'lastIndex' */ -#define DUK_HEAP_STRING_LAST_INDEX(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LAST_INDEX) -#define DUK_HTHREAD_STRING_LAST_INDEX(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LAST_INDEX) -#define DUK_STRIDX_FLAGS 48 /* 'flags' */ -#define DUK_HEAP_STRING_FLAGS(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_FLAGS) -#define DUK_HTHREAD_STRING_FLAGS(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_FLAGS) -#define DUK_STRIDX_INDEX 49 /* 'index' */ -#define DUK_HEAP_STRING_INDEX(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INDEX) -#define DUK_HTHREAD_STRING_INDEX(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INDEX) -#define DUK_STRIDX_PROTOTYPE 50 /* 'prototype' */ -#define DUK_HEAP_STRING_PROTOTYPE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_PROTOTYPE) -#define DUK_HTHREAD_STRING_PROTOTYPE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_PROTOTYPE) -#define DUK_STRIDX_CONSTRUCTOR 51 /* 'constructor' */ -#define DUK_HEAP_STRING_CONSTRUCTOR(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CONSTRUCTOR) -#define DUK_HTHREAD_STRING_CONSTRUCTOR(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CONSTRUCTOR) -#define DUK_STRIDX_MESSAGE 52 /* 'message' */ -#define DUK_HEAP_STRING_MESSAGE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_MESSAGE) -#define DUK_HTHREAD_STRING_MESSAGE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_MESSAGE) -#define DUK_STRIDX_LC_BOOLEAN 53 /* 'boolean' */ -#define DUK_HEAP_STRING_LC_BOOLEAN(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_BOOLEAN) -#define DUK_HTHREAD_STRING_LC_BOOLEAN(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_BOOLEAN) -#define DUK_STRIDX_LC_NUMBER 54 /* 'number' */ -#define DUK_HEAP_STRING_LC_NUMBER(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_NUMBER) -#define DUK_HTHREAD_STRING_LC_NUMBER(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_NUMBER) -#define DUK_STRIDX_LC_STRING 55 /* 'string' */ -#define DUK_HEAP_STRING_LC_STRING(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_STRING) -#define DUK_HTHREAD_STRING_LC_STRING(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_STRING) -#define DUK_STRIDX_LC_SYMBOL 56 /* 'symbol' */ -#define DUK_HEAP_STRING_LC_SYMBOL(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_SYMBOL) -#define DUK_HTHREAD_STRING_LC_SYMBOL(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_SYMBOL) -#define DUK_STRIDX_LC_OBJECT 57 /* 'object' */ -#define DUK_HEAP_STRING_LC_OBJECT(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_OBJECT) -#define DUK_HTHREAD_STRING_LC_OBJECT(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_OBJECT) -#define DUK_STRIDX_LC_UNDEFINED 58 /* 'undefined' */ -#define DUK_HEAP_STRING_LC_UNDEFINED(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_UNDEFINED) -#define DUK_HTHREAD_STRING_LC_UNDEFINED(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_UNDEFINED) -#define DUK_STRIDX_NAN 59 /* 'NaN' */ -#define DUK_HEAP_STRING_NAN(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_NAN) -#define DUK_HTHREAD_STRING_NAN(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_NAN) -#define DUK_STRIDX_INFINITY 60 /* 'Infinity' */ -#define DUK_HEAP_STRING_INFINITY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INFINITY) -#define DUK_HTHREAD_STRING_INFINITY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INFINITY) -#define DUK_STRIDX_MINUS_INFINITY 61 /* '-Infinity' */ -#define DUK_HEAP_STRING_MINUS_INFINITY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_MINUS_INFINITY) -#define DUK_HTHREAD_STRING_MINUS_INFINITY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_MINUS_INFINITY) -#define DUK_STRIDX_MINUS_ZERO 62 /* '-0' */ -#define DUK_HEAP_STRING_MINUS_ZERO(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_MINUS_ZERO) -#define DUK_HTHREAD_STRING_MINUS_ZERO(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_MINUS_ZERO) -#define DUK_STRIDX_COMMA 63 /* ',' */ -#define DUK_HEAP_STRING_COMMA(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_COMMA) -#define DUK_HTHREAD_STRING_COMMA(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_COMMA) -#define DUK_STRIDX_NEWLINE_4SPACE 64 /* '\n ' */ -#define DUK_HEAP_STRING_NEWLINE_4SPACE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_NEWLINE_4SPACE) -#define DUK_HTHREAD_STRING_NEWLINE_4SPACE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_NEWLINE_4SPACE) -#define DUK_STRIDX_BRACKETED_ELLIPSIS 65 /* '[...]' */ -#define DUK_HEAP_STRING_BRACKETED_ELLIPSIS(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_BRACKETED_ELLIPSIS) -#define DUK_HTHREAD_STRING_BRACKETED_ELLIPSIS(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_BRACKETED_ELLIPSIS) -#define DUK_STRIDX_INVALID_DATE 66 /* 'Invalid Date' */ -#define DUK_HEAP_STRING_INVALID_DATE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INVALID_DATE) -#define DUK_HTHREAD_STRING_INVALID_DATE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INVALID_DATE) -#define DUK_STRIDX_LC_ARGUMENTS 67 /* 'arguments' */ -#define DUK_HEAP_STRING_LC_ARGUMENTS(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_ARGUMENTS) -#define DUK_HTHREAD_STRING_LC_ARGUMENTS(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_ARGUMENTS) -#define DUK_STRIDX_CALLEE 68 /* 'callee' */ -#define DUK_HEAP_STRING_CALLEE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CALLEE) -#define DUK_HTHREAD_STRING_CALLEE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CALLEE) -#define DUK_STRIDX_CALLER 69 /* 'caller' */ -#define DUK_HEAP_STRING_CALLER(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CALLER) -#define DUK_HTHREAD_STRING_CALLER(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CALLER) -#define DUK_STRIDX_APPLY 70 /* 'apply' */ -#define DUK_HEAP_STRING_APPLY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_APPLY) -#define DUK_HTHREAD_STRING_APPLY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_APPLY) -#define DUK_STRIDX_CONSTRUCT 71 /* 'construct' */ -#define DUK_HEAP_STRING_CONSTRUCT(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CONSTRUCT) -#define DUK_HTHREAD_STRING_CONSTRUCT(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CONSTRUCT) -#define DUK_STRIDX_DELETE_PROPERTY 72 /* 'deleteProperty' */ -#define DUK_HEAP_STRING_DELETE_PROPERTY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DELETE_PROPERTY) -#define DUK_HTHREAD_STRING_DELETE_PROPERTY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DELETE_PROPERTY) -#define DUK_STRIDX_GET 73 /* 'get' */ -#define DUK_HEAP_STRING_GET(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_GET) -#define DUK_HTHREAD_STRING_GET(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_GET) -#define DUK_STRIDX_HAS 74 /* 'has' */ -#define DUK_HEAP_STRING_HAS(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_HAS) -#define DUK_HTHREAD_STRING_HAS(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_HAS) -#define DUK_STRIDX_OWN_KEYS 75 /* 'ownKeys' */ -#define DUK_HEAP_STRING_OWN_KEYS(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_OWN_KEYS) -#define DUK_HTHREAD_STRING_OWN_KEYS(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_OWN_KEYS) -#define DUK_STRIDX_WELLKNOWN_SYMBOL_TO_PRIMITIVE 76 /* '\x81Symbol.toPrimitive\xff' */ -#define DUK_HEAP_STRING_WELLKNOWN_SYMBOL_TO_PRIMITIVE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_WELLKNOWN_SYMBOL_TO_PRIMITIVE) -#define DUK_HTHREAD_STRING_WELLKNOWN_SYMBOL_TO_PRIMITIVE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_WELLKNOWN_SYMBOL_TO_PRIMITIVE) -#define DUK_STRIDX_WELLKNOWN_SYMBOL_HAS_INSTANCE 77 /* '\x81Symbol.hasInstance\xff' */ -#define DUK_HEAP_STRING_WELLKNOWN_SYMBOL_HAS_INSTANCE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_WELLKNOWN_SYMBOL_HAS_INSTANCE) -#define DUK_HTHREAD_STRING_WELLKNOWN_SYMBOL_HAS_INSTANCE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_WELLKNOWN_SYMBOL_HAS_INSTANCE) -#define DUK_STRIDX_WELLKNOWN_SYMBOL_TO_STRING_TAG 78 /* '\x81Symbol.toStringTag\xff' */ -#define DUK_HEAP_STRING_WELLKNOWN_SYMBOL_TO_STRING_TAG(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_WELLKNOWN_SYMBOL_TO_STRING_TAG) -#define DUK_HTHREAD_STRING_WELLKNOWN_SYMBOL_TO_STRING_TAG(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_WELLKNOWN_SYMBOL_TO_STRING_TAG) -#define DUK_STRIDX_WELLKNOWN_SYMBOL_IS_CONCAT_SPREADABLE 79 /* '\x81Symbol.isConcatSpreadable\xff' */ -#define DUK_HEAP_STRING_WELLKNOWN_SYMBOL_IS_CONCAT_SPREADABLE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_WELLKNOWN_SYMBOL_IS_CONCAT_SPREADABLE) -#define DUK_HTHREAD_STRING_WELLKNOWN_SYMBOL_IS_CONCAT_SPREADABLE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_WELLKNOWN_SYMBOL_IS_CONCAT_SPREADABLE) -#define DUK_STRIDX_SET_PROTOTYPE_OF 80 /* 'setPrototypeOf' */ -#define DUK_HEAP_STRING_SET_PROTOTYPE_OF(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_SET_PROTOTYPE_OF) -#define DUK_HTHREAD_STRING_SET_PROTOTYPE_OF(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_SET_PROTOTYPE_OF) -#define DUK_STRIDX___PROTO__ 81 /* '__proto__' */ -#define DUK_HEAP_STRING___PROTO__(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX___PROTO__) -#define DUK_HTHREAD_STRING___PROTO__(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX___PROTO__) -#define DUK_STRIDX_TO_STRING 82 /* 'toString' */ -#define DUK_HEAP_STRING_TO_STRING(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TO_STRING) -#define DUK_HTHREAD_STRING_TO_STRING(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TO_STRING) -#define DUK_STRIDX_TO_JSON 83 /* 'toJSON' */ -#define DUK_HEAP_STRING_TO_JSON(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TO_JSON) -#define DUK_HTHREAD_STRING_TO_JSON(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TO_JSON) -#define DUK_STRIDX_TYPE 84 /* 'type' */ -#define DUK_HEAP_STRING_TYPE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TYPE) -#define DUK_HTHREAD_STRING_TYPE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TYPE) -#define DUK_STRIDX_DATA 85 /* 'data' */ -#define DUK_HEAP_STRING_DATA(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DATA) -#define DUK_HTHREAD_STRING_DATA(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DATA) -#define DUK_STRIDX_LC_BUFFER 86 /* 'buffer' */ -#define DUK_HEAP_STRING_LC_BUFFER(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_BUFFER) -#define DUK_HTHREAD_STRING_LC_BUFFER(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_BUFFER) -#define DUK_STRIDX_LENGTH 87 /* 'length' */ -#define DUK_HEAP_STRING_LENGTH(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LENGTH) -#define DUK_HTHREAD_STRING_LENGTH(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LENGTH) -#define DUK_STRIDX_SET 88 /* 'set' */ -#define DUK_HEAP_STRING_SET(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_SET) -#define DUK_HTHREAD_STRING_SET(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_SET) -#define DUK_STRIDX_STACK 89 /* 'stack' */ -#define DUK_HEAP_STRING_STACK(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_STACK) -#define DUK_HTHREAD_STRING_STACK(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_STACK) -#define DUK_STRIDX_PC 90 /* 'pc' */ -#define DUK_HEAP_STRING_PC(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_PC) -#define DUK_HTHREAD_STRING_PC(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_PC) -#define DUK_STRIDX_LINE_NUMBER 91 /* 'lineNumber' */ -#define DUK_HEAP_STRING_LINE_NUMBER(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LINE_NUMBER) -#define DUK_HTHREAD_STRING_LINE_NUMBER(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LINE_NUMBER) -#define DUK_STRIDX_INT_TRACEDATA 92 /* '\x82Tracedata' */ -#define DUK_HEAP_STRING_INT_TRACEDATA(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_TRACEDATA) -#define DUK_HTHREAD_STRING_INT_TRACEDATA(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_TRACEDATA) -#define DUK_STRIDX_NAME 93 /* 'name' */ -#define DUK_HEAP_STRING_NAME(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_NAME) -#define DUK_HTHREAD_STRING_NAME(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_NAME) -#define DUK_STRIDX_FILE_NAME 94 /* 'fileName' */ -#define DUK_HEAP_STRING_FILE_NAME(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_FILE_NAME) -#define DUK_HTHREAD_STRING_FILE_NAME(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_FILE_NAME) -#define DUK_STRIDX_LC_POINTER 95 /* 'pointer' */ -#define DUK_HEAP_STRING_LC_POINTER(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_POINTER) -#define DUK_HTHREAD_STRING_LC_POINTER(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_POINTER) -#define DUK_STRIDX_INT_TARGET 96 /* '\x82Target' */ -#define DUK_HEAP_STRING_INT_TARGET(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_TARGET) -#define DUK_HTHREAD_STRING_INT_TARGET(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_TARGET) -#define DUK_STRIDX_INT_NEXT 97 /* '\x82Next' */ -#define DUK_HEAP_STRING_INT_NEXT(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_NEXT) -#define DUK_HTHREAD_STRING_INT_NEXT(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_NEXT) -#define DUK_STRIDX_INT_BYTECODE 98 /* '\x82Bytecode' */ -#define DUK_HEAP_STRING_INT_BYTECODE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_BYTECODE) -#define DUK_HTHREAD_STRING_INT_BYTECODE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_BYTECODE) -#define DUK_STRIDX_INT_FORMALS 99 /* '\x82Formals' */ -#define DUK_HEAP_STRING_INT_FORMALS(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_FORMALS) -#define DUK_HTHREAD_STRING_INT_FORMALS(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_FORMALS) -#define DUK_STRIDX_INT_VARMAP 100 /* '\x82Varmap' */ -#define DUK_HEAP_STRING_INT_VARMAP(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_VARMAP) -#define DUK_HTHREAD_STRING_INT_VARMAP(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_VARMAP) -#define DUK_STRIDX_INT_SOURCE 101 /* '\x82Source' */ -#define DUK_HEAP_STRING_INT_SOURCE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_SOURCE) -#define DUK_HTHREAD_STRING_INT_SOURCE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_SOURCE) -#define DUK_STRIDX_INT_PC2LINE 102 /* '\x82Pc2line' */ -#define DUK_HEAP_STRING_INT_PC2LINE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_PC2LINE) -#define DUK_HTHREAD_STRING_INT_PC2LINE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_PC2LINE) -#define DUK_STRIDX_INT_MAP 103 /* '\x82Map' */ -#define DUK_HEAP_STRING_INT_MAP(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_MAP) -#define DUK_HTHREAD_STRING_INT_MAP(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_MAP) -#define DUK_STRIDX_INT_VARENV 104 /* '\x82Varenv' */ -#define DUK_HEAP_STRING_INT_VARENV(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_VARENV) -#define DUK_HTHREAD_STRING_INT_VARENV(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_VARENV) -#define DUK_STRIDX_INT_FINALIZER 105 /* '\x82Finalizer' */ -#define DUK_HEAP_STRING_INT_FINALIZER(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_FINALIZER) -#define DUK_HTHREAD_STRING_INT_FINALIZER(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_FINALIZER) -#define DUK_STRIDX_INT_VALUE 106 /* '\x82Value' */ -#define DUK_HEAP_STRING_INT_VALUE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_VALUE) -#define DUK_HTHREAD_STRING_INT_VALUE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_VALUE) -#define DUK_STRIDX_COMPILE 107 /* 'compile' */ -#define DUK_HEAP_STRING_COMPILE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_COMPILE) -#define DUK_HTHREAD_STRING_COMPILE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_COMPILE) -#define DUK_STRIDX_INPUT 108 /* 'input' */ -#define DUK_HEAP_STRING_INPUT(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INPUT) -#define DUK_HTHREAD_STRING_INPUT(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INPUT) -#define DUK_STRIDX_ERR_CREATE 109 /* 'errCreate' */ -#define DUK_HEAP_STRING_ERR_CREATE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_ERR_CREATE) -#define DUK_HTHREAD_STRING_ERR_CREATE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_ERR_CREATE) -#define DUK_STRIDX_ERR_THROW 110 /* 'errThrow' */ -#define DUK_HEAP_STRING_ERR_THROW(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_ERR_THROW) -#define DUK_HTHREAD_STRING_ERR_THROW(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_ERR_THROW) -#define DUK_STRIDX_ENV 111 /* 'env' */ -#define DUK_HEAP_STRING_ENV(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_ENV) -#define DUK_HTHREAD_STRING_ENV(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_ENV) -#define DUK_STRIDX_HEX 112 /* 'hex' */ -#define DUK_HEAP_STRING_HEX(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_HEX) -#define DUK_HTHREAD_STRING_HEX(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_HEX) -#define DUK_STRIDX_BASE64 113 /* 'base64' */ -#define DUK_HEAP_STRING_BASE64(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_BASE64) -#define DUK_HTHREAD_STRING_BASE64(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_BASE64) -#define DUK_STRIDX_JX 114 /* 'jx' */ -#define DUK_HEAP_STRING_JX(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JX) -#define DUK_HTHREAD_STRING_JX(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JX) -#define DUK_STRIDX_JC 115 /* 'jc' */ -#define DUK_HEAP_STRING_JC(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JC) -#define DUK_HTHREAD_STRING_JC(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JC) -#define DUK_STRIDX_JSON_EXT_UNDEFINED 116 /* '{"_undef":true}' */ -#define DUK_HEAP_STRING_JSON_EXT_UNDEFINED(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JSON_EXT_UNDEFINED) -#define DUK_HTHREAD_STRING_JSON_EXT_UNDEFINED(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JSON_EXT_UNDEFINED) -#define DUK_STRIDX_JSON_EXT_NAN 117 /* '{"_nan":true}' */ -#define DUK_HEAP_STRING_JSON_EXT_NAN(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JSON_EXT_NAN) -#define DUK_HTHREAD_STRING_JSON_EXT_NAN(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JSON_EXT_NAN) -#define DUK_STRIDX_JSON_EXT_POSINF 118 /* '{"_inf":true}' */ -#define DUK_HEAP_STRING_JSON_EXT_POSINF(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JSON_EXT_POSINF) -#define DUK_HTHREAD_STRING_JSON_EXT_POSINF(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JSON_EXT_POSINF) -#define DUK_STRIDX_JSON_EXT_NEGINF 119 /* '{"_ninf":true}' */ -#define DUK_HEAP_STRING_JSON_EXT_NEGINF(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JSON_EXT_NEGINF) -#define DUK_HTHREAD_STRING_JSON_EXT_NEGINF(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JSON_EXT_NEGINF) -#define DUK_STRIDX_JSON_EXT_FUNCTION1 120 /* '{"_func":true}' */ -#define DUK_HEAP_STRING_JSON_EXT_FUNCTION1(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JSON_EXT_FUNCTION1) -#define DUK_HTHREAD_STRING_JSON_EXT_FUNCTION1(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JSON_EXT_FUNCTION1) -#define DUK_STRIDX_JSON_EXT_FUNCTION2 121 /* '{_func:true}' */ -#define DUK_HEAP_STRING_JSON_EXT_FUNCTION2(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JSON_EXT_FUNCTION2) -#define DUK_HTHREAD_STRING_JSON_EXT_FUNCTION2(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JSON_EXT_FUNCTION2) -#define DUK_STRIDX_BREAK 122 /* 'break' */ -#define DUK_HEAP_STRING_BREAK(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_BREAK) -#define DUK_HTHREAD_STRING_BREAK(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_BREAK) -#define DUK_STRIDX_CASE 123 /* 'case' */ -#define DUK_HEAP_STRING_CASE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CASE) -#define DUK_HTHREAD_STRING_CASE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CASE) -#define DUK_STRIDX_CATCH 124 /* 'catch' */ -#define DUK_HEAP_STRING_CATCH(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CATCH) -#define DUK_HTHREAD_STRING_CATCH(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CATCH) -#define DUK_STRIDX_CONTINUE 125 /* 'continue' */ -#define DUK_HEAP_STRING_CONTINUE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CONTINUE) -#define DUK_HTHREAD_STRING_CONTINUE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CONTINUE) -#define DUK_STRIDX_DEBUGGER 126 /* 'debugger' */ -#define DUK_HEAP_STRING_DEBUGGER(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DEBUGGER) -#define DUK_HTHREAD_STRING_DEBUGGER(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DEBUGGER) -#define DUK_STRIDX_DEFAULT 127 /* 'default' */ -#define DUK_HEAP_STRING_DEFAULT(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DEFAULT) -#define DUK_HTHREAD_STRING_DEFAULT(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DEFAULT) -#define DUK_STRIDX_DELETE 128 /* 'delete' */ -#define DUK_HEAP_STRING_DELETE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DELETE) -#define DUK_HTHREAD_STRING_DELETE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DELETE) -#define DUK_STRIDX_DO 129 /* 'do' */ -#define DUK_HEAP_STRING_DO(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DO) -#define DUK_HTHREAD_STRING_DO(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DO) -#define DUK_STRIDX_ELSE 130 /* 'else' */ -#define DUK_HEAP_STRING_ELSE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_ELSE) -#define DUK_HTHREAD_STRING_ELSE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_ELSE) -#define DUK_STRIDX_FINALLY 131 /* 'finally' */ -#define DUK_HEAP_STRING_FINALLY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_FINALLY) -#define DUK_HTHREAD_STRING_FINALLY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_FINALLY) -#define DUK_STRIDX_FOR 132 /* 'for' */ -#define DUK_HEAP_STRING_FOR(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_FOR) -#define DUK_HTHREAD_STRING_FOR(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_FOR) -#define DUK_STRIDX_LC_FUNCTION 133 /* 'function' */ -#define DUK_HEAP_STRING_LC_FUNCTION(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_FUNCTION) -#define DUK_HTHREAD_STRING_LC_FUNCTION(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_FUNCTION) -#define DUK_STRIDX_IF 134 /* 'if' */ -#define DUK_HEAP_STRING_IF(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_IF) -#define DUK_HTHREAD_STRING_IF(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_IF) -#define DUK_STRIDX_IN 135 /* 'in' */ -#define DUK_HEAP_STRING_IN(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_IN) -#define DUK_HTHREAD_STRING_IN(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_IN) -#define DUK_STRIDX_INSTANCEOF 136 /* 'instanceof' */ -#define DUK_HEAP_STRING_INSTANCEOF(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INSTANCEOF) -#define DUK_HTHREAD_STRING_INSTANCEOF(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INSTANCEOF) -#define DUK_STRIDX_NEW 137 /* 'new' */ -#define DUK_HEAP_STRING_NEW(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_NEW) -#define DUK_HTHREAD_STRING_NEW(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_NEW) -#define DUK_STRIDX_RETURN 138 /* 'return' */ -#define DUK_HEAP_STRING_RETURN(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_RETURN) -#define DUK_HTHREAD_STRING_RETURN(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_RETURN) -#define DUK_STRIDX_SWITCH 139 /* 'switch' */ -#define DUK_HEAP_STRING_SWITCH(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_SWITCH) -#define DUK_HTHREAD_STRING_SWITCH(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_SWITCH) -#define DUK_STRIDX_THIS 140 /* 'this' */ -#define DUK_HEAP_STRING_THIS(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_THIS) -#define DUK_HTHREAD_STRING_THIS(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_THIS) -#define DUK_STRIDX_THROW 141 /* 'throw' */ -#define DUK_HEAP_STRING_THROW(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_THROW) -#define DUK_HTHREAD_STRING_THROW(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_THROW) -#define DUK_STRIDX_TRY 142 /* 'try' */ -#define DUK_HEAP_STRING_TRY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TRY) -#define DUK_HTHREAD_STRING_TRY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TRY) -#define DUK_STRIDX_TYPEOF 143 /* 'typeof' */ -#define DUK_HEAP_STRING_TYPEOF(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TYPEOF) -#define DUK_HTHREAD_STRING_TYPEOF(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TYPEOF) -#define DUK_STRIDX_VAR 144 /* 'var' */ -#define DUK_HEAP_STRING_VAR(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_VAR) -#define DUK_HTHREAD_STRING_VAR(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_VAR) -#define DUK_STRIDX_CONST 145 /* 'const' */ -#define DUK_HEAP_STRING_CONST(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CONST) -#define DUK_HTHREAD_STRING_CONST(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CONST) -#define DUK_STRIDX_VOID 146 /* 'void' */ -#define DUK_HEAP_STRING_VOID(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_VOID) -#define DUK_HTHREAD_STRING_VOID(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_VOID) -#define DUK_STRIDX_WHILE 147 /* 'while' */ -#define DUK_HEAP_STRING_WHILE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_WHILE) -#define DUK_HTHREAD_STRING_WHILE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_WHILE) -#define DUK_STRIDX_WITH 148 /* 'with' */ -#define DUK_HEAP_STRING_WITH(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_WITH) -#define DUK_HTHREAD_STRING_WITH(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_WITH) -#define DUK_STRIDX_CLASS 149 /* 'class' */ -#define DUK_HEAP_STRING_CLASS(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CLASS) -#define DUK_HTHREAD_STRING_CLASS(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CLASS) -#define DUK_STRIDX_ENUM 150 /* 'enum' */ -#define DUK_HEAP_STRING_ENUM(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_ENUM) -#define DUK_HTHREAD_STRING_ENUM(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_ENUM) -#define DUK_STRIDX_EXPORT 151 /* 'export' */ -#define DUK_HEAP_STRING_EXPORT(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_EXPORT) -#define DUK_HTHREAD_STRING_EXPORT(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_EXPORT) -#define DUK_STRIDX_EXTENDS 152 /* 'extends' */ -#define DUK_HEAP_STRING_EXTENDS(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_EXTENDS) -#define DUK_HTHREAD_STRING_EXTENDS(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_EXTENDS) -#define DUK_STRIDX_IMPORT 153 /* 'import' */ -#define DUK_HEAP_STRING_IMPORT(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_IMPORT) -#define DUK_HTHREAD_STRING_IMPORT(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_IMPORT) -#define DUK_STRIDX_SUPER 154 /* 'super' */ -#define DUK_HEAP_STRING_SUPER(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_SUPER) -#define DUK_HTHREAD_STRING_SUPER(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_SUPER) -#define DUK_STRIDX_LC_NULL 155 /* 'null' */ -#define DUK_HEAP_STRING_LC_NULL(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_NULL) -#define DUK_HTHREAD_STRING_LC_NULL(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_NULL) -#define DUK_STRIDX_TRUE 156 /* 'true' */ -#define DUK_HEAP_STRING_TRUE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TRUE) -#define DUK_HTHREAD_STRING_TRUE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TRUE) -#define DUK_STRIDX_FALSE 157 /* 'false' */ -#define DUK_HEAP_STRING_FALSE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_FALSE) -#define DUK_HTHREAD_STRING_FALSE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_FALSE) -#define DUK_STRIDX_IMPLEMENTS 158 /* 'implements' */ -#define DUK_HEAP_STRING_IMPLEMENTS(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_IMPLEMENTS) -#define DUK_HTHREAD_STRING_IMPLEMENTS(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_IMPLEMENTS) -#define DUK_STRIDX_INTERFACE 159 /* 'interface' */ -#define DUK_HEAP_STRING_INTERFACE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INTERFACE) -#define DUK_HTHREAD_STRING_INTERFACE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INTERFACE) -#define DUK_STRIDX_LET 160 /* 'let' */ -#define DUK_HEAP_STRING_LET(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LET) -#define DUK_HTHREAD_STRING_LET(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LET) -#define DUK_STRIDX_PACKAGE 161 /* 'package' */ -#define DUK_HEAP_STRING_PACKAGE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_PACKAGE) -#define DUK_HTHREAD_STRING_PACKAGE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_PACKAGE) -#define DUK_STRIDX_PRIVATE 162 /* 'private' */ -#define DUK_HEAP_STRING_PRIVATE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_PRIVATE) -#define DUK_HTHREAD_STRING_PRIVATE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_PRIVATE) -#define DUK_STRIDX_PROTECTED 163 /* 'protected' */ -#define DUK_HEAP_STRING_PROTECTED(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_PROTECTED) -#define DUK_HTHREAD_STRING_PROTECTED(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_PROTECTED) -#define DUK_STRIDX_PUBLIC 164 /* 'public' */ -#define DUK_HEAP_STRING_PUBLIC(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_PUBLIC) -#define DUK_HTHREAD_STRING_PUBLIC(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_PUBLIC) -#define DUK_STRIDX_STATIC 165 /* 'static' */ -#define DUK_HEAP_STRING_STATIC(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_STATIC) -#define DUK_HTHREAD_STRING_STATIC(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_STATIC) -#define DUK_STRIDX_YIELD 166 /* 'yield' */ -#define DUK_HEAP_STRING_YIELD(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_YIELD) -#define DUK_HTHREAD_STRING_YIELD(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_YIELD) - -#define DUK_HEAP_NUM_STRINGS 167 -#define DUK_STRIDX_START_RESERVED 122 -#define DUK_STRIDX_START_STRICT_RESERVED 158 -#define DUK_STRIDX_END_RESERVED 167 /* exclusive endpoint */ - -/* To convert a heap stridx to a token number, subtract - * DUK_STRIDX_START_RESERVED and add DUK_TOK_START_RESERVED. - */ -#if !defined(DUK_SINGLE_FILE) -DUK_INTERNAL_DECL const duk_uint8_t duk_strings_data[972]; -#endif /* !DUK_SINGLE_FILE */ -#define DUK_STRDATA_MAX_STRLEN 27 -#define DUK_STRDATA_DATA_LENGTH 972 -#endif /* DUK_USE_ROM_STRINGS */ - -#if defined(DUK_USE_ROM_OBJECTS) -#error RAM support not enabled, rerun configure.py with --ram-support -#else /* DUK_USE_ROM_OBJECTS */ -DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_function_constructor(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_function_prototype(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_array_constructor(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_string_constructor(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_boolean_constructor(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_number_constructor(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_date_constructor(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_regexp_constructor(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_error_constructor_shared(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_type_error_thrower(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_parse_int(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_parse_float(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_thread_constructor(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_pointer_constructor(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_proxy_constructor(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_symbol_constructor_shared(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_arraybuffer_constructor(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_dataview_constructor(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_nodejs_buffer_constructor(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_textencoder_constructor(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_textdecoder_constructor(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_eval(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_is_nan(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_is_finite(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_decode_uri(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_decode_uri_component(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_encode_uri(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_encode_uri_component(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_escape(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_unescape(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_object_getprototype_shared(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_object_setprototype_shared(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_get_own_property_descriptor(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_keys_shared(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_assign(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_create(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_define_property(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_define_properties(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_seal_freeze_shared(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_prevent_extensions(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_is_sealed_frozen_shared(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_is_extensible(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_is(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_to_string(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_to_locale_string(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_value_of(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_has_own_property(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_is_prototype_of(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_property_is_enumerable(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_defineaccessor(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_lookupaccessor(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_function_prototype_to_string(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_function_prototype_apply(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_function_prototype_call(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_function_prototype_bind(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_function_prototype_hasinstance(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_native_function_length(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_native_function_name(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_array_constructor_is_array(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_to_string(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_join_shared(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_concat(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_pop(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_push(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_reverse(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_shift(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_slice(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_sort(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_splice(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_unshift(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_indexof_shared(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_iter_shared(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_reduce_shared(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_string_constructor_from_char_code(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_string_constructor_from_code_point(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_to_string(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_char_at(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_char_code_at(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_concat(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_indexof_shared(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_locale_compare(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_match(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_replace(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_search(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_slice(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_split(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_substring(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_caseconv_shared(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_trim(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_repeat(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_startswith_endswith(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_includes(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_substr(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_boolean_prototype_tostring_shared(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_number_check_shared(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_number_prototype_to_string(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_number_prototype_to_locale_string(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_number_prototype_value_of(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_number_prototype_to_fixed(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_number_prototype_to_exponential(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_number_prototype_to_precision(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_date_constructor_parse(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_date_constructor_utc(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_date_constructor_now(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_date_prototype_tostring_shared(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_date_prototype_to_json(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_date_prototype_value_of(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_date_prototype_get_shared(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_date_prototype_get_timezone_offset(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_date_prototype_set_time(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_date_prototype_set_shared(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_date_prototype_toprimitive(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_regexp_prototype_exec(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_regexp_prototype_test(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_regexp_prototype_tostring(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_regexp_prototype_flags(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_regexp_prototype_shared_getter(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_error_prototype_stack_getter(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_error_prototype_stack_setter(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_error_prototype_filename_getter(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_error_prototype_filename_setter(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_error_prototype_linenumber_getter(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_error_prototype_linenumber_setter(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_error_prototype_to_string(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_onearg_shared(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_twoarg_shared(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_clz32(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_hypot(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_imul(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_max(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_min(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_random(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_sign(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_json_object_parse(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_json_object_stringify(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_duktape_object_info(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_duktape_object_act(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_duktape_object_gc(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_duktape_object_fin(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_duktape_object_enc(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_duktape_object_dec(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_duktape_object_compact(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_thread_yield(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_thread_resume(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_thread_current(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_pointer_prototype_tostring_shared(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_reflect_apply(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_reflect_construct(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_reflect_object_delete_property(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_reflect_object_get(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_reflect_object_has(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_reflect_object_set(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_symbol_key_for(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_symbol_tostring_shared(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_symbol_toprimitive(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_arraybuffer_isview(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_typedarray_bytelength_getter(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_buffer_slice_shared(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_typedarray_byteoffset_getter(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_typedarray_buffer_getter(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_typedarray_set(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_uint8array_allocplain(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_uint8array_plainof(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_nodejs_buffer_concat(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_nodejs_buffer_is_encoding(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_nodejs_buffer_is_buffer(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_nodejs_buffer_byte_length(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_buffer_compare_shared(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_nodejs_buffer_tostring(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_nodejs_buffer_tojson(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_nodejs_buffer_fill(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_nodejs_buffer_copy(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_nodejs_buffer_write(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_cbor_encode(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_cbor_decode(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_textencoder_prototype_encoding_getter(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_textencoder_prototype_encode(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_textdecoder_prototype_shared_getter(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_textdecoder_prototype_decode(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_performance_now(duk_context *ctx); -#if !defined(DUK_SINGLE_FILE) -DUK_INTERNAL_DECL const duk_c_function duk_bi_native_functions[185]; -#endif /* !DUK_SINGLE_FILE */ -#define DUK_BIDX_GLOBAL 0 -#define DUK_BIDX_GLOBAL_ENV 1 -#define DUK_BIDX_OBJECT_CONSTRUCTOR 2 -#define DUK_BIDX_OBJECT_PROTOTYPE 3 -#define DUK_BIDX_FUNCTION_CONSTRUCTOR 4 -#define DUK_BIDX_FUNCTION_PROTOTYPE 5 -#define DUK_BIDX_NATIVE_FUNCTION_PROTOTYPE 6 -#define DUK_BIDX_ARRAY_CONSTRUCTOR 7 -#define DUK_BIDX_ARRAY_PROTOTYPE 8 -#define DUK_BIDX_STRING_CONSTRUCTOR 9 -#define DUK_BIDX_STRING_PROTOTYPE 10 -#define DUK_BIDX_BOOLEAN_CONSTRUCTOR 11 -#define DUK_BIDX_BOOLEAN_PROTOTYPE 12 -#define DUK_BIDX_NUMBER_CONSTRUCTOR 13 -#define DUK_BIDX_NUMBER_PROTOTYPE 14 -#define DUK_BIDX_DATE_CONSTRUCTOR 15 -#define DUK_BIDX_DATE_PROTOTYPE 16 -#define DUK_BIDX_REGEXP_CONSTRUCTOR 17 -#define DUK_BIDX_REGEXP_PROTOTYPE 18 -#define DUK_BIDX_ERROR_CONSTRUCTOR 19 -#define DUK_BIDX_ERROR_PROTOTYPE 20 -#define DUK_BIDX_EVAL_ERROR_CONSTRUCTOR 21 -#define DUK_BIDX_EVAL_ERROR_PROTOTYPE 22 -#define DUK_BIDX_RANGE_ERROR_CONSTRUCTOR 23 -#define DUK_BIDX_RANGE_ERROR_PROTOTYPE 24 -#define DUK_BIDX_REFERENCE_ERROR_CONSTRUCTOR 25 -#define DUK_BIDX_REFERENCE_ERROR_PROTOTYPE 26 -#define DUK_BIDX_SYNTAX_ERROR_CONSTRUCTOR 27 -#define DUK_BIDX_SYNTAX_ERROR_PROTOTYPE 28 -#define DUK_BIDX_TYPE_ERROR_CONSTRUCTOR 29 -#define DUK_BIDX_TYPE_ERROR_PROTOTYPE 30 -#define DUK_BIDX_URI_ERROR_CONSTRUCTOR 31 -#define DUK_BIDX_URI_ERROR_PROTOTYPE 32 -#define DUK_BIDX_TYPE_ERROR_THROWER 33 -#define DUK_BIDX_DUKTAPE 34 -#define DUK_BIDX_THREAD_PROTOTYPE 35 -#define DUK_BIDX_POINTER_PROTOTYPE 36 -#define DUK_BIDX_DOUBLE_ERROR 37 -#define DUK_BIDX_SYMBOL_PROTOTYPE 38 -#define DUK_BIDX_ARRAYBUFFER_PROTOTYPE 39 -#define DUK_BIDX_DATAVIEW_PROTOTYPE 40 -#define DUK_BIDX_INT8ARRAY_PROTOTYPE 41 -#define DUK_BIDX_UINT8ARRAY_PROTOTYPE 42 -#define DUK_BIDX_UINT8CLAMPEDARRAY_PROTOTYPE 43 -#define DUK_BIDX_INT16ARRAY_PROTOTYPE 44 -#define DUK_BIDX_UINT16ARRAY_PROTOTYPE 45 -#define DUK_BIDX_INT32ARRAY_PROTOTYPE 46 -#define DUK_BIDX_UINT32ARRAY_PROTOTYPE 47 -#define DUK_BIDX_FLOAT32ARRAY_PROTOTYPE 48 -#define DUK_BIDX_FLOAT64ARRAY_PROTOTYPE 49 -#define DUK_BIDX_NODEJS_BUFFER_PROTOTYPE 50 -#define DUK_NUM_BUILTINS 51 -#define DUK_NUM_BIDX_BUILTINS 51 -#define DUK_NUM_ALL_BUILTINS 80 -#if defined(DUK_USE_DOUBLE_LE) -#if !defined(DUK_SINGLE_FILE) -DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[4281]; -#endif /* !DUK_SINGLE_FILE */ -#define DUK_BUILTINS_DATA_LENGTH 4281 -#elif defined(DUK_USE_DOUBLE_BE) -#if !defined(DUK_SINGLE_FILE) -DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[4281]; -#endif /* !DUK_SINGLE_FILE */ -#define DUK_BUILTINS_DATA_LENGTH 4281 -#elif defined(DUK_USE_DOUBLE_ME) -#if !defined(DUK_SINGLE_FILE) -DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[4281]; -#endif /* !DUK_SINGLE_FILE */ -#define DUK_BUILTINS_DATA_LENGTH 4281 -#else -#error invalid endianness defines -#endif -#endif /* DUK_USE_ROM_OBJECTS */ -#endif /* DUK_BUILTINS_H_INCLUDED */ - -/* #include duk_util.h */ -/* - * Utilities - */ - -#if !defined(DUK_UTIL_H_INCLUDED) -#define DUK_UTIL_H_INCLUDED - -/* - * Some useful constants - */ - -#define DUK_DOUBLE_2TO32 4294967296.0 -#define DUK_DOUBLE_2TO31 2147483648.0 -#define DUK_DOUBLE_LOG2E 1.4426950408889634 -#define DUK_DOUBLE_LOG10E 0.4342944819032518 - -/* - * Endian conversion - */ - -#if defined(DUK_USE_INTEGER_LE) -#define DUK_HTON32(x) DUK_BSWAP32((x)) -#define DUK_NTOH32(x) DUK_BSWAP32((x)) -#define DUK_HTON16(x) DUK_BSWAP16((x)) -#define DUK_NTOH16(x) DUK_BSWAP16((x)) -#elif defined(DUK_USE_INTEGER_BE) -#define DUK_HTON32(x) (x) -#define DUK_NTOH32(x) (x) -#define DUK_HTON16(x) (x) -#define DUK_NTOH16(x) (x) -#else -#error internal error, endianness defines broken -#endif - -/* - * Bitstream decoder - */ - -struct duk_bitdecoder_ctx { - const duk_uint8_t *data; - duk_size_t offset; - duk_size_t length; - duk_uint32_t currval; - duk_small_int_t currbits; -}; - -#define DUK_BD_BITPACKED_STRING_MAXLEN 256 - -/* - * Bitstream encoder - */ - -struct duk_bitencoder_ctx { - duk_uint8_t *data; - duk_size_t offset; - duk_size_t length; - duk_uint32_t currval; - duk_small_int_t currbits; - duk_small_int_t truncated; -}; - -/* - * Raw write/read macros for big endian, unaligned basic values. - * Caller ensures there's enough space. The INC macro variants - * update the pointer argument automatically. - */ - -#define DUK_RAW_WRITE_U8(ptr, val) \ - do { \ - *(ptr) = (duk_uint8_t) (val); \ - } while (0) -#define DUK_RAW_WRITE_U16_BE(ptr, val) duk_raw_write_u16_be((ptr), (duk_uint16_t) (val)) -#define DUK_RAW_WRITE_U32_BE(ptr, val) duk_raw_write_u32_be((ptr), (duk_uint32_t) (val)) -#define DUK_RAW_WRITE_FLOAT_BE(ptr, val) duk_raw_write_float_be((ptr), (duk_float_t) (val)) -#define DUK_RAW_WRITE_DOUBLE_BE(ptr, val) duk_raw_write_double_be((ptr), (duk_double_t) (val)) -#define DUK_RAW_WRITE_XUTF8(ptr, val) duk_raw_write_xutf8((ptr), (duk_ucodepoint_t) (val)) - -#define DUK_RAW_WRITEINC_U8(ptr, val) \ - do { \ - *(ptr)++ = (duk_uint8_t) (val); \ - } while (0) -#define DUK_RAW_WRITEINC_U16_BE(ptr, val) duk_raw_writeinc_u16_be(&(ptr), (duk_uint16_t) (val)) -#define DUK_RAW_WRITEINC_U32_BE(ptr, val) duk_raw_writeinc_u32_be(&(ptr), (duk_uint32_t) (val)) -#define DUK_RAW_WRITEINC_FLOAT_BE(ptr, val) duk_raw_writeinc_float_be(&(ptr), (duk_float_t) (val)) -#define DUK_RAW_WRITEINC_DOUBLE_BE(ptr, val) duk_raw_writeinc_double_be(&(ptr), (duk_double_t) (val)) -#define DUK_RAW_WRITEINC_XUTF8(ptr, val) duk_raw_writeinc_xutf8(&(ptr), (duk_ucodepoint_t) (val)) -#define DUK_RAW_WRITEINC_CESU8(ptr, val) duk_raw_writeinc_cesu8(&(ptr), (duk_ucodepoint_t) (val)) - -#define DUK_RAW_READ_U8(ptr) ((duk_uint8_t) (*(ptr))) -#define DUK_RAW_READ_U16_BE(ptr) duk_raw_read_u16_be((ptr)); -#define DUK_RAW_READ_U32_BE(ptr) duk_raw_read_u32_be((ptr)); -#define DUK_RAW_READ_DOUBLE_BE(ptr) duk_raw_read_double_be((ptr)); - -#define DUK_RAW_READINC_U8(ptr) ((duk_uint8_t) (*(ptr)++)) -#define DUK_RAW_READINC_U16_BE(ptr) duk_raw_readinc_u16_be(&(ptr)); -#define DUK_RAW_READINC_U32_BE(ptr) duk_raw_readinc_u32_be(&(ptr)); -#define DUK_RAW_READINC_DOUBLE_BE(ptr) duk_raw_readinc_double_be(&(ptr)); - -/* - * Double and float byte order operations. - */ - -DUK_INTERNAL_DECL void duk_dblunion_host_to_little(duk_double_union *u); -DUK_INTERNAL_DECL void duk_dblunion_little_to_host(duk_double_union *u); -DUK_INTERNAL_DECL void duk_dblunion_host_to_big(duk_double_union *u); -DUK_INTERNAL_DECL void duk_dblunion_big_to_host(duk_double_union *u); -DUK_INTERNAL_DECL void duk_fltunion_host_to_big(duk_float_union *u); -DUK_INTERNAL_DECL void duk_fltunion_big_to_host(duk_float_union *u); - -/* - * Buffer writer (dynamic buffer only) - * - * Helper for writing to a dynamic buffer with a concept of a "slack" area - * to reduce resizes. You can ensure there is enough space beforehand and - * then write for a while without further checks, relying on a stable data - * pointer. Slack handling is automatic so call sites only indicate how - * much data they need right now. - * - * There are several ways to write using bufwriter. The best approach - * depends mainly on how much performance matters over code footprint. - * The key issues are (1) ensuring there is space and (2) keeping the - * pointers consistent. Fast code should ensure space for multiple writes - * with one ensure call. Fastest inner loop code can temporarily borrow - * the 'p' pointer but must write it back eventually. - * - * Be careful to ensure all macro arguments (other than static pointers like - * 'thr' and 'bw_ctx') are evaluated exactly once, using temporaries if - * necessary (if that's not possible, there should be a note near the macro). - * Buffer write arguments often contain arithmetic etc so this is - * particularly important here. - */ - -/* XXX: Migrate bufwriter and other read/write helpers to its own header? */ - -struct duk_bufwriter_ctx { - duk_uint8_t *p; - duk_uint8_t *p_base; - duk_uint8_t *p_limit; - duk_hbuffer_dynamic *buf; -}; - -#if defined(DUK_USE_PREFER_SIZE) -#define DUK_BW_SLACK_ADD 64 -#define DUK_BW_SLACK_SHIFT 4 /* 2^4 -> 1/16 = 6.25% slack */ -#else -#define DUK_BW_SLACK_ADD 64 -#define DUK_BW_SLACK_SHIFT 2 /* 2^2 -> 1/4 = 25% slack */ -#endif - -/* Initialization and finalization (compaction), converting to other types. */ - -#define DUK_BW_INIT_PUSHBUF(thr, bw_ctx, sz) \ - do { \ - duk_bw_init_pushbuf((thr), (bw_ctx), (sz)); \ - } while (0) -#define DUK_BW_INIT_WITHBUF(thr, bw_ctx, buf) \ - do { \ - duk_bw_init((thr), (bw_ctx), (buf)); \ - } while (0) -#define DUK_BW_COMPACT(thr, bw_ctx) \ - do { \ - /* Make underlying buffer compact to match DUK_BW_GET_SIZE(). */ \ - duk_bw_compact((thr), (bw_ctx)); \ - } while (0) -#define DUK_BW_PUSH_AS_STRING(thr, bw_ctx) \ - do { \ - duk_push_lstring((thr), (const char *) (bw_ctx)->p_base, (duk_size_t) ((bw_ctx)->p - (bw_ctx)->p_base)); \ - } while (0) - -/* Pointers may be NULL for a while when 'buf' size is zero and before any - * ENSURE calls have been made. Once an ENSURE has been made, the pointers - * are required to be non-NULL so that it's always valid to use memcpy() and - * memmove(), even for zero size. - */ -#if defined(DUK_USE_ASSERTIONS) -DUK_INTERNAL_DECL void duk_bw_assert_valid(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx); -#define DUK_BW_ASSERT_VALID_EXPR(thr, bw_ctx) (duk_bw_assert_valid((thr), (bw_ctx))) -#define DUK_BW_ASSERT_VALID(thr, bw_ctx) \ - do { \ - duk_bw_assert_valid((thr), (bw_ctx)); \ - } while (0) -#else -#define DUK_BW_ASSERT_VALID_EXPR(thr, bw_ctx) DUK_ASSERT_EXPR(1) -#define DUK_BW_ASSERT_VALID(thr, bw_ctx) \ - do { \ - } while (0) -#endif - -/* Working with the pointer and current size. */ - -#define DUK_BW_GET_PTR(thr, bw_ctx) ((bw_ctx)->p) -#define DUK_BW_SET_PTR(thr, bw_ctx, ptr) \ - do { \ - (bw_ctx)->p = (ptr); \ - } while (0) -#define DUK_BW_ADD_PTR(thr, bw_ctx, delta) \ - do { \ - (bw_ctx)->p += (delta); \ - } while (0) -#define DUK_BW_GET_BASEPTR(thr, bw_ctx) ((bw_ctx)->p_base) -#define DUK_BW_GET_LIMITPTR(thr, bw_ctx) ((bw_ctx)->p_limit) -#define DUK_BW_GET_SIZE(thr, bw_ctx) ((duk_size_t) ((bw_ctx)->p - (bw_ctx)->p_base)) -#define DUK_BW_SET_SIZE(thr, bw_ctx, sz) \ - do { \ - DUK_ASSERT((duk_size_t) (sz) <= (duk_size_t) ((bw_ctx)->p - (bw_ctx)->p_base)); \ - (bw_ctx)->p = (bw_ctx)->p_base + (sz); \ - } while (0) -#define DUK_BW_RESET_SIZE(thr, bw_ctx) \ - do { \ - /* Reset to zero size, keep current limit. */ \ - (bw_ctx)->p = (bw_ctx)->p_base; \ - } while (0) -#define DUK_BW_GET_BUFFER(thr, bw_ctx) ((bw_ctx)->buf) - -/* Ensuring (reserving) space. */ - -#define DUK_BW_ENSURE(thr, bw_ctx, sz) \ - do { \ - duk_size_t duk__sz, duk__space; \ - DUK_BW_ASSERT_VALID((thr), (bw_ctx)); \ - duk__sz = (sz); \ - duk__space = (duk_size_t) ((bw_ctx)->p_limit - (bw_ctx)->p); \ - if (duk__space < duk__sz) { \ - (void) duk_bw_resize((thr), (bw_ctx), duk__sz); \ - } \ - } while (0) -/* NOTE: Multiple evaluation of 'ptr' in this macro. */ -/* XXX: Rework to use an always-inline function? */ -#define DUK_BW_ENSURE_RAW(thr, bw_ctx, sz, ptr) \ - (((duk_size_t) ((bw_ctx)->p_limit - (ptr)) >= (sz)) ? (ptr) : ((bw_ctx)->p = (ptr), duk_bw_resize((thr), (bw_ctx), (sz)))) -#define DUK_BW_ENSURE_GETPTR(thr, bw_ctx, sz) DUK_BW_ENSURE_RAW((thr), (bw_ctx), (sz), (bw_ctx)->p) -#define DUK_BW_ASSERT_SPACE_EXPR(thr, bw_ctx, sz) \ - (DUK_BW_ASSERT_VALID_EXPR((thr), (bw_ctx)), \ - DUK_ASSERT_EXPR((duk_size_t) ((bw_ctx)->p_limit - (bw_ctx)->p) >= (duk_size_t) (sz))) -#define DUK_BW_ASSERT_SPACE(thr, bw_ctx, sz) \ - do { \ - DUK_BW_ASSERT_SPACE_EXPR((thr), (bw_ctx), (sz)); \ - } while (0) - -/* Miscellaneous. */ - -#define DUK_BW_SETPTR_AND_COMPACT(thr, bw_ctx, ptr) \ - do { \ - (bw_ctx)->p = (ptr); \ - duk_bw_compact((thr), (bw_ctx)); \ - } while (0) - -/* Fast write calls which assume you control the slack beforehand. - * Multibyte write variants exist and use a temporary write pointer - * because byte writes alias with anything: with a stored pointer - * explicit pointer load/stores get generated (e.g. gcc -Os). - */ - -#define DUK_BW_WRITE_RAW_U8(thr, bw_ctx, val) \ - do { \ - DUK_BW_ASSERT_SPACE((thr), (bw_ctx), 1); \ - *(bw_ctx)->p++ = (duk_uint8_t) (val); \ - } while (0) -#define DUK_BW_WRITE_RAW_U8_2(thr, bw_ctx, val1, val2) \ - do { \ - duk_uint8_t *duk__p; \ - DUK_BW_ASSERT_SPACE((thr), (bw_ctx), 2); \ - duk__p = (bw_ctx)->p; \ - *duk__p++ = (duk_uint8_t) (val1); \ - *duk__p++ = (duk_uint8_t) (val2); \ - (bw_ctx)->p = duk__p; \ - } while (0) -#define DUK_BW_WRITE_RAW_U8_3(thr, bw_ctx, val1, val2, val3) \ - do { \ - duk_uint8_t *duk__p; \ - DUK_BW_ASSERT_SPACE((thr), (bw_ctx), 3); \ - duk__p = (bw_ctx)->p; \ - *duk__p++ = (duk_uint8_t) (val1); \ - *duk__p++ = (duk_uint8_t) (val2); \ - *duk__p++ = (duk_uint8_t) (val3); \ - (bw_ctx)->p = duk__p; \ - } while (0) -#define DUK_BW_WRITE_RAW_U8_4(thr, bw_ctx, val1, val2, val3, val4) \ - do { \ - duk_uint8_t *duk__p; \ - DUK_BW_ASSERT_SPACE((thr), (bw_ctx), 4); \ - duk__p = (bw_ctx)->p; \ - *duk__p++ = (duk_uint8_t) (val1); \ - *duk__p++ = (duk_uint8_t) (val2); \ - *duk__p++ = (duk_uint8_t) (val3); \ - *duk__p++ = (duk_uint8_t) (val4); \ - (bw_ctx)->p = duk__p; \ - } while (0) -#define DUK_BW_WRITE_RAW_U8_5(thr, bw_ctx, val1, val2, val3, val4, val5) \ - do { \ - duk_uint8_t *duk__p; \ - DUK_BW_ASSERT_SPACE((thr), (bw_ctx), 5); \ - duk__p = (bw_ctx)->p; \ - *duk__p++ = (duk_uint8_t) (val1); \ - *duk__p++ = (duk_uint8_t) (val2); \ - *duk__p++ = (duk_uint8_t) (val3); \ - *duk__p++ = (duk_uint8_t) (val4); \ - *duk__p++ = (duk_uint8_t) (val5); \ - (bw_ctx)->p = duk__p; \ - } while (0) -#define DUK_BW_WRITE_RAW_U8_6(thr, bw_ctx, val1, val2, val3, val4, val5, val6) \ - do { \ - duk_uint8_t *duk__p; \ - DUK_BW_ASSERT_SPACE((thr), (bw_ctx), 6); \ - duk__p = (bw_ctx)->p; \ - *duk__p++ = (duk_uint8_t) (val1); \ - *duk__p++ = (duk_uint8_t) (val2); \ - *duk__p++ = (duk_uint8_t) (val3); \ - *duk__p++ = (duk_uint8_t) (val4); \ - *duk__p++ = (duk_uint8_t) (val5); \ - *duk__p++ = (duk_uint8_t) (val6); \ - (bw_ctx)->p = duk__p; \ - } while (0) -#define DUK_BW_WRITE_RAW_XUTF8(thr, bw_ctx, cp) \ - do { \ - duk_ucodepoint_t duk__cp; \ - duk_small_int_t duk__enc_len; \ - duk__cp = (duk_ucodepoint_t) (cp); \ - DUK_BW_ASSERT_SPACE((thr), (bw_ctx), duk_unicode_get_xutf8_length(duk__cp)); \ - duk__enc_len = duk_unicode_encode_xutf8(duk__cp, (bw_ctx)->p); \ - (bw_ctx)->p += duk__enc_len; \ - } while (0) -#define DUK_BW_WRITE_RAW_CESU8(thr, bw_ctx, cp) \ - do { \ - duk_ucodepoint_t duk__cp; \ - duk_small_int_t duk__enc_len; \ - duk__cp = (duk_ucodepoint_t) (cp); \ - DUK_BW_ASSERT_SPACE((thr), (bw_ctx), duk_unicode_get_cesu8_length(duk__cp)); \ - duk__enc_len = duk_unicode_encode_cesu8(duk__cp, (bw_ctx)->p); \ - (bw_ctx)->p += duk__enc_len; \ - } while (0) -/* XXX: add temporary duk__p pointer here too; sharing */ -/* XXX: avoid unsafe variants */ -#define DUK_BW_WRITE_RAW_BYTES(thr, bw_ctx, valptr, valsz) \ - do { \ - const void *duk__valptr; \ - duk_size_t duk__valsz; \ - duk__valptr = (const void *) (valptr); \ - duk__valsz = (duk_size_t) (valsz); \ - duk_memcpy_unsafe((void *) ((bw_ctx)->p), duk__valptr, duk__valsz); \ - (bw_ctx)->p += duk__valsz; \ - } while (0) -#define DUK_BW_WRITE_RAW_CSTRING(thr, bw_ctx, val) \ - do { \ - const duk_uint8_t *duk__val; \ - duk_size_t duk__val_len; \ - duk__val = (const duk_uint8_t *) (val); \ - duk__val_len = DUK_STRLEN((const char *) duk__val); \ - duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) duk__val, duk__val_len); \ - (bw_ctx)->p += duk__val_len; \ - } while (0) -#define DUK_BW_WRITE_RAW_HSTRING(thr, bw_ctx, val) \ - do { \ - duk_size_t duk__val_len; \ - duk__val_len = DUK_HSTRING_GET_BYTELEN((val)); \ - duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) DUK_HSTRING_GET_DATA((val)), duk__val_len); \ - (bw_ctx)->p += duk__val_len; \ - } while (0) -#define DUK_BW_WRITE_RAW_HBUFFER(thr, bw_ctx, val) \ - do { \ - duk_size_t duk__val_len; \ - duk__val_len = DUK_HBUFFER_GET_SIZE((val)); \ - duk_memcpy_unsafe((void *) ((bw_ctx)->p), \ - (const void *) DUK_HBUFFER_GET_DATA_PTR((thr)->heap, (val)), \ - duk__val_len); \ - (bw_ctx)->p += duk__val_len; \ - } while (0) -#define DUK_BW_WRITE_RAW_HBUFFER_FIXED(thr, bw_ctx, val) \ - do { \ - duk_size_t duk__val_len; \ - duk__val_len = DUK_HBUFFER_FIXED_GET_SIZE((val)); \ - duk_memcpy_unsafe((void *) ((bw_ctx)->p), \ - (const void *) DUK_HBUFFER_FIXED_GET_DATA_PTR((thr)->heap, (val)), \ - duk__val_len); \ - (bw_ctx)->p += duk__val_len; \ - } while (0) -#define DUK_BW_WRITE_RAW_HBUFFER_DYNAMIC(thr, bw_ctx, val) \ - do { \ - duk_size_t duk__val_len; \ - duk__val_len = DUK_HBUFFER_DYNAMIC_GET_SIZE((val)); \ - duk_memcpy_unsafe((void *) ((bw_ctx)->p), \ - (const void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR((thr)->heap, (val)), \ - duk__val_len); \ - (bw_ctx)->p += duk__val_len; \ - } while (0) - -/* Append bytes from a slice already in the buffer. */ -#define DUK_BW_WRITE_RAW_SLICE(thr, bw, dst_off, dst_len) duk_bw_write_raw_slice((thr), (bw), (dst_off), (dst_len)) - -/* Insert bytes in the middle of the buffer from an external buffer. */ -#define DUK_BW_INSERT_RAW_BYTES(thr, bw, dst_off, buf, len) duk_bw_insert_raw_bytes((thr), (bw), (dst_off), (buf), (len)) - -/* Insert bytes in the middle of the buffer from a slice already - * in the buffer. Source offset is interpreted "before" the operation. - */ -#define DUK_BW_INSERT_RAW_SLICE(thr, bw, dst_off, src_off, len) duk_bw_insert_raw_slice((thr), (bw), (dst_off), (src_off), (len)) - -/* Insert a reserved area somewhere in the buffer; caller fills it. - * Evaluates to a (duk_uint_t *) pointing to the start of the reserved - * area for convenience. - */ -#define DUK_BW_INSERT_RAW_AREA(thr, bw, off, len) duk_bw_insert_raw_area((thr), (bw), (off), (len)) - -/* Remove a slice from inside buffer. */ -#define DUK_BW_REMOVE_RAW_SLICE(thr, bw, off, len) duk_bw_remove_raw_slice((thr), (bw), (off), (len)) - -/* Safe write calls which will ensure space first. */ - -#define DUK_BW_WRITE_ENSURE_U8(thr, bw_ctx, val) \ - do { \ - DUK_BW_ENSURE((thr), (bw_ctx), 1); \ - DUK_BW_WRITE_RAW_U8((thr), (bw_ctx), (val)); \ - } while (0) -#define DUK_BW_WRITE_ENSURE_U8_2(thr, bw_ctx, val1, val2) \ - do { \ - DUK_BW_ENSURE((thr), (bw_ctx), 2); \ - DUK_BW_WRITE_RAW_U8_2((thr), (bw_ctx), (val1), (val2)); \ - } while (0) -#define DUK_BW_WRITE_ENSURE_U8_3(thr, bw_ctx, val1, val2, val3) \ - do { \ - DUK_BW_ENSURE((thr), (bw_ctx), 3); \ - DUK_BW_WRITE_RAW_U8_3((thr), (bw_ctx), (val1), (val2), (val3)); \ - } while (0) -#define DUK_BW_WRITE_ENSURE_U8_4(thr, bw_ctx, val1, val2, val3, val4) \ - do { \ - DUK_BW_ENSURE((thr), (bw_ctx), 4); \ - DUK_BW_WRITE_RAW_U8_4((thr), (bw_ctx), (val1), (val2), (val3), (val4)); \ - } while (0) -#define DUK_BW_WRITE_ENSURE_U8_5(thr, bw_ctx, val1, val2, val3, val4, val5) \ - do { \ - DUK_BW_ENSURE((thr), (bw_ctx), 5); \ - DUK_BW_WRITE_RAW_U8_5((thr), (bw_ctx), (val1), (val2), (val3), (val4), (val5)); \ - } while (0) -#define DUK_BW_WRITE_ENSURE_U8_6(thr, bw_ctx, val1, val2, val3, val4, val5, val6) \ - do { \ - DUK_BW_ENSURE((thr), (bw_ctx), 6); \ - DUK_BW_WRITE_RAW_U8_6((thr), (bw_ctx), (val1), (val2), (val3), (val4), (val5), (val6)); \ - } while (0) -#define DUK_BW_WRITE_ENSURE_XUTF8(thr, bw_ctx, cp) \ - do { \ - DUK_BW_ENSURE((thr), (bw_ctx), DUK_UNICODE_MAX_XUTF8_LENGTH); \ - DUK_BW_WRITE_RAW_XUTF8((thr), (bw_ctx), (cp)); \ - } while (0) -#define DUK_BW_WRITE_ENSURE_CESU8(thr, bw_ctx, cp) \ - do { \ - DUK_BW_ENSURE((thr), (bw_ctx), DUK_UNICODE_MAX_CESU8_LENGTH); \ - DUK_BW_WRITE_RAW_CESU8((thr), (bw_ctx), (cp)); \ - } while (0) -/* XXX: add temporary duk__p pointer here too; sharing */ -/* XXX: avoid unsafe */ -#define DUK_BW_WRITE_ENSURE_BYTES(thr, bw_ctx, valptr, valsz) \ - do { \ - const void *duk__valptr; \ - duk_size_t duk__valsz; \ - duk__valptr = (const void *) (valptr); \ - duk__valsz = (duk_size_t) (valsz); \ - DUK_BW_ENSURE((thr), (bw_ctx), duk__valsz); \ - duk_memcpy_unsafe((void *) ((bw_ctx)->p), duk__valptr, duk__valsz); \ - (bw_ctx)->p += duk__valsz; \ - } while (0) -#define DUK_BW_WRITE_ENSURE_CSTRING(thr, bw_ctx, val) \ - do { \ - const duk_uint8_t *duk__val; \ - duk_size_t duk__val_len; \ - duk__val = (const duk_uint8_t *) (val); \ - duk__val_len = DUK_STRLEN((const char *) duk__val); \ - DUK_BW_ENSURE((thr), (bw_ctx), duk__val_len); \ - duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) duk__val, duk__val_len); \ - (bw_ctx)->p += duk__val_len; \ - } while (0) -#define DUK_BW_WRITE_ENSURE_HSTRING(thr, bw_ctx, val) \ - do { \ - duk_size_t duk__val_len; \ - duk__val_len = DUK_HSTRING_GET_BYTELEN((val)); \ - DUK_BW_ENSURE((thr), (bw_ctx), duk__val_len); \ - duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) DUK_HSTRING_GET_DATA((val)), duk__val_len); \ - (bw_ctx)->p += duk__val_len; \ - } while (0) -#define DUK_BW_WRITE_ENSURE_HBUFFER(thr, bw_ctx, val) \ - do { \ - duk_size_t duk__val_len; \ - duk__val_len = DUK_HBUFFER_GET_SIZE((val)); \ - DUK_BW_ENSURE((thr), (bw_ctx), duk__val_len); \ - duk_memcpy_unsafe((void *) ((bw_ctx)->p), \ - (const void *) DUK_HBUFFER_GET_DATA_PTR((thr)->heap, (val)), \ - duk__val_len); \ - (bw_ctx)->p += duk__val_len; \ - } while (0) -#define DUK_BW_WRITE_ENSURE_HBUFFER_FIXED(thr, bw_ctx, val) \ - do { \ - duk_size_t duk__val_len; \ - duk__val_len = DUK_HBUFFER_FIXED_GET_SIZE((val)); \ - DUK_BW_ENSURE((thr), (bw_ctx), duk__val_len); \ - duk_memcpy_unsafe((void *) ((bw_ctx)->p), \ - (const void *) DUK_HBUFFER_FIXED_GET_DATA_PTR((thr)->heap, (val)), \ - duk__val_len); \ - (bw_ctx)->p += duk__val_len; \ - } while (0) -#define DUK_BW_WRITE_ENSURE_HBUFFER_DYNAMIC(thr, bw_ctx, val) \ - do { \ - duk_size_t duk__val_len; \ - duk__val_len = DUK_HBUFFER_DYNAMIC_GET_SIZE((val)); \ - DUK_BW_ENSURE((thr), (bw_ctx), duk__val_len); \ - duk_memcpy_unsafe((void *) ((bw_ctx)->p), \ - (const void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR((thr)->heap, (val)), \ - duk__val_len); \ - (bw_ctx)->p += duk__val_len; \ - } while (0) - -#define DUK_BW_WRITE_ENSURE_SLICE(thr, bw, dst_off, dst_len) duk_bw_write_ensure_slice((thr), (bw), (dst_off), (dst_len)) -#define DUK_BW_INSERT_ENSURE_BYTES(thr, bw, dst_off, buf, len) duk_bw_insert_ensure_bytes((thr), (bw), (dst_off), (buf), (len)) -#define DUK_BW_INSERT_ENSURE_SLICE(thr, bw, dst_off, src_off, len) \ - duk_bw_insert_ensure_slice((thr), (bw), (dst_off), (src_off), (len)) -#define DUK_BW_INSERT_ENSURE_AREA(thr, bw, off, len) \ - /* Evaluates to (duk_uint8_t *) pointing to start of area. */ \ - duk_bw_insert_ensure_area((thr), (bw), (off), (len)) -#define DUK_BW_REMOVE_ENSURE_SLICE(thr, bw, off, len) \ - /* No difference between raw/ensure because the buffer shrinks. */ \ - DUK_BW_REMOVE_RAW_SLICE((thr), (bw), (off), (len)) - -/* - * Externs and prototypes - */ - -#if !defined(DUK_SINGLE_FILE) -DUK_INTERNAL_DECL const duk_uint8_t duk_lc_digits[36]; -DUK_INTERNAL_DECL const duk_uint8_t duk_uc_nybbles[16]; -DUK_INTERNAL_DECL const duk_int8_t duk_hex_dectab[256]; -#if defined(DUK_USE_HEX_FASTPATH) -DUK_INTERNAL_DECL const duk_int16_t duk_hex_dectab_shift4[256]; -DUK_INTERNAL_DECL const duk_uint16_t duk_hex_enctab[256]; -#endif -#endif /* !DUK_SINGLE_FILE */ - -/* Note: assumes that duk_util_probe_steps size is 32 */ -#if defined(DUK_USE_HOBJECT_HASH_PART) -#if !defined(DUK_SINGLE_FILE) -DUK_INTERNAL_DECL duk_uint8_t duk_util_probe_steps[32]; -#endif /* !DUK_SINGLE_FILE */ -#endif - -#if defined(DUK_USE_STRHASH_DENSE) -DUK_INTERNAL_DECL duk_uint32_t duk_util_hashbytes(const duk_uint8_t *data, duk_size_t len, duk_uint32_t seed); -#endif - -DUK_INTERNAL_DECL duk_uint32_t duk_bd_decode(duk_bitdecoder_ctx *ctx, duk_small_int_t bits); -DUK_INTERNAL_DECL duk_small_uint_t duk_bd_decode_flag(duk_bitdecoder_ctx *ctx); -DUK_INTERNAL_DECL duk_uint32_t duk_bd_decode_flagged(duk_bitdecoder_ctx *ctx, duk_small_int_t bits, duk_uint32_t def_value); -DUK_INTERNAL_DECL duk_int32_t duk_bd_decode_flagged_signed(duk_bitdecoder_ctx *ctx, duk_small_int_t bits, duk_int32_t def_value); -DUK_INTERNAL_DECL duk_uint32_t duk_bd_decode_varuint(duk_bitdecoder_ctx *ctx); -DUK_INTERNAL_DECL duk_small_uint_t duk_bd_decode_bitpacked_string(duk_bitdecoder_ctx *bd, duk_uint8_t *out); - -DUK_INTERNAL_DECL void duk_be_encode(duk_bitencoder_ctx *ctx, duk_uint32_t data, duk_small_int_t bits); -DUK_INTERNAL_DECL void duk_be_finish(duk_bitencoder_ctx *ctx); - -#if !defined(DUK_USE_GET_RANDOM_DOUBLE) -DUK_INTERNAL_DECL duk_double_t duk_util_tinyrandom_get_double(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_util_tinyrandom_prepare_seed(duk_hthread *thr); -#endif - -DUK_INTERNAL_DECL void duk_bw_init(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, duk_hbuffer_dynamic *h_buf); -DUK_INTERNAL_DECL void duk_bw_init_pushbuf(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, duk_size_t buf_size); -DUK_INTERNAL_DECL duk_uint8_t *duk_bw_resize(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, duk_size_t sz); -DUK_INTERNAL_DECL void duk_bw_compact(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx); -DUK_INTERNAL_DECL void duk_bw_write_raw_slice(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t src_off, duk_size_t len); -DUK_INTERNAL_DECL void duk_bw_write_ensure_slice(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t src_off, duk_size_t len); -DUK_INTERNAL_DECL void duk_bw_insert_raw_bytes(duk_hthread *thr, - duk_bufwriter_ctx *bw, - duk_size_t dst_off, - const duk_uint8_t *buf, - duk_size_t len); -DUK_INTERNAL_DECL void duk_bw_insert_ensure_bytes(duk_hthread *thr, - duk_bufwriter_ctx *bw, - duk_size_t dst_off, - const duk_uint8_t *buf, - duk_size_t len); -DUK_INTERNAL_DECL void duk_bw_insert_raw_slice(duk_hthread *thr, - duk_bufwriter_ctx *bw, - duk_size_t dst_off, - duk_size_t src_off, - duk_size_t len); -DUK_INTERNAL_DECL void duk_bw_insert_ensure_slice(duk_hthread *thr, - duk_bufwriter_ctx *bw, - duk_size_t dst_off, - duk_size_t src_off, - duk_size_t len); -DUK_INTERNAL_DECL duk_uint8_t *duk_bw_insert_raw_area(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t off, duk_size_t len); -DUK_INTERNAL_DECL duk_uint8_t *duk_bw_insert_ensure_area(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t off, duk_size_t len); -DUK_INTERNAL_DECL void duk_bw_remove_raw_slice(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t off, duk_size_t len); -/* No duk_bw_remove_ensure_slice(), functionality would be identical. */ - -DUK_INTERNAL_DECL duk_uint16_t duk_raw_read_u16_be(const duk_uint8_t *p); -DUK_INTERNAL_DECL duk_uint32_t duk_raw_read_u32_be(const duk_uint8_t *p); -DUK_INTERNAL_DECL duk_float_t duk_raw_read_float_be(const duk_uint8_t *p); -DUK_INTERNAL_DECL duk_double_t duk_raw_read_double_be(const duk_uint8_t *p); -DUK_INTERNAL_DECL duk_uint16_t duk_raw_readinc_u16_be(const duk_uint8_t **p); -DUK_INTERNAL_DECL duk_uint32_t duk_raw_readinc_u32_be(const duk_uint8_t **p); -DUK_INTERNAL_DECL duk_float_t duk_raw_readinc_float_be(const duk_uint8_t **p); -DUK_INTERNAL_DECL duk_double_t duk_raw_readinc_double_be(const duk_uint8_t **p); -DUK_INTERNAL_DECL void duk_raw_write_u16_be(duk_uint8_t *p, duk_uint16_t val); -DUK_INTERNAL_DECL void duk_raw_write_u32_be(duk_uint8_t *p, duk_uint32_t val); -DUK_INTERNAL_DECL void duk_raw_write_float_be(duk_uint8_t *p, duk_float_t val); -DUK_INTERNAL_DECL void duk_raw_write_double_be(duk_uint8_t *p, duk_double_t val); -DUK_INTERNAL_DECL duk_small_int_t duk_raw_write_xutf8(duk_uint8_t *p, duk_ucodepoint_t val); -DUK_INTERNAL_DECL duk_small_int_t duk_raw_write_cesu8(duk_uint8_t *p, duk_ucodepoint_t val); -DUK_INTERNAL_DECL void duk_raw_writeinc_u16_be(duk_uint8_t **p, duk_uint16_t val); -DUK_INTERNAL_DECL void duk_raw_writeinc_u32_be(duk_uint8_t **p, duk_uint32_t val); -DUK_INTERNAL_DECL void duk_raw_writeinc_float_be(duk_uint8_t **p, duk_float_t val); -DUK_INTERNAL_DECL void duk_raw_writeinc_double_be(duk_uint8_t **p, duk_double_t val); -DUK_INTERNAL_DECL void duk_raw_writeinc_xutf8(duk_uint8_t **p, duk_ucodepoint_t val); -DUK_INTERNAL_DECL void duk_raw_writeinc_cesu8(duk_uint8_t **p, duk_ucodepoint_t val); - -#if defined(DUK_USE_DEBUGGER_SUPPORT) /* For now only needed by the debugger. */ -DUK_INTERNAL_DECL void duk_byteswap_bytes(duk_uint8_t *p, duk_small_uint_t len); -#endif - -DUK_INTERNAL_DECL duk_double_t duk_util_get_random_double(duk_hthread *thr); - -/* memcpy(), memmove() etc wrappers. The plain variants like duk_memcpy() - * assume C99+ and 'src' and 'dst' pointers must be non-NULL even when the - * operation size is zero. The unsafe variants like duk_memcpy_safe() deal - * with the zero size case explicitly, and allow NULL pointers in that case - * (which is undefined behavior in C99+). For the majority of actual targets - * a NULL pointer with a zero length is fine in practice. These wrappers are - * macros to force inlining; because there are hundreds of call sites, even a - * few extra bytes per call site adds up to ~1kB footprint. - */ -#if defined(DUK_USE_ALLOW_UNDEFINED_BEHAVIOR) -#define duk_memcpy(dst, src, len) \ - do { \ - void *duk__dst = (dst); \ - const void *duk__src = (src); \ - duk_size_t duk__len = (len); \ - DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \ - DUK_ASSERT(duk__src != NULL || duk__len == 0U); \ - (void) DUK_MEMCPY(duk__dst, duk__src, (size_t) duk__len); \ - } while (0) -#define duk_memcpy_unsafe(dst, src, len) duk_memcpy((dst), (src), (len)) -#define duk_memmove(dst, src, len) \ - do { \ - void *duk__dst = (dst); \ - const void *duk__src = (src); \ - duk_size_t duk__len = (len); \ - DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \ - DUK_ASSERT(duk__src != NULL || duk__len == 0U); \ - (void) DUK_MEMMOVE(duk__dst, duk__src, (size_t) duk__len); \ - } while (0) -#define duk_memmove_unsafe(dst, src, len) duk_memmove((dst), (src), (len)) -#define duk_memset(dst, val, len) \ - do { \ - void *duk__dst = (dst); \ - duk_small_int_t duk__val = (val); \ - duk_size_t duk__len = (len); \ - DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \ - (void) DUK_MEMSET(duk__dst, duk__val, (size_t) duk__len); \ - } while (0) -#define duk_memset_unsafe(dst, val, len) duk_memset((dst), (val), (len)) -#define duk_memzero(dst, len) \ - do { \ - void *duk__dst = (dst); \ - duk_size_t duk__len = (len); \ - DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \ - (void) DUK_MEMZERO(duk__dst, (size_t) duk__len); \ - } while (0) -#define duk_memzero_unsafe(dst, len) duk_memzero((dst), (len)) -#else /* DUK_USE_ALLOW_UNDEFINED_BEHAVIOR */ -#define duk_memcpy(dst, src, len) \ - do { \ - void *duk__dst = (dst); \ - const void *duk__src = (src); \ - duk_size_t duk__len = (len); \ - DUK_ASSERT(duk__dst != NULL); \ - DUK_ASSERT(duk__src != NULL); \ - (void) DUK_MEMCPY(duk__dst, duk__src, (size_t) duk__len); \ - } while (0) -#define duk_memcpy_unsafe(dst, src, len) \ - do { \ - void *duk__dst = (dst); \ - const void *duk__src = (src); \ - duk_size_t duk__len = (len); \ - DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \ - DUK_ASSERT(duk__src != NULL || duk__len == 0U); \ - if (DUK_LIKELY(duk__len > 0U)) { \ - DUK_ASSERT(duk__dst != NULL); \ - DUK_ASSERT(duk__src != NULL); \ - (void) DUK_MEMCPY(duk__dst, duk__src, (size_t) duk__len); \ - } \ - } while (0) -#define duk_memmove(dst, src, len) \ - do { \ - void *duk__dst = (dst); \ - const void *duk__src = (src); \ - duk_size_t duk__len = (len); \ - DUK_ASSERT(duk__dst != NULL); \ - DUK_ASSERT(duk__src != NULL); \ - (void) DUK_MEMMOVE(duk__dst, duk__src, (size_t) duk__len); \ - } while (0) -#define duk_memmove_unsafe(dst, src, len) \ - do { \ - void *duk__dst = (dst); \ - const void *duk__src = (src); \ - duk_size_t duk__len = (len); \ - DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \ - DUK_ASSERT(duk__src != NULL || duk__len == 0U); \ - if (DUK_LIKELY(duk__len > 0U)) { \ - DUK_ASSERT(duk__dst != NULL); \ - DUK_ASSERT(duk__src != NULL); \ - (void) DUK_MEMMOVE(duk__dst, duk__src, (size_t) duk__len); \ - } \ - } while (0) -#define duk_memset(dst, val, len) \ - do { \ - void *duk__dst = (dst); \ - duk_small_int_t duk__val = (val); \ - duk_size_t duk__len = (len); \ - DUK_ASSERT(duk__dst != NULL); \ - (void) DUK_MEMSET(duk__dst, duk__val, (size_t) duk__len); \ - } while (0) -#define duk_memset_unsafe(dst, val, len) \ - do { \ - void *duk__dst = (dst); \ - duk_small_int_t duk__val = (val); \ - duk_size_t duk__len = (len); \ - DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \ - if (DUK_LIKELY(duk__len > 0U)) { \ - DUK_ASSERT(duk__dst != NULL); \ - (void) DUK_MEMSET(duk__dst, duk__val, (size_t) duk__len); \ - } \ - } while (0) -#define duk_memzero(dst, len) \ - do { \ - void *duk__dst = (dst); \ - duk_size_t duk__len = (len); \ - DUK_ASSERT(duk__dst != NULL); \ - (void) DUK_MEMZERO(duk__dst, (size_t) duk__len); \ - } while (0) -#define duk_memzero_unsafe(dst, len) \ - do { \ - void *duk__dst = (dst); \ - duk_size_t duk__len = (len); \ - DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \ - if (DUK_LIKELY(duk__len > 0U)) { \ - DUK_ASSERT(duk__dst != NULL); \ - (void) DUK_MEMZERO(duk__dst, (size_t) duk__len); \ - } \ - } while (0) -#endif /* DUK_USE_ALLOW_UNDEFINED_BEHAVIOR */ - -DUK_INTERNAL_DECL duk_small_int_t duk_memcmp(const void *s1, const void *s2, duk_size_t len); -DUK_INTERNAL_DECL duk_small_int_t duk_memcmp_unsafe(const void *s1, const void *s2, duk_size_t len); - -DUK_INTERNAL_DECL duk_bool_t duk_is_whole_get_int32_nonegzero(duk_double_t x, duk_int32_t *ival); -DUK_INTERNAL_DECL duk_bool_t duk_is_whole_get_int32(duk_double_t x, duk_int32_t *ival); -DUK_INTERNAL_DECL duk_bool_t duk_double_is_anyinf(duk_double_t x); -DUK_INTERNAL_DECL duk_bool_t duk_double_is_posinf(duk_double_t x); -DUK_INTERNAL_DECL duk_bool_t duk_double_is_neginf(duk_double_t x); -DUK_INTERNAL_DECL duk_bool_t duk_double_is_nan(duk_double_t x); -DUK_INTERNAL_DECL duk_bool_t duk_double_is_nan_or_zero(duk_double_t x); -DUK_INTERNAL_DECL duk_bool_t duk_double_is_nan_or_inf(duk_double_t x); -DUK_INTERNAL_DECL duk_bool_t duk_double_is_nan_zero_inf(duk_double_t x); -DUK_INTERNAL_DECL duk_small_uint_t duk_double_signbit(duk_double_t x); -DUK_INTERNAL_DECL duk_double_t duk_double_trunc_towards_zero(duk_double_t x); -DUK_INTERNAL_DECL duk_bool_t duk_double_same_sign(duk_double_t x, duk_double_t y); -DUK_INTERNAL_DECL duk_double_t duk_double_fmin(duk_double_t x, duk_double_t y); -DUK_INTERNAL_DECL duk_double_t duk_double_fmax(duk_double_t x, duk_double_t y); -DUK_INTERNAL_DECL duk_bool_t duk_double_is_finite(duk_double_t x); -DUK_INTERNAL_DECL duk_bool_t duk_double_is_integer(duk_double_t x); -DUK_INTERNAL_DECL duk_bool_t duk_double_is_safe_integer(duk_double_t x); - -DUK_INTERNAL_DECL duk_double_t duk_double_div(duk_double_t x, duk_double_t y); -DUK_INTERNAL_DECL duk_int_t duk_double_to_int_t(duk_double_t x); -DUK_INTERNAL_DECL duk_uint_t duk_double_to_uint_t(duk_double_t x); -DUK_INTERNAL_DECL duk_int32_t duk_double_to_int32_t(duk_double_t x); -DUK_INTERNAL_DECL duk_uint32_t duk_double_to_uint32_t(duk_double_t x); -DUK_INTERNAL_DECL duk_float_t duk_double_to_float_t(duk_double_t x); -DUK_INTERNAL_DECL duk_bool_t duk_double_equals(duk_double_t x, duk_double_t y); -DUK_INTERNAL_DECL duk_bool_t duk_float_equals(duk_float_t x, duk_float_t y); - -/* - * Miscellaneous - */ - -/* Example: x = 0x10 = 0b00010000 - * x - 1 = 0x0f = 0b00001111 - * x & (x - 1) == 0 - * - * x = 0x07 = 0b00000111 - * x - 1 = 0x06 = 0b00000110 - * x & (x - 1) != 0 - * - * However, incorrectly true for x == 0 so check for that explicitly. - */ -#define DUK_IS_POWER_OF_TWO(x) ((x) != 0U && ((x) & ((x) -1U)) == 0U) - -#endif /* DUK_UTIL_H_INCLUDED */ -/* #include duk_strings.h */ -/* - * Shared string macros. - * - * Using shared macros helps minimize strings data size because it's easy - * to check if an existing string could be used. String constants don't - * need to be all defined here; defining a string here makes sense if there's - * a high chance the string could be reused. Also, using macros allows - * a call site express the exact string needed, but the macro may map to an - * approximate string to reduce unique string count. Macros can also be - * more easily tuned for low memory targets than #if defined()s throughout - * the code base. - * - * Because format strings behave differently in the call site (they need to - * be followed by format arguments), they use a special prefix DUK_STR_FMT_. - * - * On some compilers using explicit shared strings is preferable; on others - * it may be better to use straight literals because the compiler will combine - * them anyway, and such strings won't end up unnecessarily in a symbol table. - */ - -#if !defined(DUK_ERRMSG_H_INCLUDED) -#define DUK_ERRMSG_H_INCLUDED - -/* Mostly API and built-in method related */ -#define DUK_STR_INTERNAL_ERROR "internal error" -#define DUK_STR_UNSUPPORTED "unsupported" -#define DUK_STR_INVALID_COUNT "invalid count" -#define DUK_STR_INVALID_ARGS "invalid args" -#define DUK_STR_INVALID_STATE "invalid state" -#define DUK_STR_INVALID_INPUT "invalid input" -#define DUK_STR_INVALID_LENGTH "invalid length" -#define DUK_STR_NOT_CONSTRUCTABLE "not constructable" -#define DUK_STR_CONSTRUCT_ONLY "constructor requires 'new'" -#define DUK_STR_NOT_CALLABLE "not callable" -#define DUK_STR_NOT_EXTENSIBLE "not extensible" -#define DUK_STR_NOT_WRITABLE "not writable" -#define DUK_STR_NOT_CONFIGURABLE "not configurable" -#define DUK_STR_INVALID_CONTEXT "invalid context" -#define DUK_STR_INVALID_INDEX "invalid args" -#define DUK_STR_PUSH_BEYOND_ALLOC_STACK "cannot push beyond allocated stack" -#define DUK_STR_NOT_UNDEFINED "unexpected type" -#define DUK_STR_NOT_NULL "unexpected type" -#define DUK_STR_NOT_BOOLEAN "unexpected type" -#define DUK_STR_NOT_NUMBER "unexpected type" -#define DUK_STR_NOT_STRING "unexpected type" -#define DUK_STR_NOT_OBJECT "unexpected type" -#define DUK_STR_NOT_POINTER "unexpected type" -#define DUK_STR_NOT_BUFFER "not buffer" /* still in use with verbose messages */ -#define DUK_STR_UNEXPECTED_TYPE "unexpected type" -#define DUK_STR_NOT_THREAD "unexpected type" -#define DUK_STR_NOT_COMPFUNC "unexpected type" -#define DUK_STR_NOT_NATFUNC "unexpected type" -#define DUK_STR_NOT_C_FUNCTION "unexpected type" -#define DUK_STR_NOT_FUNCTION "unexpected type" -#define DUK_STR_NOT_REGEXP "unexpected type" -#define DUK_STR_TOPRIMITIVE_FAILED "coercion to primitive failed" -#define DUK_STR_NUMBER_OUTSIDE_RANGE "number outside range" -#define DUK_STR_NOT_OBJECT_COERCIBLE "not object coercible" -#define DUK_STR_CANNOT_NUMBER_COERCE_SYMBOL "cannot number coerce Symbol" -#define DUK_STR_CANNOT_STRING_COERCE_SYMBOL "cannot string coerce Symbol" -#define DUK_STR_STRING_TOO_LONG "string too long" -#define DUK_STR_BUFFER_TOO_LONG "buffer too long" -#define DUK_STR_ALLOC_FAILED "alloc failed" -#define DUK_STR_WRONG_BUFFER_TYPE "wrong buffer type" -#define DUK_STR_BASE64_ENCODE_FAILED "base64 encode failed" -#define DUK_STR_SOURCE_DECODE_FAILED "source decode failed" -#define DUK_STR_UTF8_DECODE_FAILED "utf-8 decode failed" -#define DUK_STR_BASE64_DECODE_FAILED "base64 decode failed" -#define DUK_STR_HEX_DECODE_FAILED "hex decode failed" -#define DUK_STR_INVALID_BYTECODE "invalid bytecode" -#define DUK_STR_NO_SOURCECODE "no sourcecode" -#define DUK_STR_RESULT_TOO_LONG "result too long" -#define DUK_STR_INVALID_CFUNC_RC "invalid C function rc" -#define DUK_STR_INVALID_INSTANCEOF_RVAL "invalid instanceof rval" -#define DUK_STR_INVALID_INSTANCEOF_RVAL_NOPROTO "instanceof rval has no .prototype" - -/* JSON */ -#define DUK_STR_FMT_PTR "%p" -#define DUK_STR_FMT_INVALID_JSON "invalid json (at offset %ld)" -#define DUK_STR_CYCLIC_INPUT "cyclic input" - -/* Generic codec */ -#define DUK_STR_DEC_RECLIMIT "decode recursion limit" -#define DUK_STR_ENC_RECLIMIT "encode recursion limit" - -/* Object property access */ -#define DUK_STR_INVALID_BASE "invalid base value" -#define DUK_STR_STRICT_CALLER_READ "cannot read strict 'caller'" -#define DUK_STR_PROXY_REJECTED "proxy rejected" -#define DUK_STR_INVALID_ARRAY_LENGTH "invalid array length" -#define DUK_STR_SETTER_UNDEFINED "setter undefined" -#define DUK_STR_INVALID_DESCRIPTOR "invalid descriptor" - -/* Proxy */ -#define DUK_STR_PROXY_REVOKED "proxy revoked" -#define DUK_STR_INVALID_TRAP_RESULT "invalid trap result" - -/* Variables */ - -/* Lexer */ -#define DUK_STR_INVALID_ESCAPE "invalid escape" -#define DUK_STR_UNTERMINATED_STRING "unterminated string" -#define DUK_STR_UNTERMINATED_COMMENT "unterminated comment" -#define DUK_STR_UNTERMINATED_REGEXP "unterminated regexp" -#define DUK_STR_TOKEN_LIMIT "token limit" -#define DUK_STR_REGEXP_SUPPORT_DISABLED "regexp support disabled" -#define DUK_STR_INVALID_NUMBER_LITERAL "invalid number literal" -#define DUK_STR_INVALID_TOKEN "invalid token" - -/* Compiler */ -#define DUK_STR_PARSE_ERROR "parse error" -#define DUK_STR_DUPLICATE_LABEL "duplicate label" -#define DUK_STR_INVALID_LABEL "invalid label" -#define DUK_STR_INVALID_ARRAY_LITERAL "invalid array literal" -#define DUK_STR_INVALID_OBJECT_LITERAL "invalid object literal" -#define DUK_STR_INVALID_VAR_DECLARATION "invalid variable declaration" -#define DUK_STR_CANNOT_DELETE_IDENTIFIER "cannot delete identifier" -#define DUK_STR_INVALID_EXPRESSION "invalid expression" -#define DUK_STR_INVALID_LVALUE "invalid lvalue" -#define DUK_STR_INVALID_NEWTARGET "invalid new.target" -#define DUK_STR_EXPECTED_IDENTIFIER "expected identifier" -#define DUK_STR_EMPTY_EXPR_NOT_ALLOWED "empty expression not allowed" -#define DUK_STR_INVALID_FOR "invalid for statement" -#define DUK_STR_INVALID_SWITCH "invalid switch statement" -#define DUK_STR_INVALID_BREAK_CONT_LABEL "invalid break/continue label" -#define DUK_STR_INVALID_RETURN "invalid return" -#define DUK_STR_INVALID_TRY "invalid try" -#define DUK_STR_INVALID_THROW "invalid throw" -#define DUK_STR_WITH_IN_STRICT_MODE "with in strict mode" -#define DUK_STR_FUNC_STMT_NOT_ALLOWED "function statement not allowed" -#define DUK_STR_UNTERMINATED_STMT "unterminated statement" -#define DUK_STR_INVALID_ARG_NAME "invalid argument name" -#define DUK_STR_INVALID_FUNC_NAME "invalid function name" -#define DUK_STR_INVALID_GETSET_NAME "invalid getter/setter name" -#define DUK_STR_FUNC_NAME_REQUIRED "function name required" - -/* RegExp */ -#define DUK_STR_INVALID_QUANTIFIER "invalid regexp quantifier" -#define DUK_STR_INVALID_QUANTIFIER_NO_ATOM "quantifier without preceding atom" -#define DUK_STR_INVALID_QUANTIFIER_VALUES "quantifier values invalid (qmin > qmax)" -#define DUK_STR_QUANTIFIER_TOO_MANY_COPIES "quantifier requires too many atom copies" -#define DUK_STR_UNEXPECTED_CLOSING_PAREN "unexpected closing parenthesis" -#define DUK_STR_UNEXPECTED_END_OF_PATTERN "unexpected end of pattern" -#define DUK_STR_UNEXPECTED_REGEXP_TOKEN "unexpected token in regexp" -#define DUK_STR_INVALID_REGEXP_FLAGS "invalid regexp flags" -#define DUK_STR_INVALID_REGEXP_ESCAPE "invalid regexp escape" -#define DUK_STR_INVALID_BACKREFS "invalid backreference(s)" -#define DUK_STR_INVALID_REGEXP_CHARACTER "invalid regexp character" -#define DUK_STR_INVALID_REGEXP_GROUP "invalid regexp group" -#define DUK_STR_UNTERMINATED_CHARCLASS "unterminated character class" -#define DUK_STR_INVALID_RANGE "invalid range" - -/* Limits */ -#define DUK_STR_VALSTACK_LIMIT "valstack limit" -#define DUK_STR_CALLSTACK_LIMIT "callstack limit" -#define DUK_STR_PROTOTYPE_CHAIN_LIMIT "prototype chain limit" -#define DUK_STR_BOUND_CHAIN_LIMIT "function call bound chain limit" -#define DUK_STR_NATIVE_STACK_LIMIT "C stack depth limit" -#define DUK_STR_COMPILER_RECURSION_LIMIT "compiler recursion limit" -#define DUK_STR_BYTECODE_LIMIT "bytecode limit" -#define DUK_STR_REG_LIMIT "register limit" -#define DUK_STR_TEMP_LIMIT "temp limit" -#define DUK_STR_CONST_LIMIT "const limit" -#define DUK_STR_FUNC_LIMIT "function limit" -#define DUK_STR_REGEXP_COMPILER_RECURSION_LIMIT "regexp compiler recursion limit" -#define DUK_STR_REGEXP_EXECUTOR_RECURSION_LIMIT "regexp executor recursion limit" -#define DUK_STR_REGEXP_EXECUTOR_STEP_LIMIT "regexp step limit" - -#endif /* DUK_ERRMSG_H_INCLUDED */ -/* #include duk_js_bytecode.h */ -/* - * ECMAScript bytecode - */ - -#if !defined(DUK_JS_BYTECODE_H_INCLUDED) -#define DUK_JS_BYTECODE_H_INCLUDED - -/* - * Bytecode instruction layout - * =========================== - * - * Instructions are unsigned 32-bit integers divided as follows: - * - * !3!3!2!2!2!2!2!2!2!2!2!2!1!1!1!1!1!1!1!1!1!1! ! ! ! ! ! ! ! ! ! ! - * !1!0!9!8!7!6!5!4!3!2!1!0!9!8!7!6!5!4!3!2!1!0!9!8!7!6!5!4!3!2!1!0! - * +-----------------------------------------------+---------------+ - * ! C ! B ! A ! OP ! - * +-----------------------------------------------+---------------+ - * - * OP (8 bits): opcode (DUK_OP_*), access should be fastest - * consecutive opcodes allocated when opcode needs flags - * A (8 bits): typically a target register number - * B (8 bits): typically first source register/constant number - * C (8 bits): typically second source register/constant number - * - * Some instructions combine BC or ABC together for larger parameter values. - * Signed integers (e.g. jump offsets) are encoded as unsigned, with an - * opcode specific bias. - * - * Some opcodes have flags which are handled by allocating consecutive - * opcodes to make space for 1-N flags. Flags can also be e.g. in the 'A' - * field when there's room for the specific opcode. - * - * For example, if three flags were needed, they could be allocated from - * the opcode field as follows: - * - * !3!3!2!2!2!2!2!2!2!2!2!2!1!1!1!1!1!1!1!1!1!1! ! ! ! ! ! ! ! ! ! ! - * !1!0!9!8!7!6!5!4!3!2!1!0!9!8!7!6!5!4!3!2!1!0!9!8!7!6!5!4!3!2!1!0! - * +-----------------------------------------------+---------------+ - * ! C ! B ! A ! OP !Z!Y!X! - * +-----------------------------------------------+---------------+ - * - * Some opcodes accept a reg/const argument which is handled by allocating - * flags in the OP field, see DUK_BC_ISREG() and DUK_BC_ISCONST(). The - * following convention is shared by most opcodes, so that the compiler - * can handle reg/const flagging without opcode specific code paths: - * - * !3!3!2!2!2!2!2!2!2!2!2!2!1!1!1!1!1!1!1!1!1!1! ! ! ! ! ! ! ! ! ! ! - * !1!0!9!8!7!6!5!4!3!2!1!0!9!8!7!6!5!4!3!2!1!0!9!8!7!6!5!4!3!2!1!0! - * +-----------------------------------------------+---------------+ - * ! C ! B ! A ! OP !Y!X! - * +-----------------------------------------------+---------------+ - * - * X 1=B is const, 0=B is reg - * Y 1=C is const, 0=C is reg - * - * In effect OP, OP + 1, OP + 2, and OP + 3 are allocated from the - * 8-bit opcode space for a single logical opcode. The base opcode - * number should be divisible by 4. If the opcode is called 'FOO' - * the following opcode constants would be defined: - * - * DUK_OP_FOO 100 // base opcode number - * DUK_OP_FOO_RR 100 // FOO, B=reg, C=reg - * DUK_OP_FOO_CR 101 // FOO, B=const, C=reg - * DUK_OP_FOO_RC 102 // FOO, B=reg, C=const - * DUK_OP_FOO_CC 103 // FOO, B=const, C=const - * - * If only B or C is a reg/const, the unused opcode combinations can be - * used for other opcodes (which take no reg/const argument). However, - * such opcode values are initially reserved, at least while opcode space - * is available. For example, if 'BAR' uses B for a register field and - * C is a reg/const: - * - * DUK_OP_BAR 116 // base opcode number - * DUK_OP_BAR_RR 116 // BAR, B=reg, C=reg - * DUK_OP_BAR_CR_UNUSED 117 // unused, could be repurposed - * DUK_OP_BAR_RC 118 // BAR, B=reg, C=const - * DUK_OP_BAR_CC_UNUSED 119 // unused, could be repurposed - * - * Macro naming is a bit misleading, e.g. "ABC" in macro name but the - * field layout is concretely "CBA" in the register. - */ - -typedef duk_uint32_t duk_instr_t; - -#define DUK_BC_SHIFT_OP 0 -#define DUK_BC_SHIFT_A 8 -#define DUK_BC_SHIFT_B 16 -#define DUK_BC_SHIFT_C 24 -#define DUK_BC_SHIFT_BC DUK_BC_SHIFT_B -#define DUK_BC_SHIFT_ABC DUK_BC_SHIFT_A - -#define DUK_BC_UNSHIFTED_MASK_OP 0xffUL -#define DUK_BC_UNSHIFTED_MASK_A 0xffUL -#define DUK_BC_UNSHIFTED_MASK_B 0xffUL -#define DUK_BC_UNSHIFTED_MASK_C 0xffUL -#define DUK_BC_UNSHIFTED_MASK_BC 0xffffUL -#define DUK_BC_UNSHIFTED_MASK_ABC 0xffffffUL - -#define DUK_BC_SHIFTED_MASK_OP (DUK_BC_UNSHIFTED_MASK_OP << DUK_BC_SHIFT_OP) -#define DUK_BC_SHIFTED_MASK_A (DUK_BC_UNSHIFTED_MASK_A << DUK_BC_SHIFT_A) -#define DUK_BC_SHIFTED_MASK_B (DUK_BC_UNSHIFTED_MASK_B << DUK_BC_SHIFT_B) -#define DUK_BC_SHIFTED_MASK_C (DUK_BC_UNSHIFTED_MASK_C << DUK_BC_SHIFT_C) -#define DUK_BC_SHIFTED_MASK_BC (DUK_BC_UNSHIFTED_MASK_BC << DUK_BC_SHIFT_BC) -#define DUK_BC_SHIFTED_MASK_ABC (DUK_BC_UNSHIFTED_MASK_ABC << DUK_BC_SHIFT_ABC) - -#define DUK_DEC_OP(x) ((x) &0xffUL) -#define DUK_DEC_A(x) (((x) >> 8) & 0xffUL) -#define DUK_DEC_B(x) (((x) >> 16) & 0xffUL) -#define DUK_DEC_C(x) (((x) >> 24) & 0xffUL) -#define DUK_DEC_BC(x) (((x) >> 16) & 0xffffUL) -#define DUK_DEC_ABC(x) (((x) >> 8) & 0xffffffUL) - -#define DUK_ENC_OP(op) ((duk_instr_t) (op)) -#define DUK_ENC_OP_ABC(op, abc) ((duk_instr_t) ((((duk_instr_t) (abc)) << 8) | ((duk_instr_t) (op)))) -#define DUK_ENC_OP_A_BC(op, a, bc) \ - ((duk_instr_t) ((((duk_instr_t) (bc)) << 16) | (((duk_instr_t) (a)) << 8) | ((duk_instr_t) (op)))) -#define DUK_ENC_OP_A_B_C(op, a, b, c) \ - ((duk_instr_t) ((((duk_instr_t) (c)) << 24) | (((duk_instr_t) (b)) << 16) | (((duk_instr_t) (a)) << 8) | \ - ((duk_instr_t) (op)))) -#define DUK_ENC_OP_A_B(op, a, b) DUK_ENC_OP_A_B_C((op), (a), (b), 0) -#define DUK_ENC_OP_A(op, a) DUK_ENC_OP_A_B_C((op), (a), 0, 0) -#define DUK_ENC_OP_BC(op, bc) DUK_ENC_OP_A_BC((op), 0, (bc)) - -/* Get opcode base value with B/C reg/const flags cleared. */ -#define DUK_BC_NOREGCONST_OP(op) ((op) &0xfc) - -/* Constants should be signed so that signed arithmetic involving them - * won't cause values to be coerced accidentally to unsigned. - */ -#define DUK_BC_OP_MIN 0 -#define DUK_BC_OP_MAX 0xffL -#define DUK_BC_A_MIN 0 -#define DUK_BC_A_MAX 0xffL -#define DUK_BC_B_MIN 0 -#define DUK_BC_B_MAX 0xffL -#define DUK_BC_C_MIN 0 -#define DUK_BC_C_MAX 0xffL -#define DUK_BC_BC_MIN 0 -#define DUK_BC_BC_MAX 0xffffL -#define DUK_BC_ABC_MIN 0 -#define DUK_BC_ABC_MAX 0xffffffL - -/* Masks for B/C reg/const indicator in opcode field. */ -#define DUK_BC_REGCONST_B (0x01UL) -#define DUK_BC_REGCONST_C (0x02UL) - -/* Misc. masks for opcode field. */ -#define DUK_BC_INCDECP_FLAG_DEC (0x04UL) -#define DUK_BC_INCDECP_FLAG_POST (0x08UL) - -/* Opcodes. */ -#define DUK_OP_LDREG 0 -#define DUK_OP_STREG 1 -#define DUK_OP_JUMP 2 -#define DUK_OP_LDCONST 3 -#define DUK_OP_LDINT 4 -#define DUK_OP_LDINTX 5 -#define DUK_OP_LDTHIS 6 -#define DUK_OP_LDUNDEF 7 -#define DUK_OP_LDNULL 8 -#define DUK_OP_LDTRUE 9 -#define DUK_OP_LDFALSE 10 -#define DUK_OP_GETVAR 11 -#define DUK_OP_BNOT 12 -#define DUK_OP_LNOT 13 -#define DUK_OP_UNM 14 -#define DUK_OP_UNP 15 -#define DUK_OP_EQ 16 -#define DUK_OP_EQ_RR 16 -#define DUK_OP_EQ_CR 17 -#define DUK_OP_EQ_RC 18 -#define DUK_OP_EQ_CC 19 -#define DUK_OP_NEQ 20 -#define DUK_OP_NEQ_RR 20 -#define DUK_OP_NEQ_CR 21 -#define DUK_OP_NEQ_RC 22 -#define DUK_OP_NEQ_CC 23 -#define DUK_OP_SEQ 24 -#define DUK_OP_SEQ_RR 24 -#define DUK_OP_SEQ_CR 25 -#define DUK_OP_SEQ_RC 26 -#define DUK_OP_SEQ_CC 27 -#define DUK_OP_SNEQ 28 -#define DUK_OP_SNEQ_RR 28 -#define DUK_OP_SNEQ_CR 29 -#define DUK_OP_SNEQ_RC 30 -#define DUK_OP_SNEQ_CC 31 -#define DUK_OP_GT 32 -#define DUK_OP_GT_RR 32 -#define DUK_OP_GT_CR 33 -#define DUK_OP_GT_RC 34 -#define DUK_OP_GT_CC 35 -#define DUK_OP_GE 36 -#define DUK_OP_GE_RR 36 -#define DUK_OP_GE_CR 37 -#define DUK_OP_GE_RC 38 -#define DUK_OP_GE_CC 39 -#define DUK_OP_LT 40 -#define DUK_OP_LT_RR 40 -#define DUK_OP_LT_CR 41 -#define DUK_OP_LT_RC 42 -#define DUK_OP_LT_CC 43 -#define DUK_OP_LE 44 -#define DUK_OP_LE_RR 44 -#define DUK_OP_LE_CR 45 -#define DUK_OP_LE_RC 46 -#define DUK_OP_LE_CC 47 -#define DUK_OP_IFTRUE 48 -#define DUK_OP_IFTRUE_R 48 -#define DUK_OP_IFTRUE_C 49 -#define DUK_OP_IFFALSE 50 -#define DUK_OP_IFFALSE_R 50 -#define DUK_OP_IFFALSE_C 51 -#define DUK_OP_ADD 52 -#define DUK_OP_ADD_RR 52 -#define DUK_OP_ADD_CR 53 -#define DUK_OP_ADD_RC 54 -#define DUK_OP_ADD_CC 55 -#define DUK_OP_SUB 56 -#define DUK_OP_SUB_RR 56 -#define DUK_OP_SUB_CR 57 -#define DUK_OP_SUB_RC 58 -#define DUK_OP_SUB_CC 59 -#define DUK_OP_MUL 60 -#define DUK_OP_MUL_RR 60 -#define DUK_OP_MUL_CR 61 -#define DUK_OP_MUL_RC 62 -#define DUK_OP_MUL_CC 63 -#define DUK_OP_DIV 64 -#define DUK_OP_DIV_RR 64 -#define DUK_OP_DIV_CR 65 -#define DUK_OP_DIV_RC 66 -#define DUK_OP_DIV_CC 67 -#define DUK_OP_MOD 68 -#define DUK_OP_MOD_RR 68 -#define DUK_OP_MOD_CR 69 -#define DUK_OP_MOD_RC 70 -#define DUK_OP_MOD_CC 71 -#define DUK_OP_EXP 72 -#define DUK_OP_EXP_RR 72 -#define DUK_OP_EXP_CR 73 -#define DUK_OP_EXP_RC 74 -#define DUK_OP_EXP_CC 75 -#define DUK_OP_BAND 76 -#define DUK_OP_BAND_RR 76 -#define DUK_OP_BAND_CR 77 -#define DUK_OP_BAND_RC 78 -#define DUK_OP_BAND_CC 79 -#define DUK_OP_BOR 80 -#define DUK_OP_BOR_RR 80 -#define DUK_OP_BOR_CR 81 -#define DUK_OP_BOR_RC 82 -#define DUK_OP_BOR_CC 83 -#define DUK_OP_BXOR 84 -#define DUK_OP_BXOR_RR 84 -#define DUK_OP_BXOR_CR 85 -#define DUK_OP_BXOR_RC 86 -#define DUK_OP_BXOR_CC 87 -#define DUK_OP_BASL 88 -#define DUK_OP_BASL_RR 88 -#define DUK_OP_BASL_CR 89 -#define DUK_OP_BASL_RC 90 -#define DUK_OP_BASL_CC 91 -#define DUK_OP_BLSR 92 -#define DUK_OP_BLSR_RR 92 -#define DUK_OP_BLSR_CR 93 -#define DUK_OP_BLSR_RC 94 -#define DUK_OP_BLSR_CC 95 -#define DUK_OP_BASR 96 -#define DUK_OP_BASR_RR 96 -#define DUK_OP_BASR_CR 97 -#define DUK_OP_BASR_RC 98 -#define DUK_OP_BASR_CC 99 -#define DUK_OP_INSTOF 100 -#define DUK_OP_INSTOF_RR 100 -#define DUK_OP_INSTOF_CR 101 -#define DUK_OP_INSTOF_RC 102 -#define DUK_OP_INSTOF_CC 103 -#define DUK_OP_IN 104 -#define DUK_OP_IN_RR 104 -#define DUK_OP_IN_CR 105 -#define DUK_OP_IN_RC 106 -#define DUK_OP_IN_CC 107 -#define DUK_OP_GETPROP 108 -#define DUK_OP_GETPROP_RR 108 -#define DUK_OP_GETPROP_CR 109 -#define DUK_OP_GETPROP_RC 110 -#define DUK_OP_GETPROP_CC 111 -#define DUK_OP_PUTPROP 112 -#define DUK_OP_PUTPROP_RR 112 -#define DUK_OP_PUTPROP_CR 113 -#define DUK_OP_PUTPROP_RC 114 -#define DUK_OP_PUTPROP_CC 115 -#define DUK_OP_DELPROP 116 -#define DUK_OP_DELPROP_RR 116 -#define DUK_OP_DELPROP_CR_UNUSED 117 /* unused now */ -#define DUK_OP_DELPROP_RC 118 -#define DUK_OP_DELPROP_CC_UNUSED 119 /* unused now */ -#define DUK_OP_PREINCR 120 /* pre/post opcode values have constraints, */ -#define DUK_OP_PREDECR 121 /* see duk_js_executor.c and duk_js_compiler.c. */ -#define DUK_OP_POSTINCR 122 -#define DUK_OP_POSTDECR 123 -#define DUK_OP_PREINCV 124 -#define DUK_OP_PREDECV 125 -#define DUK_OP_POSTINCV 126 -#define DUK_OP_POSTDECV 127 -#define DUK_OP_PREINCP 128 /* pre/post inc/dec prop opcodes have constraints */ -#define DUK_OP_PREINCP_RR 128 -#define DUK_OP_PREINCP_CR 129 -#define DUK_OP_PREINCP_RC 130 -#define DUK_OP_PREINCP_CC 131 -#define DUK_OP_PREDECP 132 -#define DUK_OP_PREDECP_RR 132 -#define DUK_OP_PREDECP_CR 133 -#define DUK_OP_PREDECP_RC 134 -#define DUK_OP_PREDECP_CC 135 -#define DUK_OP_POSTINCP 136 -#define DUK_OP_POSTINCP_RR 136 -#define DUK_OP_POSTINCP_CR 137 -#define DUK_OP_POSTINCP_RC 138 -#define DUK_OP_POSTINCP_CC 139 -#define DUK_OP_POSTDECP 140 -#define DUK_OP_POSTDECP_RR 140 -#define DUK_OP_POSTDECP_CR 141 -#define DUK_OP_POSTDECP_RC 142 -#define DUK_OP_POSTDECP_CC 143 -#define DUK_OP_DECLVAR 144 -#define DUK_OP_DECLVAR_RR 144 -#define DUK_OP_DECLVAR_CR 145 -#define DUK_OP_DECLVAR_RC 146 -#define DUK_OP_DECLVAR_CC 147 -#define DUK_OP_REGEXP 148 -#define DUK_OP_REGEXP_RR 148 -#define DUK_OP_REGEXP_CR 149 -#define DUK_OP_REGEXP_RC 150 -#define DUK_OP_REGEXP_CC 151 -#define DUK_OP_CLOSURE 152 -#define DUK_OP_TYPEOF 153 -#define DUK_OP_TYPEOFID 154 -#define DUK_OP_PUTVAR 155 -#define DUK_OP_DELVAR 156 -#define DUK_OP_RETREG 157 -#define DUK_OP_RETUNDEF 158 -#define DUK_OP_RETCONST 159 -#define DUK_OP_RETCONSTN 160 /* return const without incref (e.g. number) */ -#define DUK_OP_LABEL 161 -#define DUK_OP_ENDLABEL 162 -#define DUK_OP_BREAK 163 -#define DUK_OP_CONTINUE 164 -#define DUK_OP_TRYCATCH 165 -#define DUK_OP_ENDTRY 166 -#define DUK_OP_ENDCATCH 167 -#define DUK_OP_ENDFIN 168 -#define DUK_OP_THROW 169 -#define DUK_OP_INVLHS 170 -#define DUK_OP_CSREG 171 -#define DUK_OP_CSVAR 172 -#define DUK_OP_CSVAR_RR 172 -#define DUK_OP_CSVAR_CR 173 -#define DUK_OP_CSVAR_RC 174 -#define DUK_OP_CSVAR_CC 175 -#define DUK_OP_CALL0 176 /* DUK_OP_CALL0 & 0x0F must be zero. */ -#define DUK_OP_CALL1 177 -#define DUK_OP_CALL2 178 -#define DUK_OP_CALL3 179 -#define DUK_OP_CALL4 180 -#define DUK_OP_CALL5 181 -#define DUK_OP_CALL6 182 -#define DUK_OP_CALL7 183 -#define DUK_OP_CALL8 184 -#define DUK_OP_CALL9 185 -#define DUK_OP_CALL10 186 -#define DUK_OP_CALL11 187 -#define DUK_OP_CALL12 188 -#define DUK_OP_CALL13 189 -#define DUK_OP_CALL14 190 -#define DUK_OP_CALL15 191 -#define DUK_OP_NEWOBJ 192 -#define DUK_OP_NEWARR 193 -#define DUK_OP_MPUTOBJ 194 -#define DUK_OP_MPUTOBJI 195 -#define DUK_OP_INITSET 196 -#define DUK_OP_INITGET 197 -#define DUK_OP_MPUTARR 198 -#define DUK_OP_MPUTARRI 199 -#define DUK_OP_SETALEN 200 -#define DUK_OP_INITENUM 201 -#define DUK_OP_NEXTENUM 202 -#define DUK_OP_NEWTARGET 203 -#define DUK_OP_DEBUGGER 204 -#define DUK_OP_NOP 205 -#define DUK_OP_INVALID 206 -#define DUK_OP_UNUSED207 207 -#define DUK_OP_GETPROPC 208 -#define DUK_OP_GETPROPC_RR 208 -#define DUK_OP_GETPROPC_CR 209 -#define DUK_OP_GETPROPC_RC 210 -#define DUK_OP_GETPROPC_CC 211 -#define DUK_OP_UNUSED212 212 -#define DUK_OP_UNUSED213 213 -#define DUK_OP_UNUSED214 214 -#define DUK_OP_UNUSED215 215 -#define DUK_OP_UNUSED216 216 -#define DUK_OP_UNUSED217 217 -#define DUK_OP_UNUSED218 218 -#define DUK_OP_UNUSED219 219 -#define DUK_OP_UNUSED220 220 -#define DUK_OP_UNUSED221 221 -#define DUK_OP_UNUSED222 222 -#define DUK_OP_UNUSED223 223 -#define DUK_OP_UNUSED224 224 -#define DUK_OP_UNUSED225 225 -#define DUK_OP_UNUSED226 226 -#define DUK_OP_UNUSED227 227 -#define DUK_OP_UNUSED228 228 -#define DUK_OP_UNUSED229 229 -#define DUK_OP_UNUSED230 230 -#define DUK_OP_UNUSED231 231 -#define DUK_OP_UNUSED232 232 -#define DUK_OP_UNUSED233 233 -#define DUK_OP_UNUSED234 234 -#define DUK_OP_UNUSED235 235 -#define DUK_OP_UNUSED236 236 -#define DUK_OP_UNUSED237 237 -#define DUK_OP_UNUSED238 238 -#define DUK_OP_UNUSED239 239 -#define DUK_OP_UNUSED240 240 -#define DUK_OP_UNUSED241 241 -#define DUK_OP_UNUSED242 242 -#define DUK_OP_UNUSED243 243 -#define DUK_OP_UNUSED244 244 -#define DUK_OP_UNUSED245 245 -#define DUK_OP_UNUSED246 246 -#define DUK_OP_UNUSED247 247 -#define DUK_OP_UNUSED248 248 -#define DUK_OP_UNUSED249 249 -#define DUK_OP_UNUSED250 250 -#define DUK_OP_UNUSED251 251 -#define DUK_OP_UNUSED252 252 -#define DUK_OP_UNUSED253 253 -#define DUK_OP_UNUSED254 254 -#define DUK_OP_UNUSED255 255 -#define DUK_OP_NONE 256 /* dummy value used as marker (doesn't fit in 8-bit field) */ - -/* XXX: Allocate flags from opcode field? Would take 16 opcode slots - * but avoids shuffling in more cases. Maybe not worth it. - */ -/* DUK_OP_TRYCATCH flags in A. */ -#define DUK_BC_TRYCATCH_FLAG_HAVE_CATCH (1U << 0) -#define DUK_BC_TRYCATCH_FLAG_HAVE_FINALLY (1U << 1) -#define DUK_BC_TRYCATCH_FLAG_CATCH_BINDING (1U << 2) -#define DUK_BC_TRYCATCH_FLAG_WITH_BINDING (1U << 3) - -/* DUK_OP_DECLVAR flags in A; bottom bits are reserved for propdesc flags - * (DUK_PROPDESC_FLAG_XXX). - */ -#define DUK_BC_DECLVAR_FLAG_FUNC_DECL (1U << 4) /* function declaration */ - -/* DUK_OP_CALLn flags, part of opcode field. Three lowest bits must match - * DUK_CALL_FLAG_xxx directly. - */ -#define DUK_BC_CALL_FLAG_TAILCALL (1U << 0) -#define DUK_BC_CALL_FLAG_CONSTRUCT (1U << 1) -#define DUK_BC_CALL_FLAG_CALLED_AS_EVAL (1U << 2) -#define DUK_BC_CALL_FLAG_INDIRECT (1U << 3) - -/* Misc constants and helper macros. */ -#define DUK_BC_LDINT_BIAS (1L << 15) -#define DUK_BC_LDINTX_SHIFT 16 -#define DUK_BC_JUMP_BIAS (1L << 23) - -#endif /* DUK_JS_BYTECODE_H_INCLUDED */ -/* #include duk_lexer.h */ -/* - * Lexer defines. - */ - -#if !defined(DUK_LEXER_H_INCLUDED) -#define DUK_LEXER_H_INCLUDED - -typedef void (*duk_re_range_callback)(void *user, duk_codepoint_t r1, duk_codepoint_t r2, duk_bool_t direct); - -/* - * A token is interpreted as any possible production of InputElementDiv - * and InputElementRegExp, see E5 Section 7 in its entirety. Note that - * the E5 "Token" production does not cover all actual tokens of the - * language (which is explicitly stated in the specification, Section 7.5). - * Null and boolean literals are defined as part of both ReservedWord - * (E5 Section 7.6.1) and Literal (E5 Section 7.8) productions. Here, - * null and boolean values have literal tokens, and are not reserved - * words. - * - * Decimal literal negative/positive sign is -not- part of DUK_TOK_NUMBER. - * The number tokens always have a non-negative value. The unary minus - * operator in "-1.0" is optimized during compilation to yield a single - * negative constant. - * - * Token numbering is free except that reserved words are required to be - * in a continuous range and in a particular order. See genstrings.py. - */ - -#define DUK_LEXER_INITCTX(ctx) duk_lexer_initctx((ctx)) - -#define DUK_LEXER_SETPOINT(ctx, pt) duk_lexer_setpoint((ctx), (pt)) - -#define DUK_LEXER_GETPOINT(ctx, pt) duk_lexer_getpoint((ctx), (pt)) - -/* Currently 6 characters of lookup are actually needed (duk_lexer.c). */ -#define DUK_LEXER_WINDOW_SIZE 6 -#if defined(DUK_USE_LEXER_SLIDING_WINDOW) -#define DUK_LEXER_BUFFER_SIZE 64 -#endif - -#define DUK_TOK_MINVAL 0 - -/* returned after EOF (infinite amount) */ -#define DUK_TOK_EOF 0 - -/* identifier names (E5 Section 7.6) */ -#define DUK_TOK_IDENTIFIER 1 - -/* reserved words: keywords */ -#define DUK_TOK_START_RESERVED 2 -#define DUK_TOK_BREAK 2 -#define DUK_TOK_CASE 3 -#define DUK_TOK_CATCH 4 -#define DUK_TOK_CONTINUE 5 -#define DUK_TOK_DEBUGGER 6 -#define DUK_TOK_DEFAULT 7 -#define DUK_TOK_DELETE 8 -#define DUK_TOK_DO 9 -#define DUK_TOK_ELSE 10 -#define DUK_TOK_FINALLY 11 -#define DUK_TOK_FOR 12 -#define DUK_TOK_FUNCTION 13 -#define DUK_TOK_IF 14 -#define DUK_TOK_IN 15 -#define DUK_TOK_INSTANCEOF 16 -#define DUK_TOK_NEW 17 -#define DUK_TOK_RETURN 18 -#define DUK_TOK_SWITCH 19 -#define DUK_TOK_THIS 20 -#define DUK_TOK_THROW 21 -#define DUK_TOK_TRY 22 -#define DUK_TOK_TYPEOF 23 -#define DUK_TOK_VAR 24 -#define DUK_TOK_CONST 25 -#define DUK_TOK_VOID 26 -#define DUK_TOK_WHILE 27 -#define DUK_TOK_WITH 28 - -/* reserved words: future reserved words */ -#define DUK_TOK_CLASS 29 -#define DUK_TOK_ENUM 30 -#define DUK_TOK_EXPORT 31 -#define DUK_TOK_EXTENDS 32 -#define DUK_TOK_IMPORT 33 -#define DUK_TOK_SUPER 34 - -/* "null", "true", and "false" are always reserved words. - * Note that "get" and "set" are not! - */ -#define DUK_TOK_NULL 35 -#define DUK_TOK_TRUE 36 -#define DUK_TOK_FALSE 37 - -/* reserved words: additional future reserved words in strict mode */ -#define DUK_TOK_START_STRICT_RESERVED 38 /* inclusive */ -#define DUK_TOK_IMPLEMENTS 38 -#define DUK_TOK_INTERFACE 39 -#define DUK_TOK_LET 40 -#define DUK_TOK_PACKAGE 41 -#define DUK_TOK_PRIVATE 42 -#define DUK_TOK_PROTECTED 43 -#define DUK_TOK_PUBLIC 44 -#define DUK_TOK_STATIC 45 -#define DUK_TOK_YIELD 46 - -#define DUK_TOK_END_RESERVED 47 /* exclusive */ - -/* "get" and "set" are tokens but NOT ReservedWords. They are currently - * parsed and identifiers and these defines are actually now unused. - */ -#define DUK_TOK_GET 47 -#define DUK_TOK_SET 48 - -/* punctuators (unlike the spec, also includes "/" and "/=") */ -#define DUK_TOK_LCURLY 49 -#define DUK_TOK_RCURLY 50 -#define DUK_TOK_LBRACKET 51 -#define DUK_TOK_RBRACKET 52 -#define DUK_TOK_LPAREN 53 -#define DUK_TOK_RPAREN 54 -#define DUK_TOK_PERIOD 55 -#define DUK_TOK_SEMICOLON 56 -#define DUK_TOK_COMMA 57 -#define DUK_TOK_LT 58 -#define DUK_TOK_GT 59 -#define DUK_TOK_LE 60 -#define DUK_TOK_GE 61 -#define DUK_TOK_EQ 62 -#define DUK_TOK_NEQ 63 -#define DUK_TOK_SEQ 64 -#define DUK_TOK_SNEQ 65 -#define DUK_TOK_ADD 66 -#define DUK_TOK_SUB 67 -#define DUK_TOK_MUL 68 -#define DUK_TOK_DIV 69 -#define DUK_TOK_MOD 70 -#define DUK_TOK_EXP 71 -#define DUK_TOK_INCREMENT 72 -#define DUK_TOK_DECREMENT 73 -#define DUK_TOK_ALSHIFT 74 /* named "arithmetic" because result is signed */ -#define DUK_TOK_ARSHIFT 75 -#define DUK_TOK_RSHIFT 76 -#define DUK_TOK_BAND 77 -#define DUK_TOK_BOR 78 -#define DUK_TOK_BXOR 79 -#define DUK_TOK_LNOT 80 -#define DUK_TOK_BNOT 81 -#define DUK_TOK_LAND 82 -#define DUK_TOK_LOR 83 -#define DUK_TOK_QUESTION 84 -#define DUK_TOK_COLON 85 -#define DUK_TOK_EQUALSIGN 86 -#define DUK_TOK_ADD_EQ 87 -#define DUK_TOK_SUB_EQ 88 -#define DUK_TOK_MUL_EQ 89 -#define DUK_TOK_DIV_EQ 90 -#define DUK_TOK_MOD_EQ 91 -#define DUK_TOK_EXP_EQ 92 -#define DUK_TOK_ALSHIFT_EQ 93 -#define DUK_TOK_ARSHIFT_EQ 94 -#define DUK_TOK_RSHIFT_EQ 95 -#define DUK_TOK_BAND_EQ 96 -#define DUK_TOK_BOR_EQ 97 -#define DUK_TOK_BXOR_EQ 98 - -/* literals (E5 Section 7.8), except null, true, false, which are treated - * like reserved words (above). - */ -#define DUK_TOK_NUMBER 99 -#define DUK_TOK_STRING 100 -#define DUK_TOK_REGEXP 101 - -#define DUK_TOK_MAXVAL 101 /* inclusive */ - -#define DUK_TOK_INVALID DUK_SMALL_UINT_MAX - -/* Convert heap string index to a token (reserved words) */ -#define DUK_STRIDX_TO_TOK(x) ((x) -DUK_STRIDX_START_RESERVED + DUK_TOK_START_RESERVED) - -/* Sanity check */ -#if (DUK_TOK_MAXVAL > 255) -#error DUK_TOK_MAXVAL too large, code assumes it fits into 8 bits -#endif - -/* Sanity checks for string and token defines */ -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_BREAK) != DUK_TOK_BREAK) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_CASE) != DUK_TOK_CASE) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_CATCH) != DUK_TOK_CATCH) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_CONTINUE) != DUK_TOK_CONTINUE) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_DEBUGGER) != DUK_TOK_DEBUGGER) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_DEFAULT) != DUK_TOK_DEFAULT) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_DELETE) != DUK_TOK_DELETE) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_DO) != DUK_TOK_DO) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_ELSE) != DUK_TOK_ELSE) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_FINALLY) != DUK_TOK_FINALLY) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_FOR) != DUK_TOK_FOR) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_LC_FUNCTION) != DUK_TOK_FUNCTION) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_IF) != DUK_TOK_IF) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_IN) != DUK_TOK_IN) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_INSTANCEOF) != DUK_TOK_INSTANCEOF) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_NEW) != DUK_TOK_NEW) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_RETURN) != DUK_TOK_RETURN) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_SWITCH) != DUK_TOK_SWITCH) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_THIS) != DUK_TOK_THIS) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_THROW) != DUK_TOK_THROW) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_TRY) != DUK_TOK_TRY) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_TYPEOF) != DUK_TOK_TYPEOF) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_VAR) != DUK_TOK_VAR) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_VOID) != DUK_TOK_VOID) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_WHILE) != DUK_TOK_WHILE) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_WITH) != DUK_TOK_WITH) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_CLASS) != DUK_TOK_CLASS) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_CONST) != DUK_TOK_CONST) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_ENUM) != DUK_TOK_ENUM) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_EXPORT) != DUK_TOK_EXPORT) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_EXTENDS) != DUK_TOK_EXTENDS) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_IMPORT) != DUK_TOK_IMPORT) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_SUPER) != DUK_TOK_SUPER) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_LC_NULL) != DUK_TOK_NULL) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_TRUE) != DUK_TOK_TRUE) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_FALSE) != DUK_TOK_FALSE) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_IMPLEMENTS) != DUK_TOK_IMPLEMENTS) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_INTERFACE) != DUK_TOK_INTERFACE) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_LET) != DUK_TOK_LET) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_PACKAGE) != DUK_TOK_PACKAGE) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_PRIVATE) != DUK_TOK_PRIVATE) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_PROTECTED) != DUK_TOK_PROTECTED) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_PUBLIC) != DUK_TOK_PUBLIC) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_STATIC) != DUK_TOK_STATIC) -#error mismatch in token defines -#endif -#if (DUK_STRIDX_TO_TOK(DUK_STRIDX_YIELD) != DUK_TOK_YIELD) -#error mismatch in token defines -#endif - -/* Regexp tokens */ -#define DUK_RETOK_EOF 0 -#define DUK_RETOK_DISJUNCTION 1 -#define DUK_RETOK_QUANTIFIER 2 -#define DUK_RETOK_ASSERT_START 3 -#define DUK_RETOK_ASSERT_END 4 -#define DUK_RETOK_ASSERT_WORD_BOUNDARY 5 -#define DUK_RETOK_ASSERT_NOT_WORD_BOUNDARY 6 -#define DUK_RETOK_ASSERT_START_POS_LOOKAHEAD 7 -#define DUK_RETOK_ASSERT_START_NEG_LOOKAHEAD 8 -#define DUK_RETOK_ATOM_PERIOD 9 -#define DUK_RETOK_ATOM_CHAR 10 -#define DUK_RETOK_ATOM_DIGIT 11 /* assumptions in regexp compiler */ -#define DUK_RETOK_ATOM_NOT_DIGIT 12 /* -""- */ -#define DUK_RETOK_ATOM_WHITE 13 /* -""- */ -#define DUK_RETOK_ATOM_NOT_WHITE 14 /* -""- */ -#define DUK_RETOK_ATOM_WORD_CHAR 15 /* -""- */ -#define DUK_RETOK_ATOM_NOT_WORD_CHAR 16 /* -""- */ -#define DUK_RETOK_ATOM_BACKREFERENCE 17 -#define DUK_RETOK_ATOM_START_CAPTURE_GROUP 18 -#define DUK_RETOK_ATOM_START_NONCAPTURE_GROUP 19 -#define DUK_RETOK_ATOM_START_CHARCLASS 20 -#define DUK_RETOK_ATOM_START_CHARCLASS_INVERTED 21 -#define DUK_RETOK_ATOM_END_GROUP 22 - -/* Constants for duk_lexer_ctx.buf. */ -#define DUK_LEXER_TEMP_BUF_LIMIT 256 - -/* A token value. Can be memcpy()'d, but note that slot1/slot2 values are on the valstack. - * Some fields (like num, str1, str2) are only valid for specific token types and may have - * stale values otherwise. - */ -struct duk_token { - duk_small_uint_t t; /* token type (with reserved word identification) */ - duk_small_uint_t t_nores; /* token type (with reserved words as DUK_TOK_IDENTIFER) */ - duk_double_t num; /* numeric value of token */ - duk_hstring *str1; /* string 1 of token (borrowed, stored to ctx->slot1_idx) */ - duk_hstring *str2; /* string 2 of token (borrowed, stored to ctx->slot2_idx) */ - duk_size_t start_offset; /* start byte offset of token in lexer input */ - duk_int_t start_line; /* start line of token (first char) */ - duk_int_t num_escapes; /* number of escapes and line continuations (for directive prologue) */ - duk_bool_t lineterm; /* token was preceded by a lineterm */ - duk_bool_t allow_auto_semi; /* token allows automatic semicolon insertion (eof or preceded by newline) */ -}; - -#define DUK_RE_QUANTIFIER_INFINITE ((duk_uint32_t) 0xffffffffUL) - -/* A regexp token value. */ -struct duk_re_token { - duk_small_uint_t t; /* token type */ - duk_small_uint_t greedy; - duk_uint32_t num; /* numeric value (character, count) */ - duk_uint32_t qmin; - duk_uint32_t qmax; -}; - -/* A structure for 'snapshotting' a point for rewinding */ -struct duk_lexer_point { - duk_size_t offset; - duk_int_t line; -}; - -/* Lexer codepoint with additional info like offset/line number */ -struct duk_lexer_codepoint { - duk_codepoint_t codepoint; - duk_size_t offset; - duk_int_t line; -}; - -/* Lexer context. Same context is used for ECMAScript and Regexp parsing. */ -struct duk_lexer_ctx { -#if defined(DUK_USE_LEXER_SLIDING_WINDOW) - duk_lexer_codepoint *window; /* unicode code points, window[0] is always next, points to 'buffer' */ - duk_lexer_codepoint buffer[DUK_LEXER_BUFFER_SIZE]; -#else - duk_lexer_codepoint window[DUK_LEXER_WINDOW_SIZE]; /* unicode code points, window[0] is always next */ -#endif - - duk_hthread *thr; /* thread; minimizes argument passing */ - - const duk_uint8_t *input; /* input string (may be a user pointer) */ - duk_size_t input_length; /* input byte length */ - duk_size_t input_offset; /* input offset for window leading edge (not window[0]) */ - duk_int_t input_line; /* input linenumber at input_offset (not window[0]), init to 1 */ - - duk_idx_t slot1_idx; /* valstack slot for 1st token value */ - duk_idx_t slot2_idx; /* valstack slot for 2nd token value */ - duk_idx_t buf_idx; /* valstack slot for temp buffer */ - duk_hbuffer_dynamic *buf; /* temp accumulation buffer */ - duk_bufwriter_ctx bw; /* bufwriter for temp accumulation */ - - duk_int_t token_count; /* number of tokens parsed */ - duk_int_t token_limit; /* maximum token count before error (sanity backstop) */ - - duk_small_uint_t flags; /* lexer flags, use compiler flag defines for now */ -}; - -/* - * Prototypes - */ - -DUK_INTERNAL_DECL void duk_lexer_initctx(duk_lexer_ctx *lex_ctx); - -DUK_INTERNAL_DECL void duk_lexer_getpoint(duk_lexer_ctx *lex_ctx, duk_lexer_point *pt); -DUK_INTERNAL_DECL void duk_lexer_setpoint(duk_lexer_ctx *lex_ctx, duk_lexer_point *pt); - -DUK_INTERNAL_DECL -void duk_lexer_parse_js_input_element(duk_lexer_ctx *lex_ctx, duk_token *out_token, duk_bool_t strict_mode, duk_bool_t regexp_mode); -#if defined(DUK_USE_REGEXP_SUPPORT) -DUK_INTERNAL_DECL void duk_lexer_parse_re_token(duk_lexer_ctx *lex_ctx, duk_re_token *out_token); -DUK_INTERNAL_DECL void duk_lexer_parse_re_ranges(duk_lexer_ctx *lex_ctx, duk_re_range_callback gen_range, void *userdata); -#endif /* DUK_USE_REGEXP_SUPPORT */ - -#endif /* DUK_LEXER_H_INCLUDED */ -/* #include duk_js_compiler.h */ -/* - * ECMAScript compiler. - */ - -#if !defined(DUK_JS_COMPILER_H_INCLUDED) -#define DUK_JS_COMPILER_H_INCLUDED - -/* ECMAScript compiler limits */ -#define DUK_COMPILER_TOKEN_LIMIT 100000000L /* 1e8: protects against deeply nested inner functions */ - -/* maximum loopcount for peephole optimization */ -#define DUK_COMPILER_PEEPHOLE_MAXITER 3 - -/* maximum bytecode length in instructions */ -#define DUK_COMPILER_MAX_BYTECODE_LENGTH (256L * 1024L * 1024L) /* 1 GB */ - -/* - * Compiler intermediate values - * - * Intermediate values describe either plain values (e.g. strings or - * numbers) or binary operations which have not yet been coerced into - * either a left-hand-side or right-hand-side role (e.g. object property). - */ - -#define DUK_IVAL_NONE 0 /* no value */ -#define DUK_IVAL_PLAIN 1 /* register, constant, or value */ -#define DUK_IVAL_ARITH 2 /* binary arithmetic; DUK_OP_ADD, DUK_OP_EQ, other binary ops */ -#define DUK_IVAL_PROP 3 /* property access */ -#define DUK_IVAL_VAR 4 /* variable access */ - -#define DUK_ISPEC_NONE 0 /* no value */ -#define DUK_ISPEC_VALUE 1 /* value resides in 'valstack_idx' */ -#define DUK_ISPEC_REGCONST 2 /* value resides in a register or constant */ - -/* Bit mask which indicates that a regconst is a constant instead of a register. - * Chosen so that when a regconst is cast to duk_int32_t, all consts are - * negative values. - */ -#define DUK_REGCONST_CONST_MARKER DUK_INT32_MIN /* = -0x80000000 */ - -/* Type to represent a reg/const reference during compilation, with <0 - * indicating a constant. Some call sites also use -1 to indicate 'none'. - */ -typedef duk_int32_t duk_regconst_t; - -typedef struct { - duk_small_uint_t t; /* DUK_ISPEC_XXX */ - duk_regconst_t regconst; - duk_idx_t valstack_idx; /* always set; points to a reserved valstack slot */ -} duk_ispec; - -typedef struct { - /* - * PLAIN: x1 - * ARITH: x1 x2 - * PROP: x1.x2 - * VAR: x1 (name) - */ - - /* XXX: can be optimized for smaller footprint esp. on 32-bit environments */ - duk_small_uint_t t; /* DUK_IVAL_XXX */ - duk_small_uint_t op; /* bytecode opcode for binary ops */ - duk_ispec x1; - duk_ispec x2; -} duk_ivalue; - -/* - * Bytecode instruction representation during compilation - * - * Contains the actual instruction and (optionally) debug info. - */ - -struct duk_compiler_instr { - duk_instr_t ins; -#if defined(DUK_USE_PC2LINE) - duk_uint32_t line; -#endif -}; - -/* - * Compiler state - */ - -#define DUK_LABEL_FLAG_ALLOW_BREAK (1U << 0) -#define DUK_LABEL_FLAG_ALLOW_CONTINUE (1U << 1) - -#define DUK_DECL_TYPE_VAR 0 -#define DUK_DECL_TYPE_FUNC 1 - -/* XXX: optimize to 16 bytes */ -typedef struct { - duk_small_uint_t flags; - duk_int_t label_id; /* numeric label_id (-1 reserved as marker) */ - duk_hstring *h_label; /* borrowed label name */ - duk_int_t catch_depth; /* catch depth at point of definition */ - duk_int_t pc_label; /* pc of label statement: - * pc+1: break jump site - * pc+2: continue jump site - */ - - /* Fast jumps (which avoid longjmp) jump directly to the jump sites - * which are always known even while the iteration/switch statement - * is still being parsed. A final peephole pass "straightens out" - * the jumps. - */ -} duk_labelinfo; - -/* Compiling state of one function, eventually converted to duk_hcompfunc */ -struct duk_compiler_func { - /* These pointers are at the start of the struct so that they pack - * nicely. Mixing pointers and integer values is bad on some - * platforms (e.g. if int is 32 bits and pointers are 64 bits). - */ - - duk_bufwriter_ctx bw_code; /* bufwriter for code */ - - duk_hstring *h_name; /* function name (borrowed reference), ends up in _name */ - /* h_code: held in bw_code */ - duk_hobject *h_consts; /* array */ - duk_hobject *h_funcs; /* array of function templates: [func1, offset1, line1, func2, offset2, line2] - * offset/line points to closing brace to allow skipping on pass 2 - */ - duk_hobject *h_decls; /* array of declarations: [ name1, val1, name2, val2, ... ] - * valN = (typeN) | (fnum << 8), where fnum is inner func number (0 for vars) - * record function and variable declarations in pass 1 - */ - duk_hobject *h_labelnames; /* array of active label names */ - duk_hbuffer_dynamic *h_labelinfos; /* C array of duk_labelinfo */ - duk_hobject *h_argnames; /* array of formal argument names (-> _Formals) */ - duk_hobject *h_varmap; /* variable map for pass 2 (identifier -> register number or null (unmapped)) */ - - /* Value stack indices for tracking objects. */ - /* code_idx: not needed */ - duk_idx_t consts_idx; - duk_idx_t funcs_idx; - duk_idx_t decls_idx; - duk_idx_t labelnames_idx; - duk_idx_t labelinfos_idx; - duk_idx_t argnames_idx; - duk_idx_t varmap_idx; - - /* Temp reg handling. */ - duk_regconst_t temp_first; /* first register that is a temporary (below: variables) */ - duk_regconst_t temp_next; /* next temporary register to allocate */ - duk_regconst_t temp_max; /* highest value of temp_reg (temp_max - 1 is highest used reg) */ - - /* Shuffle registers if large number of regs/consts. */ - duk_regconst_t shuffle1; - duk_regconst_t shuffle2; - duk_regconst_t shuffle3; - - /* Stats for current expression being parsed. */ - duk_int_t nud_count; - duk_int_t led_count; - duk_int_t paren_level; /* parenthesis count, 0 = top level */ - duk_bool_t expr_lhs; /* expression is left-hand-side compatible */ - duk_bool_t allow_in; /* current paren level allows 'in' token */ - - /* Misc. */ - duk_int_t stmt_next; /* statement id allocation (running counter) */ - duk_int_t label_next; /* label id allocation (running counter) */ - duk_int_t catch_depth; /* catch stack depth */ - duk_int_t with_depth; /* with stack depth (affects identifier lookups) */ - duk_int_t fnum_next; /* inner function numbering */ - duk_int_t num_formals; /* number of formal arguments */ - duk_regconst_t - reg_stmt_value; /* register for writing value of 'non-empty' statements (global or eval code), -1 is marker */ -#if defined(DUK_USE_DEBUGGER_SUPPORT) - duk_int_t min_line; /* XXX: typing (duk_hcompfunc has duk_uint32_t) */ - duk_int_t max_line; -#endif - - /* Status booleans. */ - duk_uint8_t is_function; /* is an actual function (not global/eval code) */ - duk_uint8_t is_eval; /* is eval code */ - duk_uint8_t is_global; /* is global code */ - duk_uint8_t is_namebinding; /* needs a name binding */ - duk_uint8_t is_constructable; /* result is constructable */ - duk_uint8_t is_setget; /* is a setter/getter */ - duk_uint8_t is_strict; /* function is strict */ - duk_uint8_t is_notail; /* function must not be tail called */ - duk_uint8_t in_directive_prologue; /* parsing in "directive prologue", recognize directives */ - duk_uint8_t in_scanning; /* parsing in "scanning" phase (first pass) */ - duk_uint8_t may_direct_eval; /* function may call direct eval */ - duk_uint8_t id_access_arguments; /* function refers to 'arguments' identifier */ - duk_uint8_t id_access_slow; /* function makes one or more slow path accesses that won't match own static variables */ - duk_uint8_t id_access_slow_own; /* function makes one or more slow path accesses that may match own static variables */ - duk_uint8_t is_arguments_shadowed; /* argument/function declaration shadows 'arguments' */ - duk_uint8_t needs_shuffle; /* function needs shuffle registers */ - duk_uint8_t - reject_regexp_in_adv; /* reject RegExp literal on next advance() call; needed for handling IdentifierName productions */ - duk_uint8_t allow_regexp_in_adv; /* allow RegExp literal on next advance() call */ -}; - -struct duk_compiler_ctx { - duk_hthread *thr; - - /* filename being compiled (ends up in functions' '_filename' property) */ - duk_hstring *h_filename; /* borrowed reference */ - - /* lexing (tokenization) state (contains two valstack slot indices) */ - duk_lexer_ctx lex; - - /* current and previous token for parsing */ - duk_token prev_token; - duk_token curr_token; - duk_idx_t tok11_idx; /* curr_token slot1 (matches 'lex' slot1_idx) */ - duk_idx_t tok12_idx; /* curr_token slot2 (matches 'lex' slot2_idx) */ - duk_idx_t tok21_idx; /* prev_token slot1 */ - duk_idx_t tok22_idx; /* prev_token slot2 */ - - /* recursion limit */ - duk_int_t recursion_depth; - duk_int_t recursion_limit; - - /* code emission temporary */ - duk_int_t emit_jumpslot_pc; - - /* current function being compiled (embedded instead of pointer for more compact access) */ - duk_compiler_func curr_func; -}; - -/* - * Prototypes - */ - -DUK_INTERNAL_DECL void duk_js_compile(duk_hthread *thr, - const duk_uint8_t *src_buffer, - duk_size_t src_length, - duk_small_uint_t flags); - -#endif /* DUK_JS_COMPILER_H_INCLUDED */ -/* #include duk_regexp.h */ -/* - * Regular expression structs, constants, and bytecode defines. - */ - -#if !defined(DUK_REGEXP_H_INCLUDED) -#define DUK_REGEXP_H_INCLUDED - -/* maximum bytecode copies for {n,m} quantifiers */ -#define DUK_RE_MAX_ATOM_COPIES 1000 - -/* regexp compilation limits */ -#define DUK_RE_COMPILE_TOKEN_LIMIT 100000000L /* 1e8 */ - -/* regexp execution limits */ -#define DUK_RE_EXECUTE_STEPS_LIMIT 1000000000L /* 1e9 */ - -/* regexp opcodes */ -#define DUK_REOP_MATCH 1 -#define DUK_REOP_CHAR 2 -#define DUK_REOP_PERIOD 3 -#define DUK_REOP_RANGES 4 -#define DUK_REOP_INVRANGES 5 -#define DUK_REOP_JUMP 6 -#define DUK_REOP_SPLIT1 7 -#define DUK_REOP_SPLIT2 8 -#define DUK_REOP_SQMINIMAL 9 -#define DUK_REOP_SQGREEDY 10 -#define DUK_REOP_SAVE 11 -#define DUK_REOP_WIPERANGE 12 -#define DUK_REOP_LOOKPOS 13 -#define DUK_REOP_LOOKNEG 14 -#define DUK_REOP_BACKREFERENCE 15 -#define DUK_REOP_ASSERT_START 16 -#define DUK_REOP_ASSERT_END 17 -#define DUK_REOP_ASSERT_WORD_BOUNDARY 18 -#define DUK_REOP_ASSERT_NOT_WORD_BOUNDARY 19 - -/* flags */ -#define DUK_RE_FLAG_GLOBAL (1U << 0) -#define DUK_RE_FLAG_IGNORE_CASE (1U << 1) -#define DUK_RE_FLAG_MULTILINE (1U << 2) - -struct duk_re_matcher_ctx { - duk_hthread *thr; - - duk_uint32_t re_flags; - const duk_uint8_t *input; - const duk_uint8_t *input_end; - const duk_uint8_t *bytecode; - const duk_uint8_t *bytecode_end; - const duk_uint8_t **saved; /* allocated from valstack (fixed buffer) */ - duk_uint32_t nsaved; - duk_uint32_t recursion_depth; - duk_uint32_t recursion_limit; - duk_uint32_t steps_count; - duk_uint32_t steps_limit; -}; - -struct duk_re_compiler_ctx { - duk_hthread *thr; - - duk_uint32_t re_flags; - duk_lexer_ctx lex; - duk_re_token curr_token; - duk_bufwriter_ctx bw; - duk_uint32_t captures; /* highest capture number emitted so far (used as: ++captures) */ - duk_uint32_t highest_backref; - duk_uint32_t recursion_depth; - duk_uint32_t recursion_limit; - duk_uint32_t nranges; /* internal temporary value, used for char classes */ -}; - -/* - * Prototypes - */ - -#if defined(DUK_USE_REGEXP_SUPPORT) -DUK_INTERNAL_DECL void duk_regexp_compile(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_regexp_create_instance(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_regexp_match(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_regexp_match_force_global(duk_hthread *thr); /* hacky helper for String.prototype.split() */ -#endif - -#endif /* DUK_REGEXP_H_INCLUDED */ -/* #include duk_heaphdr.h */ -/* - * Heap header definition and assorted macros, including ref counting. - * Access all fields through the accessor macros. - */ - -#if !defined(DUK_HEAPHDR_H_INCLUDED) -#define DUK_HEAPHDR_H_INCLUDED - -/* - * Common heap header - * - * All heap objects share the same flags and refcount fields. Objects other - * than strings also need to have a single or double linked list pointers - * for insertion into the "heap allocated" list. Strings have single linked - * list pointers for string table chaining. - * - * Technically, 'h_refcount' must be wide enough to guarantee that it cannot - * wrap; otherwise objects might be freed incorrectly after wrapping. The - * default refcount field is 32 bits even on 64-bit systems: while that's in - * theory incorrect, the Duktape heap needs to be larger than 64GB for the - * count to actually wrap (assuming 16-byte duk_tvals). This is very unlikely - * to ever be an issue, but if it is, disabling DUK_USE_REFCOUNT32 causes - * Duktape to use size_t for refcounts which should always be safe. - * - * Heap header size on 32-bit platforms: 8 bytes without reference counting, - * 16 bytes with reference counting. - * - * Note that 'raw' macros such as DUK_HEAPHDR_GET_REFCOUNT() are not - * defined without DUK_USE_REFERENCE_COUNTING, so caller must #if defined() - * around them. - */ - -/* XXX: macro for shared header fields (avoids some padding issues) */ - -struct duk_heaphdr { - duk_uint32_t h_flags; - -#if defined(DUK_USE_REFERENCE_COUNTING) -#if defined(DUK_USE_ASSERTIONS) - /* When assertions enabled, used by mark-and-sweep for refcount - * validation. Largest reasonable type; also detects overflows. - */ - duk_size_t h_assert_refcount; -#endif -#if defined(DUK_USE_REFCOUNT16) - duk_uint16_t h_refcount; -#elif defined(DUK_USE_REFCOUNT32) - duk_uint32_t h_refcount; -#else - duk_size_t h_refcount; -#endif -#endif /* DUK_USE_REFERENCE_COUNTING */ - -#if defined(DUK_USE_HEAPPTR16) - duk_uint16_t h_next16; -#else - duk_heaphdr *h_next; -#endif - -#if defined(DUK_USE_DOUBLE_LINKED_HEAP) - /* refcounting requires direct heap frees, which in turn requires a dual linked heap */ -#if defined(DUK_USE_HEAPPTR16) - duk_uint16_t h_prev16; -#else - duk_heaphdr *h_prev; -#endif -#endif - - /* When DUK_USE_HEAPPTR16 (and DUK_USE_REFCOUNT16) is in use, the - * struct won't align nicely to 4 bytes. This 16-bit extra field - * is added to make the alignment clean; the field can be used by - * heap objects when 16-bit packing is used. This field is now - * conditional to DUK_USE_HEAPPTR16 only, but it is intended to be - * used with DUK_USE_REFCOUNT16 and DUK_USE_DOUBLE_LINKED_HEAP; - * this only matter to low memory environments anyway. - */ -#if defined(DUK_USE_HEAPPTR16) - duk_uint16_t h_extra16; -#endif -}; - -struct duk_heaphdr_string { - /* 16 bits would be enough for shared heaphdr flags and duk_hstring - * flags. The initial parts of duk_heaphdr_string and duk_heaphdr - * must match so changing the flags field size here would be quite - * awkward. However, to minimize struct size, we can pack at least - * 16 bits of duk_hstring data into the flags field. - */ - duk_uint32_t h_flags; - -#if defined(DUK_USE_REFERENCE_COUNTING) -#if defined(DUK_USE_ASSERTIONS) - /* When assertions enabled, used by mark-and-sweep for refcount - * validation. Largest reasonable type; also detects overflows. - */ - duk_size_t h_assert_refcount; -#endif -#if defined(DUK_USE_REFCOUNT16) - duk_uint16_t h_refcount; - duk_uint16_t h_strextra16; /* round out to 8 bytes */ -#elif defined(DUK_USE_REFCOUNT32) - duk_uint32_t h_refcount; -#else - duk_size_t h_refcount; -#endif -#else - duk_uint16_t h_strextra16; -#endif /* DUK_USE_REFERENCE_COUNTING */ - - duk_hstring *h_next; - /* No 'h_prev' pointer for strings. */ -}; - -#define DUK_HEAPHDR_FLAGS_TYPE_MASK 0x00000003UL -#define DUK_HEAPHDR_FLAGS_FLAG_MASK (~DUK_HEAPHDR_FLAGS_TYPE_MASK) - -/* 2 bits for heap type */ -#define DUK_HEAPHDR_FLAGS_HEAP_START 2 /* 5 heap flags */ -#define DUK_HEAPHDR_FLAGS_USER_START 7 /* 25 user flags */ - -#define DUK_HEAPHDR_HEAP_FLAG_NUMBER(n) (DUK_HEAPHDR_FLAGS_HEAP_START + (n)) -#define DUK_HEAPHDR_USER_FLAG_NUMBER(n) (DUK_HEAPHDR_FLAGS_USER_START + (n)) -#define DUK_HEAPHDR_HEAP_FLAG(n) (1UL << (DUK_HEAPHDR_FLAGS_HEAP_START + (n))) -#define DUK_HEAPHDR_USER_FLAG(n) (1UL << (DUK_HEAPHDR_FLAGS_USER_START + (n))) - -#define DUK_HEAPHDR_FLAG_REACHABLE DUK_HEAPHDR_HEAP_FLAG(0) /* mark-and-sweep: reachable */ -#define DUK_HEAPHDR_FLAG_TEMPROOT DUK_HEAPHDR_HEAP_FLAG(1) /* mark-and-sweep: children not processed */ -#define DUK_HEAPHDR_FLAG_FINALIZABLE DUK_HEAPHDR_HEAP_FLAG(2) /* mark-and-sweep: finalizable (on current pass) */ -#define DUK_HEAPHDR_FLAG_FINALIZED DUK_HEAPHDR_HEAP_FLAG(3) /* mark-and-sweep: finalized (on previous pass) */ -#define DUK_HEAPHDR_FLAG_READONLY DUK_HEAPHDR_HEAP_FLAG(4) /* read-only object, in code section */ - -#define DUK_HTYPE_MIN 0 -#define DUK_HTYPE_STRING 0 -#define DUK_HTYPE_OBJECT 1 -#define DUK_HTYPE_BUFFER 2 -#define DUK_HTYPE_MAX 2 - -#if defined(DUK_USE_HEAPPTR16) -#define DUK_HEAPHDR_GET_NEXT(heap, h) ((duk_heaphdr *) DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, (h)->h_next16)) -#define DUK_HEAPHDR_SET_NEXT(heap, h, val) \ - do { \ - (h)->h_next16 = DUK_USE_HEAPPTR_ENC16((heap)->heap_udata, (void *) val); \ - } while (0) -#else -#define DUK_HEAPHDR_GET_NEXT(heap, h) ((h)->h_next) -#define DUK_HEAPHDR_SET_NEXT(heap, h, val) \ - do { \ - (h)->h_next = (val); \ - } while (0) -#endif - -#if defined(DUK_USE_DOUBLE_LINKED_HEAP) -#if defined(DUK_USE_HEAPPTR16) -#define DUK_HEAPHDR_GET_PREV(heap, h) ((duk_heaphdr *) DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, (h)->h_prev16)) -#define DUK_HEAPHDR_SET_PREV(heap, h, val) \ - do { \ - (h)->h_prev16 = DUK_USE_HEAPPTR_ENC16((heap)->heap_udata, (void *) (val)); \ - } while (0) -#else -#define DUK_HEAPHDR_GET_PREV(heap, h) ((h)->h_prev) -#define DUK_HEAPHDR_SET_PREV(heap, h, val) \ - do { \ - (h)->h_prev = (val); \ - } while (0) -#endif -#endif - -#if defined(DUK_USE_REFERENCE_COUNTING) -#define DUK_HEAPHDR_GET_REFCOUNT(h) ((h)->h_refcount) -#define DUK_HEAPHDR_SET_REFCOUNT(h, val) \ - do { \ - (h)->h_refcount = (val); \ - DUK_ASSERT((h)->h_refcount == (val)); /* No truncation. */ \ - } while (0) -#define DUK_HEAPHDR_PREINC_REFCOUNT(h) (++(h)->h_refcount) /* result: updated refcount */ -#define DUK_HEAPHDR_PREDEC_REFCOUNT(h) (--(h)->h_refcount) /* result: updated refcount */ -#else -/* refcount macros not defined without refcounting, caller must #if defined() now */ -#endif /* DUK_USE_REFERENCE_COUNTING */ - -/* - * Note: type is treated as a field separate from flags, so some masking is - * involved in the macros below. - */ - -#define DUK_HEAPHDR_GET_FLAGS_RAW(h) ((h)->h_flags) -#define DUK_HEAPHDR_SET_FLAGS_RAW(h, val) \ - do { \ - (h)->h_flags = (val); \ - } \ - } -#define DUK_HEAPHDR_GET_FLAGS(h) ((h)->h_flags & DUK_HEAPHDR_FLAGS_FLAG_MASK) -#define DUK_HEAPHDR_SET_FLAGS(h, val) \ - do { \ - (h)->h_flags = ((h)->h_flags & ~(DUK_HEAPHDR_FLAGS_FLAG_MASK)) | (val); \ - } while (0) -#define DUK_HEAPHDR_GET_TYPE(h) ((h)->h_flags & DUK_HEAPHDR_FLAGS_TYPE_MASK) -#define DUK_HEAPHDR_SET_TYPE(h, val) \ - do { \ - (h)->h_flags = ((h)->h_flags & ~(DUK_HEAPHDR_FLAGS_TYPE_MASK)) | (val); \ - } while (0) - -/* Comparison for type >= DUK_HTYPE_MIN skipped; because DUK_HTYPE_MIN is zero - * and the comparison is unsigned, it's always true and generates warnings. - */ -#define DUK_HEAPHDR_HTYPE_VALID(h) (DUK_HEAPHDR_GET_TYPE((h)) <= DUK_HTYPE_MAX) - -#define DUK_HEAPHDR_SET_TYPE_AND_FLAGS(h, tval, fval) \ - do { \ - (h)->h_flags = ((tval) &DUK_HEAPHDR_FLAGS_TYPE_MASK) | ((fval) &DUK_HEAPHDR_FLAGS_FLAG_MASK); \ - } while (0) - -#define DUK_HEAPHDR_SET_FLAG_BITS(h, bits) \ - do { \ - DUK_ASSERT(((bits) & ~(DUK_HEAPHDR_FLAGS_FLAG_MASK)) == 0); \ - (h)->h_flags |= (bits); \ - } while (0) - -#define DUK_HEAPHDR_CLEAR_FLAG_BITS(h, bits) \ - do { \ - DUK_ASSERT(((bits) & ~(DUK_HEAPHDR_FLAGS_FLAG_MASK)) == 0); \ - (h)->h_flags &= ~((bits)); \ - } while (0) - -#define DUK_HEAPHDR_CHECK_FLAG_BITS(h, bits) (((h)->h_flags & (bits)) != 0) - -#define DUK_HEAPHDR_SET_REACHABLE(h) DUK_HEAPHDR_SET_FLAG_BITS((h), DUK_HEAPHDR_FLAG_REACHABLE) -#define DUK_HEAPHDR_CLEAR_REACHABLE(h) DUK_HEAPHDR_CLEAR_FLAG_BITS((h), DUK_HEAPHDR_FLAG_REACHABLE) -#define DUK_HEAPHDR_HAS_REACHABLE(h) DUK_HEAPHDR_CHECK_FLAG_BITS((h), DUK_HEAPHDR_FLAG_REACHABLE) - -#define DUK_HEAPHDR_SET_TEMPROOT(h) DUK_HEAPHDR_SET_FLAG_BITS((h), DUK_HEAPHDR_FLAG_TEMPROOT) -#define DUK_HEAPHDR_CLEAR_TEMPROOT(h) DUK_HEAPHDR_CLEAR_FLAG_BITS((h), DUK_HEAPHDR_FLAG_TEMPROOT) -#define DUK_HEAPHDR_HAS_TEMPROOT(h) DUK_HEAPHDR_CHECK_FLAG_BITS((h), DUK_HEAPHDR_FLAG_TEMPROOT) - -#define DUK_HEAPHDR_SET_FINALIZABLE(h) DUK_HEAPHDR_SET_FLAG_BITS((h), DUK_HEAPHDR_FLAG_FINALIZABLE) -#define DUK_HEAPHDR_CLEAR_FINALIZABLE(h) DUK_HEAPHDR_CLEAR_FLAG_BITS((h), DUK_HEAPHDR_FLAG_FINALIZABLE) -#define DUK_HEAPHDR_HAS_FINALIZABLE(h) DUK_HEAPHDR_CHECK_FLAG_BITS((h), DUK_HEAPHDR_FLAG_FINALIZABLE) - -#define DUK_HEAPHDR_SET_FINALIZED(h) DUK_HEAPHDR_SET_FLAG_BITS((h), DUK_HEAPHDR_FLAG_FINALIZED) -#define DUK_HEAPHDR_CLEAR_FINALIZED(h) DUK_HEAPHDR_CLEAR_FLAG_BITS((h), DUK_HEAPHDR_FLAG_FINALIZED) -#define DUK_HEAPHDR_HAS_FINALIZED(h) DUK_HEAPHDR_CHECK_FLAG_BITS((h), DUK_HEAPHDR_FLAG_FINALIZED) - -#define DUK_HEAPHDR_SET_READONLY(h) DUK_HEAPHDR_SET_FLAG_BITS((h), DUK_HEAPHDR_FLAG_READONLY) -#define DUK_HEAPHDR_CLEAR_READONLY(h) DUK_HEAPHDR_CLEAR_FLAG_BITS((h), DUK_HEAPHDR_FLAG_READONLY) -#define DUK_HEAPHDR_HAS_READONLY(h) DUK_HEAPHDR_CHECK_FLAG_BITS((h), DUK_HEAPHDR_FLAG_READONLY) - -/* get or set a range of flags; m=first bit number, n=number of bits */ -#define DUK_HEAPHDR_GET_FLAG_RANGE(h, m, n) (((h)->h_flags >> (m)) & ((1UL << (n)) - 1UL)) - -#define DUK_HEAPHDR_SET_FLAG_RANGE(h, m, n, v) \ - do { \ - (h)->h_flags = ((h)->h_flags & (~(((1UL << (n)) - 1UL) << (m)))) | ((v) << (m)); \ - } while (0) - -/* init pointer fields to null */ -#if defined(DUK_USE_DOUBLE_LINKED_HEAP) -#define DUK_HEAPHDR_INIT_NULLS(h) \ - do { \ - DUK_HEAPHDR_SET_NEXT((h), (void *) NULL); \ - DUK_HEAPHDR_SET_PREV((h), (void *) NULL); \ - } while (0) -#else -#define DUK_HEAPHDR_INIT_NULLS(h) \ - do { \ - DUK_HEAPHDR_SET_NEXT((h), (void *) NULL); \ - } while (0) -#endif - -#define DUK_HEAPHDR_STRING_INIT_NULLS(h) \ - do { \ - (h)->h_next = NULL; \ - } while (0) - -/* - * Type tests - */ - -/* Take advantage of the fact that for DUK_HTYPE_xxx numbers the lowest bit - * is only set for DUK_HTYPE_OBJECT (= 1). - */ -#if 0 -#define DUK_HEAPHDR_IS_OBJECT(h) (DUK_HEAPHDR_GET_TYPE((h)) == DUK_HTYPE_OBJECT) -#endif -#define DUK_HEAPHDR_IS_OBJECT(h) ((h)->h_flags & 0x01UL) -#define DUK_HEAPHDR_IS_STRING(h) (DUK_HEAPHDR_GET_TYPE((h)) == DUK_HTYPE_STRING) -#define DUK_HEAPHDR_IS_BUFFER(h) (DUK_HEAPHDR_GET_TYPE((h)) == DUK_HTYPE_BUFFER) - -/* - * Assert helpers - */ - -/* Check that prev/next links are consistent: if e.g. h->prev is != NULL, - * h->prev->next should point back to h. - */ -#if defined(DUK_USE_ASSERTIONS) -DUK_INTERNAL_DECL void duk_heaphdr_assert_valid_subclassed(duk_heaphdr *h); -DUK_INTERNAL_DECL void duk_heaphdr_assert_links(duk_heap *heap, duk_heaphdr *h); -DUK_INTERNAL_DECL void duk_heaphdr_assert_valid(duk_heaphdr *h); -#define DUK_HEAPHDR_ASSERT_LINKS(heap, h) \ - do { \ - duk_heaphdr_assert_links((heap), (h)); \ - } while (0) -#define DUK_HEAPHDR_ASSERT_VALID(h) \ - do { \ - duk_heaphdr_assert_valid((h)); \ - } while (0) -#else -#define DUK_HEAPHDR_ASSERT_LINKS(heap, h) \ - do { \ - } while (0) -#define DUK_HEAPHDR_ASSERT_VALID(h) \ - do { \ - } while (0) -#endif - -#endif /* DUK_HEAPHDR_H_INCLUDED */ -/* #include duk_refcount.h */ -/* - * Reference counting helper macros. The macros take a thread argument - * and must thus always be executed in a specific thread context. The - * thread argument is not really needed anymore: DECREF can operate with - * a heap pointer only, and INCREF needs neither. - */ - -#if !defined(DUK_REFCOUNT_H_INCLUDED) -#define DUK_REFCOUNT_H_INCLUDED - -#if defined(DUK_USE_REFERENCE_COUNTING) - -#if defined(DUK_USE_ROM_OBJECTS) -/* With ROM objects "needs refcount update" is true when the value is - * heap allocated and is not a ROM object. - */ -/* XXX: double evaluation for 'tv' argument. */ -#define DUK_TVAL_NEEDS_REFCOUNT_UPDATE(tv) \ - (DUK_TVAL_IS_HEAP_ALLOCATED((tv)) && !DUK_HEAPHDR_HAS_READONLY(DUK_TVAL_GET_HEAPHDR((tv)))) -#define DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE(h) (!DUK_HEAPHDR_HAS_READONLY((h))) -#else /* DUK_USE_ROM_OBJECTS */ -/* Without ROM objects "needs refcount update" == is heap allocated. */ -#define DUK_TVAL_NEEDS_REFCOUNT_UPDATE(tv) DUK_TVAL_IS_HEAP_ALLOCATED((tv)) -#define DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE(h) 1 -#endif /* DUK_USE_ROM_OBJECTS */ - -/* Fast variants, inline refcount operations except for refzero handling. - * Can be used explicitly when speed is always more important than size. - * For a good compiler and a single file build, these are basically the - * same as a forced inline. - */ -#define DUK_TVAL_INCREF_FAST(thr, tv) \ - do { \ - duk_tval *duk__tv = (tv); \ - DUK_ASSERT(duk__tv != NULL); \ - if (DUK_TVAL_NEEDS_REFCOUNT_UPDATE(duk__tv)) { \ - duk_heaphdr *duk__h = DUK_TVAL_GET_HEAPHDR(duk__tv); \ - DUK_ASSERT(duk__h != NULL); \ - DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(duk__h)); \ - DUK_HEAPHDR_PREINC_REFCOUNT(duk__h); \ - DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(duk__h) != 0); /* No wrapping. */ \ - } \ - } while (0) -#define DUK_TVAL_DECREF_FAST(thr, tv) \ - do { \ - duk_tval *duk__tv = (tv); \ - DUK_ASSERT(duk__tv != NULL); \ - if (DUK_TVAL_NEEDS_REFCOUNT_UPDATE(duk__tv)) { \ - duk_heaphdr *duk__h = DUK_TVAL_GET_HEAPHDR(duk__tv); \ - DUK_ASSERT(duk__h != NULL); \ - DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(duk__h)); \ - DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(duk__h) > 0); \ - if (DUK_HEAPHDR_PREDEC_REFCOUNT(duk__h) == 0) { \ - duk_heaphdr_refzero((thr), duk__h); \ - } \ - } \ - } while (0) -#define DUK_TVAL_DECREF_NORZ_FAST(thr, tv) \ - do { \ - duk_tval *duk__tv = (tv); \ - DUK_ASSERT(duk__tv != NULL); \ - if (DUK_TVAL_NEEDS_REFCOUNT_UPDATE(duk__tv)) { \ - duk_heaphdr *duk__h = DUK_TVAL_GET_HEAPHDR(duk__tv); \ - DUK_ASSERT(duk__h != NULL); \ - DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(duk__h)); \ - DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(duk__h) > 0); \ - if (DUK_HEAPHDR_PREDEC_REFCOUNT(duk__h) == 0) { \ - duk_heaphdr_refzero_norz((thr), duk__h); \ - } \ - } \ - } while (0) -#define DUK_HEAPHDR_INCREF_FAST(thr, h) \ - do { \ - duk_heaphdr *duk__h = (duk_heaphdr *) (h); \ - DUK_ASSERT(duk__h != NULL); \ - DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(duk__h)); \ - if (DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE(duk__h)) { \ - DUK_HEAPHDR_PREINC_REFCOUNT(duk__h); \ - DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(duk__h) != 0); /* No wrapping. */ \ - } \ - } while (0) -#define DUK_HEAPHDR_DECREF_FAST_RAW(thr, h, rzcall, rzcast) \ - do { \ - duk_heaphdr *duk__h = (duk_heaphdr *) (h); \ - DUK_ASSERT(duk__h != NULL); \ - DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(duk__h)); \ - DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(duk__h) > 0); \ - if (DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE(duk__h)) { \ - if (DUK_HEAPHDR_PREDEC_REFCOUNT(duk__h) == 0) { \ - (rzcall)((thr), (rzcast) duk__h); \ - } \ - } \ - } while (0) -#define DUK_HEAPHDR_DECREF_FAST(thr, h) DUK_HEAPHDR_DECREF_FAST_RAW((thr), (h), duk_heaphdr_refzero, duk_heaphdr *) -#define DUK_HEAPHDR_DECREF_NORZ_FAST(thr, h) DUK_HEAPHDR_DECREF_FAST_RAW((thr), (h), duk_heaphdr_refzero_norz, duk_heaphdr *) - -/* Slow variants, call to a helper to reduce code size. - * Can be used explicitly when size is always more important than speed. - */ -#define DUK_TVAL_INCREF_SLOW(thr, tv) \ - do { \ - duk_tval_incref((tv)); \ - } while (0) -#define DUK_TVAL_DECREF_SLOW(thr, tv) \ - do { \ - duk_tval_decref((thr), (tv)); \ - } while (0) -#define DUK_TVAL_DECREF_NORZ_SLOW(thr, tv) \ - do { \ - duk_tval_decref_norz((thr), (tv)); \ - } while (0) -#define DUK_HEAPHDR_INCREF_SLOW(thr, h) \ - do { \ - duk_heaphdr_incref((duk_heaphdr *) (h)); \ - } while (0) -#define DUK_HEAPHDR_DECREF_SLOW(thr, h) \ - do { \ - duk_heaphdr_decref((thr), (duk_heaphdr *) (h)); \ - } while (0) -#define DUK_HEAPHDR_DECREF_NORZ_SLOW(thr, h) \ - do { \ - duk_heaphdr_decref_norz((thr), (duk_heaphdr *) (h)); \ - } while (0) -#define DUK_HSTRING_INCREF_SLOW(thr, h) \ - do { \ - duk_heaphdr_incref((duk_heaphdr *) (h)); \ - } while (0) -#define DUK_HSTRING_DECREF_SLOW(thr, h) \ - do { \ - duk_heaphdr_decref((thr), (duk_heaphdr *) (h)); \ - } while (0) -#define DUK_HSTRING_DECREF_NORZ_SLOW(thr, h) \ - do { \ - duk_heaphdr_decref_norz((thr), (duk_heaphdr *) (h)); \ - } while (0) -#define DUK_HBUFFER_INCREF_SLOW(thr, h) \ - do { \ - duk_heaphdr_incref((duk_heaphdr *) (h)); \ - } while (0) -#define DUK_HBUFFER_DECREF_SLOW(thr, h) \ - do { \ - duk_heaphdr_decref((thr), (duk_heaphdr *) (h)); \ - } while (0) -#define DUK_HBUFFER_DECREF_NORZ_SLOW(thr, h) \ - do { \ - duk_heaphdr_decref_norz((thr), (duk_heaphdr *) (h)); \ - } while (0) -#define DUK_HOBJECT_INCREF_SLOW(thr, h) \ - do { \ - duk_heaphdr_incref((duk_heaphdr *) (h)); \ - } while (0) -#define DUK_HOBJECT_DECREF_SLOW(thr, h) \ - do { \ - duk_heaphdr_decref((thr), (duk_heaphdr *) (h)); \ - } while (0) -#define DUK_HOBJECT_DECREF_NORZ_SLOW(thr, h) \ - do { \ - duk_heaphdr_decref_norz((thr), (duk_heaphdr *) (h)); \ - } while (0) - -/* Default variants. Selection depends on speed/size preference. - * Concretely: with gcc 4.8.1 -Os x64 the difference in final binary - * is about +1kB for _FAST variants. - */ -#if defined(DUK_USE_FAST_REFCOUNT_DEFAULT) -/* XXX: It would be nice to specialize for specific duk_hobject subtypes - * but current refzero queue handling prevents that. - */ -#define DUK_TVAL_INCREF(thr, tv) DUK_TVAL_INCREF_FAST((thr), (tv)) -#define DUK_TVAL_DECREF(thr, tv) DUK_TVAL_DECREF_FAST((thr), (tv)) -#define DUK_TVAL_DECREF_NORZ(thr, tv) DUK_TVAL_DECREF_NORZ_FAST((thr), (tv)) -#define DUK_HEAPHDR_INCREF(thr, h) DUK_HEAPHDR_INCREF_FAST((thr), (h)) -#define DUK_HEAPHDR_DECREF(thr, h) DUK_HEAPHDR_DECREF_FAST_RAW((thr), (h), duk_heaphdr_refzero, duk_heaphdr *) -#define DUK_HEAPHDR_DECREF_NORZ(thr, h) DUK_HEAPHDR_DECREF_FAST_RAW((thr), (h), duk_heaphdr_refzero_norz, duk_heaphdr *) -#define DUK_HSTRING_INCREF(thr, h) DUK_HEAPHDR_INCREF((thr), (duk_heaphdr *) (h)) -#define DUK_HSTRING_DECREF(thr, h) DUK_HEAPHDR_DECREF_FAST_RAW((thr), (h), duk_hstring_refzero, duk_hstring *) -#define DUK_HSTRING_DECREF_NORZ(thr, h) \ - DUK_HEAPHDR_DECREF_FAST_RAW((thr), (h), duk_hstring_refzero, duk_hstring *) /* no 'norz' variant */ -#define DUK_HOBJECT_INCREF(thr, h) DUK_HEAPHDR_INCREF((thr), (duk_heaphdr *) (h)) -#define DUK_HOBJECT_DECREF(thr, h) DUK_HEAPHDR_DECREF_FAST_RAW((thr), (h), duk_hobject_refzero, duk_hobject *) -#define DUK_HOBJECT_DECREF_NORZ(thr, h) DUK_HEAPHDR_DECREF_FAST_RAW((thr), (h), duk_hobject_refzero_norz, duk_hobject *) -#define DUK_HBUFFER_INCREF(thr, h) DUK_HEAPHDR_INCREF((thr), (duk_heaphdr *) (h)) -#define DUK_HBUFFER_DECREF(thr, h) DUK_HEAPHDR_DECREF_FAST_RAW((thr), (h), duk_hbuffer_refzero, duk_hbuffer *) -#define DUK_HBUFFER_DECREF_NORZ(thr, h) \ - DUK_HEAPHDR_DECREF_FAST_RAW((thr), (h), duk_hbuffer_refzero, duk_hbuffer *) /* no 'norz' variant */ -#define DUK_HCOMPFUNC_INCREF(thr, h) DUK_HEAPHDR_INCREF((thr), (duk_heaphdr *) &(h)->obj) -#define DUK_HCOMPFUNC_DECREF(thr, h) DUK_HEAPHDR_DECREF_FAST_RAW((thr), (h), duk_hobject_refzero, duk_hobject *) -#define DUK_HCOMPFUNC_DECREF_NORZ(thr, h) DUK_HEAPHDR_DECREF_FAST_RAW((thr), (h), duk_hobject_refzero_norz, duk_hobject *) -#define DUK_HNATFUNC_INCREF(thr, h) DUK_HEAPHDR_INCREF((thr), (duk_heaphdr *) &(h)->obj) -#define DUK_HNATFUNC_DECREF(thr, h) DUK_HEAPHDR_DECREF_FAST_RAW((thr), (h), duk_hobject_refzero, duk_hobject *) -#define DUK_HNATFUNC_DECREF_NORZ(thr, h) DUK_HEAPHDR_DECREF_FAST_RAW((thr), (h), duk_hobject_refzero_norz, duk_hobject *) -#define DUK_HBUFOBJ_INCREF(thr, h) DUK_HEAPHDR_INCREF((thr), (duk_heaphdr *) &(h)->obj) -#define DUK_HBUFOBJ_DECREF(thr, h) DUK_HEAPHDR_DECREF_FAST_RAW((thr), (h), duk_hobject_refzero, duk_hobject *) -#define DUK_HBUFOBJ_DECREF_NORZ(thr, h) DUK_HEAPHDR_DECREF_FAST_RAW((thr), (h), duk_hobject_refzero_norz, duk_hobject *) -#define DUK_HTHREAD_INCREF(thr, h) DUK_HEAPHDR_INCREF((thr), (duk_heaphdr *) &(h)->obj) -#define DUK_HTHREAD_DECREF(thr, h) DUK_HEAPHDR_DECREF_FAST_RAW((thr), (h), duk_hobject_refzero, duk_hobject *) -#define DUK_HTHREAD_DECREF_NORZ(thr, h) DUK_HEAPHDR_DECREF_FAST_RAW((thr), (h), duk_hobject_refzero_norz, duk_hobject *) -#else -#define DUK_TVAL_INCREF(thr, tv) DUK_TVAL_INCREF_SLOW((thr), (tv)) -#define DUK_TVAL_DECREF(thr, tv) DUK_TVAL_DECREF_SLOW((thr), (tv)) -#define DUK_TVAL_DECREF_NORZ(thr, tv) DUK_TVAL_DECREF_NORZ_SLOW((thr), (tv)) -#define DUK_HEAPHDR_INCREF(thr, h) DUK_HEAPHDR_INCREF_SLOW((thr), (h)) -#define DUK_HEAPHDR_DECREF(thr, h) DUK_HEAPHDR_DECREF_SLOW((thr), (h)) -#define DUK_HEAPHDR_DECREF_NORZ(thr, h) DUK_HEAPHDR_DECREF_NORZ_SLOW((thr), (h)) -#define DUK_HSTRING_INCREF(thr, h) DUK_HEAPHDR_INCREF((thr), (duk_heaphdr *) (h)) -#define DUK_HSTRING_DECREF(thr, h) DUK_HSTRING_DECREF_SLOW((thr), (h)) -#define DUK_HSTRING_DECREF_NORZ(thr, h) DUK_HSTRING_DECREF_NORZ_SLOW((thr), (h)) -#define DUK_HOBJECT_INCREF(thr, h) DUK_HEAPHDR_INCREF((thr), (duk_heaphdr *) (h)) -#define DUK_HOBJECT_DECREF(thr, h) DUK_HOBJECT_DECREF_SLOW((thr), (h)) -#define DUK_HOBJECT_DECREF_NORZ(thr, h) DUK_HOBJECT_DECREF_NORZ_SLOW((thr), (h)) -#define DUK_HBUFFER_INCREF(thr, h) DUK_HEAPHDR_INCREF((thr), (duk_heaphdr *) (h)) -#define DUK_HBUFFER_DECREF(thr, h) DUK_HBUFFER_DECREF_SLOW((thr), (h)) -#define DUK_HBUFFER_DECREF_NORZ(thr, h) DUK_HBUFFER_DECREF_NORZ_SLOW((thr), (h)) -#define DUK_HCOMPFUNC_INCREF(thr, h) DUK_HEAPHDR_INCREF((thr), (duk_heaphdr *) &(h)->obj) -#define DUK_HCOMPFUNC_DECREF(thr, h) DUK_HOBJECT_DECREF_SLOW((thr), (duk_hobject *) &(h)->obj) -#define DUK_HCOMPFUNC_DECREF_NORZ(thr, h) DUK_HOBJECT_DECREF_NORZ_SLOW((thr), (duk_hobject *) &(h)->obj) -#define DUK_HNATFUNC_INCREF(thr, h) DUK_HEAPHDR_INCREF((thr), (duk_heaphdr *) &(h)->obj) -#define DUK_HNATFUNC_DECREF(thr, h) DUK_HOBJECT_DECREF_SLOW((thr), (duk_hobject *) &(h)->obj) -#define DUK_HNATFUNC_DECREF_NORZ(thr, h) DUK_HOBJECT_DECREF_NORZ_SLOW((thr), (duk_hobject *) &(h)->obj) -#define DUK_HBUFOBJ_INCREF(thr, h) DUK_HEAPHDR_INCREF((thr), (duk_heaphdr *) &(h)->obj) -#define DUK_HBUFOBJ_DECREF(thr, h) DUK_HOBJECT_DECREF_SLOW((thr), (duk_hobject *) &(h)->obj) -#define DUK_HBUFOB_DECREF_NORZ(thr, h) DUK_HOBJECT_DECREF_NORZ_SLOW((thr), (duk_hobject *) &(h)->obj) -#define DUK_HTHREAD_INCREF(thr, h) DUK_HEAPHDR_INCREF((thr), (duk_heaphdr *) &(h)->obj) -#define DUK_HTHREAD_DECREF(thr, h) DUK_HOBJECT_DECREF_SLOW((thr), (duk_hobject *) &(h)->obj) -#define DUK_HTHREAD_DECREF_NORZ(thr, h) DUK_HOBJECT_DECREF_NORZ_SLOW((thr), (duk_hobject *) &(h)->obj) -#endif - -/* Convenience for some situations; the above macros don't allow NULLs - * for performance reasons. Macros cover only actually needed cases. - */ -#define DUK_HEAPHDR_INCREF_ALLOWNULL(thr, h) \ - do { \ - if ((h) != NULL) { \ - DUK_HEAPHDR_INCREF((thr), (duk_heaphdr *) (h)); \ - } \ - } while (0) -#define DUK_HEAPHDR_DECREF_ALLOWNULL(thr, h) \ - do { \ - if ((h) != NULL) { \ - DUK_HEAPHDR_DECREF((thr), (duk_heaphdr *) (h)); \ - } \ - } while (0) -#define DUK_HEAPHDR_DECREF_NORZ_ALLOWNULL(thr, h) \ - do { \ - if ((h) != NULL) { \ - DUK_HEAPHDR_DECREF_NORZ((thr), (duk_heaphdr *) (h)); \ - } \ - } while (0) -#define DUK_HOBJECT_INCREF_ALLOWNULL(thr, h) \ - do { \ - if ((h) != NULL) { \ - DUK_HOBJECT_INCREF((thr), (h)); \ - } \ - } while (0) -#define DUK_HOBJECT_DECREF_ALLOWNULL(thr, h) \ - do { \ - if ((h) != NULL) { \ - DUK_HOBJECT_DECREF((thr), (h)); \ - } \ - } while (0) -#define DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, h) \ - do { \ - if ((h) != NULL) { \ - DUK_HOBJECT_DECREF_NORZ((thr), (h)); \ - } \ - } while (0) -#define DUK_HBUFFER_INCREF_ALLOWNULL(thr, h) \ - do { \ - if ((h) != NULL) { \ - DUK_HBUFFER_INCREF((thr), (h)); \ - } \ - } while (0) -#define DUK_HBUFFER_DECREF_ALLOWNULL(thr, h) \ - do { \ - if ((h) != NULL) { \ - DUK_HBUFFER_DECREF((thr), (h)); \ - } \ - } while (0) -#define DUK_HBUFFER_DECREF_NORZ_ALLOWNULL(thr, h) \ - do { \ - if ((h) != NULL) { \ - DUK_HBUFFER_DECREF_NORZ((thr), (h)); \ - } \ - } while (0) -#define DUK_HTHREAD_INCREF_ALLOWNULL(thr, h) \ - do { \ - if ((h) != NULL) { \ - DUK_HTHREAD_INCREF((thr), (h)); \ - } \ - } while (0) -#define DUK_HTHREAD_DECREF_ALLOWNULL(thr, h) \ - do { \ - if ((h) != NULL) { \ - DUK_HTHREAD_DECREF((thr), (h)); \ - } \ - } while (0) -#define DUK_HTHREAD_DECREF_NORZ_ALLOWNULL(thr, h) \ - do { \ - if ((h) != NULL) { \ - DUK_HTHREAD_DECREF_NORZ((thr), (h)); \ - } \ - } while (0) - -/* Called after one or more DECREF NORZ calls to handle pending side effects. - * At present DECREF NORZ does freeing inline but doesn't execute finalizers, - * so these macros check for pending finalizers and execute them. The FAST - * variant is performance critical. - */ -#if defined(DUK_USE_FINALIZER_SUPPORT) -#define DUK_REFZERO_CHECK_FAST(thr) \ - do { \ - duk_refzero_check_fast((thr)); \ - } while (0) -#define DUK_REFZERO_CHECK_SLOW(thr) \ - do { \ - duk_refzero_check_slow((thr)); \ - } while (0) -#else /* DUK_USE_FINALIZER_SUPPORT */ -#define DUK_REFZERO_CHECK_FAST(thr) \ - do { \ - } while (0) -#define DUK_REFZERO_CHECK_SLOW(thr) \ - do { \ - } while (0) -#endif /* DUK_USE_FINALIZER_SUPPORT */ - -/* - * Macros to set a duk_tval and update refcount of the target (decref the - * old value and incref the new value if necessary). This is both performance - * and footprint critical; any changes made should be measured for size/speed. - */ - -#define DUK_TVAL_SET_UNDEFINED_UPDREF_ALT0(thr, tvptr_dst) \ - do { \ - duk_tval *tv__dst; \ - duk_tval tv__tmp; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \ - DUK_TVAL_SET_UNDEFINED(tv__dst); \ - DUK_TVAL_DECREF((thr), &tv__tmp); /* side effects */ \ - } while (0) - -#define DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ_ALT0(thr, tvptr_dst) \ - do { \ - duk_tval *tv__dst; \ - duk_tval tv__tmp; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \ - DUK_TVAL_SET_UNDEFINED(tv__dst); \ - DUK_TVAL_DECREF_NORZ((thr), &tv__tmp); \ - } while (0) - -#define DUK_TVAL_SET_UNUSED_UPDREF_ALT0(thr, tvptr_dst) \ - do { \ - duk_tval *tv__dst; \ - duk_tval tv__tmp; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \ - DUK_TVAL_SET_UNUSED(tv__dst); \ - DUK_TVAL_DECREF((thr), &tv__tmp); /* side effects */ \ - } while (0) - -#define DUK_TVAL_SET_NULL_UPDREF_ALT0(thr, tvptr_dst) \ - do { \ - duk_tval *tv__dst; \ - duk_tval tv__tmp; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \ - DUK_TVAL_SET_NULL(tv__dst); \ - DUK_TVAL_DECREF((thr), &tv__tmp); /* side effects */ \ - } while (0) - -#define DUK_TVAL_SET_BOOLEAN_UPDREF_ALT0(thr, tvptr_dst, newval) \ - do { \ - duk_tval *tv__dst; \ - duk_tval tv__tmp; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \ - DUK_TVAL_SET_BOOLEAN(tv__dst, (newval)); \ - DUK_TVAL_DECREF((thr), &tv__tmp); /* side effects */ \ - } while (0) - -#define DUK_TVAL_SET_NUMBER_UPDREF_ALT0(thr, tvptr_dst, newval) \ - do { \ - duk_tval *tv__dst; \ - duk_tval tv__tmp; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \ - DUK_TVAL_SET_NUMBER(tv__dst, (newval)); \ - DUK_TVAL_DECREF((thr), &tv__tmp); /* side effects */ \ - } while (0) -#define DUK_TVAL_SET_NUMBER_CHKFAST_UPDREF_ALT0(thr, tvptr_dst, newval) \ - do { \ - duk_tval *tv__dst; \ - duk_tval tv__tmp; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \ - DUK_TVAL_SET_NUMBER_CHKFAST_FAST(tv__dst, (newval)); \ - DUK_TVAL_DECREF((thr), &tv__tmp); /* side effects */ \ - } while (0) -#define DUK_TVAL_SET_DOUBLE_UPDREF_ALT0(thr, tvptr_dst, newval) \ - do { \ - duk_tval *tv__dst; \ - duk_tval tv__tmp; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \ - DUK_TVAL_SET_DOUBLE(tv__dst, (newval)); \ - DUK_TVAL_DECREF((thr), &tv__tmp); /* side effects */ \ - } while (0) -#define DUK_TVAL_SET_NAN_UPDREF_ALT0(thr, tvptr_dst) \ - do { \ - duk_tval *tv__dst; \ - duk_tval tv__tmp; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \ - DUK_TVAL_SET_NAN(tv__dst); \ - DUK_TVAL_DECREF((thr), &tv__tmp); /* side effects */ \ - } while (0) -#if defined(DUK_USE_FASTINT) -#define DUK_TVAL_SET_I48_UPDREF_ALT0(thr, tvptr_dst, newval) \ - do { \ - duk_tval *tv__dst; \ - duk_tval tv__tmp; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \ - DUK_TVAL_SET_I48(tv__dst, (newval)); \ - DUK_TVAL_DECREF((thr), &tv__tmp); /* side effects */ \ - } while (0) -#define DUK_TVAL_SET_I32_UPDREF_ALT0(thr, tvptr_dst, newval) \ - do { \ - duk_tval *tv__dst; \ - duk_tval tv__tmp; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \ - DUK_TVAL_SET_I32(tv__dst, (newval)); \ - DUK_TVAL_DECREF((thr), &tv__tmp); /* side effects */ \ - } while (0) -#define DUK_TVAL_SET_U32_UPDREF_ALT0(thr, tvptr_dst, newval) \ - do { \ - duk_tval *tv__dst; \ - duk_tval tv__tmp; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \ - DUK_TVAL_SET_U32(tv__dst, (newval)); \ - DUK_TVAL_DECREF((thr), &tv__tmp); /* side effects */ \ - } while (0) -#else -#define DUK_TVAL_SET_DOUBLE_CAST_UPDREF(thr, tvptr_dst, newval) \ - DUK_TVAL_SET_DOUBLE_UPDREF((thr), (tvptr_dst), (duk_double_t) (newval)) -#endif /* DUK_USE_FASTINT */ - -#define DUK_TVAL_SET_LIGHTFUNC_UPDREF_ALT0(thr, tvptr_dst, lf_v, lf_fp, lf_flags) \ - do { \ - duk_tval *tv__dst; \ - duk_tval tv__tmp; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \ - DUK_TVAL_SET_LIGHTFUNC(tv__dst, (lf_v), (lf_fp), (lf_flags)); \ - DUK_TVAL_DECREF((thr), &tv__tmp); /* side effects */ \ - } while (0) - -#define DUK_TVAL_SET_STRING_UPDREF_ALT0(thr, tvptr_dst, newval) \ - do { \ - duk_tval *tv__dst; \ - duk_tval tv__tmp; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \ - DUK_TVAL_SET_STRING(tv__dst, (newval)); \ - DUK_HSTRING_INCREF((thr), (newval)); \ - DUK_TVAL_DECREF((thr), &tv__tmp); /* side effects */ \ - } while (0) - -#define DUK_TVAL_SET_OBJECT_UPDREF_ALT0(thr, tvptr_dst, newval) \ - do { \ - duk_tval *tv__dst; \ - duk_tval tv__tmp; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \ - DUK_TVAL_SET_OBJECT(tv__dst, (newval)); \ - DUK_HOBJECT_INCREF((thr), (newval)); \ - DUK_TVAL_DECREF((thr), &tv__tmp); /* side effects */ \ - } while (0) - -#define DUK_TVAL_SET_BUFFER_UPDREF_ALT0(thr, tvptr_dst, newval) \ - do { \ - duk_tval *tv__dst; \ - duk_tval tv__tmp; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \ - DUK_TVAL_SET_BUFFER(tv__dst, (newval)); \ - DUK_HBUFFER_INCREF((thr), (newval)); \ - DUK_TVAL_DECREF((thr), &tv__tmp); /* side effects */ \ - } while (0) - -#define DUK_TVAL_SET_POINTER_UPDREF_ALT0(thr, tvptr_dst, newval) \ - do { \ - duk_tval *tv__dst; \ - duk_tval tv__tmp; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \ - DUK_TVAL_SET_POINTER(tv__dst, (newval)); \ - DUK_TVAL_DECREF((thr), &tv__tmp); /* side effects */ \ - } while (0) - -/* DUK_TVAL_SET_TVAL_UPDREF() is used a lot in executor, property lookups, - * etc, so it's very important for performance. Measure when changing. - * - * NOTE: the source and destination duk_tval pointers may be the same, and - * the macros MUST deal with that correctly. - */ - -/* Original idiom used, minimal code size. */ -#define DUK_TVAL_SET_TVAL_UPDREF_ALT0(thr, tvptr_dst, tvptr_src) \ - do { \ - duk_tval *tv__dst, *tv__src; \ - duk_tval tv__tmp; \ - tv__dst = (tvptr_dst); \ - tv__src = (tvptr_src); \ - DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \ - DUK_TVAL_SET_TVAL(tv__dst, tv__src); \ - DUK_TVAL_INCREF((thr), tv__src); \ - DUK_TVAL_DECREF((thr), &tv__tmp); /* side effects */ \ - } while (0) - -/* Faster alternative: avoid making a temporary copy of tvptr_dst and use - * fast incref/decref macros. - */ -#define DUK_TVAL_SET_TVAL_UPDREF_ALT1(thr, tvptr_dst, tvptr_src) \ - do { \ - duk_tval *tv__dst, *tv__src; \ - duk_heaphdr *h__obj; \ - tv__dst = (tvptr_dst); \ - tv__src = (tvptr_src); \ - DUK_TVAL_INCREF_FAST((thr), tv__src); \ - if (DUK_TVAL_NEEDS_REFCOUNT_UPDATE(tv__dst)) { \ - h__obj = DUK_TVAL_GET_HEAPHDR(tv__dst); \ - DUK_ASSERT(h__obj != NULL); \ - DUK_TVAL_SET_TVAL(tv__dst, tv__src); \ - DUK_HEAPHDR_DECREF_FAST((thr), h__obj); /* side effects */ \ - } else { \ - DUK_TVAL_SET_TVAL(tv__dst, tv__src); \ - } \ - } while (0) - -/* XXX: no optimized variants yet */ -#define DUK_TVAL_SET_UNDEFINED_UPDREF DUK_TVAL_SET_UNDEFINED_UPDREF_ALT0 -#define DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ_ALT0 -#define DUK_TVAL_SET_UNUSED_UPDREF DUK_TVAL_SET_UNUSED_UPDREF_ALT0 -#define DUK_TVAL_SET_NULL_UPDREF DUK_TVAL_SET_NULL_UPDREF_ALT0 -#define DUK_TVAL_SET_BOOLEAN_UPDREF DUK_TVAL_SET_BOOLEAN_UPDREF_ALT0 -#define DUK_TVAL_SET_NUMBER_UPDREF DUK_TVAL_SET_NUMBER_UPDREF_ALT0 -#define DUK_TVAL_SET_NUMBER_CHKFAST_UPDREF DUK_TVAL_SET_NUMBER_CHKFAST_UPDREF_ALT0 -#define DUK_TVAL_SET_DOUBLE_UPDREF DUK_TVAL_SET_DOUBLE_UPDREF_ALT0 -#define DUK_TVAL_SET_NAN_UPDREF DUK_TVAL_SET_NAN_UPDREF_ALT0 -#if defined(DUK_USE_FASTINT) -#define DUK_TVAL_SET_I48_UPDREF DUK_TVAL_SET_I48_UPDREF_ALT0 -#define DUK_TVAL_SET_I32_UPDREF DUK_TVAL_SET_I32_UPDREF_ALT0 -#define DUK_TVAL_SET_U32_UPDREF DUK_TVAL_SET_U32_UPDREF_ALT0 -#else -#define DUK_TVAL_SET_I48_UPDREF DUK_TVAL_SET_DOUBLE_CAST_UPDREF /* XXX: fast int-to-double */ -#define DUK_TVAL_SET_I32_UPDREF DUK_TVAL_SET_DOUBLE_CAST_UPDREF -#define DUK_TVAL_SET_U32_UPDREF DUK_TVAL_SET_DOUBLE_CAST_UPDREF -#endif /* DUK_USE_FASTINT */ -#define DUK_TVAL_SET_FASTINT_UPDREF DUK_TVAL_SET_I48_UPDREF /* convenience */ -#define DUK_TVAL_SET_LIGHTFUNC_UPDREF DUK_TVAL_SET_LIGHTFUNC_UPDREF_ALT0 -#define DUK_TVAL_SET_STRING_UPDREF DUK_TVAL_SET_STRING_UPDREF_ALT0 -#define DUK_TVAL_SET_OBJECT_UPDREF DUK_TVAL_SET_OBJECT_UPDREF_ALT0 -#define DUK_TVAL_SET_BUFFER_UPDREF DUK_TVAL_SET_BUFFER_UPDREF_ALT0 -#define DUK_TVAL_SET_POINTER_UPDREF DUK_TVAL_SET_POINTER_UPDREF_ALT0 - -#if defined(DUK_USE_FAST_REFCOUNT_DEFAULT) -/* Optimized for speed. */ -#define DUK_TVAL_SET_TVAL_UPDREF DUK_TVAL_SET_TVAL_UPDREF_ALT1 -#define DUK_TVAL_SET_TVAL_UPDREF_FAST DUK_TVAL_SET_TVAL_UPDREF_ALT1 -#define DUK_TVAL_SET_TVAL_UPDREF_SLOW DUK_TVAL_SET_TVAL_UPDREF_ALT0 -#else -/* Optimized for size. */ -#define DUK_TVAL_SET_TVAL_UPDREF DUK_TVAL_SET_TVAL_UPDREF_ALT0 -#define DUK_TVAL_SET_TVAL_UPDREF_FAST DUK_TVAL_SET_TVAL_UPDREF_ALT0 -#define DUK_TVAL_SET_TVAL_UPDREF_SLOW DUK_TVAL_SET_TVAL_UPDREF_ALT0 -#endif - -#else /* DUK_USE_REFERENCE_COUNTING */ - -#define DUK_TVAL_NEEDS_REFCOUNT_UPDATE(tv) 0 -#define DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE(h) 0 - -#define DUK_TVAL_INCREF_FAST(thr, v) \ - do { \ - } while (0) /* nop */ -#define DUK_TVAL_DECREF_FAST(thr, v) \ - do { \ - } while (0) /* nop */ -#define DUK_TVAL_DECREF_NORZ_FAST(thr, v) \ - do { \ - } while (0) /* nop */ -#define DUK_TVAL_INCREF_SLOW(thr, v) \ - do { \ - } while (0) /* nop */ -#define DUK_TVAL_DECREF_SLOW(thr, v) \ - do { \ - } while (0) /* nop */ -#define DUK_TVAL_DECREF_NORZ_SLOW(thr, v) \ - do { \ - } while (0) /* nop */ -#define DUK_TVAL_INCREF(thr, v) \ - do { \ - } while (0) /* nop */ -#define DUK_TVAL_DECREF(thr, v) \ - do { \ - } while (0) /* nop */ -#define DUK_TVAL_DECREF_NORZ(thr, v) \ - do { \ - } while (0) /* nop */ -#define DUK_HEAPHDR_INCREF_FAST(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HEAPHDR_DECREF_FAST(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HEAPHDR_DECREF_NORZ_FAST(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HEAPHDR_INCREF_SLOW(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HEAPHDR_DECREF_SLOW(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HEAPHDR_DECREF_NORZ_SLOW(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HEAPHDR_INCREF(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HEAPHDR_DECREF(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HEAPHDR_DECREF_NORZ(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HSTRING_INCREF_FAST(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HSTRING_DECREF_FAST(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HSTRING_DECREF_NORZ_FAST(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HSTRING_INCREF_SLOW(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HSTRING_DECREF_SLOW(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HSTRING_DECREF_NORZ_SLOW(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HSTRING_INCREF(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HSTRING_DECREF(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HSTRING_DECREF_NORZ(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HOBJECT_INCREF_FAST(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HOBJECT_DECREF_FAST(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HOBJECT_DECREF_NORZ_FAST(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HOBJECT_INCREF_SLOW(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HOBJECT_DECREF_SLOW(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HOBJECT_DECREF_NORZ_SLOW(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HOBJECT_INCREF(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HOBJECT_DECREF(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HOBJECT_DECREF_NORZ(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HBUFFER_INCREF_FAST(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HBUFFER_DECREF_FAST(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HBUFFER_DECREF_NORZ_FAST(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HBUFFER_INCREF_SLOW(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HBUFFER_DECREF_SLOW(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HBUFFER_DECREF_NORZ_SLOW(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HBUFFER_INCREF(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HBUFFER_DECREF(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HBUFFER_DECREF_NORZ(thr, h) \ - do { \ - } while (0) /* nop */ - -#define DUK_HCOMPFUNC_INCREF(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HCOMPFUNC_DECREF(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HCOMPFUNC_DECREF_NORZ(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HNATFUNC_INCREF(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HNATFUNC_DECREF(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HNATFUNC_DECREF_NORZ(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HBUFOBJ_INCREF(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HBUFOBJ_DECREF(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HBUFOBJ_DECREF_NORZ(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HTHREAD_INCREF(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HTHREAD_DECREF(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HTHREAD_DECREF_NORZ(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HOBJECT_INCREF_ALLOWNULL(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HOBJECT_DECREF_ALLOWNULL(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HBUFFER_INCREF_ALLOWNULL(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HBUFFER_DECREF_ALLOWNULL(thr, h) \ - do { \ - } while (0) /* nop */ -#define DUK_HBUFFER_DECREF_NORZ_ALLOWNULL(thr, h) \ - do { \ - } while (0) /* nop */ - -#define DUK_REFZERO_CHECK_FAST(thr) \ - do { \ - } while (0) /* nop */ -#define DUK_REFZERO_CHECK_SLOW(thr) \ - do { \ - } while (0) /* nop */ - -#define DUK_TVAL_SET_UNDEFINED_UPDREF_ALT0(thr, tvptr_dst) \ - do { \ - duk_tval *tv__dst; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_UNDEFINED(tv__dst); \ - DUK_UNREF((thr)); \ - } while (0) - -#define DUK_TVAL_SET_UNUSED_UPDREF_ALT0(thr, tvptr_dst) \ - do { \ - duk_tval *tv__dst; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_UNUSED(tv__dst); \ - DUK_UNREF((thr)); \ - } while (0) - -#define DUK_TVAL_SET_NULL_UPDREF_ALT0(thr, tvptr_dst) \ - do { \ - duk_tval *tv__dst; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_NULL(tv__dst); \ - DUK_UNREF((thr)); \ - } while (0) - -#define DUK_TVAL_SET_BOOLEAN_UPDREF_ALT0(thr, tvptr_dst, newval) \ - do { \ - duk_tval *tv__dst; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_BOOLEAN(tv__dst, (newval)); \ - DUK_UNREF((thr)); \ - } while (0) - -#define DUK_TVAL_SET_NUMBER_UPDREF_ALT0(thr, tvptr_dst, newval) \ - do { \ - duk_tval *tv__dst; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_NUMBER(tv__dst, (newval)); \ - DUK_UNREF((thr)); \ - } while (0) -#define DUK_TVAL_SET_NUMBER_CHKFAST_UPDREF_ALT0(thr, tvptr_dst, newval) \ - do { \ - duk_tval *tv__dst; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_NUMBER_CHKFAST_FAST(tv__dst, (newval)); \ - DUK_UNREF((thr)); \ - } while (0) -#define DUK_TVAL_SET_DOUBLE_UPDREF_ALT0(thr, tvptr_dst, newval) \ - do { \ - duk_tval *tv__dst; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_DOUBLE(tv__dst, (newval)); \ - DUK_UNREF((thr)); \ - } while (0) -#define DUK_TVAL_SET_NAN_UPDREF_ALT0(thr, tvptr_dst) \ - do { \ - duk_tval *tv__dst; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_NAN(tv__dst); \ - DUK_UNREF((thr)); \ - } while (0) -#if defined(DUK_USE_FASTINT) -#define DUK_TVAL_SET_I48_UPDREF_ALT0(thr, tvptr_dst, newval) \ - do { \ - duk_tval *tv__dst; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_I48(tv__dst, (newval)); \ - DUK_UNREF((thr)); \ - } while (0) -#define DUK_TVAL_SET_I32_UPDREF_ALT0(thr, tvptr_dst, newval) \ - do { \ - duk_tval *tv__dst; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_I32(tv__dst, (newval)); \ - DUK_UNREF((thr)); \ - } while (0) -#define DUK_TVAL_SET_U32_UPDREF_ALT0(thr, tvptr_dst, newval) \ - do { \ - duk_tval *tv__dst; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_U32(tv__dst, (newval)); \ - DUK_UNREF((thr)); \ - } while (0) -#else -#define DUK_TVAL_SET_DOUBLE_CAST_UPDREF(thr, tvptr_dst, newval) \ - DUK_TVAL_SET_DOUBLE_UPDREF((thr), (tvptr_dst), (duk_double_t) (newval)) -#endif /* DUK_USE_FASTINT */ - -#define DUK_TVAL_SET_LIGHTFUNC_UPDREF_ALT0(thr, tvptr_dst, lf_v, lf_fp, lf_flags) \ - do { \ - duk_tval *tv__dst; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_LIGHTFUNC(tv__dst, (lf_v), (lf_fp), (lf_flags)); \ - DUK_UNREF((thr)); \ - } while (0) - -#define DUK_TVAL_SET_STRING_UPDREF_ALT0(thr, tvptr_dst, newval) \ - do { \ - duk_tval *tv__dst; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_STRING(tv__dst, (newval)); \ - DUK_UNREF((thr)); \ - } while (0) - -#define DUK_TVAL_SET_OBJECT_UPDREF_ALT0(thr, tvptr_dst, newval) \ - do { \ - duk_tval *tv__dst; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_OBJECT(tv__dst, (newval)); \ - DUK_UNREF((thr)); \ - } while (0) - -#define DUK_TVAL_SET_BUFFER_UPDREF_ALT0(thr, tvptr_dst, newval) \ - do { \ - duk_tval *tv__dst; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_BUFFER(tv__dst, (newval)); \ - DUK_UNREF((thr)); \ - } while (0) - -#define DUK_TVAL_SET_POINTER_UPDREF_ALT0(thr, tvptr_dst, newval) \ - do { \ - duk_tval *tv__dst; \ - tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_POINTER(tv__dst, (newval)); \ - DUK_UNREF((thr)); \ - } while (0) - -#define DUK_TVAL_SET_TVAL_UPDREF_ALT0(thr, tvptr_dst, tvptr_src) \ - do { \ - duk_tval *tv__dst, *tv__src; \ - tv__dst = (tvptr_dst); \ - tv__src = (tvptr_src); \ - DUK_TVAL_SET_TVAL(tv__dst, tv__src); \ - DUK_UNREF((thr)); \ - } while (0) - -#define DUK_TVAL_SET_UNDEFINED_UPDREF DUK_TVAL_SET_UNDEFINED_UPDREF_ALT0 -#define DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ DUK_TVAL_SET_UNDEFINED_UPDREF_ALT0 -#define DUK_TVAL_SET_UNUSED_UPDREF DUK_TVAL_SET_UNUSED_UPDREF_ALT0 -#define DUK_TVAL_SET_NULL_UPDREF DUK_TVAL_SET_NULL_UPDREF_ALT0 -#define DUK_TVAL_SET_BOOLEAN_UPDREF DUK_TVAL_SET_BOOLEAN_UPDREF_ALT0 -#define DUK_TVAL_SET_NUMBER_UPDREF DUK_TVAL_SET_NUMBER_UPDREF_ALT0 -#define DUK_TVAL_SET_NUMBER_CHKFAST_UPDREF DUK_TVAL_SET_NUMBER_CHKFAST_UPDREF_ALT0 -#define DUK_TVAL_SET_DOUBLE_UPDREF DUK_TVAL_SET_DOUBLE_UPDREF_ALT0 -#define DUK_TVAL_SET_NAN_UPDREF DUK_TVAL_SET_NAN_UPDREF_ALT0 -#if defined(DUK_USE_FASTINT) -#define DUK_TVAL_SET_I48_UPDREF DUK_TVAL_SET_I48_UPDREF_ALT0 -#define DUK_TVAL_SET_I32_UPDREF DUK_TVAL_SET_I32_UPDREF_ALT0 -#define DUK_TVAL_SET_U32_UPDREF DUK_TVAL_SET_U32_UPDREF_ALT0 -#else -#define DUK_TVAL_SET_I48_UPDREF DUK_TVAL_SET_DOUBLE_CAST_UPDREF /* XXX: fast-int-to-double */ -#define DUK_TVAL_SET_I32_UPDREF DUK_TVAL_SET_DOUBLE_CAST_UPDREF -#define DUK_TVAL_SET_U32_UPDREF DUK_TVAL_SET_DOUBLE_CAST_UPDREF -#endif /* DUK_USE_FASTINT */ -#define DUK_TVAL_SET_FASTINT_UPDREF DUK_TVAL_SET_I48_UPDREF /* convenience */ -#define DUK_TVAL_SET_LIGHTFUNC_UPDREF DUK_TVAL_SET_LIGHTFUNC_UPDREF_ALT0 -#define DUK_TVAL_SET_STRING_UPDREF DUK_TVAL_SET_STRING_UPDREF_ALT0 -#define DUK_TVAL_SET_OBJECT_UPDREF DUK_TVAL_SET_OBJECT_UPDREF_ALT0 -#define DUK_TVAL_SET_BUFFER_UPDREF DUK_TVAL_SET_BUFFER_UPDREF_ALT0 -#define DUK_TVAL_SET_POINTER_UPDREF DUK_TVAL_SET_POINTER_UPDREF_ALT0 - -#define DUK_TVAL_SET_TVAL_UPDREF DUK_TVAL_SET_TVAL_UPDREF_ALT0 -#define DUK_TVAL_SET_TVAL_UPDREF_FAST DUK_TVAL_SET_TVAL_UPDREF_ALT0 -#define DUK_TVAL_SET_TVAL_UPDREF_SLOW DUK_TVAL_SET_TVAL_UPDREF_ALT0 - -#endif /* DUK_USE_REFERENCE_COUNTING */ - -/* - * Some convenience macros that don't have optimized implementations now. - */ - -#define DUK_TVAL_SET_TVAL_UPDREF_NORZ(thr, tv_dst, tv_src) \ - do { \ - duk_hthread *duk__thr = (thr); \ - duk_tval *duk__dst = (tv_dst); \ - duk_tval *duk__src = (tv_src); \ - DUK_UNREF(duk__thr); \ - DUK_TVAL_DECREF_NORZ(thr, duk__dst); \ - DUK_TVAL_SET_TVAL(duk__dst, duk__src); \ - DUK_TVAL_INCREF(thr, duk__dst); \ - } while (0) - -#define DUK_TVAL_SET_U32_UPDREF_NORZ(thr, tv_dst, val) \ - do { \ - duk_hthread *duk__thr = (thr); \ - duk_tval *duk__dst = (tv_dst); \ - duk_uint32_t duk__val = (duk_uint32_t) (val); \ - DUK_UNREF(duk__thr); \ - DUK_TVAL_DECREF_NORZ(thr, duk__dst); \ - DUK_TVAL_SET_U32(duk__dst, duk__val); \ - } while (0) - -/* - * Prototypes - */ - -#if defined(DUK_USE_REFERENCE_COUNTING) -#if defined(DUK_USE_FINALIZER_SUPPORT) -DUK_INTERNAL_DECL void duk_refzero_check_slow(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_refzero_check_fast(duk_hthread *thr); -#endif -DUK_INTERNAL_DECL void duk_heaphdr_refcount_finalize_norz(duk_heap *heap, duk_heaphdr *hdr); -DUK_INTERNAL_DECL void duk_hobject_refcount_finalize_norz(duk_heap *heap, duk_hobject *h); -#if 0 /* Not needed: fast path handles inline; slow path uses duk_heaphdr_decref() which is needed anyway. */ -DUK_INTERNAL_DECL void duk_hstring_decref(duk_hthread *thr, duk_hstring *h); -DUK_INTERNAL_DECL void duk_hstring_decref_norz(duk_hthread *thr, duk_hstring *h); -DUK_INTERNAL_DECL void duk_hbuffer_decref(duk_hthread *thr, duk_hbuffer *h); -DUK_INTERNAL_DECL void duk_hbuffer_decref_norz(duk_hthread *thr, duk_hbuffer *h); -DUK_INTERNAL_DECL void duk_hobject_decref(duk_hthread *thr, duk_hobject *h); -DUK_INTERNAL_DECL void duk_hobject_decref_norz(duk_hthread *thr, duk_hobject *h); -#endif -DUK_INTERNAL_DECL void duk_heaphdr_refzero(duk_hthread *thr, duk_heaphdr *h); -DUK_INTERNAL_DECL void duk_heaphdr_refzero_norz(duk_hthread *thr, duk_heaphdr *h); -#if defined(DUK_USE_FAST_REFCOUNT_DEFAULT) -DUK_INTERNAL_DECL void duk_hstring_refzero(duk_hthread *thr, duk_hstring *h); /* no 'norz' variant */ -DUK_INTERNAL_DECL void duk_hbuffer_refzero(duk_hthread *thr, duk_hbuffer *h); /* no 'norz' variant */ -DUK_INTERNAL_DECL void duk_hobject_refzero(duk_hthread *thr, duk_hobject *h); -DUK_INTERNAL_DECL void duk_hobject_refzero_norz(duk_hthread *thr, duk_hobject *h); -#else -DUK_INTERNAL_DECL void duk_tval_incref(duk_tval *tv); -DUK_INTERNAL_DECL void duk_tval_decref(duk_hthread *thr, duk_tval *tv); -DUK_INTERNAL_DECL void duk_tval_decref_norz(duk_hthread *thr, duk_tval *tv); -DUK_INTERNAL_DECL void duk_heaphdr_incref(duk_heaphdr *h); -DUK_INTERNAL_DECL void duk_heaphdr_decref(duk_hthread *thr, duk_heaphdr *h); -DUK_INTERNAL_DECL void duk_heaphdr_decref_norz(duk_hthread *thr, duk_heaphdr *h); -#endif -#else /* DUK_USE_REFERENCE_COUNTING */ -/* no refcounting */ -#endif /* DUK_USE_REFERENCE_COUNTING */ - -#endif /* DUK_REFCOUNT_H_INCLUDED */ -/* #include duk_api_internal.h */ -/* - * Internal API calls which have (stack and other) semantics similar - * to the public API. - */ - -#if !defined(DUK_API_INTERNAL_H_INCLUDED) -#define DUK_API_INTERNAL_H_INCLUDED - -/* Inline macro helpers. */ -#if defined(DUK_USE_PREFER_SIZE) -#define DUK_INLINE_PERF -#define DUK_ALWAYS_INLINE_PERF -#define DUK_NOINLINE_PERF -#else -#define DUK_INLINE_PERF DUK_INLINE -#define DUK_ALWAYS_INLINE_PERF DUK_ALWAYS_INLINE -#define DUK_NOINLINE_PERF DUK_NOINLINE -#endif - -/* Inline macro helpers, for bytecode executor. */ -#if defined(DUK_USE_EXEC_PREFER_SIZE) -#define DUK_EXEC_INLINE_PERF -#define DUK_EXEC_ALWAYS_INLINE_PERF -#define DUK_EXEC_NOINLINE_PERF -#else -#define DUK_EXEC_INLINE_PERF DUK_INLINE -#define DUK_EXEC_ALWAYS_INLINE_PERF DUK_ALWAYS_INLINE -#define DUK_EXEC_NOINLINE_PERF DUK_NOINLINE -#endif - -/* duk_push_sprintf constants */ -#define DUK_PUSH_SPRINTF_INITIAL_SIZE 256L -#define DUK_PUSH_SPRINTF_SANITY_LIMIT (1L * 1024L * 1024L * 1024L) - -/* Flag ORed to err_code to indicate __FILE__ / __LINE__ is not - * blamed as source of error for error fileName / lineNumber. - */ -#define DUK_ERRCODE_FLAG_NOBLAME_FILELINE (1L << 24) - -/* Current convention is to use duk_size_t for value stack sizes and global indices, - * and duk_idx_t for local frame indices. - */ -DUK_INTERNAL_DECL void duk_valstack_grow_check_throw(duk_hthread *thr, duk_size_t min_bytes); -DUK_INTERNAL_DECL duk_bool_t duk_valstack_grow_check_nothrow(duk_hthread *thr, duk_size_t min_bytes); -DUK_INTERNAL_DECL void duk_valstack_shrink_check_nothrow(duk_hthread *thr, duk_bool_t snug); - -DUK_INTERNAL_DECL void duk_copy_tvals_incref(duk_hthread *thr, duk_tval *tv_dst, duk_tval *tv_src, duk_size_t count); - -DUK_INTERNAL_DECL duk_tval *duk_reserve_gap(duk_hthread *thr, duk_idx_t idx_base, duk_idx_t count); - -DUK_INTERNAL_DECL void duk_set_top_unsafe(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL void duk_set_top_and_wipe(duk_hthread *thr, duk_idx_t top, duk_idx_t idx_wipe_start); - -DUK_INTERNAL_DECL void duk_dup_0(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_dup_1(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_dup_2(duk_hthread *thr); -/* duk_dup_m1() would be same as duk_dup_top() */ -DUK_INTERNAL_DECL void duk_dup_m2(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_dup_m3(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_dup_m4(duk_hthread *thr); - -DUK_INTERNAL_DECL void duk_remove_unsafe(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL void duk_remove_m2(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_remove_n(duk_hthread *thr, duk_idx_t idx, duk_idx_t count); -DUK_INTERNAL_DECL void duk_remove_n_unsafe(duk_hthread *thr, duk_idx_t idx, duk_idx_t count); - -DUK_INTERNAL_DECL duk_int_t duk_get_type_tval(duk_tval *tv); -DUK_INTERNAL_DECL duk_uint_t duk_get_type_mask_tval(duk_tval *tv); - -#if defined(DUK_USE_VERBOSE_ERRORS) && defined(DUK_USE_PARANOID_ERRORS) -DUK_INTERNAL_DECL const char *duk_get_type_name(duk_hthread *thr, duk_idx_t idx); -#endif -DUK_INTERNAL_DECL duk_small_uint_t duk_get_class_number(duk_hthread *thr, duk_idx_t idx); - -DUK_INTERNAL_DECL duk_tval *duk_get_tval(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL duk_tval *duk_get_tval_or_unused(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL duk_tval *duk_require_tval(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL void duk_push_tval(duk_hthread *thr, duk_tval *tv); - -/* Push the current 'this' binding; throw TypeError if binding is not object - * coercible (CheckObjectCoercible). - */ -DUK_INTERNAL_DECL void duk_push_this_check_object_coercible(duk_hthread *thr); - -/* duk_push_this() + CheckObjectCoercible() + duk_to_object() */ -DUK_INTERNAL_DECL duk_hobject *duk_push_this_coercible_to_object(duk_hthread *thr); - -/* duk_push_this() + CheckObjectCoercible() + duk_to_string() */ -DUK_INTERNAL_DECL duk_hstring *duk_push_this_coercible_to_string(duk_hthread *thr); - -DUK_INTERNAL_DECL duk_hstring *duk_push_uint_to_hstring(duk_hthread *thr, duk_uint_t i); - -/* Get a borrowed duk_tval pointer to the current 'this' binding. Caller must - * make sure there's an active callstack entry. Note that the returned pointer - * is unstable with regards to side effects. - */ -DUK_INTERNAL_DECL duk_tval *duk_get_borrowed_this_tval(duk_hthread *thr); - -/* XXX: add fastint support? */ -#define duk_push_u64(thr, val) duk_push_number((thr), (duk_double_t) (val)) -#define duk_push_i64(thr, val) duk_push_number((thr), (duk_double_t) (val)) - -/* duk_push_(u)int() is guaranteed to support at least (un)signed 32-bit range */ -#define duk_push_u32(thr, val) duk_push_uint((thr), (duk_uint_t) (val)) -#define duk_push_i32(thr, val) duk_push_int((thr), (duk_int_t) (val)) - -/* sometimes stack and array indices need to go on the stack */ -#define duk_push_idx(thr, val) duk_push_int((thr), (duk_int_t) (val)) -#define duk_push_uarridx(thr, val) duk_push_uint((thr), (duk_uint_t) (val)) -#define duk_push_size_t(thr, val) duk_push_uint((thr), (duk_uint_t) (val)) /* XXX: assumed to fit for now */ - -DUK_INTERNAL_DECL duk_bool_t duk_is_string_notsymbol(duk_hthread *thr, duk_idx_t idx); - -DUK_INTERNAL_DECL duk_bool_t duk_is_callable_tval(duk_hthread *thr, duk_tval *tv); - -DUK_INTERNAL_DECL duk_bool_t duk_is_bare_object(duk_hthread *thr, duk_idx_t idx); - -DUK_INTERNAL_DECL duk_hstring *duk_get_hstring(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL duk_hstring *duk_get_hstring_notsymbol(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL const char *duk_get_string_notsymbol(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL duk_hobject *duk_get_hobject(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL duk_hbuffer *duk_get_hbuffer(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL duk_hthread *duk_get_hthread(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL duk_hcompfunc *duk_get_hcompfunc(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL duk_hnatfunc *duk_get_hnatfunc(duk_hthread *thr, duk_idx_t idx); - -DUK_INTERNAL_DECL void *duk_get_buffer_data_raw(duk_hthread *thr, - duk_idx_t idx, - duk_size_t *out_size, - void *def_ptr, - duk_size_t def_len, - duk_bool_t throw_flag, - duk_bool_t *out_isbuffer); - -DUK_INTERNAL_DECL duk_hobject *duk_get_hobject_with_class(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t classnum); - -DUK_INTERNAL_DECL duk_hobject *duk_get_hobject_promote_mask(duk_hthread *thr, duk_idx_t idx, duk_uint_t type_mask); -DUK_INTERNAL_DECL duk_hobject *duk_require_hobject_promote_mask(duk_hthread *thr, duk_idx_t idx, duk_uint_t type_mask); -DUK_INTERNAL_DECL duk_hobject *duk_require_hobject_accept_mask(duk_hthread *thr, duk_idx_t idx, duk_uint_t type_mask); -#define duk_require_hobject_promote_lfunc(thr, idx) duk_require_hobject_promote_mask((thr), (idx), DUK_TYPE_MASK_LIGHTFUNC) -#define duk_get_hobject_promote_lfunc(thr, idx) duk_get_hobject_promote_mask((thr), (idx), DUK_TYPE_MASK_LIGHTFUNC) - -#if 0 /*unused*/ -DUK_INTERNAL_DECL void *duk_get_voidptr(duk_hthread *thr, duk_idx_t idx); -#endif - -DUK_INTERNAL_DECL duk_hstring *duk_known_hstring(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL duk_hobject *duk_known_hobject(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL duk_hbuffer *duk_known_hbuffer(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL duk_hcompfunc *duk_known_hcompfunc(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL duk_hnatfunc *duk_known_hnatfunc(duk_hthread *thr, duk_idx_t idx); - -DUK_INTERNAL_DECL duk_double_t duk_to_number_tval(duk_hthread *thr, duk_tval *tv); - -DUK_INTERNAL_DECL duk_hstring *duk_to_hstring(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL duk_hstring *duk_to_hstring_m1(duk_hthread *thr); -DUK_INTERNAL_DECL duk_hstring *duk_to_hstring_acceptsymbol(duk_hthread *thr, duk_idx_t idx); - -DUK_INTERNAL_DECL duk_hobject *duk_to_hobject(duk_hthread *thr, duk_idx_t idx); - -DUK_INTERNAL_DECL duk_double_t duk_to_number_m1(duk_hthread *thr); -DUK_INTERNAL_DECL duk_double_t duk_to_number_m2(duk_hthread *thr); - -DUK_INTERNAL_DECL duk_bool_t duk_to_boolean_top_pop(duk_hthread *thr); - -#if defined(DUK_USE_DEBUGGER_SUPPORT) /* only needed by debugger for now */ -DUK_INTERNAL_DECL duk_hstring *duk_safe_to_hstring(duk_hthread *thr, duk_idx_t idx); -#endif -DUK_INTERNAL_DECL void duk_push_class_string_tval(duk_hthread *thr, duk_tval *tv, duk_bool_t avoid_side_effects); - -DUK_INTERNAL_DECL duk_int_t duk_to_int_clamped_raw(duk_hthread *thr, - duk_idx_t idx, - duk_int_t minval, - duk_int_t maxval, - duk_bool_t *out_clamped); /* out_clamped=NULL, RangeError if outside range */ -DUK_INTERNAL_DECL duk_int_t duk_to_int_clamped(duk_hthread *thr, duk_idx_t idx, duk_int_t minval, duk_int_t maxval); -DUK_INTERNAL_DECL duk_int_t duk_to_int_check_range(duk_hthread *thr, duk_idx_t idx, duk_int_t minval, duk_int_t maxval); -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL_DECL duk_uint8_t duk_to_uint8clamped(duk_hthread *thr, duk_idx_t idx); -#endif -DUK_INTERNAL_DECL duk_hstring *duk_to_property_key_hstring(duk_hthread *thr, duk_idx_t idx); - -DUK_INTERNAL_DECL duk_hstring *duk_require_hstring(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL duk_hstring *duk_require_hstring_notsymbol(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL const char *duk_require_lstring_notsymbol(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len); -DUK_INTERNAL_DECL const char *duk_require_string_notsymbol(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL duk_hobject *duk_require_hobject(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL duk_hbuffer *duk_require_hbuffer(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL duk_hthread *duk_require_hthread(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL duk_hcompfunc *duk_require_hcompfunc(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL duk_hnatfunc *duk_require_hnatfunc(duk_hthread *thr, duk_idx_t idx); - -DUK_INTERNAL_DECL duk_hobject *duk_require_hobject_with_class(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t classnum); - -DUK_INTERNAL_DECL void duk_push_hstring(duk_hthread *thr, duk_hstring *h); -DUK_INTERNAL_DECL void duk_push_hstring_stridx(duk_hthread *thr, duk_small_uint_t stridx); -DUK_INTERNAL_DECL void duk_push_hstring_empty(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_push_hobject(duk_hthread *thr, duk_hobject *h); -DUK_INTERNAL_DECL void duk_push_hbuffer(duk_hthread *thr, duk_hbuffer *h); -#define duk_push_hthread(thr, h) duk_push_hobject((thr), (duk_hobject *) (h)) -#define duk_push_hnatfunc(thr, h) duk_push_hobject((thr), (duk_hobject *) (h)) -DUK_INTERNAL_DECL void duk_push_hobject_bidx(duk_hthread *thr, duk_small_int_t builtin_idx); -DUK_INTERNAL_DECL duk_hobject *duk_push_object_helper(duk_hthread *thr, - duk_uint_t hobject_flags_and_class, - duk_small_int_t prototype_bidx); -DUK_INTERNAL_DECL duk_hobject *duk_push_object_helper_proto(duk_hthread *thr, - duk_uint_t hobject_flags_and_class, - duk_hobject *proto); -DUK_INTERNAL_DECL duk_hcompfunc *duk_push_hcompfunc(duk_hthread *thr); -DUK_INTERNAL_DECL duk_hboundfunc *duk_push_hboundfunc(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_push_c_function_builtin(duk_hthread *thr, duk_c_function func, duk_int_t nargs); -DUK_INTERNAL_DECL void duk_push_c_function_builtin_noconstruct(duk_hthread *thr, duk_c_function func, duk_int_t nargs); - -/* XXX: duk_push_harray() and duk_push_hcompfunc() are inconsistent with - * duk_push_hobject() etc which don't create a new value. - */ -DUK_INTERNAL_DECL duk_harray *duk_push_harray(duk_hthread *thr); -DUK_INTERNAL_DECL duk_harray *duk_push_harray_with_size(duk_hthread *thr, duk_uint32_t size); -DUK_INTERNAL_DECL duk_tval *duk_push_harray_with_size_outptr(duk_hthread *thr, duk_uint32_t size); - -DUK_INTERNAL_DECL void duk_push_string_funcptr(duk_hthread *thr, duk_uint8_t *ptr, duk_size_t sz); -DUK_INTERNAL_DECL void duk_push_lightfunc_name_raw(duk_hthread *thr, duk_c_function func, duk_small_uint_t lf_flags); -DUK_INTERNAL_DECL void duk_push_lightfunc_name(duk_hthread *thr, duk_tval *tv); -DUK_INTERNAL_DECL void duk_push_lightfunc_tostring(duk_hthread *thr, duk_tval *tv); -#if 0 /* not used yet */ -DUK_INTERNAL_DECL void duk_push_hnatfunc_name(duk_hthread *thr, duk_hnatfunc *h); -#endif -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL_DECL duk_hbufobj *duk_push_bufobj_raw(duk_hthread *thr, - duk_uint_t hobject_flags_and_class, - duk_small_int_t prototype_bidx); -#endif - -DUK_INTERNAL_DECL void *duk_push_fixed_buffer_nozero(duk_hthread *thr, duk_size_t len); -DUK_INTERNAL_DECL void *duk_push_fixed_buffer_zero(duk_hthread *thr, duk_size_t len); - -DUK_INTERNAL_DECL const char *duk_push_string_readable(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL const char *duk_push_string_tval_readable(duk_hthread *thr, duk_tval *tv); -DUK_INTERNAL_DECL const char *duk_push_string_tval_readable_error(duk_hthread *thr, duk_tval *tv); - -/* The duk_xxx_prop_stridx_short() variants expect their arguments to be short - * enough to be packed into a single 32-bit integer argument. Argument limits - * vary per call; typically 16 bits are assigned to the signed value stack index - * and the stridx. In practice these work well for footprint with constant - * arguments and such call sites are also easiest to verify to be correct. - */ - -DUK_INTERNAL_DECL duk_bool_t duk_get_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx); /* [] -> [val] */ -DUK_INTERNAL_DECL duk_bool_t duk_get_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args); -#define duk_get_prop_stridx_short(thr, obj_idx, stridx) \ - (DUK_ASSERT_EXPR((duk_int_t) (obj_idx) >= -0x8000L && (duk_int_t) (obj_idx) <= 0x7fffL), \ - DUK_ASSERT_EXPR((duk_int_t) (stridx) >= 0 && (duk_int_t) (stridx) <= 0xffffL), \ - duk_get_prop_stridx_short_raw((thr), (((duk_uint_t) (obj_idx)) << 16) + ((duk_uint_t) (stridx)))) -DUK_INTERNAL_DECL duk_bool_t duk_get_prop_stridx_boolean(duk_hthread *thr, - duk_idx_t obj_idx, - duk_small_uint_t stridx, - duk_bool_t *out_has_prop); /* [] -> [] */ - -DUK_INTERNAL_DECL duk_bool_t duk_xget_owndataprop(duk_hthread *thr, duk_idx_t obj_idx); -DUK_INTERNAL_DECL duk_bool_t duk_xget_owndataprop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx); -DUK_INTERNAL_DECL duk_bool_t duk_xget_owndataprop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args); -#define duk_xget_owndataprop_stridx_short(thr, obj_idx, stridx) \ - (DUK_ASSERT_EXPR((duk_int_t) (obj_idx) >= -0x8000L && (duk_int_t) (obj_idx) <= 0x7fffL), \ - DUK_ASSERT_EXPR((duk_int_t) (stridx) >= 0 && (duk_int_t) (stridx) <= 0xffffL), \ - duk_xget_owndataprop_stridx_short_raw((thr), (((duk_uint_t) (obj_idx)) << 16) + ((duk_uint_t) (stridx)))) - -DUK_INTERNAL_DECL duk_bool_t duk_put_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx); /* [val] -> [] */ -DUK_INTERNAL_DECL duk_bool_t duk_put_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args); -#define duk_put_prop_stridx_short(thr, obj_idx, stridx) \ - (DUK_ASSERT_EXPR((duk_int_t) (obj_idx) >= -0x8000L && (duk_int_t) (obj_idx) <= 0x7fffL), \ - DUK_ASSERT_EXPR((duk_int_t) (stridx) >= 0 && (duk_int_t) (stridx) <= 0xffffL), \ - duk_put_prop_stridx_short_raw((thr), (((duk_uint_t) (obj_idx)) << 16) + ((duk_uint_t) (stridx)))) - -DUK_INTERNAL_DECL duk_bool_t duk_del_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx); /* [] -> [] */ -#if 0 /* Too few call sites to be useful. */ -DUK_INTERNAL_DECL duk_bool_t duk_del_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args); -#define duk_del_prop_stridx_short(thr, obj_idx, stridx) \ - (DUK_ASSERT_EXPR((obj_idx) >= -0x8000L && (obj_idx) <= 0x7fffL), \ - DUK_ASSERT_EXPR((stridx) >= 0 && (stridx) <= 0xffffL), \ - duk_del_prop_stridx_short_raw((thr), (((duk_uint_t) (obj_idx)) << 16) + ((duk_uint_t) (stridx)))) -#endif -#define duk_del_prop_stridx_short(thr, obj_idx, stridx) duk_del_prop_stridx((thr), (obj_idx), (stridx)) - -DUK_INTERNAL_DECL duk_bool_t duk_has_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx); /* [] -> [] */ -#if 0 /* Too few call sites to be useful. */ -DUK_INTERNAL_DECL duk_bool_t duk_has_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args); -#define duk_has_prop_stridx_short(thr, obj_idx, stridx) \ - (DUK_ASSERT_EXPR((obj_idx) >= -0x8000L && (obj_idx) <= 0x7fffL), \ - DUK_ASSERT_EXPR((stridx) >= 0 && (stridx) <= 0xffffL), \ - duk_has_prop_stridx_short_raw((thr), (((duk_uint_t) (obj_idx)) << 16) + ((duk_uint_t) (stridx)))) -#endif -#define duk_has_prop_stridx_short(thr, obj_idx, stridx) duk_has_prop_stridx((thr), (obj_idx), (stridx)) - -DUK_INTERNAL_DECL void duk_xdef_prop(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t desc_flags); /* [key val] -> [] */ - -DUK_INTERNAL_DECL void duk_xdef_prop_index(duk_hthread *thr, - duk_idx_t obj_idx, - duk_uarridx_t arr_idx, - duk_small_uint_t desc_flags); /* [val] -> [] */ - -/* XXX: Because stridx and desc_flags have a limited range, this call could - * always pack stridx and desc_flags into a single argument. - */ -DUK_INTERNAL_DECL void duk_xdef_prop_stridx(duk_hthread *thr, - duk_idx_t obj_idx, - duk_small_uint_t stridx, - duk_small_uint_t desc_flags); /* [val] -> [] */ -DUK_INTERNAL_DECL void duk_xdef_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args); -#define duk_xdef_prop_stridx_short(thr, obj_idx, stridx, desc_flags) \ - (DUK_ASSERT_EXPR((duk_int_t) (obj_idx) >= -0x80L && (duk_int_t) (obj_idx) <= 0x7fL), \ - DUK_ASSERT_EXPR((duk_int_t) (stridx) >= 0 && (duk_int_t) (stridx) <= 0xffffL), \ - DUK_ASSERT_EXPR((duk_int_t) (desc_flags) >= 0 && (duk_int_t) (desc_flags) <= 0xffL), \ - duk_xdef_prop_stridx_short_raw((thr), \ - (((duk_uint_t) (obj_idx)) << 24) + (((duk_uint_t) (stridx)) << 8) + \ - (duk_uint_t) (desc_flags))) - -#define duk_xdef_prop_wec(thr, obj_idx) duk_xdef_prop((thr), (obj_idx), DUK_PROPDESC_FLAGS_WEC) -#define duk_xdef_prop_index_wec(thr, obj_idx, arr_idx) duk_xdef_prop_index((thr), (obj_idx), (arr_idx), DUK_PROPDESC_FLAGS_WEC) -#define duk_xdef_prop_stridx_wec(thr, obj_idx, stridx) duk_xdef_prop_stridx((thr), (obj_idx), (stridx), DUK_PROPDESC_FLAGS_WEC) -#define duk_xdef_prop_stridx_short_wec(thr, obj_idx, stridx) \ - duk_xdef_prop_stridx_short((thr), (obj_idx), (stridx), DUK_PROPDESC_FLAGS_WEC) - -#if 0 /*unused*/ -DUK_INTERNAL_DECL void duk_xdef_prop_stridx_builtin(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx, duk_small_int_t builtin_idx, duk_small_uint_t desc_flags); /* [] -> [] */ -#endif - -DUK_INTERNAL_DECL void duk_xdef_prop_stridx_thrower(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx); /* [] -> [] */ - -DUK_INTERNAL_DECL duk_bool_t duk_get_method_stridx(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t stridx); - -DUK_INTERNAL_DECL void duk_pack(duk_hthread *thr, duk_idx_t count); -DUK_INTERNAL_DECL duk_idx_t duk_unpack_array_like(duk_hthread *thr, duk_idx_t idx); -#if 0 -DUK_INTERNAL_DECL void duk_unpack(duk_hthread *thr); -#endif - -DUK_INTERNAL_DECL void duk_push_symbol_descriptive_string(duk_hthread *thr, duk_hstring *h); - -DUK_INTERNAL_DECL void duk_resolve_nonbound_function(duk_hthread *thr); - -DUK_INTERNAL_DECL duk_idx_t duk_get_top_require_min(duk_hthread *thr, duk_idx_t min_top); -DUK_INTERNAL_DECL duk_idx_t duk_get_top_index_unsafe(duk_hthread *thr); - -DUK_INTERNAL_DECL void duk_pop_n_unsafe(duk_hthread *thr, duk_idx_t count); -DUK_INTERNAL_DECL void duk_pop_unsafe(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_pop_2_unsafe(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_pop_3_unsafe(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_pop_n_nodecref_unsafe(duk_hthread *thr, duk_idx_t count); -DUK_INTERNAL_DECL void duk_pop_nodecref_unsafe(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_pop_2_nodecref_unsafe(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_pop_3_nodecref_unsafe(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_pop_undefined(duk_hthread *thr); - -DUK_INTERNAL_DECL void duk_compact_m1(duk_hthread *thr); - -DUK_INTERNAL_DECL void duk_seal_freeze_raw(duk_hthread *thr, duk_idx_t obj_idx, duk_bool_t is_freeze); - -DUK_INTERNAL_DECL void duk_insert_undefined(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL void duk_insert_undefined_n(duk_hthread *thr, duk_idx_t idx, duk_idx_t count); - -DUK_INTERNAL_DECL void duk_concat_2(duk_hthread *thr); - -DUK_INTERNAL_DECL duk_int_t duk_pcall_method_flags(duk_hthread *thr, duk_idx_t nargs, duk_small_uint_t call_flags); - -#if defined(DUK_USE_SYMBOL_BUILTIN) -DUK_INTERNAL_DECL void duk_to_primitive_ordinary(duk_hthread *thr, duk_idx_t idx, duk_int_t hint); -#endif - -DUK_INTERNAL_DECL void duk_clear_prototype(duk_hthread *thr, duk_idx_t idx); - -/* Raw internal valstack access macros: access is unsafe so call site - * must have a guarantee that the index is valid. When that is the case, - * using these macro results in faster and smaller code than duk_get_tval(). - * Both 'ctx' and 'idx' are evaluted multiple times, but only for asserts. - */ -#define DUK_ASSERT_VALID_NEGIDX(thr, idx) \ - (DUK_ASSERT_EXPR((duk_int_t) (idx) < 0), DUK_ASSERT_EXPR(duk_is_valid_index((thr), (idx)))) -#define DUK_ASSERT_VALID_POSIDX(thr, idx) \ - (DUK_ASSERT_EXPR((duk_int_t) (idx) >= 0), DUK_ASSERT_EXPR(duk_is_valid_index((thr), (idx)))) -#define DUK_GET_TVAL_NEGIDX(thr, idx) (DUK_ASSERT_VALID_NEGIDX((thr), (idx)), ((duk_hthread *) (thr))->valstack_top + (idx)) -#define DUK_GET_TVAL_POSIDX(thr, idx) (DUK_ASSERT_VALID_POSIDX((thr), (idx)), ((duk_hthread *) (thr))->valstack_bottom + (idx)) -#define DUK_GET_HOBJECT_NEGIDX(thr, idx) \ - (DUK_ASSERT_VALID_NEGIDX((thr), (idx)), DUK_TVAL_GET_OBJECT(((duk_hthread *) (thr))->valstack_top + (idx))) -#define DUK_GET_HOBJECT_POSIDX(thr, idx) \ - (DUK_ASSERT_VALID_POSIDX((thr), (idx)), DUK_TVAL_GET_OBJECT(((duk_hthread *) (thr))->valstack_bottom + (idx))) - -#define DUK_GET_THIS_TVAL_PTR(thr) (DUK_ASSERT_EXPR((thr)->valstack_bottom > (thr)->valstack), (thr)->valstack_bottom - 1) - -DUK_INTERNAL_DECL duk_double_t duk_time_get_ecmascript_time(duk_hthread *thr); -DUK_INTERNAL_DECL duk_double_t duk_time_get_ecmascript_time_nofrac(duk_hthread *thr); -DUK_INTERNAL_DECL duk_double_t duk_time_get_monotonic_time(duk_hthread *thr); - -#endif /* DUK_API_INTERNAL_H_INCLUDED */ -/* #include duk_hstring.h */ -/* - * Heap string representation. - * - * Strings are byte sequences ordinarily stored in extended UTF-8 format, - * allowing values larger than the official UTF-8 range (used internally) - * and also allowing UTF-8 encoding of surrogate pairs (CESU-8 format). - * Strings may also be invalid UTF-8 altogether which is the case e.g. with - * strings used as internal property names and raw buffers converted to - * strings. In such cases the 'clen' field contains an inaccurate value. - * - * ECMAScript requires support for 32-bit long strings. However, since each - * 16-bit codepoint can take 3 bytes in CESU-8, this representation can only - * support about 1.4G codepoint long strings in extreme cases. This is not - * really a practical issue. - */ - -#if !defined(DUK_HSTRING_H_INCLUDED) -#define DUK_HSTRING_H_INCLUDED - -/* Impose a maximum string length for now. Restricted artificially to - * ensure adding a heap header length won't overflow size_t. The limit - * should be synchronized with DUK_HBUFFER_MAX_BYTELEN. - * - * E5.1 makes provisions to support strings longer than 4G characters. - * This limit should be eliminated on 64-bit platforms (and increased - * closer to maximum support on 32-bit platforms). - */ - -#if defined(DUK_USE_STRLEN16) -#define DUK_HSTRING_MAX_BYTELEN (0x0000ffffUL) -#else -#define DUK_HSTRING_MAX_BYTELEN (0x7fffffffUL) -#endif - -/* XXX: could add flags for "is valid CESU-8" (ECMAScript compatible strings), - * "is valid UTF-8", "is valid extended UTF-8" (internal strings are not, - * regexp bytecode is), and "contains non-BMP characters". These are not - * needed right now. - */ - -/* With lowmem builds the high 16 bits of duk_heaphdr are used for other - * purposes, so this leaves 7 duk_heaphdr flags and 9 duk_hstring flags. - */ -#define DUK_HSTRING_FLAG_ASCII DUK_HEAPHDR_USER_FLAG(0) /* string is ASCII, clen == blen */ -#define DUK_HSTRING_FLAG_ARRIDX DUK_HEAPHDR_USER_FLAG(1) /* string is a valid array index */ -#define DUK_HSTRING_FLAG_SYMBOL DUK_HEAPHDR_USER_FLAG(2) /* string is a symbol (invalid utf-8) */ -#define DUK_HSTRING_FLAG_HIDDEN \ - DUK_HEAPHDR_USER_FLAG(3) /* string is a hidden symbol (implies symbol, Duktape 1.x internal string) */ -#define DUK_HSTRING_FLAG_RESERVED_WORD DUK_HEAPHDR_USER_FLAG(4) /* string is a reserved word (non-strict) */ -#define DUK_HSTRING_FLAG_STRICT_RESERVED_WORD DUK_HEAPHDR_USER_FLAG(5) /* string is a reserved word (strict) */ -#define DUK_HSTRING_FLAG_EVAL_OR_ARGUMENTS DUK_HEAPHDR_USER_FLAG(6) /* string is 'eval' or 'arguments' */ -#define DUK_HSTRING_FLAG_EXTDATA DUK_HEAPHDR_USER_FLAG(7) /* string data is external (duk_hstring_external) */ -#define DUK_HSTRING_FLAG_PINNED_LITERAL DUK_HEAPHDR_USER_FLAG(8) /* string is a literal, and pinned */ - -#define DUK_HSTRING_HAS_ASCII(x) DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_ASCII) -#define DUK_HSTRING_HAS_ARRIDX(x) DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_ARRIDX) -#define DUK_HSTRING_HAS_SYMBOL(x) DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_SYMBOL) -#define DUK_HSTRING_HAS_HIDDEN(x) DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_HIDDEN) -#define DUK_HSTRING_HAS_RESERVED_WORD(x) DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_RESERVED_WORD) -#define DUK_HSTRING_HAS_STRICT_RESERVED_WORD(x) DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_STRICT_RESERVED_WORD) -#define DUK_HSTRING_HAS_EVAL_OR_ARGUMENTS(x) DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_EVAL_OR_ARGUMENTS) -#define DUK_HSTRING_HAS_EXTDATA(x) DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_EXTDATA) -#define DUK_HSTRING_HAS_PINNED_LITERAL(x) DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_PINNED_LITERAL) - -#define DUK_HSTRING_SET_ASCII(x) DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_ASCII) -#define DUK_HSTRING_SET_ARRIDX(x) DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_ARRIDX) -#define DUK_HSTRING_SET_SYMBOL(x) DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_SYMBOL) -#define DUK_HSTRING_SET_HIDDEN(x) DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_HIDDEN) -#define DUK_HSTRING_SET_RESERVED_WORD(x) DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_RESERVED_WORD) -#define DUK_HSTRING_SET_STRICT_RESERVED_WORD(x) DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_STRICT_RESERVED_WORD) -#define DUK_HSTRING_SET_EVAL_OR_ARGUMENTS(x) DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_EVAL_OR_ARGUMENTS) -#define DUK_HSTRING_SET_EXTDATA(x) DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_EXTDATA) -#define DUK_HSTRING_SET_PINNED_LITERAL(x) DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_PINNED_LITERAL) - -#define DUK_HSTRING_CLEAR_ASCII(x) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_ASCII) -#define DUK_HSTRING_CLEAR_ARRIDX(x) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_ARRIDX) -#define DUK_HSTRING_CLEAR_SYMBOL(x) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_SYMBOL) -#define DUK_HSTRING_CLEAR_HIDDEN(x) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_HIDDEN) -#define DUK_HSTRING_CLEAR_RESERVED_WORD(x) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_RESERVED_WORD) -#define DUK_HSTRING_CLEAR_STRICT_RESERVED_WORD(x) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_STRICT_RESERVED_WORD) -#define DUK_HSTRING_CLEAR_EVAL_OR_ARGUMENTS(x) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_EVAL_OR_ARGUMENTS) -#define DUK_HSTRING_CLEAR_EXTDATA(x) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_EXTDATA) -#define DUK_HSTRING_CLEAR_PINNED_LITERAL(x) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_PINNED_LITERAL) - -#if 0 /* Slightly smaller code without explicit flag, but explicit flag \ - * is very useful when 'clen' is dropped. \ - */ -#define DUK_HSTRING_IS_ASCII(x) (DUK_HSTRING_GET_BYTELEN((x)) == DUK_HSTRING_GET_CHARLEN((x))) -#endif -#define DUK_HSTRING_IS_ASCII(x) DUK_HSTRING_HAS_ASCII((x)) /* lazily set! */ -#define DUK_HSTRING_IS_EMPTY(x) (DUK_HSTRING_GET_BYTELEN((x)) == 0) - -#if defined(DUK_USE_STRHASH16) -#define DUK_HSTRING_GET_HASH(x) ((x)->hdr.h_flags >> 16) -#define DUK_HSTRING_SET_HASH(x, v) \ - do { \ - (x)->hdr.h_flags = ((x)->hdr.h_flags & 0x0000ffffUL) | ((v) << 16); \ - } while (0) -#else -#define DUK_HSTRING_GET_HASH(x) ((x)->hash) -#define DUK_HSTRING_SET_HASH(x, v) \ - do { \ - (x)->hash = (v); \ - } while (0) -#endif - -#if defined(DUK_USE_STRLEN16) -#define DUK_HSTRING_GET_BYTELEN(x) ((x)->hdr.h_strextra16) -#define DUK_HSTRING_SET_BYTELEN(x, v) \ - do { \ - (x)->hdr.h_strextra16 = (v); \ - } while (0) -#if defined(DUK_USE_HSTRING_CLEN) -#define DUK_HSTRING_GET_CHARLEN(x) duk_hstring_get_charlen((x)) -#define DUK_HSTRING_SET_CHARLEN(x, v) \ - do { \ - (x)->clen16 = (v); \ - } while (0) -#else -#define DUK_HSTRING_GET_CHARLEN(x) duk_hstring_get_charlen((x)) -#define DUK_HSTRING_SET_CHARLEN(x, v) \ - do { \ - DUK_ASSERT(0); /* should never be called */ \ - } while (0) -#endif -#else -#define DUK_HSTRING_GET_BYTELEN(x) ((x)->blen) -#define DUK_HSTRING_SET_BYTELEN(x, v) \ - do { \ - (x)->blen = (v); \ - } while (0) -#define DUK_HSTRING_GET_CHARLEN(x) duk_hstring_get_charlen((x)) -#define DUK_HSTRING_SET_CHARLEN(x, v) \ - do { \ - (x)->clen = (v); \ - } while (0) -#endif - -#if defined(DUK_USE_HSTRING_EXTDATA) -#define DUK_HSTRING_GET_EXTDATA(x) ((x)->extdata) -#define DUK_HSTRING_GET_DATA(x) \ - (DUK_HSTRING_HAS_EXTDATA((x)) ? DUK_HSTRING_GET_EXTDATA((const duk_hstring_external *) (x)) : \ - ((const duk_uint8_t *) ((x) + 1))) -#else -#define DUK_HSTRING_GET_DATA(x) ((const duk_uint8_t *) ((x) + 1)) -#endif - -#define DUK_HSTRING_GET_DATA_END(x) (DUK_HSTRING_GET_DATA((x)) + (x)->blen) - -/* Marker value; in E5 2^32-1 is not a valid array index (2^32-2 is highest - * valid). - */ -#define DUK_HSTRING_NO_ARRAY_INDEX (0xffffffffUL) - -#if defined(DUK_USE_HSTRING_ARRIDX) -#define DUK_HSTRING_GET_ARRIDX_FAST(h) ((h)->arridx) -#define DUK_HSTRING_GET_ARRIDX_SLOW(h) ((h)->arridx) -#else -/* Get array index related to string (or return DUK_HSTRING_NO_ARRAY_INDEX); - * avoids helper call if string has no array index value. - */ -#define DUK_HSTRING_GET_ARRIDX_FAST(h) \ - (DUK_HSTRING_HAS_ARRIDX((h)) ? duk_js_to_arrayindex_hstring_fast_known((h)) : DUK_HSTRING_NO_ARRAY_INDEX) - -/* Slower but more compact variant. */ -#define DUK_HSTRING_GET_ARRIDX_SLOW(h) (duk_js_to_arrayindex_hstring_fast((h))) -#endif - -/* XXX: these actually fit into duk_hstring */ -#define DUK_SYMBOL_TYPE_HIDDEN 0 -#define DUK_SYMBOL_TYPE_GLOBAL 1 -#define DUK_SYMBOL_TYPE_LOCAL 2 -#define DUK_SYMBOL_TYPE_WELLKNOWN 3 - -/* Assertion for duk_hstring validity. */ -#if defined(DUK_USE_ASSERTIONS) -DUK_INTERNAL_DECL void duk_hstring_assert_valid(duk_hstring *h); -#define DUK_HSTRING_ASSERT_VALID(h) \ - do { \ - duk_hstring_assert_valid((h)); \ - } while (0) -#else -#define DUK_HSTRING_ASSERT_VALID(h) \ - do { \ - } while (0) -#endif - -/* - * Misc - */ - -struct duk_hstring { - /* Smaller heaphdr than for other objects, because strings are held - * in string intern table which requires no link pointers. Much of - * the 32-bit flags field is unused by flags, so we can stuff a 16-bit - * field in there. - */ - duk_heaphdr_string hdr; - - /* String hash. */ -#if defined(DUK_USE_STRHASH16) - /* If 16-bit hash is in use, stuff it into duk_heaphdr_string flags. */ -#else - duk_uint32_t hash; -#endif - - /* Precomputed array index (or DUK_HSTRING_NO_ARRAY_INDEX). */ -#if defined(DUK_USE_HSTRING_ARRIDX) - duk_uarridx_t arridx; -#endif - - /* Length in bytes (not counting NUL term). */ -#if defined(DUK_USE_STRLEN16) - /* placed in duk_heaphdr_string */ -#else - duk_uint32_t blen; -#endif - - /* Length in codepoints (must be E5 compatible). */ -#if defined(DUK_USE_STRLEN16) -#if defined(DUK_USE_HSTRING_CLEN) - duk_uint16_t clen16; -#else - /* computed live */ -#endif -#else - duk_uint32_t clen; -#endif - - /* - * String data of 'blen+1' bytes follows (+1 for NUL termination - * convenience for C API). No alignment needs to be guaranteed - * for strings, but fields above should guarantee alignment-by-4 - * (but not alignment-by-8). - */ -}; - -/* The external string struct is defined even when the feature is inactive. */ -struct duk_hstring_external { - duk_hstring str; - - /* - * For an external string, the NUL-terminated string data is stored - * externally. The user must guarantee that data behind this pointer - * doesn't change while it's used. - */ - - const duk_uint8_t *extdata; -}; - -/* - * Prototypes - */ - -DUK_INTERNAL_DECL duk_ucodepoint_t duk_hstring_char_code_at_raw(duk_hthread *thr, - duk_hstring *h, - duk_uint_t pos, - duk_bool_t surrogate_aware); -DUK_INTERNAL_DECL duk_bool_t duk_hstring_equals_ascii_cstring(duk_hstring *h, const char *cstr); -DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); -#if !defined(DUK_USE_HSTRING_LAZY_CLEN) -DUK_INTERNAL_DECL void duk_hstring_init_charlen(duk_hstring *h); -#endif - -#endif /* DUK_HSTRING_H_INCLUDED */ -/* #include duk_hobject.h */ -/* - * Heap object representation. - * - * Heap objects are used for ECMAScript objects, arrays, and functions, - * but also for internal control like declarative and object environment - * records. Compiled functions, native functions, and threads are also - * objects but with an extended C struct. - * - * Objects provide the required ECMAScript semantics and exotic behaviors - * especially for property access. - * - * Properties are stored in three conceptual parts: - * - * 1. A linear 'entry part' contains ordered key-value-attributes triples - * and is the main method of string properties. - * - * 2. An optional linear 'array part' is used for array objects to store a - * (dense) range of [0,N[ array indexed entries with default attributes - * (writable, enumerable, configurable). If the array part would become - * sparse or non-default attributes are required, the array part is - * abandoned and moved to the 'entry part'. - * - * 3. An optional 'hash part' is used to optimize lookups of the entry - * part; it is used only for objects with sufficiently many properties - * and can be abandoned without loss of information. - * - * These three conceptual parts are stored in a single memory allocated area. - * This minimizes memory allocation overhead but also means that all three - * parts are resized together, and makes property access a bit complicated. - */ - -#if !defined(DUK_HOBJECT_H_INCLUDED) -#define DUK_HOBJECT_H_INCLUDED - -/* Object flags. Make sure this stays in sync with debugger object - * inspection code. - */ - -/* XXX: some flags are object subtype specific (e.g. common to all function - * subtypes, duk_harray, etc) and could be reused for different subtypes. - */ -#define DUK_HOBJECT_FLAG_EXTENSIBLE DUK_HEAPHDR_USER_FLAG(0) /* object is extensible */ -#define DUK_HOBJECT_FLAG_CONSTRUCTABLE DUK_HEAPHDR_USER_FLAG(1) /* object is constructable */ -#define DUK_HOBJECT_FLAG_CALLABLE DUK_HEAPHDR_USER_FLAG(2) /* object is callable */ -#define DUK_HOBJECT_FLAG_BOUNDFUNC DUK_HEAPHDR_USER_FLAG(3) /* object established using Function.prototype.bind() */ -#define DUK_HOBJECT_FLAG_COMPFUNC DUK_HEAPHDR_USER_FLAG(4) /* object is a compiled function (duk_hcompfunc) */ -#define DUK_HOBJECT_FLAG_NATFUNC DUK_HEAPHDR_USER_FLAG(5) /* object is a native function (duk_hnatfunc) */ -#define DUK_HOBJECT_FLAG_BUFOBJ DUK_HEAPHDR_USER_FLAG(6) /* object is a buffer object (duk_hbufobj) (always exotic) */ -#define DUK_HOBJECT_FLAG_FASTREFS \ - DUK_HEAPHDR_USER_FLAG(7) /* object has no fields needing DECREF/marking beyond base duk_hobject header */ -#define DUK_HOBJECT_FLAG_ARRAY_PART DUK_HEAPHDR_USER_FLAG(8) /* object has an array part (a_size may still be 0) */ -#define DUK_HOBJECT_FLAG_STRICT DUK_HEAPHDR_USER_FLAG(9) /* function: function object is strict */ -#define DUK_HOBJECT_FLAG_NOTAIL DUK_HEAPHDR_USER_FLAG(10) /* function: function must not be tail called */ -#define DUK_HOBJECT_FLAG_NEWENV DUK_HEAPHDR_USER_FLAG(11) /* function: create new environment when called (see duk_hcompfunc) */ -#define DUK_HOBJECT_FLAG_NAMEBINDING \ - DUK_HEAPHDR_USER_FLAG( \ - 12) /* function: create binding for func name (function templates only, used for named function expressions) */ -#define DUK_HOBJECT_FLAG_CREATEARGS DUK_HEAPHDR_USER_FLAG(13) /* function: create an arguments object on function call */ -#define DUK_HOBJECT_FLAG_HAVE_FINALIZER DUK_HEAPHDR_USER_FLAG(14) /* object has a callable (own) finalizer property */ -#define DUK_HOBJECT_FLAG_EXOTIC_ARRAY DUK_HEAPHDR_USER_FLAG(15) /* 'Array' object, array length and index exotic behavior */ -#define DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ DUK_HEAPHDR_USER_FLAG(16) /* 'String' object, array index exotic behavior */ -#define DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS \ - DUK_HEAPHDR_USER_FLAG(17) /* 'Arguments' object and has arguments exotic behavior (non-strict callee) */ -#define DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ DUK_HEAPHDR_USER_FLAG(18) /* 'Proxy' object */ -#define DUK_HOBJECT_FLAG_SPECIAL_CALL DUK_HEAPHDR_USER_FLAG(19) /* special casing in call behavior, for .call(), .apply(), etc. */ - -#define DUK_HOBJECT_FLAG_CLASS_BASE DUK_HEAPHDR_USER_FLAG_NUMBER(20) -#define DUK_HOBJECT_FLAG_CLASS_BITS 5 - -#define DUK_HOBJECT_GET_CLASS_NUMBER(h) \ - DUK_HEAPHDR_GET_FLAG_RANGE(&(h)->hdr, DUK_HOBJECT_FLAG_CLASS_BASE, DUK_HOBJECT_FLAG_CLASS_BITS) -#define DUK_HOBJECT_SET_CLASS_NUMBER(h, v) \ - DUK_HEAPHDR_SET_FLAG_RANGE(&(h)->hdr, DUK_HOBJECT_FLAG_CLASS_BASE, DUK_HOBJECT_FLAG_CLASS_BITS, (v)) - -#define DUK_HOBJECT_GET_CLASS_MASK(h) \ - (1UL << DUK_HEAPHDR_GET_FLAG_RANGE(&(h)->hdr, DUK_HOBJECT_FLAG_CLASS_BASE, DUK_HOBJECT_FLAG_CLASS_BITS)) - -/* Macro for creating flag initializer from a class number. - * Unsigned type cast is needed to avoid warnings about coercing - * a signed integer to an unsigned one; the largest class values - * have the highest bit (bit 31) set which causes this. - */ -#define DUK_HOBJECT_CLASS_AS_FLAGS(v) (((duk_uint_t) (v)) << DUK_HOBJECT_FLAG_CLASS_BASE) - -/* E5 Section 8.6.2 + custom classes */ -#define DUK_HOBJECT_CLASS_NONE 0 -#define DUK_HOBJECT_CLASS_OBJECT 1 -#define DUK_HOBJECT_CLASS_ARRAY 2 -#define DUK_HOBJECT_CLASS_FUNCTION 3 -#define DUK_HOBJECT_CLASS_ARGUMENTS 4 -#define DUK_HOBJECT_CLASS_BOOLEAN 5 -#define DUK_HOBJECT_CLASS_DATE 6 -#define DUK_HOBJECT_CLASS_ERROR 7 -#define DUK_HOBJECT_CLASS_JSON 8 -#define DUK_HOBJECT_CLASS_MATH 9 -#define DUK_HOBJECT_CLASS_NUMBER 10 -#define DUK_HOBJECT_CLASS_REGEXP 11 -#define DUK_HOBJECT_CLASS_STRING 12 -#define DUK_HOBJECT_CLASS_GLOBAL 13 -#define DUK_HOBJECT_CLASS_SYMBOL 14 -#define DUK_HOBJECT_CLASS_OBJENV 15 /* custom */ -#define DUK_HOBJECT_CLASS_DECENV 16 /* custom */ -#define DUK_HOBJECT_CLASS_POINTER 17 /* custom */ -#define DUK_HOBJECT_CLASS_THREAD 18 /* custom; implies DUK_HOBJECT_IS_THREAD */ -#define DUK_HOBJECT_CLASS_BUFOBJ_MIN 19 -#define DUK_HOBJECT_CLASS_ARRAYBUFFER 19 /* implies DUK_HOBJECT_IS_BUFOBJ */ -#define DUK_HOBJECT_CLASS_DATAVIEW 20 -#define DUK_HOBJECT_CLASS_INT8ARRAY 21 -#define DUK_HOBJECT_CLASS_UINT8ARRAY 22 -#define DUK_HOBJECT_CLASS_UINT8CLAMPEDARRAY 23 -#define DUK_HOBJECT_CLASS_INT16ARRAY 24 -#define DUK_HOBJECT_CLASS_UINT16ARRAY 25 -#define DUK_HOBJECT_CLASS_INT32ARRAY 26 -#define DUK_HOBJECT_CLASS_UINT32ARRAY 27 -#define DUK_HOBJECT_CLASS_FLOAT32ARRAY 28 -#define DUK_HOBJECT_CLASS_FLOAT64ARRAY 29 -#define DUK_HOBJECT_CLASS_BUFOBJ_MAX 29 -#define DUK_HOBJECT_CLASS_MAX 29 - -/* Class masks. */ -#define DUK_HOBJECT_CMASK_ALL ((1UL << (DUK_HOBJECT_CLASS_MAX + 1)) - 1UL) -#define DUK_HOBJECT_CMASK_NONE (1UL << DUK_HOBJECT_CLASS_NONE) -#define DUK_HOBJECT_CMASK_ARGUMENTS (1UL << DUK_HOBJECT_CLASS_ARGUMENTS) -#define DUK_HOBJECT_CMASK_ARRAY (1UL << DUK_HOBJECT_CLASS_ARRAY) -#define DUK_HOBJECT_CMASK_BOOLEAN (1UL << DUK_HOBJECT_CLASS_BOOLEAN) -#define DUK_HOBJECT_CMASK_DATE (1UL << DUK_HOBJECT_CLASS_DATE) -#define DUK_HOBJECT_CMASK_ERROR (1UL << DUK_HOBJECT_CLASS_ERROR) -#define DUK_HOBJECT_CMASK_FUNCTION (1UL << DUK_HOBJECT_CLASS_FUNCTION) -#define DUK_HOBJECT_CMASK_JSON (1UL << DUK_HOBJECT_CLASS_JSON) -#define DUK_HOBJECT_CMASK_MATH (1UL << DUK_HOBJECT_CLASS_MATH) -#define DUK_HOBJECT_CMASK_NUMBER (1UL << DUK_HOBJECT_CLASS_NUMBER) -#define DUK_HOBJECT_CMASK_OBJECT (1UL << DUK_HOBJECT_CLASS_OBJECT) -#define DUK_HOBJECT_CMASK_REGEXP (1UL << DUK_HOBJECT_CLASS_REGEXP) -#define DUK_HOBJECT_CMASK_STRING (1UL << DUK_HOBJECT_CLASS_STRING) -#define DUK_HOBJECT_CMASK_GLOBAL (1UL << DUK_HOBJECT_CLASS_GLOBAL) -#define DUK_HOBJECT_CMASK_SYMBOL (1UL << DUK_HOBJECT_CLASS_SYMBOL) -#define DUK_HOBJECT_CMASK_OBJENV (1UL << DUK_HOBJECT_CLASS_OBJENV) -#define DUK_HOBJECT_CMASK_DECENV (1UL << DUK_HOBJECT_CLASS_DECENV) -#define DUK_HOBJECT_CMASK_POINTER (1UL << DUK_HOBJECT_CLASS_POINTER) -#define DUK_HOBJECT_CMASK_ARRAYBUFFER (1UL << DUK_HOBJECT_CLASS_ARRAYBUFFER) -#define DUK_HOBJECT_CMASK_DATAVIEW (1UL << DUK_HOBJECT_CLASS_DATAVIEW) -#define DUK_HOBJECT_CMASK_INT8ARRAY (1UL << DUK_HOBJECT_CLASS_INT8ARRAY) -#define DUK_HOBJECT_CMASK_UINT8ARRAY (1UL << DUK_HOBJECT_CLASS_UINT8ARRAY) -#define DUK_HOBJECT_CMASK_UINT8CLAMPEDARRAY (1UL << DUK_HOBJECT_CLASS_UINT8CLAMPEDARRAY) -#define DUK_HOBJECT_CMASK_INT16ARRAY (1UL << DUK_HOBJECT_CLASS_INT16ARRAY) -#define DUK_HOBJECT_CMASK_UINT16ARRAY (1UL << DUK_HOBJECT_CLASS_UINT16ARRAY) -#define DUK_HOBJECT_CMASK_INT32ARRAY (1UL << DUK_HOBJECT_CLASS_INT32ARRAY) -#define DUK_HOBJECT_CMASK_UINT32ARRAY (1UL << DUK_HOBJECT_CLASS_UINT32ARRAY) -#define DUK_HOBJECT_CMASK_FLOAT32ARRAY (1UL << DUK_HOBJECT_CLASS_FLOAT32ARRAY) -#define DUK_HOBJECT_CMASK_FLOAT64ARRAY (1UL << DUK_HOBJECT_CLASS_FLOAT64ARRAY) - -#define DUK_HOBJECT_CMASK_ALL_BUFOBJS \ - (DUK_HOBJECT_CMASK_ARRAYBUFFER | DUK_HOBJECT_CMASK_DATAVIEW | DUK_HOBJECT_CMASK_INT8ARRAY | DUK_HOBJECT_CMASK_UINT8ARRAY | \ - DUK_HOBJECT_CMASK_UINT8CLAMPEDARRAY | DUK_HOBJECT_CMASK_INT16ARRAY | DUK_HOBJECT_CMASK_UINT16ARRAY | \ - DUK_HOBJECT_CMASK_INT32ARRAY | DUK_HOBJECT_CMASK_UINT32ARRAY | DUK_HOBJECT_CMASK_FLOAT32ARRAY | \ - DUK_HOBJECT_CMASK_FLOAT64ARRAY) - -#define DUK_HOBJECT_IS_OBJENV(h) (DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_OBJENV) -#define DUK_HOBJECT_IS_DECENV(h) (DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_DECENV) -#define DUK_HOBJECT_IS_ENV(h) (DUK_HOBJECT_IS_OBJENV((h)) || DUK_HOBJECT_IS_DECENV((h))) -#define DUK_HOBJECT_IS_ARRAY(h) DUK_HOBJECT_HAS_EXOTIC_ARRAY((h)) /* Rely on class Array <=> exotic Array */ -#define DUK_HOBJECT_IS_BOUNDFUNC(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BOUNDFUNC) -#define DUK_HOBJECT_IS_COMPFUNC(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_COMPFUNC) -#define DUK_HOBJECT_IS_NATFUNC(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NATFUNC) -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -#define DUK_HOBJECT_IS_BUFOBJ(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BUFOBJ) -#else -#define DUK_HOBJECT_IS_BUFOBJ(h) 0 -#endif -#define DUK_HOBJECT_IS_THREAD(h) (DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_THREAD) -#if defined(DUK_USE_ES6_PROXY) -#define DUK_HOBJECT_IS_PROXY(h) DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ((h)) -#else -#define DUK_HOBJECT_IS_PROXY(h) 0 -#endif - -#define DUK_HOBJECT_IS_NONBOUND_FUNCTION(h) \ - DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_COMPFUNC | DUK_HOBJECT_FLAG_NATFUNC) - -#define DUK_HOBJECT_IS_FUNCTION(h) \ - DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BOUNDFUNC | DUK_HOBJECT_FLAG_COMPFUNC | DUK_HOBJECT_FLAG_NATFUNC) - -#define DUK_HOBJECT_IS_CALLABLE(h) DUK_HOBJECT_HAS_CALLABLE((h)) - -/* Object has any exotic behavior(s). */ -#define DUK_HOBJECT_EXOTIC_BEHAVIOR_FLAGS \ - (DUK_HOBJECT_FLAG_EXOTIC_ARRAY | DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS | DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ | \ - DUK_HOBJECT_FLAG_BUFOBJ | DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ) -#define DUK_HOBJECT_HAS_EXOTIC_BEHAVIOR(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_EXOTIC_BEHAVIOR_FLAGS) - -/* Object has any virtual properties (not counting Proxy behavior). */ -#define DUK_HOBJECT_VIRTUAL_PROPERTY_FLAGS \ - (DUK_HOBJECT_FLAG_EXOTIC_ARRAY | DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ | DUK_HOBJECT_FLAG_BUFOBJ) -#define DUK_HOBJECT_HAS_VIRTUAL_PROPERTIES(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_VIRTUAL_PROPERTY_FLAGS) - -#define DUK_HOBJECT_HAS_EXTENSIBLE(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXTENSIBLE) -#define DUK_HOBJECT_HAS_CONSTRUCTABLE(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CONSTRUCTABLE) -#define DUK_HOBJECT_HAS_CALLABLE(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CALLABLE) -#define DUK_HOBJECT_HAS_BOUNDFUNC(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BOUNDFUNC) -#define DUK_HOBJECT_HAS_COMPFUNC(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_COMPFUNC) -#define DUK_HOBJECT_HAS_NATFUNC(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NATFUNC) -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -#define DUK_HOBJECT_HAS_BUFOBJ(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BUFOBJ) -#else -#define DUK_HOBJECT_HAS_BUFOBJ(h) 0 -#endif -#define DUK_HOBJECT_HAS_FASTREFS(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_FASTREFS) -#define DUK_HOBJECT_HAS_ARRAY_PART(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_ARRAY_PART) -#define DUK_HOBJECT_HAS_STRICT(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_STRICT) -#define DUK_HOBJECT_HAS_NOTAIL(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NOTAIL) -#define DUK_HOBJECT_HAS_NEWENV(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NEWENV) -#define DUK_HOBJECT_HAS_NAMEBINDING(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NAMEBINDING) -#define DUK_HOBJECT_HAS_CREATEARGS(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CREATEARGS) -#define DUK_HOBJECT_HAS_HAVE_FINALIZER(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_HAVE_FINALIZER) -#define DUK_HOBJECT_HAS_EXOTIC_ARRAY(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_ARRAY) -#define DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ) -#define DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS) -#if defined(DUK_USE_ES6_PROXY) -#define DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ) -#else -#define DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(h) 0 -#endif -#define DUK_HOBJECT_HAS_SPECIAL_CALL(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_SPECIAL_CALL) - -#define DUK_HOBJECT_SET_EXTENSIBLE(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXTENSIBLE) -#define DUK_HOBJECT_SET_CONSTRUCTABLE(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CONSTRUCTABLE) -#define DUK_HOBJECT_SET_CALLABLE(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CALLABLE) -#define DUK_HOBJECT_SET_BOUNDFUNC(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BOUNDFUNC) -#define DUK_HOBJECT_SET_COMPFUNC(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_COMPFUNC) -#define DUK_HOBJECT_SET_NATFUNC(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NATFUNC) -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -#define DUK_HOBJECT_SET_BUFOBJ(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BUFOBJ) -#endif -#define DUK_HOBJECT_SET_FASTREFS(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_FASTREFS) -#define DUK_HOBJECT_SET_ARRAY_PART(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_ARRAY_PART) -#define DUK_HOBJECT_SET_STRICT(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_STRICT) -#define DUK_HOBJECT_SET_NOTAIL(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NOTAIL) -#define DUK_HOBJECT_SET_NEWENV(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NEWENV) -#define DUK_HOBJECT_SET_NAMEBINDING(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NAMEBINDING) -#define DUK_HOBJECT_SET_CREATEARGS(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CREATEARGS) -#define DUK_HOBJECT_SET_HAVE_FINALIZER(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_HAVE_FINALIZER) -#define DUK_HOBJECT_SET_EXOTIC_ARRAY(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_ARRAY) -#define DUK_HOBJECT_SET_EXOTIC_STRINGOBJ(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ) -#define DUK_HOBJECT_SET_EXOTIC_ARGUMENTS(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS) -#if defined(DUK_USE_ES6_PROXY) -#define DUK_HOBJECT_SET_EXOTIC_PROXYOBJ(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ) -#endif -#define DUK_HOBJECT_SET_SPECIAL_CALL(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_SPECIAL_CALL) - -#define DUK_HOBJECT_CLEAR_EXTENSIBLE(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXTENSIBLE) -#define DUK_HOBJECT_CLEAR_CONSTRUCTABLE(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CONSTRUCTABLE) -#define DUK_HOBJECT_CLEAR_CALLABLE(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CALLABLE) -#define DUK_HOBJECT_CLEAR_BOUNDFUNC(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BOUNDFUNC) -#define DUK_HOBJECT_CLEAR_COMPFUNC(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_COMPFUNC) -#define DUK_HOBJECT_CLEAR_NATFUNC(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NATFUNC) -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -#define DUK_HOBJECT_CLEAR_BUFOBJ(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BUFOBJ) -#endif -#define DUK_HOBJECT_CLEAR_FASTREFS(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_FASTREFS) -#define DUK_HOBJECT_CLEAR_ARRAY_PART(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_ARRAY_PART) -#define DUK_HOBJECT_CLEAR_STRICT(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_STRICT) -#define DUK_HOBJECT_CLEAR_NOTAIL(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NOTAIL) -#define DUK_HOBJECT_CLEAR_NEWENV(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NEWENV) -#define DUK_HOBJECT_CLEAR_NAMEBINDING(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NAMEBINDING) -#define DUK_HOBJECT_CLEAR_CREATEARGS(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CREATEARGS) -#define DUK_HOBJECT_CLEAR_HAVE_FINALIZER(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_HAVE_FINALIZER) -#define DUK_HOBJECT_CLEAR_EXOTIC_ARRAY(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_ARRAY) -#define DUK_HOBJECT_CLEAR_EXOTIC_STRINGOBJ(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ) -#define DUK_HOBJECT_CLEAR_EXOTIC_ARGUMENTS(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS) -#if defined(DUK_USE_ES6_PROXY) -#define DUK_HOBJECT_CLEAR_EXOTIC_PROXYOBJ(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ) -#endif -#define DUK_HOBJECT_CLEAR_SPECIAL_CALL(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_SPECIAL_CALL) - -/* Object can/cannot use FASTREFS, i.e. has no strong reference fields beyond - * duk_hobject base header. This is used just for asserts so doesn't need to - * be optimized. - */ -#define DUK_HOBJECT_PROHIBITS_FASTREFS(h) \ - (DUK_HOBJECT_IS_COMPFUNC((h)) || DUK_HOBJECT_IS_DECENV((h)) || DUK_HOBJECT_IS_OBJENV((h)) || DUK_HOBJECT_IS_BUFOBJ((h)) || \ - DUK_HOBJECT_IS_THREAD((h)) || DUK_HOBJECT_IS_PROXY((h)) || DUK_HOBJECT_IS_BOUNDFUNC((h))) -#define DUK_HOBJECT_ALLOWS_FASTREFS(h) (!DUK_HOBJECT_PROHIBITS_FASTREFS((h))) - -/* Flags used for property attributes in duk_propdesc and packed flags. - * Must fit into 8 bits. - */ -#define DUK_PROPDESC_FLAG_WRITABLE (1U << 0) /* E5 Section 8.6.1 */ -#define DUK_PROPDESC_FLAG_ENUMERABLE (1U << 1) /* E5 Section 8.6.1 */ -#define DUK_PROPDESC_FLAG_CONFIGURABLE (1U << 2) /* E5 Section 8.6.1 */ -#define DUK_PROPDESC_FLAG_ACCESSOR (1U << 3) /* accessor */ -#define DUK_PROPDESC_FLAG_VIRTUAL \ - (1U << 4) /* property is virtual: used in duk_propdesc, never stored \ - * (used by e.g. buffer virtual properties) \ - */ -#define DUK_PROPDESC_FLAGS_MASK \ - (DUK_PROPDESC_FLAG_WRITABLE | DUK_PROPDESC_FLAG_ENUMERABLE | DUK_PROPDESC_FLAG_CONFIGURABLE | DUK_PROPDESC_FLAG_ACCESSOR) - -/* Additional flags which are passed in the same flags argument as property - * flags but are not stored in object properties. - */ -#define DUK_PROPDESC_FLAG_NO_OVERWRITE (1U << 4) /* internal define property: skip write silently if exists */ - -/* Convenience defines for property attributes. */ -#define DUK_PROPDESC_FLAGS_NONE 0 -#define DUK_PROPDESC_FLAGS_W (DUK_PROPDESC_FLAG_WRITABLE) -#define DUK_PROPDESC_FLAGS_E (DUK_PROPDESC_FLAG_ENUMERABLE) -#define DUK_PROPDESC_FLAGS_C (DUK_PROPDESC_FLAG_CONFIGURABLE) -#define DUK_PROPDESC_FLAGS_WE (DUK_PROPDESC_FLAG_WRITABLE | DUK_PROPDESC_FLAG_ENUMERABLE) -#define DUK_PROPDESC_FLAGS_WC (DUK_PROPDESC_FLAG_WRITABLE | DUK_PROPDESC_FLAG_CONFIGURABLE) -#define DUK_PROPDESC_FLAGS_EC (DUK_PROPDESC_FLAG_ENUMERABLE | DUK_PROPDESC_FLAG_CONFIGURABLE) -#define DUK_PROPDESC_FLAGS_WEC (DUK_PROPDESC_FLAG_WRITABLE | DUK_PROPDESC_FLAG_ENUMERABLE | DUK_PROPDESC_FLAG_CONFIGURABLE) - -/* Flags for duk_hobject_get_own_propdesc() and variants. */ -#define DUK_GETDESC_FLAG_PUSH_VALUE (1U << 0) /* push value to stack */ -#define DUK_GETDESC_FLAG_IGNORE_PROTOLOOP (1U << 1) /* don't throw for prototype loop */ - -/* - * Macro for object validity check - * - * Assert for currently guaranteed relations between flags, for instance. - */ - -#if defined(DUK_USE_ASSERTIONS) -DUK_INTERNAL_DECL void duk_hobject_assert_valid(duk_hobject *h); -#define DUK_HOBJECT_ASSERT_VALID(h) \ - do { \ - duk_hobject_assert_valid((h)); \ - } while (0) -#else -#define DUK_HOBJECT_ASSERT_VALID(h) \ - do { \ - } while (0) -#endif - -/* - * Macros to access the 'props' allocation. - */ - -#if defined(DUK_USE_HEAPPTR16) -#define DUK_HOBJECT_GET_PROPS(heap, h) ((duk_uint8_t *) DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, ((duk_heaphdr *) (h))->h_extra16)) -#define DUK_HOBJECT_SET_PROPS(heap, h, x) \ - do { \ - ((duk_heaphdr *) (h))->h_extra16 = DUK_USE_HEAPPTR_ENC16((heap)->heap_udata, (void *) (x)); \ - } while (0) -#else -#define DUK_HOBJECT_GET_PROPS(heap, h) ((h)->props) -#define DUK_HOBJECT_SET_PROPS(heap, h, x) \ - do { \ - (h)->props = (duk_uint8_t *) (x); \ - } while (0) -#endif - -#if defined(DUK_USE_HOBJECT_LAYOUT_1) -/* LAYOUT 1 */ -#define DUK_HOBJECT_E_GET_KEY_BASE(heap, h) ((duk_hstring **) (void *) (DUK_HOBJECT_GET_PROPS((heap), (h)))) -#define DUK_HOBJECT_E_GET_VALUE_BASE(heap, h) \ - ((duk_propvalue *) (void *) (DUK_HOBJECT_GET_PROPS((heap), (h)) + DUK_HOBJECT_GET_ESIZE((h)) * sizeof(duk_hstring *))) -#define DUK_HOBJECT_E_GET_FLAGS_BASE(heap, h) \ - ((duk_uint8_t *) (void *) (DUK_HOBJECT_GET_PROPS((heap), (h)) + \ - DUK_HOBJECT_GET_ESIZE((h)) * (sizeof(duk_hstring *) + sizeof(duk_propvalue)))) -#define DUK_HOBJECT_A_GET_BASE(heap, h) \ - ((duk_tval *) (void *) (DUK_HOBJECT_GET_PROPS((heap), (h)) + \ - DUK_HOBJECT_GET_ESIZE((h)) * \ - (sizeof(duk_hstring *) + sizeof(duk_propvalue) + sizeof(duk_uint8_t)))) -#define DUK_HOBJECT_H_GET_BASE(heap, h) \ - ((duk_uint32_t *) (void *) (DUK_HOBJECT_GET_PROPS((heap), (h)) + \ - DUK_HOBJECT_GET_ESIZE((h)) * \ - (sizeof(duk_hstring *) + sizeof(duk_propvalue) + sizeof(duk_uint8_t)) + \ - DUK_HOBJECT_GET_ASIZE((h)) * sizeof(duk_tval))) -#define DUK_HOBJECT_P_COMPUTE_SIZE(n_ent, n_arr, n_hash) \ - ((n_ent) * (sizeof(duk_hstring *) + sizeof(duk_propvalue) + sizeof(duk_uint8_t)) + (n_arr) * sizeof(duk_tval) + \ - (n_hash) * sizeof(duk_uint32_t)) -#define DUK_HOBJECT_P_SET_REALLOC_PTRS(p_base, set_e_k, set_e_pv, set_e_f, set_a, set_h, n_ent, n_arr, n_hash) \ - do { \ - (set_e_k) = (duk_hstring **) (void *) (p_base); \ - (set_e_pv) = (duk_propvalue *) (void *) ((set_e_k) + (n_ent)); \ - (set_e_f) = (duk_uint8_t *) (void *) ((set_e_pv) + (n_ent)); \ - (set_a) = (duk_tval *) (void *) ((set_e_f) + (n_ent)); \ - (set_h) = (duk_uint32_t *) (void *) ((set_a) + (n_arr)); \ - } while (0) -#elif defined(DUK_USE_HOBJECT_LAYOUT_2) -/* LAYOUT 2 */ -#if (DUK_USE_ALIGN_BY == 4) -#define DUK_HOBJECT_E_FLAG_PADDING(e_sz) ((4 - (e_sz)) & 0x03) -#elif (DUK_USE_ALIGN_BY == 8) -#define DUK_HOBJECT_E_FLAG_PADDING(e_sz) ((8 - (e_sz)) & 0x07) -#elif (DUK_USE_ALIGN_BY == 1) -#define DUK_HOBJECT_E_FLAG_PADDING(e_sz) 0 -#else -#error invalid DUK_USE_ALIGN_BY -#endif -#define DUK_HOBJECT_E_GET_KEY_BASE(heap, h) \ - ((duk_hstring **) (void *) (DUK_HOBJECT_GET_PROPS((heap), (h)) + DUK_HOBJECT_GET_ESIZE((h)) * sizeof(duk_propvalue))) -#define DUK_HOBJECT_E_GET_VALUE_BASE(heap, h) ((duk_propvalue *) (void *) (DUK_HOBJECT_GET_PROPS((heap), (h)))) -#define DUK_HOBJECT_E_GET_FLAGS_BASE(heap, h) \ - ((duk_uint8_t *) (void *) (DUK_HOBJECT_GET_PROPS((heap), (h)) + \ - DUK_HOBJECT_GET_ESIZE((h)) * (sizeof(duk_hstring *) + sizeof(duk_propvalue)))) -#define DUK_HOBJECT_A_GET_BASE(heap, h) \ - ((duk_tval *) (void *) (DUK_HOBJECT_GET_PROPS((heap), (h)) + \ - DUK_HOBJECT_GET_ESIZE((h)) * \ - (sizeof(duk_hstring *) + sizeof(duk_propvalue) + sizeof(duk_uint8_t)) + \ - DUK_HOBJECT_E_FLAG_PADDING(DUK_HOBJECT_GET_ESIZE((h))))) -#define DUK_HOBJECT_H_GET_BASE(heap, h) \ - ((duk_uint32_t *) (void *) (DUK_HOBJECT_GET_PROPS((heap), (h)) + \ - DUK_HOBJECT_GET_ESIZE((h)) * \ - (sizeof(duk_hstring *) + sizeof(duk_propvalue) + sizeof(duk_uint8_t)) + \ - DUK_HOBJECT_E_FLAG_PADDING(DUK_HOBJECT_GET_ESIZE((h))) + \ - DUK_HOBJECT_GET_ASIZE((h)) * sizeof(duk_tval))) -#define DUK_HOBJECT_P_COMPUTE_SIZE(n_ent, n_arr, n_hash) \ - ((n_ent) * (sizeof(duk_hstring *) + sizeof(duk_propvalue) + sizeof(duk_uint8_t)) + DUK_HOBJECT_E_FLAG_PADDING((n_ent)) + \ - (n_arr) * sizeof(duk_tval) + (n_hash) * sizeof(duk_uint32_t)) -#define DUK_HOBJECT_P_SET_REALLOC_PTRS(p_base, set_e_k, set_e_pv, set_e_f, set_a, set_h, n_ent, n_arr, n_hash) \ - do { \ - (set_e_pv) = (duk_propvalue *) (void *) (p_base); \ - (set_e_k) = (duk_hstring **) (void *) ((set_e_pv) + (n_ent)); \ - (set_e_f) = (duk_uint8_t *) (void *) ((set_e_k) + (n_ent)); \ - (set_a) = (duk_tval *) (void *) (((duk_uint8_t *) (set_e_f)) + sizeof(duk_uint8_t) * (n_ent) + \ - DUK_HOBJECT_E_FLAG_PADDING((n_ent))); \ - (set_h) = (duk_uint32_t *) (void *) ((set_a) + (n_arr)); \ - } while (0) -#elif defined(DUK_USE_HOBJECT_LAYOUT_3) -/* LAYOUT 3 */ -#define DUK_HOBJECT_E_GET_KEY_BASE(heap, h) \ - ((duk_hstring **) (void *) (DUK_HOBJECT_GET_PROPS((heap), (h)) + DUK_HOBJECT_GET_ESIZE((h)) * sizeof(duk_propvalue) + \ - DUK_HOBJECT_GET_ASIZE((h)) * sizeof(duk_tval))) -#define DUK_HOBJECT_E_GET_VALUE_BASE(heap, h) ((duk_propvalue *) (void *) (DUK_HOBJECT_GET_PROPS((heap), (h)))) -#define DUK_HOBJECT_E_GET_FLAGS_BASE(heap, h) \ - ((duk_uint8_t *) (void *) (DUK_HOBJECT_GET_PROPS((heap), (h)) + \ - DUK_HOBJECT_GET_ESIZE((h)) * (sizeof(duk_propvalue) + sizeof(duk_hstring *)) + \ - DUK_HOBJECT_GET_ASIZE((h)) * sizeof(duk_tval) + \ - DUK_HOBJECT_GET_HSIZE((h)) * sizeof(duk_uint32_t))) -#define DUK_HOBJECT_A_GET_BASE(heap, h) \ - ((duk_tval *) (void *) (DUK_HOBJECT_GET_PROPS((heap), (h)) + DUK_HOBJECT_GET_ESIZE((h)) * sizeof(duk_propvalue))) -#define DUK_HOBJECT_H_GET_BASE(heap, h) \ - ((duk_uint32_t *) (void *) (DUK_HOBJECT_GET_PROPS((heap), (h)) + \ - DUK_HOBJECT_GET_ESIZE((h)) * (sizeof(duk_propvalue) + sizeof(duk_hstring *)) + \ - DUK_HOBJECT_GET_ASIZE((h)) * sizeof(duk_tval))) -#define DUK_HOBJECT_P_COMPUTE_SIZE(n_ent, n_arr, n_hash) \ - ((n_ent) * (sizeof(duk_propvalue) + sizeof(duk_hstring *) + sizeof(duk_uint8_t)) + (n_arr) * sizeof(duk_tval) + \ - (n_hash) * sizeof(duk_uint32_t)) -#define DUK_HOBJECT_P_SET_REALLOC_PTRS(p_base, set_e_k, set_e_pv, set_e_f, set_a, set_h, n_ent, n_arr, n_hash) \ - do { \ - (set_e_pv) = (duk_propvalue *) (void *) (p_base); \ - (set_a) = (duk_tval *) (void *) ((set_e_pv) + (n_ent)); \ - (set_e_k) = (duk_hstring **) (void *) ((set_a) + (n_arr)); \ - (set_h) = (duk_uint32_t *) (void *) ((set_e_k) + (n_ent)); \ - (set_e_f) = (duk_uint8_t *) (void *) ((set_h) + (n_hash)); \ - } while (0) -#else -#error invalid hobject layout defines -#endif /* hobject property layout */ - -#define DUK_HOBJECT_P_ALLOC_SIZE(h) \ - DUK_HOBJECT_P_COMPUTE_SIZE(DUK_HOBJECT_GET_ESIZE((h)), DUK_HOBJECT_GET_ASIZE((h)), DUK_HOBJECT_GET_HSIZE((h))) - -#define DUK_HOBJECT_E_GET_KEY(heap, h, i) (DUK_HOBJECT_E_GET_KEY_BASE((heap), (h))[(i)]) -#define DUK_HOBJECT_E_GET_KEY_PTR(heap, h, i) (&DUK_HOBJECT_E_GET_KEY_BASE((heap), (h))[(i)]) -#define DUK_HOBJECT_E_GET_VALUE(heap, h, i) (DUK_HOBJECT_E_GET_VALUE_BASE((heap), (h))[(i)]) -#define DUK_HOBJECT_E_GET_VALUE_PTR(heap, h, i) (&DUK_HOBJECT_E_GET_VALUE_BASE((heap), (h))[(i)]) -#define DUK_HOBJECT_E_GET_VALUE_TVAL(heap, h, i) (DUK_HOBJECT_E_GET_VALUE((heap), (h), (i)).v) -#define DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(heap, h, i) (&DUK_HOBJECT_E_GET_VALUE((heap), (h), (i)).v) -#define DUK_HOBJECT_E_GET_VALUE_GETTER(heap, h, i) (DUK_HOBJECT_E_GET_VALUE((heap), (h), (i)).a.get) -#define DUK_HOBJECT_E_GET_VALUE_GETTER_PTR(heap, h, i) (&DUK_HOBJECT_E_GET_VALUE((heap), (h), (i)).a.get) -#define DUK_HOBJECT_E_GET_VALUE_SETTER(heap, h, i) (DUK_HOBJECT_E_GET_VALUE((heap), (h), (i)).a.set) -#define DUK_HOBJECT_E_GET_VALUE_SETTER_PTR(heap, h, i) (&DUK_HOBJECT_E_GET_VALUE((heap), (h), (i)).a.set) -#define DUK_HOBJECT_E_GET_FLAGS(heap, h, i) (DUK_HOBJECT_E_GET_FLAGS_BASE((heap), (h))[(i)]) -#define DUK_HOBJECT_E_GET_FLAGS_PTR(heap, h, i) (&DUK_HOBJECT_E_GET_FLAGS_BASE((heap), (h))[(i)]) -#define DUK_HOBJECT_A_GET_VALUE(heap, h, i) (DUK_HOBJECT_A_GET_BASE((heap), (h))[(i)]) -#define DUK_HOBJECT_A_GET_VALUE_PTR(heap, h, i) (&DUK_HOBJECT_A_GET_BASE((heap), (h))[(i)]) -#define DUK_HOBJECT_H_GET_INDEX(heap, h, i) (DUK_HOBJECT_H_GET_BASE((heap), (h))[(i)]) -#define DUK_HOBJECT_H_GET_INDEX_PTR(heap, h, i) (&DUK_HOBJECT_H_GET_BASE((heap), (h))[(i)]) - -#define DUK_HOBJECT_E_SET_KEY(heap, h, i, k) \ - do { \ - DUK_HOBJECT_E_GET_KEY((heap), (h), (i)) = (k); \ - } while (0) -#define DUK_HOBJECT_E_SET_VALUE(heap, h, i, v) \ - do { \ - DUK_HOBJECT_E_GET_VALUE((heap), (h), (i)) = (v); \ - } while (0) -#define DUK_HOBJECT_E_SET_VALUE_TVAL(heap, h, i, v) \ - do { \ - DUK_HOBJECT_E_GET_VALUE((heap), (h), (i)).v = (v); \ - } while (0) -#define DUK_HOBJECT_E_SET_VALUE_GETTER(heap, h, i, v) \ - do { \ - DUK_HOBJECT_E_GET_VALUE((heap), (h), (i)).a.get = (v); \ - } while (0) -#define DUK_HOBJECT_E_SET_VALUE_SETTER(heap, h, i, v) \ - do { \ - DUK_HOBJECT_E_GET_VALUE((heap), (h), (i)).a.set = (v); \ - } while (0) -#define DUK_HOBJECT_E_SET_FLAGS(heap, h, i, f) \ - do { \ - DUK_HOBJECT_E_GET_FLAGS((heap), (h), (i)) = (duk_uint8_t) (f); \ - } while (0) -#define DUK_HOBJECT_A_SET_VALUE(heap, h, i, v) \ - do { \ - DUK_HOBJECT_A_GET_VALUE((heap), (h), (i)) = (v); \ - } while (0) -#define DUK_HOBJECT_A_SET_VALUE_TVAL(heap, h, i, v) DUK_HOBJECT_A_SET_VALUE((heap), (h), (i), (v)) /* alias for above */ -#define DUK_HOBJECT_H_SET_INDEX(heap, h, i, v) \ - do { \ - DUK_HOBJECT_H_GET_INDEX((heap), (h), (i)) = (v); \ - } while (0) - -#define DUK_HOBJECT_E_SET_FLAG_BITS(heap, h, i, mask) \ - do { \ - DUK_HOBJECT_E_GET_FLAGS_BASE((heap), (h))[(i)] |= (mask); \ - } while (0) - -#define DUK_HOBJECT_E_CLEAR_FLAG_BITS(heap, h, i, mask) \ - do { \ - DUK_HOBJECT_E_GET_FLAGS_BASE((heap), (h))[(i)] &= ~(mask); \ - } while (0) - -#define DUK_HOBJECT_E_SLOT_IS_WRITABLE(heap, h, i) ((DUK_HOBJECT_E_GET_FLAGS((heap), (h), (i)) & DUK_PROPDESC_FLAG_WRITABLE) != 0) -#define DUK_HOBJECT_E_SLOT_IS_ENUMERABLE(heap, h, i) \ - ((DUK_HOBJECT_E_GET_FLAGS((heap), (h), (i)) & DUK_PROPDESC_FLAG_ENUMERABLE) != 0) -#define DUK_HOBJECT_E_SLOT_IS_CONFIGURABLE(heap, h, i) \ - ((DUK_HOBJECT_E_GET_FLAGS((heap), (h), (i)) & DUK_PROPDESC_FLAG_CONFIGURABLE) != 0) -#define DUK_HOBJECT_E_SLOT_IS_ACCESSOR(heap, h, i) ((DUK_HOBJECT_E_GET_FLAGS((heap), (h), (i)) & DUK_PROPDESC_FLAG_ACCESSOR) != 0) - -#define DUK_HOBJECT_E_SLOT_SET_WRITABLE(heap, h, i) DUK_HOBJECT_E_SET_FLAG_BITS((heap), (h), (i), DUK_PROPDESC_FLAG_WRITABLE) -#define DUK_HOBJECT_E_SLOT_SET_ENUMERABLE(heap, h, i) DUK_HOBJECT_E_SET_FLAG_BITS((heap), (h), (i), DUK_PROPDESC_FLAG_ENUMERABLE) -#define DUK_HOBJECT_E_SLOT_SET_CONFIGURABLE(heap, h, i) \ - DUK_HOBJECT_E_SET_FLAG_BITS((heap), (h), (i), DUK_PROPDESC_FLAG_CONFIGURABLE) -#define DUK_HOBJECT_E_SLOT_SET_ACCESSOR(heap, h, i) DUK_HOBJECT_E_SET_FLAG_BITS((heap), (h), (i), DUK_PROPDESC_FLAG_ACCESSOR) - -#define DUK_HOBJECT_E_SLOT_CLEAR_WRITABLE(heap, h, i) DUK_HOBJECT_E_CLEAR_FLAG_BITS((heap), (h), (i), DUK_PROPDESC_FLAG_WRITABLE) -#define DUK_HOBJECT_E_SLOT_CLEAR_ENUMERABLE(heap, h, i) \ - DUK_HOBJECT_E_CLEAR_FLAG_BITS((heap), (h), (i), DUK_PROPDESC_FLAG_ENUMERABLE) -#define DUK_HOBJECT_E_SLOT_CLEAR_CONFIGURABLE(heap, h, i) \ - DUK_HOBJECT_E_CLEAR_FLAG_BITS((heap), (h), (i), DUK_PROPDESC_FLAG_CONFIGURABLE) -#define DUK_HOBJECT_E_SLOT_CLEAR_ACCESSOR(heap, h, i) DUK_HOBJECT_E_CLEAR_FLAG_BITS((heap), (h), (i), DUK_PROPDESC_FLAG_ACCESSOR) - -#define DUK_PROPDESC_IS_WRITABLE(p) (((p)->flags & DUK_PROPDESC_FLAG_WRITABLE) != 0) -#define DUK_PROPDESC_IS_ENUMERABLE(p) (((p)->flags & DUK_PROPDESC_FLAG_ENUMERABLE) != 0) -#define DUK_PROPDESC_IS_CONFIGURABLE(p) (((p)->flags & DUK_PROPDESC_FLAG_CONFIGURABLE) != 0) -#define DUK_PROPDESC_IS_ACCESSOR(p) (((p)->flags & DUK_PROPDESC_FLAG_ACCESSOR) != 0) - -#define DUK_HOBJECT_HASHIDX_UNUSED 0xffffffffUL -#define DUK_HOBJECT_HASHIDX_DELETED 0xfffffffeUL - -/* - * Macros for accessing size fields - */ - -#if defined(DUK_USE_OBJSIZES16) -#define DUK_HOBJECT_GET_ESIZE(h) ((h)->e_size16) -#define DUK_HOBJECT_SET_ESIZE(h, v) \ - do { \ - (h)->e_size16 = (v); \ - } while (0) -#define DUK_HOBJECT_GET_ENEXT(h) ((h)->e_next16) -#define DUK_HOBJECT_SET_ENEXT(h, v) \ - do { \ - (h)->e_next16 = (v); \ - } while (0) -#define DUK_HOBJECT_POSTINC_ENEXT(h) ((h)->e_next16++) -#define DUK_HOBJECT_GET_ASIZE(h) ((h)->a_size16) -#define DUK_HOBJECT_SET_ASIZE(h, v) \ - do { \ - (h)->a_size16 = (v); \ - } while (0) -#if defined(DUK_USE_HOBJECT_HASH_PART) -#define DUK_HOBJECT_GET_HSIZE(h) ((h)->h_size16) -#define DUK_HOBJECT_SET_HSIZE(h, v) \ - do { \ - (h)->h_size16 = (v); \ - } while (0) -#else -#define DUK_HOBJECT_GET_HSIZE(h) 0 -#define DUK_HOBJECT_SET_HSIZE(h, v) \ - do { \ - DUK_ASSERT((v) == 0); \ - } while (0) -#endif -#else -#define DUK_HOBJECT_GET_ESIZE(h) ((h)->e_size) -#define DUK_HOBJECT_SET_ESIZE(h, v) \ - do { \ - (h)->e_size = (v); \ - } while (0) -#define DUK_HOBJECT_GET_ENEXT(h) ((h)->e_next) -#define DUK_HOBJECT_SET_ENEXT(h, v) \ - do { \ - (h)->e_next = (v); \ - } while (0) -#define DUK_HOBJECT_POSTINC_ENEXT(h) ((h)->e_next++) -#define DUK_HOBJECT_GET_ASIZE(h) ((h)->a_size) -#define DUK_HOBJECT_SET_ASIZE(h, v) \ - do { \ - (h)->a_size = (v); \ - } while (0) -#if defined(DUK_USE_HOBJECT_HASH_PART) -#define DUK_HOBJECT_GET_HSIZE(h) ((h)->h_size) -#define DUK_HOBJECT_SET_HSIZE(h, v) \ - do { \ - (h)->h_size = (v); \ - } while (0) -#else -#define DUK_HOBJECT_GET_HSIZE(h) 0 -#define DUK_HOBJECT_SET_HSIZE(h, v) \ - do { \ - DUK_ASSERT((v) == 0); \ - } while (0) -#endif -#endif - -/* - * Misc - */ - -/* Maximum prototype traversal depth. Sanity limit which handles e.g. - * prototype loops (even complex ones like 1->2->3->4->2->3->4->2->3->4). - */ -#define DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY 10000L - -/* - * ECMAScript [[Class]] - */ - -/* range check not necessary because all 4-bit values are mapped */ -#define DUK_HOBJECT_CLASS_NUMBER_TO_STRIDX(n) duk_class_number_to_stridx[(n)] - -#define DUK_HOBJECT_GET_CLASS_STRING(heap, h) \ - DUK_HEAP_GET_STRING((heap), DUK_HOBJECT_CLASS_NUMBER_TO_STRIDX(DUK_HOBJECT_GET_CLASS_NUMBER((h)))) - -/* - * Macros for property handling - */ - -#if defined(DUK_USE_HEAPPTR16) -#define DUK_HOBJECT_GET_PROTOTYPE(heap, h) ((duk_hobject *) DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, (h)->prototype16)) -#define DUK_HOBJECT_SET_PROTOTYPE(heap, h, x) \ - do { \ - (h)->prototype16 = DUK_USE_HEAPPTR_ENC16((heap)->heap_udata, (void *) (x)); \ - } while (0) -#else -#define DUK_HOBJECT_GET_PROTOTYPE(heap, h) ((h)->prototype) -#define DUK_HOBJECT_SET_PROTOTYPE(heap, h, x) \ - do { \ - (h)->prototype = (x); \ - } while (0) -#endif - -/* Set prototype, DECREF earlier value, INCREF new value (tolerating NULLs). */ -#define DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h, p) duk_hobject_set_prototype_updref((thr), (h), (p)) - -/* Set initial prototype, assume NULL previous prototype, INCREF new value, - * tolerate NULL. - */ -#define DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, h, proto) \ - do { \ - duk_hthread *duk__thr = (thr); \ - duk_hobject *duk__obj = (h); \ - duk_hobject *duk__proto = (proto); \ - DUK_UNREF(duk__thr); \ - DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(duk__thr->heap, duk__obj) == NULL); \ - DUK_HOBJECT_SET_PROTOTYPE(duk__thr->heap, duk__obj, duk__proto); \ - DUK_HOBJECT_INCREF_ALLOWNULL(duk__thr, duk__proto); \ - } while (0) - -/* - * Finalizer check - */ - -#if defined(DUK_USE_HEAPPTR16) -#define DUK_HOBJECT_HAS_FINALIZER_FAST(heap, h) duk_hobject_has_finalizer_fast_raw((heap), (h)) -#else -#define DUK_HOBJECT_HAS_FINALIZER_FAST(heap, h) duk_hobject_has_finalizer_fast_raw((h)) -#endif - -/* - * Resizing and hash behavior - */ - -/* Sanity limit on max number of properties (allocated, not necessarily used). - * This is somewhat arbitrary, but if we're close to 2**32 properties some - * algorithms will fail (e.g. hash size selection, next prime selection). - * Also, we use negative array/entry table indices to indicate 'not found', - * so anything above 0x80000000 will cause trouble now. - */ -#if defined(DUK_USE_OBJSIZES16) -#define DUK_HOBJECT_MAX_PROPERTIES 0x0000ffffUL -#else -#define DUK_HOBJECT_MAX_PROPERTIES 0x3fffffffUL /* 2**30-1 ~= 1G properties */ -#endif - -/* internal align target for props allocation, must be 2*n for some n */ -#if (DUK_USE_ALIGN_BY == 4) -#define DUK_HOBJECT_ALIGN_TARGET 4 -#elif (DUK_USE_ALIGN_BY == 8) -#define DUK_HOBJECT_ALIGN_TARGET 8 -#elif (DUK_USE_ALIGN_BY == 1) -#define DUK_HOBJECT_ALIGN_TARGET 1 -#else -#error invalid DUK_USE_ALIGN_BY -#endif - -/* - * PC-to-line constants - */ - -#define DUK_PC2LINE_SKIP 64 - -/* maximum length for a SKIP-1 diffstream: 35 bits per entry, rounded up to bytes */ -#define DUK_PC2LINE_MAX_DIFF_LENGTH (((DUK_PC2LINE_SKIP - 1) * 35 + 7) / 8) - -/* - * Struct defs - */ - -struct duk_propaccessor { - duk_hobject *get; - duk_hobject *set; -}; - -union duk_propvalue { - /* The get/set pointers could be 16-bit pointer compressed but it - * would make no difference on 32-bit platforms because duk_tval is - * 8 bytes or more anyway. - */ - duk_tval v; - duk_propaccessor a; -}; - -struct duk_propdesc { - /* read-only values 'lifted' for ease of use */ - duk_small_uint_t flags; - duk_hobject *get; - duk_hobject *set; - - /* for updating (all are set to < 0 for virtual properties) */ - duk_int_t e_idx; /* prop index in 'entry part', < 0 if not there */ - duk_int_t h_idx; /* prop index in 'hash part', < 0 if not there */ - duk_int_t a_idx; /* prop index in 'array part', < 0 if not there */ -}; - -struct duk_hobject { - duk_heaphdr hdr; - - /* - * 'props' contains {key,value,flags} entries, optional array entries, and - * an optional hash lookup table for non-array entries in a single 'sliced' - * allocation. There are several layout options, which differ slightly in - * generated code size/speed and alignment/padding; duk_features.h selects - * the layout used. - * - * Layout 1 (DUK_USE_HOBJECT_LAYOUT_1): - * - * e_size * sizeof(duk_hstring *) bytes of entry keys (e_next gc reachable) - * e_size * sizeof(duk_propvalue) bytes of entry values (e_next gc reachable) - * e_size * sizeof(duk_uint8_t) bytes of entry flags (e_next gc reachable) - * a_size * sizeof(duk_tval) bytes of (opt) array values (plain only) (all gc reachable) - * h_size * sizeof(duk_uint32_t) bytes of (opt) hash indexes to entries (e_size), - * 0xffffffffUL = unused, 0xfffffffeUL = deleted - * - * Layout 2 (DUK_USE_HOBJECT_LAYOUT_2): - * - * e_size * sizeof(duk_propvalue) bytes of entry values (e_next gc reachable) - * e_size * sizeof(duk_hstring *) bytes of entry keys (e_next gc reachable) - * e_size * sizeof(duk_uint8_t) + pad bytes of entry flags (e_next gc reachable) - * a_size * sizeof(duk_tval) bytes of (opt) array values (plain only) (all gc reachable) - * h_size * sizeof(duk_uint32_t) bytes of (opt) hash indexes to entries (e_size), - * 0xffffffffUL = unused, 0xfffffffeUL = deleted - * - * Layout 3 (DUK_USE_HOBJECT_LAYOUT_3): - * - * e_size * sizeof(duk_propvalue) bytes of entry values (e_next gc reachable) - * a_size * sizeof(duk_tval) bytes of (opt) array values (plain only) (all gc reachable) - * e_size * sizeof(duk_hstring *) bytes of entry keys (e_next gc reachable) - * h_size * sizeof(duk_uint32_t) bytes of (opt) hash indexes to entries (e_size), - * 0xffffffffUL = unused, 0xfffffffeUL = deleted - * e_size * sizeof(duk_uint8_t) bytes of entry flags (e_next gc reachable) - * - * In layout 1, the 'e_next' count is rounded to 4 or 8 on platforms - * requiring 4 or 8 byte alignment. This ensures proper alignment - * for the entries, at the cost of memory footprint. However, it's - * probably preferable to use another layout on such platforms instead. - * - * In layout 2, the key and value parts are swapped to avoid padding - * the key array on platforms requiring alignment by 8. The flags part - * is padded to get alignment for array entries. The 'e_next' count does - * not need to be rounded as in layout 1. - * - * In layout 3, entry values and array values are always aligned properly, - * and assuming pointers are at most 8 bytes, so are the entry keys. Hash - * indices will be properly aligned (assuming pointers are at least 4 bytes). - * Finally, flags don't need additional alignment. This layout provides - * compact allocations without padding (even on platforms with alignment - * requirements) at the cost of a bit slower lookups. - * - * Objects with few keys don't have a hash index; keys are looked up linearly, - * which is cache efficient because the keys are consecutive. Larger objects - * have a hash index part which contains integer indexes to the entries part. - * - * A single allocation reduces memory allocation overhead but requires more - * work when any part needs to be resized. A sliced allocation for entries - * makes linear key matching faster on most platforms (more locality) and - * skimps on flags size (which would be followed by 3 bytes of padding in - * most architectures if entries were placed in a struct). - * - * 'props' also contains internal properties distinguished with a non-BMP - * prefix. Often used properties should be placed early in 'props' whenever - * possible to make accessing them as fast a possible. - */ - -#if defined(DUK_USE_HEAPPTR16) - /* Located in duk_heaphdr h_extra16. Subclasses of duk_hobject (like - * duk_hcompfunc) are not free to use h_extra16 for this reason. - */ -#else - duk_uint8_t *props; -#endif - - /* prototype: the only internal property lifted outside 'e' as it is so central */ -#if defined(DUK_USE_HEAPPTR16) - duk_uint16_t prototype16; -#else - duk_hobject *prototype; -#endif - -#if defined(DUK_USE_OBJSIZES16) - duk_uint16_t e_size16; - duk_uint16_t e_next16; - duk_uint16_t a_size16; -#if defined(DUK_USE_HOBJECT_HASH_PART) - duk_uint16_t h_size16; -#endif -#else - duk_uint32_t e_size; /* entry part size */ - duk_uint32_t e_next; /* index for next new key ([0,e_next[ are gc reachable) */ - duk_uint32_t a_size; /* array part size (entirely gc reachable) */ -#if defined(DUK_USE_HOBJECT_HASH_PART) - duk_uint32_t h_size; /* hash part size or 0 if unused */ -#endif -#endif -}; - -/* - * Exposed data - */ - -#if !defined(DUK_SINGLE_FILE) -DUK_INTERNAL_DECL duk_uint8_t duk_class_number_to_stridx[32]; -#endif /* !DUK_SINGLE_FILE */ - -/* - * Prototypes - */ - -/* alloc and init */ -DUK_INTERNAL_DECL duk_hobject *duk_hobject_alloc_unchecked(duk_heap *heap, duk_uint_t hobject_flags); -DUK_INTERNAL_DECL duk_hobject *duk_hobject_alloc(duk_hthread *thr, duk_uint_t hobject_flags); -DUK_INTERNAL_DECL duk_harray *duk_harray_alloc(duk_hthread *thr, duk_uint_t hobject_flags); -DUK_INTERNAL_DECL duk_hcompfunc *duk_hcompfunc_alloc(duk_hthread *thr, duk_uint_t hobject_flags); -DUK_INTERNAL_DECL duk_hnatfunc *duk_hnatfunc_alloc(duk_hthread *thr, duk_uint_t hobject_flags); -DUK_INTERNAL_DECL duk_hboundfunc *duk_hboundfunc_alloc(duk_heap *heap, duk_uint_t hobject_flags); -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL_DECL duk_hbufobj *duk_hbufobj_alloc(duk_hthread *thr, duk_uint_t hobject_flags); -#endif -DUK_INTERNAL_DECL duk_hthread *duk_hthread_alloc_unchecked(duk_heap *heap, duk_uint_t hobject_flags); -DUK_INTERNAL_DECL duk_hthread *duk_hthread_alloc(duk_hthread *thr, duk_uint_t hobject_flags); -DUK_INTERNAL_DECL duk_hdecenv *duk_hdecenv_alloc(duk_hthread *thr, duk_uint_t hobject_flags); -DUK_INTERNAL_DECL duk_hobjenv *duk_hobjenv_alloc(duk_hthread *thr, duk_uint_t hobject_flags); -DUK_INTERNAL_DECL duk_hproxy *duk_hproxy_alloc(duk_hthread *thr, duk_uint_t hobject_flags); - -/* resize */ -DUK_INTERNAL_DECL void duk_hobject_realloc_props(duk_hthread *thr, - duk_hobject *obj, - duk_uint32_t new_e_size, - duk_uint32_t new_a_size, - duk_uint32_t new_h_size, - duk_bool_t abandon_array); -DUK_INTERNAL_DECL void duk_hobject_resize_entrypart(duk_hthread *thr, duk_hobject *obj, duk_uint32_t new_e_size); -#if 0 /*unused*/ -DUK_INTERNAL_DECL void duk_hobject_resize_arraypart(duk_hthread *thr, - duk_hobject *obj, - duk_uint32_t new_a_size); -#endif - -/* low-level property functions */ -DUK_INTERNAL_DECL duk_bool_t -duk_hobject_find_entry(duk_heap *heap, duk_hobject *obj, duk_hstring *key, duk_int_t *e_idx, duk_int_t *h_idx); -DUK_INTERNAL_DECL duk_tval *duk_hobject_find_entry_tval_ptr(duk_heap *heap, duk_hobject *obj, duk_hstring *key); -DUK_INTERNAL_DECL duk_tval *duk_hobject_find_entry_tval_ptr_stridx(duk_heap *heap, duk_hobject *obj, duk_small_uint_t stridx); -DUK_INTERNAL_DECL duk_tval *duk_hobject_find_entry_tval_ptr_and_attrs(duk_heap *heap, - duk_hobject *obj, - duk_hstring *key, - duk_uint_t *out_attrs); -DUK_INTERNAL_DECL duk_tval *duk_hobject_find_array_entry_tval_ptr(duk_heap *heap, duk_hobject *obj, duk_uarridx_t i); -DUK_INTERNAL_DECL duk_bool_t -duk_hobject_get_own_propdesc(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_propdesc *out_desc, duk_small_uint_t flags); - -/* core property functions */ -DUK_INTERNAL_DECL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key); -DUK_INTERNAL_DECL duk_bool_t -duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key, duk_tval *tv_val, duk_bool_t throw_flag); -DUK_INTERNAL_DECL duk_bool_t duk_hobject_delprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key, duk_bool_t throw_flag); -DUK_INTERNAL_DECL duk_bool_t duk_hobject_hasprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key); - -/* internal property functions */ -#define DUK_DELPROP_FLAG_THROW (1U << 0) -#define DUK_DELPROP_FLAG_FORCE (1U << 1) -DUK_INTERNAL_DECL duk_bool_t duk_hobject_delprop_raw(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_small_uint_t flags); -DUK_INTERNAL_DECL duk_bool_t duk_hobject_hasprop_raw(duk_hthread *thr, duk_hobject *obj, duk_hstring *key); -DUK_INTERNAL_DECL void duk_hobject_define_property_internal(duk_hthread *thr, - duk_hobject *obj, - duk_hstring *key, - duk_small_uint_t flags); -DUK_INTERNAL_DECL void duk_hobject_define_property_internal_arridx(duk_hthread *thr, - duk_hobject *obj, - duk_uarridx_t arr_idx, - duk_small_uint_t flags); -DUK_INTERNAL_DECL duk_size_t duk_hobject_get_length(duk_hthread *thr, duk_hobject *obj); -#if defined(DUK_USE_HEAPPTR16) -DUK_INTERNAL_DECL duk_bool_t duk_hobject_has_finalizer_fast_raw(duk_heap *heap, duk_hobject *obj); -#else -DUK_INTERNAL_DECL duk_bool_t duk_hobject_has_finalizer_fast_raw(duk_hobject *obj); -#endif - -/* helpers for defineProperty() and defineProperties() */ -DUK_INTERNAL_DECL void duk_hobject_prepare_property_descriptor(duk_hthread *thr, - duk_idx_t idx_in, - duk_uint_t *out_defprop_flags, - duk_idx_t *out_idx_value, - duk_hobject **out_getter, - duk_hobject **out_setter); -DUK_INTERNAL_DECL duk_bool_t duk_hobject_define_property_helper(duk_hthread *thr, - duk_uint_t defprop_flags, - duk_hobject *obj, - duk_hstring *key, - duk_idx_t idx_value, - duk_hobject *get, - duk_hobject *set, - duk_bool_t throw_flag); - -/* Object built-in methods */ -DUK_INTERNAL_DECL void duk_hobject_object_get_own_property_descriptor(duk_hthread *thr, duk_idx_t obj_idx); -DUK_INTERNAL_DECL void duk_hobject_object_seal_freeze_helper(duk_hthread *thr, duk_hobject *obj, duk_bool_t is_freeze); -DUK_INTERNAL_DECL duk_bool_t duk_hobject_object_is_sealed_frozen_helper(duk_hthread *thr, duk_hobject *obj, duk_bool_t is_frozen); -DUK_INTERNAL_DECL duk_bool_t duk_hobject_object_ownprop_helper(duk_hthread *thr, duk_small_uint_t required_desc_flags); - -/* internal properties */ -DUK_INTERNAL_DECL duk_tval *duk_hobject_get_internal_value_tval_ptr(duk_heap *heap, duk_hobject *obj); -DUK_INTERNAL_DECL duk_hstring *duk_hobject_get_internal_value_string(duk_heap *heap, duk_hobject *obj); -DUK_INTERNAL_DECL duk_harray *duk_hobject_get_formals(duk_hthread *thr, duk_hobject *obj); -DUK_INTERNAL_DECL duk_hobject *duk_hobject_get_varmap(duk_hthread *thr, duk_hobject *obj); - -/* hobject management functions */ -DUK_INTERNAL_DECL void duk_hobject_compact_props(duk_hthread *thr, duk_hobject *obj); - -/* ES2015 proxy */ -#if defined(DUK_USE_ES6_PROXY) -DUK_INTERNAL_DECL duk_bool_t duk_hobject_proxy_check(duk_hobject *obj, duk_hobject **out_target, duk_hobject **out_handler); -DUK_INTERNAL_DECL duk_hobject *duk_hobject_resolve_proxy_target(duk_hobject *obj); -#endif - -/* enumeration */ -DUK_INTERNAL_DECL void duk_hobject_enumerator_create(duk_hthread *thr, duk_small_uint_t enum_flags); -DUK_INTERNAL_DECL duk_ret_t duk_hobject_get_enumerated_keys(duk_hthread *thr, duk_small_uint_t enum_flags); -DUK_INTERNAL_DECL duk_bool_t duk_hobject_enumerator_next(duk_hthread *thr, duk_bool_t get_value); - -/* macros */ -DUK_INTERNAL_DECL void duk_hobject_set_prototype_updref(duk_hthread *thr, duk_hobject *h, duk_hobject *p); - -/* pc2line */ -#if defined(DUK_USE_PC2LINE) -DUK_INTERNAL_DECL void duk_hobject_pc2line_pack(duk_hthread *thr, duk_compiler_instr *instrs, duk_uint_fast32_t length); -DUK_INTERNAL_DECL duk_uint_fast32_t duk_hobject_pc2line_query(duk_hthread *thr, duk_idx_t idx_func, duk_uint_fast32_t pc); -#endif - -/* misc */ -DUK_INTERNAL_DECL duk_bool_t duk_hobject_prototype_chain_contains(duk_hthread *thr, - duk_hobject *h, - duk_hobject *p, - duk_bool_t ignore_loop); - -#if !defined(DUK_USE_OBJECT_BUILTIN) -/* These declarations are needed when related built-in is disabled and - * genbuiltins.py won't automatically emit the declerations. - */ -DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_to_string(duk_hthread *thr); -DUK_INTERNAL_DECL duk_ret_t duk_bi_function_prototype(duk_hthread *thr); -#endif - -#endif /* DUK_HOBJECT_H_INCLUDED */ -/* #include duk_hcompfunc.h */ -/* - * Heap compiled function (ECMAScript function) representation. - * - * There is a single data buffer containing the ECMAScript function's - * bytecode, constants, and inner functions. - */ - -#if !defined(DUK_HCOMPFUNC_H_INCLUDED) -#define DUK_HCOMPFUNC_H_INCLUDED - -/* - * Field accessor macros - */ - -/* XXX: casts could be improved, especially for GET/SET DATA */ - -#if defined(DUK_USE_HEAPPTR16) -#define DUK_HCOMPFUNC_GET_DATA(heap, h) ((duk_hbuffer_fixed *) (void *) DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, (h)->data16)) -#define DUK_HCOMPFUNC_SET_DATA(heap, h, v) \ - do { \ - (h)->data16 = DUK_USE_HEAPPTR_ENC16((heap)->heap_udata, (void *) (v)); \ - } while (0) -#define DUK_HCOMPFUNC_GET_FUNCS(heap, h) ((duk_hobject **) (void *) (DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, (h)->funcs16))) -#define DUK_HCOMPFUNC_SET_FUNCS(heap, h, v) \ - do { \ - (h)->funcs16 = DUK_USE_HEAPPTR_ENC16((heap)->heap_udata, (void *) (v)); \ - } while (0) -#define DUK_HCOMPFUNC_GET_BYTECODE(heap, h) ((duk_instr_t *) (void *) (DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, (h)->bytecode16))) -#define DUK_HCOMPFUNC_SET_BYTECODE(heap, h, v) \ - do { \ - (h)->bytecode16 = DUK_USE_HEAPPTR_ENC16((heap)->heap_udata, (void *) (v)); \ - } while (0) -#define DUK_HCOMPFUNC_GET_LEXENV(heap, h) ((duk_hobject *) (void *) (DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, (h)->lex_env16))) -#define DUK_HCOMPFUNC_SET_LEXENV(heap, h, v) \ - do { \ - (h)->lex_env16 = DUK_USE_HEAPPTR_ENC16((heap)->heap_udata, (void *) (v)); \ - } while (0) -#define DUK_HCOMPFUNC_GET_VARENV(heap, h) ((duk_hobject *) (void *) (DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, (h)->var_env16))) -#define DUK_HCOMPFUNC_SET_VARENV(heap, h, v) \ - do { \ - (h)->var_env16 = DUK_USE_HEAPPTR_ENC16((heap)->heap_udata, (void *) (v)); \ - } while (0) -#else -#define DUK_HCOMPFUNC_GET_DATA(heap, h) ((duk_hbuffer_fixed *) (void *) (h)->data) -#define DUK_HCOMPFUNC_SET_DATA(heap, h, v) \ - do { \ - (h)->data = (duk_hbuffer *) (v); \ - } while (0) -#define DUK_HCOMPFUNC_GET_FUNCS(heap, h) ((h)->funcs) -#define DUK_HCOMPFUNC_SET_FUNCS(heap, h, v) \ - do { \ - (h)->funcs = (v); \ - } while (0) -#define DUK_HCOMPFUNC_GET_BYTECODE(heap, h) ((h)->bytecode) -#define DUK_HCOMPFUNC_SET_BYTECODE(heap, h, v) \ - do { \ - (h)->bytecode = (v); \ - } while (0) -#define DUK_HCOMPFUNC_GET_LEXENV(heap, h) ((h)->lex_env) -#define DUK_HCOMPFUNC_SET_LEXENV(heap, h, v) \ - do { \ - (h)->lex_env = (v); \ - } while (0) -#define DUK_HCOMPFUNC_GET_VARENV(heap, h) ((h)->var_env) -#define DUK_HCOMPFUNC_SET_VARENV(heap, h, v) \ - do { \ - (h)->var_env = (v); \ - } while (0) -#endif - -/* - * Accessor macros for function specific data areas - */ - -/* Note: assumes 'data' is always a fixed buffer */ -#define DUK_HCOMPFUNC_GET_BUFFER_BASE(heap, h) DUK_HBUFFER_FIXED_GET_DATA_PTR((heap), DUK_HCOMPFUNC_GET_DATA((heap), (h))) - -#define DUK_HCOMPFUNC_GET_CONSTS_BASE(heap, h) ((duk_tval *) (void *) DUK_HCOMPFUNC_GET_BUFFER_BASE((heap), (h))) - -#define DUK_HCOMPFUNC_GET_FUNCS_BASE(heap, h) DUK_HCOMPFUNC_GET_FUNCS((heap), (h)) - -#define DUK_HCOMPFUNC_GET_CODE_BASE(heap, h) DUK_HCOMPFUNC_GET_BYTECODE((heap), (h)) - -#define DUK_HCOMPFUNC_GET_CONSTS_END(heap, h) ((duk_tval *) (void *) DUK_HCOMPFUNC_GET_FUNCS((heap), (h))) - -#define DUK_HCOMPFUNC_GET_FUNCS_END(heap, h) ((duk_hobject **) (void *) DUK_HCOMPFUNC_GET_BYTECODE((heap), (h))) - -/* XXX: double evaluation of DUK_HCOMPFUNC_GET_DATA() */ -#define DUK_HCOMPFUNC_GET_CODE_END(heap, h) \ - ((duk_instr_t *) (void *) (DUK_HBUFFER_FIXED_GET_DATA_PTR((heap), DUK_HCOMPFUNC_GET_DATA((heap), (h))) + \ - DUK_HBUFFER_GET_SIZE((duk_hbuffer *) DUK_HCOMPFUNC_GET_DATA((heap), h)))) - -#define DUK_HCOMPFUNC_GET_CONSTS_SIZE(heap, h) \ - ((duk_size_t) (((const duk_uint8_t *) DUK_HCOMPFUNC_GET_CONSTS_END((heap), (h))) - \ - ((const duk_uint8_t *) DUK_HCOMPFUNC_GET_CONSTS_BASE((heap), (h))))) - -#define DUK_HCOMPFUNC_GET_FUNCS_SIZE(heap, h) \ - ((duk_size_t) (((const duk_uint8_t *) DUK_HCOMPFUNC_GET_FUNCS_END((heap), (h))) - \ - ((const duk_uint8_t *) DUK_HCOMPFUNC_GET_FUNCS_BASE((heap), (h))))) - -#define DUK_HCOMPFUNC_GET_CODE_SIZE(heap, h) \ - ((duk_size_t) (((const duk_uint8_t *) DUK_HCOMPFUNC_GET_CODE_END((heap), (h))) - \ - ((const duk_uint8_t *) DUK_HCOMPFUNC_GET_CODE_BASE((heap), (h))))) - -#define DUK_HCOMPFUNC_GET_CONSTS_COUNT(heap, h) ((duk_size_t) (DUK_HCOMPFUNC_GET_CONSTS_SIZE((heap), (h)) / sizeof(duk_tval))) - -#define DUK_HCOMPFUNC_GET_FUNCS_COUNT(heap, h) ((duk_size_t) (DUK_HCOMPFUNC_GET_FUNCS_SIZE((heap), (h)) / sizeof(duk_hobject *))) - -#define DUK_HCOMPFUNC_GET_CODE_COUNT(heap, h) ((duk_size_t) (DUK_HCOMPFUNC_GET_CODE_SIZE((heap), (h)) / sizeof(duk_instr_t))) - -/* - * Validity assert - */ - -#if defined(DUK_USE_ASSERTIONS) -DUK_INTERNAL_DECL void duk_hcompfunc_assert_valid(duk_hcompfunc *h); -#define DUK_HCOMPFUNC_ASSERT_VALID(h) \ - do { \ - duk_hcompfunc_assert_valid((h)); \ - } while (0) -#else -#define DUK_HCOMPFUNC_ASSERT_VALID(h) \ - do { \ - } while (0) -#endif - -/* - * Main struct - */ - -struct duk_hcompfunc { - /* shared object part */ - duk_hobject obj; - - /* - * Pointers to function data area for faster access. Function - * data is a buffer shared between all closures of the same - * "template" function. The data buffer is always fixed (non- - * dynamic, hence stable), with a layout as follows: - * - * constants (duk_tval) - * inner functions (duk_hobject *) - * bytecode (duk_instr_t) - * - * Note: bytecode end address can be computed from 'data' buffer - * size. It is not strictly necessary functionally, assuming - * bytecode never jumps outside its allocated area. However, - * it's a safety/robustness feature for avoiding the chance of - * executing random data as bytecode due to a compiler error. - * - * Note: values in the data buffer must be incref'd (they will - * be decref'd on release) for every compiledfunction referring - * to the 'data' element. - */ - - /* Data area, fixed allocation, stable data ptrs. */ -#if defined(DUK_USE_HEAPPTR16) - duk_uint16_t data16; -#else - duk_hbuffer *data; -#endif - - /* No need for constants pointer (= same as data). - * - * When using 16-bit packing alignment to 4 is nice. 'funcs' will be - * 4-byte aligned because 'constants' are duk_tvals. For now the - * inner function pointers are not compressed, so that 'bytecode' will - * also be 4-byte aligned. - */ -#if defined(DUK_USE_HEAPPTR16) - duk_uint16_t funcs16; - duk_uint16_t bytecode16; -#else - duk_hobject **funcs; - duk_instr_t *bytecode; -#endif - - /* Lexenv: lexical environment of closure, NULL for templates. - * Varenv: variable environment of closure, NULL for templates. - */ -#if defined(DUK_USE_HEAPPTR16) - duk_uint16_t lex_env16; - duk_uint16_t var_env16; -#else - duk_hobject *lex_env; - duk_hobject *var_env; -#endif - - /* - * 'nregs' registers are allocated on function entry, at most 'nargs' - * are initialized to arguments, and the rest to undefined. Arguments - * above 'nregs' are not mapped to registers. All registers in the - * active stack range must be initialized because they are GC reachable. - * 'nargs' is needed so that if the function is given more than 'nargs' - * arguments, the additional arguments do not 'clobber' registers - * beyond 'nregs' which must be consistently initialized to undefined. - * - * Usually there is no need to know which registers are mapped to - * local variables. Registers may be allocated to variable in any - * way (even including gaps). However, a register-variable mapping - * must be the same for the duration of the function execution and - * the register cannot be used for anything else. - * - * When looking up variables by name, the '_Varmap' map is used. - * When an activation closes, registers mapped to arguments are - * copied into the environment record based on the same map. The - * reverse map (from register to variable) is not currently needed - * at run time, except for debugging, so it is not maintained. - */ - - duk_uint16_t nregs; /* regs to allocate */ - duk_uint16_t nargs; /* number of arguments allocated to regs */ - - /* - * Additional control information is placed into the object itself - * as internal properties to avoid unnecessary fields for the - * majority of functions. The compiler tries to omit internal - * control fields when possible. - * - * Function templates: - * - * { - * name: "func", // declaration, named function expressions - * fileName: - * _Varmap: { "arg1": 0, "arg2": 1, "varname": 2 }, - * _Formals: [ "arg1", "arg2" ], - * _Source: "function func(arg1, arg2) { ... }", - * _Pc2line: , - * } - * - * Function instances: - * - * { - * length: 2, - * prototype: { constructor: }, - * caller: , - * arguments: , - * name: "func", // declaration, named function expressions - * fileName: - * _Varmap: { "arg1": 0, "arg2": 1, "varname": 2 }, - * _Formals: [ "arg1", "arg2" ], - * _Source: "function func(arg1, arg2) { ... }", - * _Pc2line: , - * } - * - * More detailed description of these properties can be found - * in the documentation. - */ - -#if defined(DUK_USE_DEBUGGER_SUPPORT) - /* Line number range for function. Needed during debugging to - * determine active breakpoints. - */ - duk_uint32_t start_line; - duk_uint32_t end_line; -#endif -}; - -#endif /* DUK_HCOMPFUNC_H_INCLUDED */ -/* #include duk_hnatfunc.h */ -/* - * Heap native function representation. - */ - -#if !defined(DUK_HNATFUNC_H_INCLUDED) -#define DUK_HNATFUNC_H_INCLUDED - -#if defined(DUK_USE_ASSERTIONS) -DUK_INTERNAL_DECL void duk_hnatfunc_assert_valid(duk_hnatfunc *h); -#define DUK_HNATFUNC_ASSERT_VALID(h) \ - do { \ - duk_hnatfunc_assert_valid((h)); \ - } while (0) -#else -#define DUK_HNATFUNC_ASSERT_VALID(h) \ - do { \ - } while (0) -#endif - -#define DUK_HNATFUNC_NARGS_VARARGS ((duk_int16_t) -1) -#define DUK_HNATFUNC_NARGS_MAX ((duk_int16_t) 0x7fff) - -struct duk_hnatfunc { - /* shared object part */ - duk_hobject obj; - - duk_c_function func; - duk_int16_t nargs; - duk_int16_t magic; - - /* The 'magic' field allows an opaque 16-bit field to be accessed by the - * Duktape/C function. This allows, for instance, the same native function - * to be used for a set of very similar functions, with the 'magic' field - * providing the necessary non-argument flags / values to guide the behavior - * of the native function. The value is signed on purpose: it is easier to - * convert a signed value to unsigned (simply AND with 0xffff) than vice - * versa. - * - * Note: cannot place nargs/magic into the heaphdr flags, because - * duk_hobject takes almost all flags already. - */ -}; - -#endif /* DUK_HNATFUNC_H_INCLUDED */ -/* #include duk_hboundfunc.h */ -/* - * Bound function representation. - */ - -#if !defined(DUK_HBOUNDFUNC_H_INCLUDED) -#define DUK_HBOUNDFUNC_H_INCLUDED - -/* Artificial limit for args length. Ensures arithmetic won't overflow - * 32 bits when combining bound functions. - */ -#define DUK_HBOUNDFUNC_MAX_ARGS 0x20000000UL - -#if defined(DUK_USE_ASSERTIONS) -DUK_INTERNAL_DECL void duk_hboundfunc_assert_valid(duk_hboundfunc *h); -#define DUK_HBOUNDFUNC_ASSERT_VALID(h) \ - do { \ - duk_hboundfunc_assert_valid((h)); \ - } while (0) -#else -#define DUK_HBOUNDFUNC_ASSERT_VALID(h) \ - do { \ - } while (0) -#endif - -struct duk_hboundfunc { - /* Shared object part. */ - duk_hobject obj; - - /* Final target function, stored as duk_tval so that lightfunc can be - * represented too. - */ - duk_tval target; - - /* This binding. */ - duk_tval this_binding; - - /* Arguments to prepend. */ - duk_tval *args; /* Separate allocation. */ - duk_idx_t nargs; -}; - -#endif /* DUK_HBOUNDFUNC_H_INCLUDED */ -/* #include duk_hbufobj.h */ -/* - * Heap Buffer object representation. Used for all Buffer variants. - */ - -#if !defined(DUK_HBUFOBJ_H_INCLUDED) -#define DUK_HBUFOBJ_H_INCLUDED - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) - -/* All element accessors are host endian now (driven by TypedArray spec). */ -#define DUK_HBUFOBJ_ELEM_UINT8 0 -#define DUK_HBUFOBJ_ELEM_UINT8CLAMPED 1 -#define DUK_HBUFOBJ_ELEM_INT8 2 -#define DUK_HBUFOBJ_ELEM_UINT16 3 -#define DUK_HBUFOBJ_ELEM_INT16 4 -#define DUK_HBUFOBJ_ELEM_UINT32 5 -#define DUK_HBUFOBJ_ELEM_INT32 6 -#define DUK_HBUFOBJ_ELEM_FLOAT32 7 -#define DUK_HBUFOBJ_ELEM_FLOAT64 8 -#define DUK_HBUFOBJ_ELEM_MAX 8 - -#if defined(DUK_USE_ASSERTIONS) -DUK_INTERNAL_DECL void duk_hbufobj_assert_valid(duk_hbufobj *h); -#define DUK_HBUFOBJ_ASSERT_VALID(h) \ - do { \ - duk_hbufobj_assert_valid((h)); \ - } while (0) -#else -#define DUK_HBUFOBJ_ASSERT_VALID(h) \ - do { \ - } while (0) -#endif - -/* Get the current data pointer (caller must ensure buf != NULL) as a - * duk_uint8_t ptr. Note that the result may be NULL if the underlying - * buffer has zero size and is not a fixed buffer. - */ -#define DUK_HBUFOBJ_GET_SLICE_BASE(heap, h) \ - (DUK_ASSERT_EXPR((h) != NULL), \ - DUK_ASSERT_EXPR((h)->buf != NULL), \ - (((duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR((heap), (h)->buf)) + (h)->offset)) - -/* True if slice is full, i.e. offset is zero and length covers the entire - * buffer. This status may change independently of the duk_hbufobj if - * the underlying buffer is dynamic and changes without the hbufobj - * being changed. - */ -#define DUK_HBUFOBJ_FULL_SLICE(h) \ - (DUK_ASSERT_EXPR((h) != NULL), \ - DUK_ASSERT_EXPR((h)->buf != NULL), \ - ((h)->offset == 0 && (h)->length == DUK_HBUFFER_GET_SIZE((h)->buf))) - -/* Validate that the whole slice [0,length[ is contained in the underlying - * buffer. Caller must ensure 'buf' != NULL. - */ -#define DUK_HBUFOBJ_VALID_SLICE(h) \ - (DUK_ASSERT_EXPR((h) != NULL), \ - DUK_ASSERT_EXPR((h)->buf != NULL), \ - ((h)->offset + (h)->length <= DUK_HBUFFER_GET_SIZE((h)->buf))) - -/* Validate byte read/write for virtual 'offset', i.e. check that the - * offset, taking into account h->offset, is within the underlying - * buffer size. This is a safety check which is needed to ensure - * that even a misconfigured duk_hbufobj never causes memory unsafe - * behavior (e.g. if an underlying dynamic buffer changes after being - * setup). Caller must ensure 'buf' != NULL. - */ -#define DUK_HBUFOBJ_VALID_BYTEOFFSET_INCL(h, off) \ - (DUK_ASSERT_EXPR((h) != NULL), DUK_ASSERT_EXPR((h)->buf != NULL), ((h)->offset + (off) < DUK_HBUFFER_GET_SIZE((h)->buf))) - -#define DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h, off) \ - (DUK_ASSERT_EXPR((h) != NULL), DUK_ASSERT_EXPR((h)->buf != NULL), ((h)->offset + (off) <= DUK_HBUFFER_GET_SIZE((h)->buf))) - -/* Clamp an input byte length (already assumed to be within the nominal - * duk_hbufobj 'length') to the current dynamic buffer limits to yield - * a byte length limit that's safe for memory accesses. This value can - * be invalidated by any side effect because it may trigger a user - * callback that resizes the underlying buffer. - */ -#define DUK_HBUFOBJ_CLAMP_BYTELENGTH(h, len) (DUK_ASSERT_EXPR((h) != NULL), duk_hbufobj_clamp_bytelength((h), (len))) - -/* Typed arrays have virtual indices, ArrayBuffer and DataView do not. */ -#define DUK_HBUFOBJ_HAS_VIRTUAL_INDICES(h) ((h)->is_typedarray) - -struct duk_hbufobj { - /* Shared object part. */ - duk_hobject obj; - - /* Underlying buffer (refcounted), may be NULL. */ - duk_hbuffer *buf; - - /* .buffer reference to an ArrayBuffer, may be NULL. */ - duk_hobject *buf_prop; - - /* Slice and accessor information. - * - * Because the underlying buffer may be dynamic, these may be - * invalidated by the buffer being modified so that both offset - * and length should be validated before every access. Behavior - * when the underlying buffer has changed doesn't need to be clean: - * virtual 'length' doesn't need to be affected, reads can return - * zero/NaN, and writes can be ignored. - * - * Note that a data pointer cannot be precomputed because 'buf' may - * be dynamic and its pointer unstable. - */ - - duk_uint_t offset; /* byte offset to buf */ - duk_uint_t length; /* byte index limit for element access, exclusive */ - duk_uint8_t shift; /* element size shift: - * 0 = u8/i8 - * 1 = u16/i16 - * 2 = u32/i32/float - * 3 = double - */ - duk_uint8_t elem_type; /* element type */ - duk_uint8_t is_typedarray; -}; - -DUK_INTERNAL_DECL duk_uint_t duk_hbufobj_clamp_bytelength(duk_hbufobj *h_bufobj, duk_uint_t len); -DUK_INTERNAL_DECL void duk_hbufobj_push_uint8array_from_plain(duk_hthread *thr, duk_hbuffer *h_buf); -DUK_INTERNAL_DECL void duk_hbufobj_push_validated_read(duk_hthread *thr, - duk_hbufobj *h_bufobj, - duk_uint8_t *p, - duk_small_uint_t elem_size); -DUK_INTERNAL_DECL void duk_hbufobj_validated_write(duk_hthread *thr, - duk_hbufobj *h_bufobj, - duk_uint8_t *p, - duk_small_uint_t elem_size); -DUK_INTERNAL_DECL void duk_hbufobj_promote_plain(duk_hthread *thr, duk_idx_t idx); - -#else /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -/* nothing */ - -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ -#endif /* DUK_HBUFOBJ_H_INCLUDED */ -/* #include duk_hthread.h */ -/* - * Heap thread object representation. - * - * duk_hthread is also the 'context' for public API functions via a - * different typedef. Most API calls operate on the topmost frame - * of the value stack only. - */ - -#if !defined(DUK_HTHREAD_H_INCLUDED) -#define DUK_HTHREAD_H_INCLUDED - -/* - * Stack constants - */ - -/* Initial valstack size, roughly 0.7kiB. */ -#define DUK_VALSTACK_INITIAL_SIZE 96U - -/* Internal extra elements assumed on function entry, always added to - * user-defined 'extra' for e.g. the duk_check_stack() call. - */ -#define DUK_VALSTACK_INTERNAL_EXTRA 32U - -/* Number of elements guaranteed to be user accessible (in addition to call - * arguments) on Duktape/C function entry. This is the major public API - * commitment. - */ -#define DUK_VALSTACK_API_ENTRY_MINIMUM DUK_API_ENTRY_STACK - -/* - * Activation defines - */ - -#define DUK_ACT_FLAG_STRICT (1U << 0) /* function executes in strict mode */ -#define DUK_ACT_FLAG_TAILCALLED (1U << 1) /* activation has tail called one or more times */ -#define DUK_ACT_FLAG_CONSTRUCT (1U << 2) /* function executes as a constructor (called via "new") */ -#define DUK_ACT_FLAG_PREVENT_YIELD (1U << 3) /* activation prevents yield (native call or "new") */ -#define DUK_ACT_FLAG_DIRECT_EVAL (1U << 4) /* activation is a direct eval call */ -#define DUK_ACT_FLAG_CONSTRUCT_PROXY (1U << 5) /* activation is for Proxy 'construct' call, special return value handling */ -#define DUK_ACT_FLAG_BREAKPOINT_ACTIVE (1U << 6) /* activation has active breakpoint(s) */ - -#define DUK_ACT_GET_FUNC(act) ((act)->func) - -/* - * Flags for __FILE__ / __LINE__ registered into tracedata - */ - -#define DUK_TB_FLAG_NOBLAME_FILELINE (1U << 0) /* don't report __FILE__ / __LINE__ as fileName/lineNumber */ - -/* - * Catcher defines - */ - -/* XXX: remove catcher type entirely */ - -/* flags field: LLLLLLFT, L = label (24 bits), F = flags (4 bits), T = type (4 bits) */ -#define DUK_CAT_TYPE_MASK 0x0000000fUL -#define DUK_CAT_TYPE_BITS 4 -#define DUK_CAT_LABEL_MASK 0xffffff00UL -#define DUK_CAT_LABEL_BITS 24 -#define DUK_CAT_LABEL_SHIFT 8 - -#define DUK_CAT_FLAG_CATCH_ENABLED (1U << 4) /* catch part will catch */ -#define DUK_CAT_FLAG_FINALLY_ENABLED (1U << 5) /* finally part will catch */ -#define DUK_CAT_FLAG_CATCH_BINDING_ENABLED (1U << 6) /* request to create catch binding */ -#define DUK_CAT_FLAG_LEXENV_ACTIVE (1U << 7) /* catch or with binding is currently active */ - -#define DUK_CAT_TYPE_UNKNOWN 0 -#define DUK_CAT_TYPE_TCF 1 -#define DUK_CAT_TYPE_LABEL 2 - -#define DUK_CAT_GET_TYPE(c) ((c)->flags & DUK_CAT_TYPE_MASK) -#define DUK_CAT_GET_LABEL(c) (((c)->flags & DUK_CAT_LABEL_MASK) >> DUK_CAT_LABEL_SHIFT) - -#define DUK_CAT_HAS_CATCH_ENABLED(c) ((c)->flags & DUK_CAT_FLAG_CATCH_ENABLED) -#define DUK_CAT_HAS_FINALLY_ENABLED(c) ((c)->flags & DUK_CAT_FLAG_FINALLY_ENABLED) -#define DUK_CAT_HAS_CATCH_BINDING_ENABLED(c) ((c)->flags & DUK_CAT_FLAG_CATCH_BINDING_ENABLED) -#define DUK_CAT_HAS_LEXENV_ACTIVE(c) ((c)->flags & DUK_CAT_FLAG_LEXENV_ACTIVE) - -#define DUK_CAT_SET_CATCH_ENABLED(c) \ - do { \ - (c)->flags |= DUK_CAT_FLAG_CATCH_ENABLED; \ - } while (0) -#define DUK_CAT_SET_FINALLY_ENABLED(c) \ - do { \ - (c)->flags |= DUK_CAT_FLAG_FINALLY_ENABLED; \ - } while (0) -#define DUK_CAT_SET_CATCH_BINDING_ENABLED(c) \ - do { \ - (c)->flags |= DUK_CAT_FLAG_CATCH_BINDING_ENABLED; \ - } while (0) -#define DUK_CAT_SET_LEXENV_ACTIVE(c) \ - do { \ - (c)->flags |= DUK_CAT_FLAG_LEXENV_ACTIVE; \ - } while (0) - -#define DUK_CAT_CLEAR_CATCH_ENABLED(c) \ - do { \ - (c)->flags &= ~DUK_CAT_FLAG_CATCH_ENABLED; \ - } while (0) -#define DUK_CAT_CLEAR_FINALLY_ENABLED(c) \ - do { \ - (c)->flags &= ~DUK_CAT_FLAG_FINALLY_ENABLED; \ - } while (0) -#define DUK_CAT_CLEAR_CATCH_BINDING_ENABLED(c) \ - do { \ - (c)->flags &= ~DUK_CAT_FLAG_CATCH_BINDING_ENABLED; \ - } while (0) -#define DUK_CAT_CLEAR_LEXENV_ACTIVE(c) \ - do { \ - (c)->flags &= ~DUK_CAT_FLAG_LEXENV_ACTIVE; \ - } while (0) - -/* - * Thread defines - */ - -#if defined(DUK_USE_ROM_STRINGS) -#define DUK_HTHREAD_GET_STRING(thr, idx) ((duk_hstring *) DUK_LOSE_CONST(duk_rom_strings_stridx[(idx)])) -#else /* DUK_USE_ROM_STRINGS */ -#if defined(DUK_USE_HEAPPTR16) -#define DUK_HTHREAD_GET_STRING(thr, idx) ((duk_hstring *) DUK_USE_HEAPPTR_DEC16((thr)->heap->heap_udata, (thr)->strs16[(idx)])) -#else -#define DUK_HTHREAD_GET_STRING(thr, idx) ((thr)->strs[(idx)]) -#endif -#endif /* DUK_USE_ROM_STRINGS */ - -/* values for the state field */ -#define DUK_HTHREAD_STATE_INACTIVE 1 /* thread not currently running */ -#define DUK_HTHREAD_STATE_RUNNING 2 /* thread currently running (only one at a time) */ -#define DUK_HTHREAD_STATE_RESUMED 3 /* thread resumed another thread (active but not running) */ -#define DUK_HTHREAD_STATE_YIELDED 4 /* thread has yielded */ -#define DUK_HTHREAD_STATE_TERMINATED 5 /* thread has terminated */ - -/* Executor interrupt default interval when nothing else requires a - * smaller value. The default interval must be small enough to allow - * for reasonable execution timeout checking but large enough to keep - * impact on execution performance low. - */ -#if defined(DUK_USE_INTERRUPT_COUNTER) -#define DUK_HTHREAD_INTCTR_DEFAULT (256L * 1024L) -#endif - -/* - * Assert context is valid: non-NULL pointer, fields look sane. - * - * This is used by public API call entrypoints to catch invalid 'ctx' pointers - * as early as possible; invalid 'ctx' pointers cause very odd and difficult to - * diagnose behavior so it's worth checking even when the check is not 100%. - */ - -#if defined(DUK_USE_ASSERTIONS) -/* Assertions for internals. */ -DUK_INTERNAL_DECL void duk_hthread_assert_valid(duk_hthread *thr); -#define DUK_HTHREAD_ASSERT_VALID(thr) \ - do { \ - duk_hthread_assert_valid((thr)); \ - } while (0) - -/* Assertions for public API calls; a bit stronger. */ -DUK_INTERNAL_DECL void duk_ctx_assert_valid(duk_hthread *thr); -#define DUK_CTX_ASSERT_VALID(thr) \ - do { \ - duk_ctx_assert_valid((thr)); \ - } while (0) -#else -#define DUK_HTHREAD_ASSERT_VALID(thr) \ - do { \ - } while (0) -#define DUK_CTX_ASSERT_VALID(thr) \ - do { \ - } while (0) -#endif - -/* Assertions for API call entry specifically. Checks 'ctx' but also may - * check internal state (e.g. not in a debugger transport callback). - */ -#define DUK_ASSERT_API_ENTRY(thr) \ - do { \ - DUK_CTX_ASSERT_VALID((thr)); \ - DUK_ASSERT((thr)->heap != NULL); \ - DUK_ASSERT((thr)->heap->dbg_calling_transport == 0); \ - } while (0) - -/* - * Assertion helpers. - */ - -#define DUK_ASSERT_STRIDX_VALID(val) DUK_ASSERT((duk_uint_t) (val) < DUK_HEAP_NUM_STRINGS) - -#define DUK_ASSERT_BIDX_VALID(val) DUK_ASSERT((duk_uint_t) (val) < DUK_NUM_BUILTINS) - -/* - * Misc - */ - -/* Fast access to 'this' binding. Assumes there's a call in progress. */ -#define DUK_HTHREAD_THIS_PTR(thr) \ - (DUK_ASSERT_EXPR((thr) != NULL), DUK_ASSERT_EXPR((thr)->valstack_bottom > (thr)->valstack), (thr)->valstack_bottom - 1) - -/* - * Struct defines - */ - -/* Fields are ordered for alignment/packing. */ -struct duk_activation { - duk_tval tv_func; /* borrowed: full duk_tval for function being executed; for lightfuncs */ - duk_hobject *func; /* borrowed: function being executed; for bound function calls, this is the final, real function, NULL - for lightfuncs */ - duk_activation *parent; /* previous (parent) activation (or NULL if none) */ - duk_hobject *var_env; /* current variable environment (may be NULL if delayed) */ - duk_hobject *lex_env; /* current lexical environment (may be NULL if delayed) */ - duk_catcher *cat; /* current catcher (or NULL) */ - -#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY) - /* Previous value of 'func' caller, restored when unwound. Only in use - * when 'func' is non-strict. - */ - duk_hobject *prev_caller; -#endif - - duk_instr_t *curr_pc; /* next instruction to execute (points to 'func' bytecode, stable pointer), NULL for native calls */ - - /* bottom_byteoff and retval_byteoff are only used for book-keeping - * of ECMAScript-initiated calls, to allow returning to an ECMAScript - * function properly. - */ - - /* Bottom of valstack for this activation, used to reset - * valstack_bottom on return; offset is absolute. There's - * no need to track 'top' because native call handling deals - * with that using locals, and for ECMAScript returns 'nregs' - * indicates the necessary top. - */ - duk_size_t bottom_byteoff; - - /* Return value when returning to this activation (points to caller - * reg, not callee reg); offset is absolute (only set if activation is - * not topmost). - * - * Note: bottom_byteoff is always set, while retval_byteoff is only - * applicable for activations below the topmost one. Currently - * retval_byteoff for the topmost activation is considered garbage - * (and it not initialized on entry or cleared on return; may contain - * previous or garbage values). - */ - duk_size_t retval_byteoff; - - /* Current 'this' binding is the value just below bottom. - * Previously, 'this' binding was handled with an index to the - * (calling) valstack. This works for everything except tail - * calls, which must not "accumulate" valstack temps. - */ - - /* Value stack reserve (valstack_end) byte offset to be restored - * when returning to this activation. Only used by the bytecode - * executor. - */ - duk_size_t reserve_byteoff; - -#if defined(DUK_USE_DEBUGGER_SUPPORT) - duk_uint32_t prev_line; /* needed for stepping */ -#endif - - duk_small_uint_t flags; -}; - -struct duk_catcher { - duk_catcher *parent; /* previous (parent) catcher (or NULL if none) */ - duk_hstring *h_varname; /* borrowed reference to catch variable name (or NULL if none) */ - /* (reference is valid as long activation exists) */ - duk_instr_t *pc_base; /* resume execution from pc_base or pc_base+1 (points to 'func' bytecode, stable pointer) */ - duk_size_t idx_base; /* idx_base and idx_base+1 get completion value and type */ - duk_uint32_t flags; /* type and control flags, label number */ - /* XXX: could pack 'flags' and 'idx_base' to same value in practice, - * on 32-bit targets this would make duk_catcher 16 bytes. - */ -}; - -struct duk_hthread { - /* Shared object part */ - duk_hobject obj; - - /* Pointer to bytecode executor's 'curr_pc' variable. Used to copy - * the current PC back into the topmost activation when activation - * state is about to change (or "syncing" is otherwise needed). This - * is rather awkward but important for performance, see execution.rst. - */ - duk_instr_t **ptr_curr_pc; - - /* Backpointers. */ - duk_heap *heap; - - /* Current strictness flag: affects API calls. */ - duk_uint8_t strict; - - /* Thread state. */ - duk_uint8_t state; - duk_uint8_t unused1; - duk_uint8_t unused2; - - /* XXX: Valstack and callstack are currently assumed to have non-NULL - * pointers. Relaxing this would not lead to big benefits (except - * perhaps for terminated threads). - */ - - /* Value stack: these are expressed as pointers for faster stack - * manipulation. [valstack,valstack_top[ is GC-reachable, - * [valstack_top,valstack_alloc_end[ is not GC-reachable but kept - * initialized as 'undefined'. [valstack,valstack_end[ is the - * guaranteed/reserved space and the valstack cannot be resized to - * a smaller size. [valstack_end,valstack_alloc_end[ is currently - * allocated slack that can be used to grow the current guaranteed - * space but may be shrunk away without notice. - * - * - * <----------------------- guaranteed ---> - * <---- slack ---> - * <--- frame ---> - * .-------------+=============+----------+--------------. - * |xxxxxxxxxxxxx|yyyyyyyyyyyyy|uuuuuuuuuu|uuuuuuuuuuuuuu| - * `-------------+=============+----------+--------------' - * - * ^ ^ ^ ^ ^ - * | | | | | - * valstack bottom top end alloc_end - * - * xxx = arbitrary values, below current frame - * yyy = arbitrary values, inside current frame - * uuu = outside active value stack, initialized to 'undefined' - */ - duk_tval *valstack; /* start of valstack allocation */ - duk_tval *valstack_end; /* end of valstack reservation/guarantee (exclusive) */ - duk_tval *valstack_alloc_end; /* end of valstack allocation */ - duk_tval *valstack_bottom; /* bottom of current frame */ - duk_tval *valstack_top; /* top of current frame (exclusive) */ - - /* Call stack, represented as a linked list starting from the current - * activation (or NULL if nothing is active). - */ - duk_activation *callstack_curr; /* current activation (or NULL if none) */ - duk_size_t callstack_top; /* number of activation records in callstack (0 if none) */ - duk_size_t callstack_preventcount; /* number of activation records in callstack preventing a yield */ - - /* Yield/resume book-keeping. */ - duk_hthread *resumer; /* who resumed us (if any) */ - - /* Current compiler state (if any), used for augmenting SyntaxErrors. */ - duk_compiler_ctx *compile_ctx; - -#if defined(DUK_USE_INTERRUPT_COUNTER) - /* Interrupt counter for triggering a slow path check for execution - * timeout, debugger interaction such as breakpoints, etc. The value - * is valid for the current running thread, and both the init and - * counter values are copied whenever a thread switch occurs. It's - * important for the counter to be conveniently accessible for the - * bytecode executor inner loop for performance reasons. - */ - duk_int_t interrupt_counter; /* countdown state */ - duk_int_t interrupt_init; /* start value for current countdown */ -#endif - - /* Builtin-objects; may or may not be shared with other threads, - * threads existing in different "compartments" will have different - * built-ins. Must be stored on a per-thread basis because there - * is no intermediate structure for a thread group / compartment. - * This takes quite a lot of space, currently 43x4 = 172 bytes on - * 32-bit platforms. - * - * In some cases the builtins array could be ROM based, but it's - * sometimes edited (e.g. for sandboxing) so it's better to keep - * this array in RAM. - */ - duk_hobject *builtins[DUK_NUM_BUILTINS]; - - /* Convenience copies from heap/vm for faster access. */ -#if defined(DUK_USE_ROM_STRINGS) - /* No field needed when strings are in ROM. */ -#else -#if defined(DUK_USE_HEAPPTR16) - duk_uint16_t *strs16; -#else - duk_hstring **strs; -#endif -#endif -}; - -/* - * Prototypes - */ - -DUK_INTERNAL_DECL void duk_hthread_copy_builtin_objects(duk_hthread *thr_from, duk_hthread *thr_to); -DUK_INTERNAL_DECL void duk_hthread_create_builtin_objects(duk_hthread *thr); -DUK_INTERNAL_DECL duk_bool_t duk_hthread_init_stacks(duk_heap *heap, duk_hthread *thr); -DUK_INTERNAL_DECL void duk_hthread_terminate(duk_hthread *thr); - -DUK_INTERNAL_DECL duk_activation *duk_hthread_activation_alloc(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_hthread_activation_free(duk_hthread *thr, duk_activation *act); -DUK_INTERNAL_DECL void duk_hthread_activation_unwind_norz(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_hthread_activation_unwind_reuse_norz(duk_hthread *thr); -DUK_INTERNAL_DECL duk_activation *duk_hthread_get_activation_for_level(duk_hthread *thr, duk_int_t level); - -DUK_INTERNAL_DECL duk_catcher *duk_hthread_catcher_alloc(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_hthread_catcher_free(duk_hthread *thr, duk_catcher *cat); -DUK_INTERNAL_DECL void duk_hthread_catcher_unwind_norz(duk_hthread *thr, duk_activation *act); -DUK_INTERNAL_DECL void duk_hthread_catcher_unwind_nolexenv_norz(duk_hthread *thr, duk_activation *act); - -#if defined(DUK_USE_FINALIZER_TORTURE) -DUK_INTERNAL_DECL void duk_hthread_valstack_torture_realloc(duk_hthread *thr); -#endif - -DUK_INTERNAL_DECL void *duk_hthread_get_valstack_ptr(duk_heap *heap, void *ud); /* indirect allocs */ - -#if defined(DUK_USE_DEBUGGER_SUPPORT) -DUK_INTERNAL_DECL duk_uint_fast32_t duk_hthread_get_act_curr_pc(duk_hthread *thr, duk_activation *act); -#endif -DUK_INTERNAL_DECL duk_uint_fast32_t duk_hthread_get_act_prev_pc(duk_hthread *thr, duk_activation *act); -DUK_INTERNAL_DECL void duk_hthread_sync_currpc(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_hthread_sync_and_null_currpc(duk_hthread *thr); - -#endif /* DUK_HTHREAD_H_INCLUDED */ -/* #include duk_harray.h */ -/* - * Array object representation, used for actual Array instances. - * - * All objects with the exotic array behavior (which must coincide with having - * internal class array) MUST be duk_harrays. No other object can be a - * duk_harray. However, duk_harrays may not always have an array part. - */ - -#if !defined(DUK_HARRAY_H_INCLUDED) -#define DUK_HARRAY_H_INCLUDED - -#if defined(DUK_USE_ASSERTIONS) -DUK_INTERNAL_DECL void duk_harray_assert_valid(duk_harray *h); -#define DUK_HARRAY_ASSERT_VALID(h) \ - do { \ - duk_harray_assert_valid((h)); \ - } while (0) -#else -#define DUK_HARRAY_ASSERT_VALID(h) \ - do { \ - } while (0) -#endif - -#define DUK_HARRAY_LENGTH_WRITABLE(h) (!(h)->length_nonwritable) -#define DUK_HARRAY_LENGTH_NONWRITABLE(h) ((h)->length_nonwritable) -#define DUK_HARRAY_SET_LENGTH_WRITABLE(h) \ - do { \ - (h)->length_nonwritable = 0; \ - } while (0) -#define DUK_HARRAY_SET_LENGTH_NONWRITABLE(h) \ - do { \ - (h)->length_nonwritable = 1; \ - } while (0) - -struct duk_harray { - /* Shared object part. */ - duk_hobject obj; - - /* Array .length. - * - * At present Array .length may be smaller, equal, or even larger - * than the allocated underlying array part. Fast path code must - * always take this into account carefully. - */ - duk_uint32_t length; - - /* Array .length property attributes. The property is always - * non-enumerable and non-configurable. It's initially writable - * but per Object.defineProperty() rules it can be made non-writable - * even if it is non-configurable. Thus we need to track the - * writability explicitly. - * - * XXX: this field to be eliminated and moved into duk_hobject - * flags field to save space. - */ - duk_bool_t length_nonwritable; -}; - -#endif /* DUK_HARRAY_H_INCLUDED */ -/* #include duk_henv.h */ -/* - * Environment object representation. - */ - -#if !defined(DUK_HENV_H_INCLUDED) -#define DUK_HENV_H_INCLUDED - -#if defined(DUK_USE_ASSERTIONS) -DUK_INTERNAL_DECL void duk_hdecenv_assert_valid(duk_hdecenv *h); -DUK_INTERNAL_DECL void duk_hobjenv_assert_valid(duk_hobjenv *h); -#define DUK_HDECENV_ASSERT_VALID(h) \ - do { \ - duk_hdecenv_assert_valid((h)); \ - } while (0) -#define DUK_HOBJENV_ASSERT_VALID(h) \ - do { \ - duk_hobjenv_assert_valid((h)); \ - } while (0) -#else -#define DUK_HDECENV_ASSERT_VALID(h) \ - do { \ - } while (0) -#define DUK_HOBJENV_ASSERT_VALID(h) \ - do { \ - } while (0) -#endif - -struct duk_hdecenv { - /* Shared object part. */ - duk_hobject obj; - - /* These control variables provide enough information to access live - * variables for a closure that is still open. If thread == NULL, - * the record is closed and the identifiers are in the property table. - */ - duk_hthread *thread; - duk_hobject *varmap; - duk_size_t regbase_byteoff; -}; - -struct duk_hobjenv { - /* Shared object part. */ - duk_hobject obj; - - /* Target object and 'this' binding for object binding. */ - duk_hobject *target; - - /* The 'target' object is used as a this binding in only some object - * environments. For example, the global environment does not provide - * a this binding, but a with statement does. - */ - duk_bool_t has_this; -}; - -#endif /* DUK_HENV_H_INCLUDED */ -/* #include duk_hbuffer.h */ -/* - * Heap buffer representation. - * - * Heap allocated user data buffer which is either: - * - * 1. A fixed size buffer (data follows header statically) - * 2. A dynamic size buffer (data pointer follows header) - * - * The data pointer for a variable size buffer of zero size may be NULL. - */ - -#if !defined(DUK_HBUFFER_H_INCLUDED) -#define DUK_HBUFFER_H_INCLUDED - -/* - * Flags - * - * Fixed buffer: 0 - * Dynamic buffer: DUK_HBUFFER_FLAG_DYNAMIC - * External buffer: DUK_HBUFFER_FLAG_DYNAMIC | DUK_HBUFFER_FLAG_EXTERNAL - */ - -#define DUK_HBUFFER_FLAG_DYNAMIC DUK_HEAPHDR_USER_FLAG(0) /* buffer is behind a pointer, dynamic or external */ -#define DUK_HBUFFER_FLAG_EXTERNAL DUK_HEAPHDR_USER_FLAG(1) /* buffer pointer is to an externally allocated buffer */ - -#define DUK_HBUFFER_HAS_DYNAMIC(x) DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HBUFFER_FLAG_DYNAMIC) -#define DUK_HBUFFER_HAS_EXTERNAL(x) DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HBUFFER_FLAG_EXTERNAL) - -#define DUK_HBUFFER_SET_DYNAMIC(x) DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HBUFFER_FLAG_DYNAMIC) -#define DUK_HBUFFER_SET_EXTERNAL(x) DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HBUFFER_FLAG_EXTERNAL) - -#define DUK_HBUFFER_CLEAR_DYNAMIC(x) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HBUFFER_FLAG_DYNAMIC) -#define DUK_HBUFFER_CLEAR_EXTERNAL(x) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HBUFFER_FLAG_EXTERNAL) - -/* - * Misc defines - */ - -/* Impose a maximum buffer length for now. Restricted artificially to - * ensure resize computations or adding a heap header length won't - * overflow size_t and that a signed duk_int_t can hold a buffer - * length. The limit should be synchronized with DUK_HSTRING_MAX_BYTELEN. - */ - -#if defined(DUK_USE_BUFLEN16) -#define DUK_HBUFFER_MAX_BYTELEN (0x0000ffffUL) -#else -/* Intentionally not 0x7fffffffUL; at least JSON code expects that - * 2*len + 2 fits in 32 bits. - */ -#define DUK_HBUFFER_MAX_BYTELEN (0x7ffffffeUL) -#endif - -/* - * Field access - */ - -#if defined(DUK_USE_BUFLEN16) -/* size stored in duk_heaphdr unused flag bits */ -#define DUK_HBUFFER_GET_SIZE(x) ((x)->hdr.h_flags >> 16) -#define DUK_HBUFFER_SET_SIZE(x, v) \ - do { \ - duk_size_t duk__v; \ - duk__v = (v); \ - DUK_ASSERT(duk__v <= 0xffffUL); \ - (x)->hdr.h_flags = ((x)->hdr.h_flags & 0x0000ffffUL) | (((duk_uint32_t) duk__v) << 16); \ - } while (0) -#define DUK_HBUFFER_ADD_SIZE(x, dv) \ - do { \ - (x)->hdr.h_flags += ((dv) << 16); \ - } while (0) -#define DUK_HBUFFER_SUB_SIZE(x, dv) \ - do { \ - (x)->hdr.h_flags -= ((dv) << 16); \ - } while (0) -#else -#define DUK_HBUFFER_GET_SIZE(x) (((duk_hbuffer *) (x))->size) -#define DUK_HBUFFER_SET_SIZE(x, v) \ - do { \ - ((duk_hbuffer *) (x))->size = (v); \ - } while (0) -#define DUK_HBUFFER_ADD_SIZE(x, dv) \ - do { \ - (x)->size += (dv); \ - } while (0) -#define DUK_HBUFFER_SUB_SIZE(x, dv) \ - do { \ - (x)->size -= (dv); \ - } while (0) -#endif - -#define DUK_HBUFFER_FIXED_GET_SIZE(x) DUK_HBUFFER_GET_SIZE((duk_hbuffer *) (x)) -#define DUK_HBUFFER_FIXED_SET_SIZE(x, v) DUK_HBUFFER_SET_SIZE((duk_hbuffer *) (x)) - -#define DUK_HBUFFER_DYNAMIC_GET_SIZE(x) DUK_HBUFFER_GET_SIZE((duk_hbuffer *) (x)) -#define DUK_HBUFFER_DYNAMIC_SET_SIZE(x, v) DUK_HBUFFER_SET_SIZE((duk_hbuffer *) (x), (v)) -#define DUK_HBUFFER_DYNAMIC_ADD_SIZE(x, dv) DUK_HBUFFER_ADD_SIZE((duk_hbuffer *) (x), (dv)) -#define DUK_HBUFFER_DYNAMIC_SUB_SIZE(x, dv) DUK_HBUFFER_SUB_SIZE((duk_hbuffer *) (x), (dv)) - -#define DUK_HBUFFER_EXTERNAL_GET_SIZE(x) DUK_HBUFFER_GET_SIZE((duk_hbuffer *) (x)) -#define DUK_HBUFFER_EXTERNAL_SET_SIZE(x, v) DUK_HBUFFER_SET_SIZE((duk_hbuffer *) (x), (v)) - -#define DUK_HBUFFER_FIXED_GET_DATA_PTR(heap, x) ((duk_uint8_t *) (((duk_hbuffer_fixed *) (void *) (x)) + 1)) - -#if defined(DUK_USE_HEAPPTR16) -#define DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(heap, x) \ - ((void *) DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, ((duk_heaphdr *) (x))->h_extra16)) -#define DUK_HBUFFER_DYNAMIC_SET_DATA_PTR(heap, x, v) \ - do { \ - ((duk_heaphdr *) (x))->h_extra16 = DUK_USE_HEAPPTR_ENC16((heap)->heap_udata, (void *) (v)); \ - } while (0) -#define DUK_HBUFFER_DYNAMIC_SET_DATA_PTR_NULL(heap, x) \ - do { \ - ((duk_heaphdr *) (x))->h_extra16 = 0; /* assume 0 <=> NULL */ \ - } while (0) -#else -#define DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(heap, x) ((x)->curr_alloc) -#define DUK_HBUFFER_DYNAMIC_SET_DATA_PTR(heap, x, v) \ - do { \ - (x)->curr_alloc = (void *) (v); \ - } while (0) -#define DUK_HBUFFER_DYNAMIC_SET_DATA_PTR_NULL(heap, x) \ - do { \ - (x)->curr_alloc = (void *) NULL; \ - } while (0) -#endif - -/* No pointer compression because pointer is potentially outside of - * Duktape heap. - */ -#if defined(DUK_USE_HEAPPTR16) -#define DUK_HBUFFER_EXTERNAL_GET_DATA_PTR(heap, x) ((void *) (x)->curr_alloc) -#define DUK_HBUFFER_EXTERNAL_SET_DATA_PTR(heap, x, v) \ - do { \ - (x)->curr_alloc = (void *) (v); \ - } while (0) -#define DUK_HBUFFER_EXTERNAL_SET_DATA_PTR_NULL(heap, x) \ - do { \ - (x)->curr_alloc = (void *) NULL; \ - } while (0) -#else -#define DUK_HBUFFER_EXTERNAL_GET_DATA_PTR(heap, x) ((void *) (x)->curr_alloc) -#define DUK_HBUFFER_EXTERNAL_SET_DATA_PTR(heap, x, v) \ - do { \ - (x)->curr_alloc = (void *) (v); \ - } while (0) -#define DUK_HBUFFER_EXTERNAL_SET_DATA_PTR_NULL(heap, x) \ - do { \ - (x)->curr_alloc = (void *) NULL; \ - } while (0) -#endif - -/* Get a pointer to the current buffer contents (matching current allocation - * size). May be NULL for zero size dynamic/external buffer. - */ -#if defined(DUK_USE_HEAPPTR16) -#define DUK_HBUFFER_GET_DATA_PTR(heap, x) \ - (DUK_HBUFFER_HAS_DYNAMIC((x)) ? \ - (DUK_HBUFFER_HAS_EXTERNAL((x)) ? DUK_HBUFFER_EXTERNAL_GET_DATA_PTR((heap), (duk_hbuffer_external *) (x)) : \ - DUK_HBUFFER_DYNAMIC_GET_DATA_PTR((heap), (duk_hbuffer_dynamic *) (x))) : \ - DUK_HBUFFER_FIXED_GET_DATA_PTR((heap), (duk_hbuffer_fixed *) (void *) (x))) -#else -/* Without heap pointer compression duk_hbuffer_dynamic and duk_hbuffer_external - * have the same layout so checking for fixed vs. dynamic (or external) is enough. - */ -#define DUK_HBUFFER_GET_DATA_PTR(heap, x) \ - (DUK_HBUFFER_HAS_DYNAMIC((x)) ? DUK_HBUFFER_DYNAMIC_GET_DATA_PTR((heap), (duk_hbuffer_dynamic *) (x)) : \ - DUK_HBUFFER_FIXED_GET_DATA_PTR((heap), (duk_hbuffer_fixed *) (void *) (x))) -#endif - -/* Validity assert. */ -#if defined(DUK_USE_ASSERTIONS) -DUK_INTERNAL_DECL void duk_hbuffer_assert_valid(duk_hbuffer *h); -#define DUK_HBUFFER_ASSERT_VALID(h) \ - do { \ - duk_hbuffer_assert_valid((h)); \ - } while (0) -#else -#define DUK_HBUFFER_ASSERT_VALID(h) \ - do { \ - } while (0) -#endif - -/* - * Structs - */ - -/* Shared prefix for all buffer types. */ -struct duk_hbuffer { - duk_heaphdr hdr; - - /* It's not strictly necessary to track the current size, but - * it is useful for writing robust native code. - */ - - /* Current size. */ -#if defined(DUK_USE_BUFLEN16) - /* Stored in duk_heaphdr unused flags. */ -#else - duk_size_t size; -#endif - - /* - * Data following the header depends on the DUK_HBUFFER_FLAG_DYNAMIC - * flag. - * - * If the flag is clear (the buffer is a fixed size one), the buffer - * data follows the header directly, consisting of 'size' bytes. - * - * If the flag is set, the actual buffer is allocated separately, and - * a few control fields follow the header. Specifically: - * - * - a "void *" pointing to the current allocation - * - a duk_size_t indicating the full allocated size (always >= 'size') - * - * If DUK_HBUFFER_FLAG_EXTERNAL is set, the buffer has been allocated - * by user code, so that Duktape won't be able to resize it and won't - * free it. This allows buffers to point to e.g. an externally - * allocated structure such as a frame buffer. - * - * Unlike strings, no terminator byte (NUL) is guaranteed after the - * data. This would be convenient, but would pad aligned user buffers - * unnecessarily upwards in size. For instance, if user code requested - * a 64-byte dynamic buffer, 65 bytes would actually be allocated which - * would then potentially round upwards to perhaps 68 or 72 bytes. - */ -}; - -/* Fixed buffer; data follows struct, with proper alignment guaranteed by - * struct size. - */ -#if (DUK_USE_ALIGN_BY == 8) && defined(DUK_USE_PACK_MSVC_PRAGMA) -#pragma pack(push, 8) -#endif -struct duk_hbuffer_fixed { - /* A union is used here as a portable struct size / alignment trick: - * by adding a 32-bit or a 64-bit (unused) union member, the size of - * the struct is effectively forced to be a multiple of 4 or 8 bytes - * (respectively) without increasing the size of the struct unless - * necessary. - */ - union { - struct { - duk_heaphdr hdr; -#if defined(DUK_USE_BUFLEN16) - /* Stored in duk_heaphdr unused flags. */ -#else - duk_size_t size; -#endif - } s; -#if (DUK_USE_ALIGN_BY == 4) - duk_uint32_t dummy_for_align4; -#elif (DUK_USE_ALIGN_BY == 8) - duk_double_t dummy_for_align8_1; -#if defined(DUK_USE_64BIT_OPS) - duk_uint64_t dummy_for_align8_2; -#endif -#elif (DUK_USE_ALIGN_BY == 1) - /* no extra padding */ -#else -#error invalid DUK_USE_ALIGN_BY -#endif - } u; - - /* - * Data follows the struct header. The struct size is padded by the - * compiler based on the struct members. This guarantees that the - * buffer data will be aligned-by-4 but not necessarily aligned-by-8. - * - * On platforms where alignment does not matter, the struct padding - * could be removed (if there is any). On platforms where alignment - * by 8 is required, the struct size must be forced to be a multiple - * of 8 by some means. Without it, some user code may break, and also - * Duktape itself breaks (e.g. the compiler stores duk_tvals in a - * dynamic buffer). - */ -} -#if (DUK_USE_ALIGN_BY == 8) && defined(DUK_USE_PACK_GCC_ATTR) -__attribute__((aligned(8))) -#elif (DUK_USE_ALIGN_BY == 8) && defined(DUK_USE_PACK_CLANG_ATTR) -__attribute__((aligned(8))) -#endif -; -#if (DUK_USE_ALIGN_BY == 8) && defined(DUK_USE_PACK_MSVC_PRAGMA) -#pragma pack(pop) -#endif - -/* Dynamic buffer with 'curr_alloc' pointing to a dynamic area allocated using - * heap allocation primitives. Also used for external buffers when low memory - * options are not used. - */ -struct duk_hbuffer_dynamic { - duk_heaphdr hdr; - -#if defined(DUK_USE_BUFLEN16) - /* Stored in duk_heaphdr unused flags. */ -#else - duk_size_t size; -#endif - -#if defined(DUK_USE_HEAPPTR16) - /* Stored in duk_heaphdr h_extra16. */ -#else - void *curr_alloc; /* may be NULL if alloc_size == 0 */ -#endif - - /* - * Allocation size for 'curr_alloc' is alloc_size. There is no - * automatic NUL terminator for buffers (see above for rationale). - * - * 'curr_alloc' is explicitly allocated with heap allocation - * primitives and will thus always have alignment suitable for - * e.g. duk_tval and an IEEE double. - */ -}; - -/* External buffer with 'curr_alloc' managed by user code and pointing to an - * arbitrary address. When heap pointer compression is not used, this struct - * has the same layout as duk_hbuffer_dynamic. - */ -struct duk_hbuffer_external { - duk_heaphdr hdr; - -#if defined(DUK_USE_BUFLEN16) - /* Stored in duk_heaphdr unused flags. */ -#else - duk_size_t size; -#endif - - /* Cannot be compressed as a heap pointer because may point to - * an arbitrary address. - */ - void *curr_alloc; /* may be NULL if alloc_size == 0 */ -}; - -/* - * Prototypes - */ - -DUK_INTERNAL_DECL duk_hbuffer *duk_hbuffer_alloc(duk_heap *heap, duk_size_t size, duk_small_uint_t flags, void **out_bufdata); -DUK_INTERNAL_DECL void *duk_hbuffer_get_dynalloc_ptr(duk_heap *heap, void *ud); /* indirect allocs */ - -/* dynamic buffer ops */ -DUK_INTERNAL_DECL void duk_hbuffer_resize(duk_hthread *thr, duk_hbuffer_dynamic *buf, duk_size_t new_size); -DUK_INTERNAL_DECL void duk_hbuffer_reset(duk_hthread *thr, duk_hbuffer_dynamic *buf); - -#endif /* DUK_HBUFFER_H_INCLUDED */ -/* #include duk_hproxy.h */ -/* - * Proxy object representation. - */ - -#if !defined(DUK_HPROXY_H_INCLUDED) -#define DUK_HPROXY_H_INCLUDED - -#if defined(DUK_USE_ASSERTIONS) -DUK_INTERNAL_DECL void duk_hproxy_assert_valid(duk_hproxy *h); -#define DUK_HPROXY_ASSERT_VALID(h) \ - do { \ - duk_hproxy_assert_valid((h)); \ - } while (0) -#else -#define DUK_HPROXY_ASSERT_VALID(h) \ - do { \ - } while (0) -#endif - -struct duk_hproxy { - /* Shared object part. */ - duk_hobject obj; - - /* Proxy target object. */ - duk_hobject *target; - - /* Proxy handlers (traps). */ - duk_hobject *handler; -}; - -#endif /* DUK_HPROXY_H_INCLUDED */ -/* #include duk_heap.h */ -/* - * Heap structure. - * - * Heap contains allocated heap objects, interned strings, and built-in - * strings for one or more threads. - */ - -#if !defined(DUK_HEAP_H_INCLUDED) -#define DUK_HEAP_H_INCLUDED - -/* alloc function typedefs in duktape.h */ - -/* - * Heap flags - */ - -#define DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED \ - (1U << 0) /* mark-and-sweep marking reached a recursion limit and must use multi-pass marking */ -#define DUK_HEAP_FLAG_INTERRUPT_RUNNING (1U << 1) /* executor interrupt running (used to avoid nested interrupts) */ -#define DUK_HEAP_FLAG_FINALIZER_NORESCUE (1U << 2) /* heap destruction ongoing, finalizer rescue no longer possible */ -#define DUK_HEAP_FLAG_DEBUGGER_PAUSED (1U << 3) /* debugger is paused: talk with debug client until step/resume */ - -#define DUK__HEAP_HAS_FLAGS(heap, bits) ((heap)->flags & (bits)) -#define DUK__HEAP_SET_FLAGS(heap, bits) \ - do { \ - (heap)->flags |= (bits); \ - } while (0) -#define DUK__HEAP_CLEAR_FLAGS(heap, bits) \ - do { \ - (heap)->flags &= ~(bits); \ - } while (0) - -#define DUK_HEAP_HAS_MARKANDSWEEP_RECLIMIT_REACHED(heap) DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED) -#define DUK_HEAP_HAS_INTERRUPT_RUNNING(heap) DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_INTERRUPT_RUNNING) -#define DUK_HEAP_HAS_FINALIZER_NORESCUE(heap) DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_FINALIZER_NORESCUE) -#define DUK_HEAP_HAS_DEBUGGER_PAUSED(heap) DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_DEBUGGER_PAUSED) - -#define DUK_HEAP_SET_MARKANDSWEEP_RECLIMIT_REACHED(heap) DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED) -#define DUK_HEAP_SET_INTERRUPT_RUNNING(heap) DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_INTERRUPT_RUNNING) -#define DUK_HEAP_SET_FINALIZER_NORESCUE(heap) DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_FINALIZER_NORESCUE) -#define DUK_HEAP_SET_DEBUGGER_PAUSED(heap) DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_DEBUGGER_PAUSED) - -#define DUK_HEAP_CLEAR_MARKANDSWEEP_RECLIMIT_REACHED(heap) \ - DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED) -#define DUK_HEAP_CLEAR_INTERRUPT_RUNNING(heap) DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_INTERRUPT_RUNNING) -#define DUK_HEAP_CLEAR_FINALIZER_NORESCUE(heap) DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_FINALIZER_NORESCUE) -#define DUK_HEAP_CLEAR_DEBUGGER_PAUSED(heap) DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_DEBUGGER_PAUSED) - -/* - * Longjmp types, also double as identifying continuation type for a rethrow (in 'finally') - */ - -#define DUK_LJ_TYPE_UNKNOWN 0 /* unused */ -#define DUK_LJ_TYPE_THROW 1 /* value1 -> error object */ -#define DUK_LJ_TYPE_YIELD 2 /* value1 -> yield value, iserror -> error / normal */ -#define DUK_LJ_TYPE_RESUME 3 /* value1 -> resume value, value2 -> resumee thread, iserror -> error/normal */ -#define DUK_LJ_TYPE_BREAK 4 /* value1 -> label number, pseudo-type to indicate a break continuation (for ENDFIN) */ -#define DUK_LJ_TYPE_CONTINUE 5 /* value1 -> label number, pseudo-type to indicate a continue continuation (for ENDFIN) */ -#define DUK_LJ_TYPE_RETURN 6 /* value1 -> return value, pseudo-type to indicate a return continuation (for ENDFIN) */ -#define DUK_LJ_TYPE_NORMAL 7 /* no value, pseudo-type to indicate a normal continuation (for ENDFIN) */ - -/* - * Mark-and-sweep flags - * - * These are separate from heap level flags now but could be merged. - * The heap structure only contains a 'base mark-and-sweep flags' - * field and the GC caller can impose further flags. - */ - -/* Emergency mark-and-sweep: try extra hard, even at the cost of - * performance. - */ -#define DUK_MS_FLAG_EMERGENCY (1U << 0) - -/* Postpone rescue decisions for reachable objects with FINALIZED set. - * Used during finalize_list processing to avoid incorrect rescue - * decisions due to finalize_list being a reachability root. - */ -#define DUK_MS_FLAG_POSTPONE_RESCUE (1U << 1) - -/* Don't compact objects; needed during object property table resize - * to prevent a recursive resize. It would suffice to protect only the - * current object being resized, but this is not yet implemented. - */ -#define DUK_MS_FLAG_NO_OBJECT_COMPACTION (1U << 2) - -/* - * Thread switching - * - * To switch heap->curr_thread, use the macro below so that interrupt counters - * get updated correctly. The macro allows a NULL target thread because that - * happens e.g. in call handling. - */ - -#if defined(DUK_USE_INTERRUPT_COUNTER) -#define DUK_HEAP_SWITCH_THREAD(heap, newthr) duk_heap_switch_thread((heap), (newthr)) -#else -#define DUK_HEAP_SWITCH_THREAD(heap, newthr) \ - do { \ - (heap)->curr_thread = (newthr); \ - } while (0) -#endif - -/* - * Stats - */ - -#if defined(DUK_USE_DEBUG) -#define DUK_STATS_INC(heap, fieldname) \ - do { \ - (heap)->fieldname += 1; \ - } while (0) -#else -#define DUK_STATS_INC(heap, fieldname) \ - do { \ - } while (0) -#endif - -/* - * Other heap related defines - */ - -/* Mark-and-sweep interval is relative to combined count of objects and - * strings kept in the heap during the latest mark-and-sweep pass. - * Fixed point .8 multiplier and .0 adder. Trigger count (interval) is - * decreased by each (re)allocation attempt (regardless of size), and each - * refzero processed object. - * - * 'SKIP' indicates how many (re)allocations to wait until a retry if - * GC is skipped because there is no thread do it with yet (happens - * only during init phases). - */ -#if defined(DUK_USE_REFERENCE_COUNTING) -#define DUK_HEAP_MARK_AND_SWEEP_TRIGGER_MULT 12800L /* 50x heap size */ -#define DUK_HEAP_MARK_AND_SWEEP_TRIGGER_ADD 1024L -#define DUK_HEAP_MARK_AND_SWEEP_TRIGGER_SKIP 256L -#else -#define DUK_HEAP_MARK_AND_SWEEP_TRIGGER_MULT 256L /* 1x heap size */ -#define DUK_HEAP_MARK_AND_SWEEP_TRIGGER_ADD 1024L -#define DUK_HEAP_MARK_AND_SWEEP_TRIGGER_SKIP 256L -#endif - -/* GC torture. */ -#if defined(DUK_USE_GC_TORTURE) -#define DUK_GC_TORTURE(heap) \ - do { \ - duk_heap_mark_and_sweep((heap), 0); \ - } while (0) -#else -#define DUK_GC_TORTURE(heap) \ - do { \ - } while (0) -#endif - -/* Stringcache is used for speeding up char-offset-to-byte-offset - * translations for non-ASCII strings. - */ -#define DUK_HEAP_STRCACHE_SIZE 4 -#define DUK_HEAP_STRINGCACHE_NOCACHE_LIMIT 16 /* strings up to the this length are not cached */ - -/* Some list management macros. */ -#define DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap, hdr) duk_heap_insert_into_heap_allocated((heap), (hdr)) -#if defined(DUK_USE_REFERENCE_COUNTING) -#define DUK_HEAP_REMOVE_FROM_HEAP_ALLOCATED(heap, hdr) duk_heap_remove_from_heap_allocated((heap), (hdr)) -#endif -#if defined(DUK_USE_FINALIZER_SUPPORT) -#define DUK_HEAP_INSERT_INTO_FINALIZE_LIST(heap, hdr) duk_heap_insert_into_finalize_list((heap), (hdr)) -#define DUK_HEAP_REMOVE_FROM_FINALIZE_LIST(heap, hdr) duk_heap_remove_from_finalize_list((heap), (hdr)) -#endif - -/* - * Built-in strings - */ - -/* heap string indices are autogenerated in duk_strings.h */ -#if defined(DUK_USE_ROM_STRINGS) -#define DUK_HEAP_GET_STRING(heap, idx) ((duk_hstring *) DUK_LOSE_CONST(duk_rom_strings_stridx[(idx)])) -#else /* DUK_USE_ROM_STRINGS */ -#if defined(DUK_USE_HEAPPTR16) -#define DUK_HEAP_GET_STRING(heap, idx) ((duk_hstring *) DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, (heap)->strs16[(idx)])) -#else -#define DUK_HEAP_GET_STRING(heap, idx) ((heap)->strs[(idx)]) -#endif -#endif /* DUK_USE_ROM_STRINGS */ - -/* - * Raw memory calls: relative to heap, but no GC interaction - */ - -#define DUK_ALLOC_RAW(heap, size) ((heap)->alloc_func((heap)->heap_udata, (size))) - -#define DUK_REALLOC_RAW(heap, ptr, newsize) ((heap)->realloc_func((heap)->heap_udata, (void *) (ptr), (newsize))) - -#define DUK_FREE_RAW(heap, ptr) ((heap)->free_func((heap)->heap_udata, (void *) (ptr))) - -/* - * Memory calls: relative to heap, GC interaction, but no error throwing. - * - * XXX: Currently a mark-and-sweep triggered by memory allocation will run - * using the heap->heap_thread. This thread is also used for running - * mark-and-sweep finalization; this is not ideal because it breaks the - * isolation between multiple global environments. - * - * Notes: - * - * - DUK_FREE() is required to ignore NULL and any other possible return - * value of a zero-sized alloc/realloc (same as ANSI C free()). - * - * - There is no DUK_REALLOC_ZEROED because we don't assume to know the - * old size. Caller must zero the reallocated memory. - * - * - DUK_REALLOC_INDIRECT() must be used when a mark-and-sweep triggered - * by an allocation failure might invalidate the original 'ptr', thus - * causing a realloc retry to use an invalid pointer. Example: we're - * reallocating the value stack and a finalizer resizes the same value - * stack during mark-and-sweep. The indirect variant requests for the - * current location of the pointer being reallocated using a callback - * right before every realloc attempt; this circuitous approach is used - * to avoid strict aliasing issues in a more straightforward indirect - * pointer (void **) approach. Note: the pointer in the storage - * location is read but is NOT updated; the caller must do that. - */ - -/* callback for indirect reallocs, request for current pointer */ -typedef void *(*duk_mem_getptr)(duk_heap *heap, void *ud); - -#define DUK_ALLOC(heap, size) duk_heap_mem_alloc((heap), (size)) -#define DUK_ALLOC_ZEROED(heap, size) duk_heap_mem_alloc_zeroed((heap), (size)) -#define DUK_REALLOC(heap, ptr, newsize) duk_heap_mem_realloc((heap), (ptr), (newsize)) -#define DUK_REALLOC_INDIRECT(heap, cb, ud, newsize) duk_heap_mem_realloc_indirect((heap), (cb), (ud), (newsize)) -#define DUK_FREE(heap, ptr) duk_heap_mem_free((heap), (ptr)) - -/* - * Checked allocation, relative to a thread - * - * DUK_FREE_CHECKED() doesn't actually throw, but accepts a 'thr' argument - * for convenience. - */ - -#define DUK_ALLOC_CHECKED(thr, size) duk_heap_mem_alloc_checked((thr), (size)) -#define DUK_ALLOC_CHECKED_ZEROED(thr, size) duk_heap_mem_alloc_checked_zeroed((thr), (size)) -#define DUK_FREE_CHECKED(thr, ptr) duk_heap_mem_free((thr)->heap, (ptr)) - -/* - * Memory constants - */ - -#define DUK_HEAP_ALLOC_FAIL_MARKANDSWEEP_LIMIT \ - 10 /* Retry allocation after mark-and-sweep for this \ - * many times. A single mark-and-sweep round is \ - * not guaranteed to free all unreferenced memory \ - * because of finalization (in fact, ANY number of \ - * rounds is strictly not enough). \ - */ - -#define DUK_HEAP_ALLOC_FAIL_MARKANDSWEEP_EMERGENCY_LIMIT \ - 3 /* Starting from this round, use emergency mode \ - * for mark-and-sweep. \ - */ - -/* - * Debugger support - */ - -/* Maximum number of breakpoints. Only breakpoints that are set are - * consulted so increasing this has no performance impact. - */ -#define DUK_HEAP_MAX_BREAKPOINTS 16 - -/* Opcode interval for a Date-based status/peek rate limit check. Only - * relevant when debugger is attached. Requesting a timestamp may be a - * slow operation on some platforms so this shouldn't be too low. On the - * other hand a high value makes Duktape react to a pause request slowly. - */ -#define DUK_HEAP_DBG_RATELIMIT_OPCODES 4000 - -/* Milliseconds between status notify and transport peeks. */ -#define DUK_HEAP_DBG_RATELIMIT_MILLISECS 200 - -/* Debugger pause flags. */ -#define DUK_PAUSE_FLAG_ONE_OPCODE (1U << 0) /* pause when a single opcode has been executed */ -#define DUK_PAUSE_FLAG_ONE_OPCODE_ACTIVE (1U << 1) /* one opcode pause actually active; artifact of current implementation */ -#define DUK_PAUSE_FLAG_LINE_CHANGE (1U << 2) /* pause when current line number changes */ -#define DUK_PAUSE_FLAG_FUNC_ENTRY (1U << 3) /* pause when entering a function */ -#define DUK_PAUSE_FLAG_FUNC_EXIT (1U << 4) /* pause when exiting current function */ -#define DUK_PAUSE_FLAG_CAUGHT_ERROR (1U << 5) /* pause when about to throw an error that is caught */ -#define DUK_PAUSE_FLAG_UNCAUGHT_ERROR (1U << 6) /* pause when about to throw an error that won't be caught */ - -struct duk_breakpoint { - duk_hstring *filename; - duk_uint32_t line; -}; - -/* - * String cache should ideally be at duk_hthread level, but that would - * cause string finalization to slow down relative to the number of - * threads; string finalization must check the string cache for "weak" - * references to the string being finalized to avoid dead pointers. - * - * Thus, string caches are now at the heap level now. - */ - -struct duk_strcache_entry { - duk_hstring *h; - duk_uint32_t bidx; - duk_uint32_t cidx; -}; - -/* - * Longjmp state, contains the information needed to perform a longjmp. - * Longjmp related values are written to value1, value2, and iserror. - */ - -struct duk_ljstate { - duk_jmpbuf *jmpbuf_ptr; /* current setjmp() catchpoint */ - duk_small_uint_t type; /* longjmp type */ - duk_bool_t iserror; /* isError flag for yield */ - duk_tval value1; /* 1st related value (type specific) */ - duk_tval value2; /* 2nd related value (type specific) */ -}; - -#define DUK_ASSERT_LJSTATE_UNSET(heap) \ - do { \ - DUK_ASSERT(heap != NULL); \ - DUK_ASSERT(heap->lj.type == DUK_LJ_TYPE_UNKNOWN); \ - DUK_ASSERT(heap->lj.iserror == 0); \ - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&heap->lj.value1)); \ - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&heap->lj.value2)); \ - } while (0) -#define DUK_ASSERT_LJSTATE_SET(heap) \ - do { \ - DUK_ASSERT(heap != NULL); \ - DUK_ASSERT(heap->lj.type != DUK_LJ_TYPE_UNKNOWN); \ - } while (0) - -/* - * Literal intern cache - */ - -struct duk_litcache_entry { - const duk_uint8_t *addr; - duk_hstring *h; -}; - -/* - * Main heap structure - */ - -#if defined(DUK_USE_ASSERTIONS) -DUK_INTERNAL_DECL void duk_heap_assert_valid(duk_heap *heap); -#define DUK_HEAP_ASSERT_VALID(heap) \ - do { \ - duk_heap_assert_valid((heap)); \ - } while (0) -#else -#define DUK_HEAP_ASSERT_VALID(heap) \ - do { \ - } while (0) -#endif - -struct duk_heap { - duk_small_uint_t flags; - - /* Allocator functions. */ - duk_alloc_function alloc_func; - duk_realloc_function realloc_func; - duk_free_function free_func; - - /* Heap udata, used for allocator functions but also for other heap - * level callbacks like fatal function, pointer compression, etc. - */ - void *heap_udata; - - /* Fatal error handling, called e.g. when a longjmp() is needed but - * lj.jmpbuf_ptr is NULL. fatal_func must never return; it's not - * declared as "noreturn" because doing that for typedefs is a bit - * challenging portability-wise. - */ - duk_fatal_function fatal_func; - - /* Main list of allocated heap objects. Objects are either here, - * in finalize_list waiting for processing, or in refzero_list - * temporarily while a DECREF refzero cascade finishes. - */ - duk_heaphdr *heap_allocated; - - /* Temporary work list for freeing a cascade of objects when a DECREF - * (or DECREF_NORZ) encounters a zero refcount. Using a work list - * allows fixed C stack size when refcounts go to zero for a chain of - * objects. Outside of DECREF this is always a NULL because DECREF is - * processed without side effects (only memory free calls). - */ -#if defined(DUK_USE_REFERENCE_COUNTING) - duk_heaphdr *refzero_list; -#endif - -#if defined(DUK_USE_FINALIZER_SUPPORT) - /* Work list for objects to be finalized. */ - duk_heaphdr *finalize_list; -#if defined(DUK_USE_ASSERTIONS) - /* Object whose finalizer is executing right now (no nesting). */ - duk_heaphdr *currently_finalizing; -#endif -#endif - - /* Freelist for duk_activations and duk_catchers. */ -#if defined(DUK_USE_CACHE_ACTIVATION) - duk_activation *activation_free; -#endif -#if defined(DUK_USE_CACHE_CATCHER) - duk_catcher *catcher_free; -#endif - - /* Voluntary mark-and-sweep trigger counter. Intentionally signed - * because we continue decreasing the value when voluntary GC cannot - * run. - */ -#if defined(DUK_USE_VOLUNTARY_GC) - duk_int_t ms_trigger_counter; -#endif - - /* Mark-and-sweep recursion control: too deep recursion causes - * multi-pass processing to avoid growing C stack without bound. - */ - duk_uint_t ms_recursion_depth; - - /* Mark-and-sweep flags automatically active (used for critical sections). */ - duk_small_uint_t ms_base_flags; - - /* Mark-and-sweep running flag. Prevents re-entry, and also causes - * refzero events to be ignored (= objects won't be queued to refzero_list). - * - * 0: mark-and-sweep not running - * 1: mark-and-sweep is running - * 2: heap destruction active or debugger active, prevent mark-and-sweep - * and refzero processing (but mark-and-sweep not itself running) - */ - duk_uint_t ms_running; - - /* Mark-and-sweep prevent count, stacking. Used to avoid M&S side - * effects (besides finalizers which are controlled separately) such - * as compacting the string table or object property tables. This - * is also bumped when ms_running is set to prevent recursive re-entry. - * Can also be bumped when mark-and-sweep is not running. - */ - duk_uint_t ms_prevent_count; - - /* Finalizer processing prevent count, stacking. Bumped when finalizers - * are processed to prevent recursive finalizer processing (first call site - * processing finalizers handles all finalizers until the list is empty). - * Can also be bumped explicitly to prevent finalizer execution. - */ - duk_uint_t pf_prevent_count; - - /* When processing finalize_list, don't actually run finalizers but - * queue finalizable objects back to heap_allocated as is. This is - * used during heap destruction to deal with finalizers that keep - * on creating more finalizable garbage. - */ - duk_uint_t pf_skip_finalizers; - -#if defined(DUK_USE_ASSERTIONS) - /* Set when we're in a critical path where an error throw would cause - * e.g. sandboxing/protected call violations or state corruption. This - * is just used for asserts. - */ - duk_bool_t error_not_allowed; -#endif - -#if defined(DUK_USE_ASSERTIONS) - /* Set when heap is still being initialized, helps with writing - * some assertions. - */ - duk_bool_t heap_initializing; -#endif - - /* Marker for detecting internal "double faults", errors thrown when - * we're trying to create an error object, see duk_error_throw.c. - */ - duk_bool_t creating_error; - - /* Marker for indicating we're calling a user error augmentation - * (errCreate/errThrow) function. Errors created/thrown during - * such a call are not augmented. - */ -#if defined(DUK_USE_AUGMENT_ERROR_THROW) || defined(DUK_USE_AUGMENT_ERROR_CREATE) - duk_bool_t augmenting_error; -#endif - - /* Longjmp state. */ - duk_ljstate lj; - - /* Heap thread, used internally and for finalization. */ - duk_hthread *heap_thread; - - /* Current running thread. */ - duk_hthread *curr_thread; - - /* Heap level "stash" object (e.g., various reachability roots). */ - duk_hobject *heap_object; - - /* duk_handle_call / duk_handle_safe_call recursion depth limiting */ - duk_int_t call_recursion_depth; - duk_int_t call_recursion_limit; - - /* Mix-in value for computing string hashes; should be reasonably unpredictable. */ - duk_uint32_t hash_seed; - - /* Random number state for duk_util_tinyrandom.c. */ -#if !defined(DUK_USE_GET_RANDOM_DOUBLE) -#if defined(DUK_USE_PREFER_SIZE) || !defined(DUK_USE_64BIT_OPS) - duk_uint32_t rnd_state; /* State for Shamir's three-op algorithm */ -#else - duk_uint64_t rnd_state[2]; /* State for xoroshiro128+ */ -#endif -#endif - - /* Counter for unique local symbol creation. */ - /* XXX: When 64-bit types are available, it would be more efficient to - * use a duk_uint64_t at least for incrementing but maybe also for - * string formatting in the Symbol constructor. - */ - duk_uint32_t sym_counter[2]; - - /* For manual debugging: instruction count based on executor and - * interrupt counter book-keeping. Inspect debug logs to see how - * they match up. - */ -#if defined(DUK_USE_INTERRUPT_COUNTER) && defined(DUK_USE_DEBUG) - duk_int_t inst_count_exec; - duk_int_t inst_count_interrupt; -#endif - - /* Debugger state. */ -#if defined(DUK_USE_DEBUGGER_SUPPORT) - /* Callbacks and udata; dbg_read_cb != NULL is used to indicate attached state. */ - duk_debug_read_function dbg_read_cb; /* required, NULL implies detached */ - duk_debug_write_function dbg_write_cb; /* required */ - duk_debug_peek_function dbg_peek_cb; - duk_debug_read_flush_function dbg_read_flush_cb; - duk_debug_write_flush_function dbg_write_flush_cb; - duk_debug_request_function dbg_request_cb; - duk_debug_detached_function dbg_detached_cb; - void *dbg_udata; - - /* The following are only relevant when debugger is attached. */ - duk_bool_t dbg_processing; /* currently processing messages or breakpoints: don't enter message processing recursively (e.g. - no breakpoints when processing debugger eval) */ - duk_bool_t dbg_state_dirty; /* resend state next time executor is about to run */ - duk_bool_t - dbg_force_restart; /* force executor restart to recheck breakpoints; used to handle function returns (see GH-303) */ - duk_bool_t dbg_detaching; /* debugger detaching; used to avoid calling detach handler recursively */ - duk_small_uint_t dbg_pause_flags; /* flags for automatic pause behavior */ - duk_activation *dbg_pause_act; /* activation related to pause behavior (pause on line change, function entry/exit) */ - duk_uint32_t dbg_pause_startline; /* starting line number for line change related pause behavior */ - duk_breakpoint dbg_breakpoints[DUK_HEAP_MAX_BREAKPOINTS]; /* breakpoints: [0,breakpoint_count[ gc reachable */ - duk_small_uint_t dbg_breakpoint_count; - duk_breakpoint - *dbg_breakpoints_active[DUK_HEAP_MAX_BREAKPOINTS + 1]; /* currently active breakpoints: NULL term, borrowed pointers */ - /* XXX: make active breakpoints actual copies instead of pointers? */ - - /* These are for rate limiting Status notifications and transport peeking. */ - duk_uint_t dbg_exec_counter; /* cumulative opcode execution count (overflows are OK) */ - duk_uint_t dbg_last_counter; /* value of dbg_exec_counter when we last did a Date-based check */ - duk_double_t dbg_last_time; /* time when status/peek was last done (Date-based rate limit) */ - - /* Used to support single-byte stream lookahead. */ - duk_bool_t dbg_have_next_byte; - duk_uint8_t dbg_next_byte; -#endif /* DUK_USE_DEBUGGER_SUPPORT */ -#if defined(DUK_USE_ASSERTIONS) - duk_bool_t dbg_calling_transport; /* transport call in progress, calling into Duktape forbidden */ -#endif - - /* String intern table (weak refs). */ -#if defined(DUK_USE_STRTAB_PTRCOMP) - duk_uint16_t *strtable16; -#else - duk_hstring **strtable; -#endif - duk_uint32_t st_mask; /* mask for lookup, st_size - 1 */ - duk_uint32_t st_size; /* stringtable size */ -#if (DUK_USE_STRTAB_MINSIZE != DUK_USE_STRTAB_MAXSIZE) - duk_uint32_t st_count; /* string count for resize load factor checks */ -#endif - duk_bool_t st_resizing; /* string table is being resized; avoid recursive resize */ - - /* String access cache (codepoint offset -> byte offset) for fast string - * character looping; 'weak' reference which needs special handling in GC. - */ - duk_strcache_entry strcache[DUK_HEAP_STRCACHE_SIZE]; - -#if defined(DUK_USE_LITCACHE_SIZE) - /* Literal intern cache. When enabled, strings interned as literals - * (e.g. duk_push_literal()) will be pinned and cached for the lifetime - * of the heap. - */ - duk_litcache_entry litcache[DUK_USE_LITCACHE_SIZE]; -#endif - - /* Built-in strings. */ -#if defined(DUK_USE_ROM_STRINGS) - /* No field needed when strings are in ROM. */ -#else -#if defined(DUK_USE_HEAPPTR16) - duk_uint16_t strs16[DUK_HEAP_NUM_STRINGS]; -#else - duk_hstring *strs[DUK_HEAP_NUM_STRINGS]; -#endif -#endif - - /* Stats. */ -#if defined(DUK_USE_DEBUG) - duk_int_t stats_exec_opcodes; - duk_int_t stats_exec_interrupt; - duk_int_t stats_exec_throw; - duk_int_t stats_call_all; - duk_int_t stats_call_tailcall; - duk_int_t stats_call_ecmatoecma; - duk_int_t stats_safecall_all; - duk_int_t stats_safecall_nothrow; - duk_int_t stats_safecall_throw; - duk_int_t stats_ms_try_count; - duk_int_t stats_ms_skip_count; - duk_int_t stats_ms_emergency_count; - duk_int_t stats_strtab_intern_hit; - duk_int_t stats_strtab_intern_miss; - duk_int_t stats_strtab_resize_check; - duk_int_t stats_strtab_resize_grow; - duk_int_t stats_strtab_resize_shrink; - duk_int_t stats_strtab_litcache_hit; - duk_int_t stats_strtab_litcache_miss; - duk_int_t stats_strtab_litcache_pin; - duk_int_t stats_object_realloc_props; - duk_int_t stats_object_abandon_array; - duk_int_t stats_getownpropdesc_count; - duk_int_t stats_getownpropdesc_hit; - duk_int_t stats_getownpropdesc_miss; - duk_int_t stats_getpropdesc_count; - duk_int_t stats_getpropdesc_hit; - duk_int_t stats_getpropdesc_miss; - duk_int_t stats_getprop_all; - duk_int_t stats_getprop_arrayidx; - duk_int_t stats_getprop_bufobjidx; - duk_int_t stats_getprop_bufferidx; - duk_int_t stats_getprop_bufferlen; - duk_int_t stats_getprop_stringidx; - duk_int_t stats_getprop_stringlen; - duk_int_t stats_getprop_proxy; - duk_int_t stats_getprop_arguments; - duk_int_t stats_putprop_all; - duk_int_t stats_putprop_arrayidx; - duk_int_t stats_putprop_bufobjidx; - duk_int_t stats_putprop_bufferidx; - duk_int_t stats_putprop_proxy; - duk_int_t stats_getvar_all; - duk_int_t stats_putvar_all; - duk_int_t stats_envrec_delayedcreate; - duk_int_t stats_envrec_create; - duk_int_t stats_envrec_newenv; - duk_int_t stats_envrec_oldenv; - duk_int_t stats_envrec_pushclosure; -#endif -}; - -/* - * Prototypes - */ - -DUK_INTERNAL_DECL -duk_heap *duk_heap_alloc(duk_alloc_function alloc_func, - duk_realloc_function realloc_func, - duk_free_function free_func, - void *heap_udata, - duk_fatal_function fatal_func); -DUK_INTERNAL_DECL void duk_heap_free(duk_heap *heap); -DUK_INTERNAL_DECL void duk_free_hobject(duk_heap *heap, duk_hobject *h); -DUK_INTERNAL_DECL void duk_free_hbuffer(duk_heap *heap, duk_hbuffer *h); -DUK_INTERNAL_DECL void duk_free_hstring(duk_heap *heap, duk_hstring *h); -DUK_INTERNAL_DECL void duk_heap_free_heaphdr_raw(duk_heap *heap, duk_heaphdr *hdr); - -DUK_INTERNAL_DECL void duk_heap_insert_into_heap_allocated(duk_heap *heap, duk_heaphdr *hdr); -#if defined(DUK_USE_REFERENCE_COUNTING) -DUK_INTERNAL_DECL void duk_heap_remove_from_heap_allocated(duk_heap *heap, duk_heaphdr *hdr); -#endif -#if defined(DUK_USE_FINALIZER_SUPPORT) -DUK_INTERNAL_DECL void duk_heap_insert_into_finalize_list(duk_heap *heap, duk_heaphdr *hdr); -DUK_INTERNAL_DECL void duk_heap_remove_from_finalize_list(duk_heap *heap, duk_heaphdr *hdr); -#endif -#if defined(DUK_USE_ASSERTIONS) -DUK_INTERNAL_DECL duk_bool_t duk_heap_in_heap_allocated(duk_heap *heap, duk_heaphdr *ptr); -#endif -#if defined(DUK_USE_INTERRUPT_COUNTER) -DUK_INTERNAL_DECL void duk_heap_switch_thread(duk_heap *heap, duk_hthread *new_thr); -#endif - -DUK_INTERNAL_DECL duk_hstring *duk_heap_strtable_intern(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen); -DUK_INTERNAL_DECL duk_hstring *duk_heap_strtable_intern_checked(duk_hthread *thr, const duk_uint8_t *str, duk_uint32_t len); -#if defined(DUK_USE_LITCACHE_SIZE) -DUK_INTERNAL_DECL duk_hstring *duk_heap_strtable_intern_literal_checked(duk_hthread *thr, - const duk_uint8_t *str, - duk_uint32_t blen); -#endif -DUK_INTERNAL_DECL duk_hstring *duk_heap_strtable_intern_u32(duk_heap *heap, duk_uint32_t val); -DUK_INTERNAL_DECL duk_hstring *duk_heap_strtable_intern_u32_checked(duk_hthread *thr, duk_uint32_t val); -#if defined(DUK_USE_REFERENCE_COUNTING) -DUK_INTERNAL_DECL void duk_heap_strtable_unlink(duk_heap *heap, duk_hstring *h); -#endif -DUK_INTERNAL_DECL void duk_heap_strtable_unlink_prev(duk_heap *heap, duk_hstring *h, duk_hstring *prev); -DUK_INTERNAL_DECL void duk_heap_strtable_force_resize(duk_heap *heap); -DUK_INTERNAL void duk_heap_strtable_free(duk_heap *heap); -#if defined(DUK_USE_DEBUG) -DUK_INTERNAL void duk_heap_strtable_dump(duk_heap *heap); -#endif - -DUK_INTERNAL_DECL void duk_heap_strcache_string_remove(duk_heap *heap, duk_hstring *h); -DUK_INTERNAL_DECL duk_uint_fast32_t duk_heap_strcache_offset_char2byte(duk_hthread *thr, - duk_hstring *h, - duk_uint_fast32_t char_offset); - -#if defined(DUK_USE_PROVIDE_DEFAULT_ALLOC_FUNCTIONS) -DUK_INTERNAL_DECL void *duk_default_alloc_function(void *udata, duk_size_t size); -DUK_INTERNAL_DECL void *duk_default_realloc_function(void *udata, void *ptr, duk_size_t newsize); -DUK_INTERNAL_DECL void duk_default_free_function(void *udata, void *ptr); -#endif - -DUK_INTERNAL_DECL void *duk_heap_mem_alloc(duk_heap *heap, duk_size_t size); -DUK_INTERNAL_DECL void *duk_heap_mem_alloc_zeroed(duk_heap *heap, duk_size_t size); -DUK_INTERNAL_DECL void *duk_heap_mem_alloc_checked(duk_hthread *thr, duk_size_t size); -DUK_INTERNAL_DECL void *duk_heap_mem_alloc_checked_zeroed(duk_hthread *thr, duk_size_t size); -DUK_INTERNAL_DECL void *duk_heap_mem_realloc(duk_heap *heap, void *ptr, duk_size_t newsize); -DUK_INTERNAL_DECL void *duk_heap_mem_realloc_indirect(duk_heap *heap, duk_mem_getptr cb, void *ud, duk_size_t newsize); -DUK_INTERNAL_DECL void duk_heap_mem_free(duk_heap *heap, void *ptr); - -DUK_INTERNAL_DECL void duk_heap_free_freelists(duk_heap *heap); - -#if defined(DUK_USE_FINALIZER_SUPPORT) -DUK_INTERNAL_DECL void duk_heap_run_finalizer(duk_heap *heap, duk_hobject *obj); -DUK_INTERNAL_DECL void duk_heap_process_finalize_list(duk_heap *heap); -#endif /* DUK_USE_FINALIZER_SUPPORT */ - -DUK_INTERNAL_DECL void duk_heap_mark_and_sweep(duk_heap *heap, duk_small_uint_t flags); - -DUK_INTERNAL_DECL duk_uint32_t duk_heap_hashstring(duk_heap *heap, const duk_uint8_t *str, duk_size_t len); - -#endif /* DUK_HEAP_H_INCLUDED */ -/* #include duk_debugger.h */ -#if !defined(DUK_DEBUGGER_H_INCLUDED) -#define DUK_DEBUGGER_H_INCLUDED - -/* Debugger protocol version is defined in the public API header. */ - -/* Initial bytes for markers. */ -#define DUK_DBG_IB_EOM 0x00 -#define DUK_DBG_IB_REQUEST 0x01 -#define DUK_DBG_IB_REPLY 0x02 -#define DUK_DBG_IB_ERROR 0x03 -#define DUK_DBG_IB_NOTIFY 0x04 - -/* Other initial bytes. */ -#define DUK_DBG_IB_INT4 0x10 -#define DUK_DBG_IB_STR4 0x11 -#define DUK_DBG_IB_STR2 0x12 -#define DUK_DBG_IB_BUF4 0x13 -#define DUK_DBG_IB_BUF2 0x14 -#define DUK_DBG_IB_UNUSED 0x15 -#define DUK_DBG_IB_UNDEFINED 0x16 -#define DUK_DBG_IB_NULL 0x17 -#define DUK_DBG_IB_TRUE 0x18 -#define DUK_DBG_IB_FALSE 0x19 -#define DUK_DBG_IB_NUMBER 0x1a -#define DUK_DBG_IB_OBJECT 0x1b -#define DUK_DBG_IB_POINTER 0x1c -#define DUK_DBG_IB_LIGHTFUNC 0x1d -#define DUK_DBG_IB_HEAPPTR 0x1e -/* The short string/integer initial bytes starting from 0x60 don't have - * defines now. - */ - -/* Error codes. */ -#define DUK_DBG_ERR_UNKNOWN 0x00 -#define DUK_DBG_ERR_UNSUPPORTED 0x01 -#define DUK_DBG_ERR_TOOMANY 0x02 -#define DUK_DBG_ERR_NOTFOUND 0x03 -#define DUK_DBG_ERR_APPLICATION 0x04 - -/* Commands and notifys initiated by Duktape. */ -#define DUK_DBG_CMD_STATUS 0x01 -#define DUK_DBG_CMD_UNUSED_2 0x02 /* Duktape 1.x: print notify */ -#define DUK_DBG_CMD_UNUSED_3 0x03 /* Duktape 1.x: alert notify */ -#define DUK_DBG_CMD_UNUSED_4 0x04 /* Duktape 1.x: log notify */ -#define DUK_DBG_CMD_THROW 0x05 -#define DUK_DBG_CMD_DETACHING 0x06 -#define DUK_DBG_CMD_APPNOTIFY 0x07 - -/* Commands initiated by debug client. */ -#define DUK_DBG_CMD_BASICINFO 0x10 -#define DUK_DBG_CMD_TRIGGERSTATUS 0x11 -#define DUK_DBG_CMD_PAUSE 0x12 -#define DUK_DBG_CMD_RESUME 0x13 -#define DUK_DBG_CMD_STEPINTO 0x14 -#define DUK_DBG_CMD_STEPOVER 0x15 -#define DUK_DBG_CMD_STEPOUT 0x16 -#define DUK_DBG_CMD_LISTBREAK 0x17 -#define DUK_DBG_CMD_ADDBREAK 0x18 -#define DUK_DBG_CMD_DELBREAK 0x19 -#define DUK_DBG_CMD_GETVAR 0x1a -#define DUK_DBG_CMD_PUTVAR 0x1b -#define DUK_DBG_CMD_GETCALLSTACK 0x1c -#define DUK_DBG_CMD_GETLOCALS 0x1d -#define DUK_DBG_CMD_EVAL 0x1e -#define DUK_DBG_CMD_DETACH 0x1f -#define DUK_DBG_CMD_DUMPHEAP 0x20 -#define DUK_DBG_CMD_GETBYTECODE 0x21 -#define DUK_DBG_CMD_APPREQUEST 0x22 -#define DUK_DBG_CMD_GETHEAPOBJINFO 0x23 -#define DUK_DBG_CMD_GETOBJPROPDESC 0x24 -#define DUK_DBG_CMD_GETOBJPROPDESCRANGE 0x25 - -/* The low 8 bits map directly to duk_hobject.h DUK_PROPDESC_FLAG_xxx. - * The remaining flags are specific to the debugger. - */ -#define DUK_DBG_PROPFLAG_SYMBOL (1U << 8) -#define DUK_DBG_PROPFLAG_HIDDEN (1U << 9) - -#if defined(DUK_USE_DEBUGGER_SUPPORT) -DUK_INTERNAL_DECL void duk_debug_do_detach(duk_heap *heap); - -DUK_INTERNAL_DECL duk_bool_t duk_debug_read_peek(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_debug_write_flush(duk_hthread *thr); - -DUK_INTERNAL_DECL void duk_debug_skip_bytes(duk_hthread *thr, duk_size_t length); -DUK_INTERNAL_DECL void duk_debug_skip_byte(duk_hthread *thr); - -DUK_INTERNAL_DECL void duk_debug_read_bytes(duk_hthread *thr, duk_uint8_t *data, duk_size_t length); -DUK_INTERNAL_DECL duk_uint8_t duk_debug_read_byte(duk_hthread *thr); -DUK_INTERNAL_DECL duk_int32_t duk_debug_read_int(duk_hthread *thr); -DUK_INTERNAL_DECL duk_hstring *duk_debug_read_hstring(duk_hthread *thr); -/* XXX: exposed duk_debug_read_pointer */ -/* XXX: exposed duk_debug_read_buffer */ -/* XXX: exposed duk_debug_read_hbuffer */ -#if 0 -DUK_INTERNAL_DECL duk_heaphdr *duk_debug_read_heapptr(duk_hthread *thr); -#endif -#if defined(DUK_USE_DEBUGGER_INSPECT) -DUK_INTERNAL_DECL duk_heaphdr *duk_debug_read_any_ptr(duk_hthread *thr); -#endif -DUK_INTERNAL_DECL duk_tval *duk_debug_read_tval(duk_hthread *thr); - -DUK_INTERNAL_DECL void duk_debug_write_bytes(duk_hthread *thr, const duk_uint8_t *data, duk_size_t length); -DUK_INTERNAL_DECL void duk_debug_write_byte(duk_hthread *thr, duk_uint8_t x); -DUK_INTERNAL_DECL void duk_debug_write_unused(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_debug_write_undefined(duk_hthread *thr); -#if defined(DUK_USE_DEBUGGER_INSPECT) -DUK_INTERNAL_DECL void duk_debug_write_null(duk_hthread *thr); -#endif -DUK_INTERNAL_DECL void duk_debug_write_boolean(duk_hthread *thr, duk_uint_t val); -DUK_INTERNAL_DECL void duk_debug_write_int(duk_hthread *thr, duk_int32_t x); -DUK_INTERNAL_DECL void duk_debug_write_uint(duk_hthread *thr, duk_uint32_t x); -DUK_INTERNAL_DECL void duk_debug_write_string(duk_hthread *thr, const char *data, duk_size_t length); -DUK_INTERNAL_DECL void duk_debug_write_cstring(duk_hthread *thr, const char *data); -DUK_INTERNAL_DECL void duk_debug_write_hstring(duk_hthread *thr, duk_hstring *h); -DUK_INTERNAL_DECL void duk_debug_write_buffer(duk_hthread *thr, const char *data, duk_size_t length); -DUK_INTERNAL_DECL void duk_debug_write_hbuffer(duk_hthread *thr, duk_hbuffer *h); -DUK_INTERNAL_DECL void duk_debug_write_pointer(duk_hthread *thr, void *ptr); -#if defined(DUK_USE_DEBUGGER_DUMPHEAP) || defined(DUK_USE_DEBUGGER_INSPECT) -DUK_INTERNAL_DECL void duk_debug_write_heapptr(duk_hthread *thr, duk_heaphdr *h); -#endif -DUK_INTERNAL_DECL void duk_debug_write_hobject(duk_hthread *thr, duk_hobject *obj); -DUK_INTERNAL_DECL void duk_debug_write_tval(duk_hthread *thr, duk_tval *tv); -#if 0 /* unused */ -DUK_INTERNAL_DECL void duk_debug_write_request(duk_hthread *thr, duk_small_uint_t command); -#endif -DUK_INTERNAL_DECL void duk_debug_write_reply(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_debug_write_error_eom(duk_hthread *thr, duk_small_uint_t err_code, const char *msg); -DUK_INTERNAL_DECL void duk_debug_write_notify(duk_hthread *thr, duk_small_uint_t command); -DUK_INTERNAL_DECL void duk_debug_write_eom(duk_hthread *thr); - -DUK_INTERNAL_DECL duk_uint_fast32_t duk_debug_curr_line(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_debug_send_status(duk_hthread *thr); -#if defined(DUK_USE_DEBUGGER_THROW_NOTIFY) -DUK_INTERNAL_DECL void duk_debug_send_throw(duk_hthread *thr, duk_bool_t fatal); -#endif - -DUK_INTERNAL_DECL void duk_debug_halt_execution(duk_hthread *thr, duk_bool_t use_prev_pc); -DUK_INTERNAL_DECL duk_bool_t duk_debug_process_messages(duk_hthread *thr, duk_bool_t no_block); - -DUK_INTERNAL_DECL duk_small_int_t duk_debug_add_breakpoint(duk_hthread *thr, duk_hstring *filename, duk_uint32_t line); -DUK_INTERNAL_DECL duk_bool_t duk_debug_remove_breakpoint(duk_hthread *thr, duk_small_uint_t breakpoint_index); - -DUK_INTERNAL_DECL duk_bool_t duk_debug_is_attached(duk_heap *heap); -DUK_INTERNAL_DECL duk_bool_t duk_debug_is_paused(duk_heap *heap); -DUK_INTERNAL_DECL void duk_debug_set_paused(duk_heap *heap); -DUK_INTERNAL_DECL void duk_debug_clear_paused(duk_heap *heap); -DUK_INTERNAL_DECL void duk_debug_clear_pause_state(duk_heap *heap); -#endif /* DUK_USE_DEBUGGER_SUPPORT */ - -#endif /* DUK_DEBUGGER_H_INCLUDED */ -/* #include duk_debug.h */ -/* - * Debugging macros, DUK_DPRINT() and its variants in particular. - * - * DUK_DPRINT() allows formatted debug prints, and supports standard - * and Duktape specific formatters. See duk_debug_vsnprintf.c for details. - * - * DUK_D(x), DUK_DD(x), and DUK_DDD(x) are used together with log macros - * for technical reasons. They are concretely used to hide 'x' from the - * compiler when the corresponding log level is disabled. This allows - * clean builds on non-C99 compilers, at the cost of more verbose code. - * Examples: - * - * DUK_D(DUK_DPRINT("foo")); - * DUK_DD(DUK_DDPRINT("foo")); - * DUK_DDD(DUK_DDDPRINT("foo")); - * - * This approach is preferable to the old "double parentheses" hack because - * double parentheses make the C99 solution worse: __FILE__ and __LINE__ can - * no longer be added transparently without going through globals, which - * works poorly with threading. - */ - -#if !defined(DUK_DEBUG_H_INCLUDED) -#define DUK_DEBUG_H_INCLUDED - -#if defined(DUK_USE_DEBUG) - -#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 0) -#define DUK_D(x) x -#else -#define DUK_D(x) \ - do { \ - } while (0) /* omit */ -#endif - -#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 1) -#define DUK_DD(x) x -#else -#define DUK_DD(x) \ - do { \ - } while (0) /* omit */ -#endif - -#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2) -#define DUK_DDD(x) x -#else -#define DUK_DDD(x) \ - do { \ - } while (0) /* omit */ -#endif - -/* - * Exposed debug macros: debugging enabled - */ - -#if defined(DUK_USE_VARIADIC_MACROS) - -/* Note: combining __FILE__, __LINE__, and __func__ into fmt would be - * possible compile time, but waste some space with shared function names. - */ -#define DUK__DEBUG_LOG(lev, ...) \ - duk_debug_log((duk_int_t) (lev), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO, DUK_FUNC_MACRO, __VA_ARGS__); - -#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 0) -#define DUK_DPRINT(...) DUK__DEBUG_LOG(DUK_LEVEL_DEBUG, __VA_ARGS__) -#else -#define DUK_DPRINT(...) -#endif - -#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 1) -#define DUK_DDPRINT(...) DUK__DEBUG_LOG(DUK_LEVEL_DDEBUG, __VA_ARGS__) -#else -#define DUK_DDPRINT(...) -#endif - -#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2) -#define DUK_DDDPRINT(...) DUK__DEBUG_LOG(DUK_LEVEL_DDDEBUG, __VA_ARGS__) -#else -#define DUK_DDDPRINT(...) -#endif - -#else /* DUK_USE_VARIADIC_MACROS */ - -#define DUK__DEBUG_STASH(lev) \ - (void) DUK_SNPRINTF(duk_debug_file_stash, DUK_DEBUG_STASH_SIZE, "%s", (const char *) DUK_FILE_MACRO), \ - (void) (duk_debug_file_stash[DUK_DEBUG_STASH_SIZE - 1] = (char) 0), \ - (void) (duk_debug_line_stash = (duk_int_t) DUK_LINE_MACRO), \ - (void) DUK_SNPRINTF(duk_debug_func_stash, DUK_DEBUG_STASH_SIZE, "%s", (const char *) DUK_FUNC_MACRO), \ - (void) (duk_debug_func_stash[DUK_DEBUG_STASH_SIZE - 1] = (char) 0), (void) (duk_debug_level_stash = (lev)) - -/* Without variadic macros resort to comma expression trickery to handle debug - * prints. This generates a lot of harmless warnings. These hacks are not - * needed normally because DUK_D() and friends will hide the entire debug log - * statement from the compiler. - */ - -#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 0) -#define DUK_DPRINT DUK__DEBUG_STASH(DUK_LEVEL_DEBUG), (void) duk_debug_log /* args go here in parens */ -#else -#define DUK_DPRINT 0 && /* args go here as a comma expression in parens */ -#endif - -#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 1) -#define DUK_DDPRINT DUK__DEBUG_STASH(DUK_LEVEL_DDEBUG), (void) duk_debug_log /* args go here in parens */ -#else -#define DUK_DDPRINT 0 && /* args */ -#endif - -#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2) -#define DUK_DDDPRINT DUK__DEBUG_STASH(DUK_LEVEL_DDDEBUG), (void) duk_debug_log /* args go here in parens */ -#else -#define DUK_DDDPRINT 0 && /* args */ -#endif - -#endif /* DUK_USE_VARIADIC_MACROS */ - -#else /* DUK_USE_DEBUG */ - -/* - * Exposed debug macros: debugging disabled - */ - -#define DUK_D(x) \ - do { \ - } while (0) /* omit */ -#define DUK_DD(x) \ - do { \ - } while (0) /* omit */ -#define DUK_DDD(x) \ - do { \ - } while (0) /* omit */ - -#if defined(DUK_USE_VARIADIC_MACROS) - -#define DUK_DPRINT(...) -#define DUK_DDPRINT(...) -#define DUK_DDDPRINT(...) - -#else /* DUK_USE_VARIADIC_MACROS */ - -#define DUK_DPRINT 0 && /* args go here as a comma expression in parens */ -#define DUK_DDPRINT 0 && /* args */ -#define DUK_DDDPRINT 0 && /* args */ - -#endif /* DUK_USE_VARIADIC_MACROS */ - -#endif /* DUK_USE_DEBUG */ - -/* - * Structs - */ - -#if defined(DUK_USE_DEBUG) -struct duk_fixedbuffer { - duk_uint8_t *buffer; - duk_size_t length; - duk_size_t offset; - duk_bool_t truncated; -}; -#endif - -/* - * Prototypes - */ - -#if defined(DUK_USE_DEBUG) -DUK_INTERNAL_DECL duk_int_t duk_debug_vsnprintf(char *str, duk_size_t size, const char *format, va_list ap); -#if 0 /*unused*/ -DUK_INTERNAL_DECL duk_int_t duk_debug_snprintf(char *str, duk_size_t size, const char *format, ...); -#endif -DUK_INTERNAL_DECL void duk_debug_format_funcptr(char *buf, duk_size_t buf_size, duk_uint8_t *fptr, duk_size_t fptr_size); - -#if defined(DUK_USE_VARIADIC_MACROS) -DUK_INTERNAL_DECL void duk_debug_log(duk_int_t level, const char *file, duk_int_t line, const char *func, const char *fmt, ...); -#else /* DUK_USE_VARIADIC_MACROS */ -/* parameter passing, not thread safe */ -#define DUK_DEBUG_STASH_SIZE 128 -#if !defined(DUK_SINGLE_FILE) -DUK_INTERNAL_DECL char duk_debug_file_stash[DUK_DEBUG_STASH_SIZE]; -DUK_INTERNAL_DECL duk_int_t duk_debug_line_stash; -DUK_INTERNAL_DECL char duk_debug_func_stash[DUK_DEBUG_STASH_SIZE]; -DUK_INTERNAL_DECL duk_int_t duk_debug_level_stash; -#endif -DUK_INTERNAL_DECL void duk_debug_log(const char *fmt, ...); -#endif /* DUK_USE_VARIADIC_MACROS */ - -DUK_INTERNAL_DECL void duk_fb_put_bytes(duk_fixedbuffer *fb, const duk_uint8_t *buffer, duk_size_t length); -DUK_INTERNAL_DECL void duk_fb_put_byte(duk_fixedbuffer *fb, duk_uint8_t x); -DUK_INTERNAL_DECL void duk_fb_put_cstring(duk_fixedbuffer *fb, const char *x); -DUK_INTERNAL_DECL void duk_fb_sprintf(duk_fixedbuffer *fb, const char *fmt, ...); -DUK_INTERNAL_DECL void duk_fb_put_funcptr(duk_fixedbuffer *fb, duk_uint8_t *fptr, duk_size_t fptr_size); -DUK_INTERNAL_DECL duk_bool_t duk_fb_is_full(duk_fixedbuffer *fb); - -#endif /* DUK_USE_DEBUG */ - -#endif /* DUK_DEBUG_H_INCLUDED */ -/* #include duk_error.h */ -/* - * Error handling macros, assertion macro, error codes. - * - * There are three types of 'errors': - * - * 1. Ordinary errors relative to a thread, cause a longjmp, catchable. - * 2. Fatal errors relative to a heap, cause fatal handler to be called. - * 3. Fatal errors without context, cause the default (not heap specific) - * fatal handler to be called. - * - * Fatal errors without context are used by debug code such as assertions. - * By providing a fatal error handler for a Duktape heap, user code can - * avoid fatal errors without context in non-debug builds. - */ - -#if !defined(DUK_ERROR_H_INCLUDED) -#define DUK_ERROR_H_INCLUDED - -/* - * Error codes: defined in duktape.h - * - * Error codes are used as a shorthand to throw exceptions from inside - * the implementation. The appropriate ECMAScript object is constructed - * based on the code. ECMAScript code throws objects directly. The error - * codes are defined in the public API header because they are also used - * by calling code. - */ - -/* - * Normal error - * - * Normal error is thrown with a longjmp() through the current setjmp() - * catchpoint record in the duk_heap. The 'curr_thread' of the duk_heap - * identifies the throwing thread. - * - * Error formatting is usually unnecessary. The error macros provide a - * zero argument version (no formatting) and separate macros for small - * argument counts. Variadic macros are not used to avoid portability - * issues and avoid the need for stash-based workarounds when they're not - * available. Vararg calls are avoided for non-formatted error calls - * because vararg call sites are larger than normal, and there are a lot - * of call sites with no formatting. - * - * Note that special formatting provided by debug macros is NOT available. - * - * The _RAW variants allow the caller to specify file and line. This makes - * it easier to write checked calls which want to use the call site of the - * checked function, not the error macro call inside the checked function. - */ - -#if defined(DUK_USE_VERBOSE_ERRORS) - -/* Because there are quite many call sites, pack error code (require at most - * 8-bit) into a single argument. - */ -#define DUK_ERROR(thr, err, msg) \ - do { \ - duk_errcode_t duk__err = (err); \ - duk_int_t duk__line = (duk_int_t) DUK_LINE_MACRO; \ - DUK_ASSERT(duk__err >= 0 && duk__err <= 0xff); \ - DUK_ASSERT(duk__line >= 0 && duk__line <= 0x00ffffffL); \ - duk_err_handle_error((thr), DUK_FILE_MACRO, (((duk_uint_t) duk__err) << 24) | ((duk_uint_t) duk__line), (msg)); \ - } while (0) -#define DUK_ERROR_RAW(thr, file, line, err, msg) \ - do { \ - duk_errcode_t duk__err = (err); \ - duk_int_t duk__line = (duk_int_t) (line); \ - DUK_ASSERT(duk__err >= 0 && duk__err <= 0xff); \ - DUK_ASSERT(duk__line >= 0 && duk__line <= 0x00ffffffL); \ - duk_err_handle_error((thr), (file), (((duk_uint_t) duk__err) << 24) | ((duk_uint_t) duk__line), (msg)); \ - } while (0) - -#define DUK_ERROR_FMT1(thr, err, fmt, arg1) \ - do { \ - duk_errcode_t duk__err = (err); \ - duk_int_t duk__line = (duk_int_t) DUK_LINE_MACRO; \ - DUK_ASSERT(duk__err >= 0 && duk__err <= 0xff); \ - DUK_ASSERT(duk__line >= 0 && duk__line <= 0x00ffffffL); \ - duk_err_handle_error_fmt((thr), \ - DUK_FILE_MACRO, \ - (((duk_uint_t) duk__err) << 24) | ((duk_uint_t) duk__line), \ - (fmt), \ - (arg1)); \ - } while (0) -#define DUK_ERROR_RAW_FMT1(thr, file, line, err, fmt, arg1) \ - do { \ - duk_errcode_t duk__err = (err); \ - duk_int_t duk__line = (duk_int_t) (line); \ - DUK_ASSERT(duk__err >= 0 && duk__err <= 0xff); \ - DUK_ASSERT(duk__line >= 0 && duk__line <= 0x00ffffffL); \ - duk_err_handle_error_fmt((thr), \ - (file), \ - (((duk_uint_t) duk__err) << 24) | ((duk_uint_t) duk__line), \ - (fmt), \ - (arg1)); \ - } while (0) - -#define DUK_ERROR_FMT2(thr, err, fmt, arg1, arg2) \ - do { \ - duk_errcode_t duk__err = (err); \ - duk_int_t duk__line = (duk_int_t) DUK_LINE_MACRO; \ - DUK_ASSERT(duk__err >= 0 && duk__err <= 0xff); \ - DUK_ASSERT(duk__line >= 0 && duk__line <= 0x00ffffffL); \ - duk_err_handle_error_fmt((thr), \ - DUK_FILE_MACRO, \ - (((duk_uint_t) duk__err) << 24) | ((duk_uint_t) duk__line), \ - (fmt), \ - (arg1), \ - (arg2)); \ - } while (0) -#define DUK_ERROR_RAW_FMT2(thr, file, line, err, fmt, arg1, arg2) \ - do { \ - duk_errcode_t duk__err = (err); \ - duk_int_t duk__line = (duk_int_t) (line); \ - DUK_ASSERT(duk__err >= 0 && duk__err <= 0xff); \ - DUK_ASSERT(duk__line >= 0 && duk__line <= 0x00ffffffL); \ - duk_err_handle_error_fmt((thr), \ - (file), \ - (((duk_uint_t) duk__err) << 24) | ((duk_uint_t) duk__line), \ - (fmt), \ - (arg1), \ - (arg2)); \ - } while (0) - -#define DUK_ERROR_FMT3(thr, err, fmt, arg1, arg2, arg3) \ - do { \ - duk_errcode_t duk__err = (err); \ - duk_int_t duk__line = (duk_int_t) DUK_LINE_MACRO; \ - DUK_ASSERT(duk__err >= 0 && duk__err <= 0xff); \ - DUK_ASSERT(duk__line >= 0 && duk__line <= 0x00ffffffL); \ - duk_err_handle_error_fmt((thr), \ - DUK_FILE_MACRO, \ - (((duk_uint_t) duk__err) << 24) | ((duk_uint_t) duk__line), \ - (fmt), \ - (arg1), \ - (arg2), \ - (arg3)); \ - } while (0) -#define DUK_ERROR_RAW_FMT3(thr, file, line, err, fmt, arg1, arg2, arg3) \ - do { \ - duk_errcode_t duk__err = (err); \ - duk_int_t duk__line = (duk_int_t) (line); \ - DUK_ASSERT(duk__err >= 0 && duk__err <= 0xff); \ - DUK_ASSERT(duk__line >= 0 && duk__line <= 0x00ffffffL); \ - duk_err_handle_error_fmt((thr), \ - (file), \ - (((duk_uint_t) duk__err) << 24) | ((duk_uint_t) duk__line), \ - (fmt), \ - (arg1), \ - (arg2), \ - (arg3)); \ - } while (0) - -#define DUK_ERROR_FMT4(thr, err, fmt, arg1, arg2, arg3, arg4) \ - do { \ - duk_errcode_t duk__err = (err); \ - duk_int_t duk__line = (duk_int_t) DUK_LINE_MACRO; \ - DUK_ASSERT(duk__err >= 0 && duk__err <= 0xff); \ - DUK_ASSERT(duk__line >= 0 && duk__line <= 0x00ffffffL); \ - duk_err_handle_error_fmt((thr), \ - DUK_FILE_MACRO, \ - (((duk_uint_t) duk__err) << 24) | ((duk_uint_t) duk__line), \ - (fmt), \ - (arg1), \ - (arg2), \ - (arg3), \ - (arg4)); \ - } while (0) -#define DUK_ERROR_RAW_FMT4(thr, file, line, err, fmt, arg1, arg2, arg3, arg4) \ - do { \ - duk_errcode_t duk__err = (err); \ - duk_int_t duk__line = (duk_int_t) (line); \ - DUK_ASSERT(duk__err >= 0 && duk__err <= 0xff); \ - DUK_ASSERT(duk__line >= 0 && duk__line <= 0x00ffffffL); \ - duk_err_handle_error_fmt((thr), \ - (file), \ - (((duk_uint_t) duk__err) << 24) | ((duk_uint_t) duk__line), \ - (fmt), \ - (arg1), \ - (arg2), \ - (arg3), \ - (arg4)); \ - } while (0) - -#else /* DUK_USE_VERBOSE_ERRORS */ - -#define DUK_ERROR(thr, err, msg) duk_err_handle_error((thr), (err)) -#define DUK_ERROR_RAW(thr, file, line, err, msg) duk_err_handle_error((thr), (err)) - -#define DUK_ERROR_FMT1(thr, err, fmt, arg1) DUK_ERROR((thr), (err), (fmt)) -#define DUK_ERROR_RAW_FMT1(thr, file, line, err, fmt, arg1) DUK_ERROR_RAW((thr), (file), (line), (err), (fmt)) - -#define DUK_ERROR_FMT2(thr, err, fmt, arg1, arg2) DUK_ERROR((thr), (err), (fmt)) -#define DUK_ERROR_RAW_FMT2(thr, file, line, err, fmt, arg1, arg2) DUK_ERROR_RAW((thr), (file), (line), (err), (fmt)) - -#define DUK_ERROR_FMT3(thr, err, fmt, arg1, arg2, arg3) DUK_ERROR((thr), (err), (fmt)) -#define DUK_ERROR_RAW_FMT3(thr, file, line, err, fmt, arg1, arg2, arg3) DUK_ERROR_RAW((thr), (file), (line), (err), (fmt)) - -#define DUK_ERROR_FMT4(thr, err, fmt, arg1, arg2, arg3, arg4) DUK_ERROR((thr), (err), (fmt)) -#define DUK_ERROR_RAW_FMT4(thr, file, line, err, fmt, arg1, arg2, arg3, arg4) DUK_ERROR_RAW((thr), (file), (line), (err), (fmt)) - -#endif /* DUK_USE_VERBOSE_ERRORS */ - -/* - * Fatal error without context - * - * The macro is an expression to make it compatible with DUK_ASSERT_EXPR(). - */ - -#define DUK_FATAL_WITHOUT_CONTEXT(msg) duk_default_fatal_handler(NULL, (msg)) - -/* - * Error throwing helpers - * - * The goal is to provide verbose and configurable error messages. Call - * sites should be clean in source code and compile to a small footprint. - * Small footprint is also useful for performance because small cold paths - * reduce code cache pressure. Adding macros here only makes sense if there - * are enough call sites to get concrete benefits. - * - * DUK_ERROR_xxx() macros are generic and can be used anywhere. - * - * DUK_DCERROR_xxx() macros can only be used in Duktape/C functions where - * the "return DUK_RET_xxx;" shorthand is available for low memory targets. - * The DUK_DCERROR_xxx() macros always either throw or perform a - * 'return DUK_RET_xxx' from the calling function. - */ - -#if defined(DUK_USE_VERBOSE_ERRORS) -/* Verbose errors with key/value summaries (non-paranoid) or without key/value - * summaries (paranoid, for some security sensitive environments), the paranoid - * vs. non-paranoid distinction affects only a few specific errors. - */ -#if defined(DUK_USE_PARANOID_ERRORS) -#define DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, expectname, lowmemstr) \ - do { \ - duk_err_require_type_index((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO, (idx), (expectname)); \ - } while (0) -#else /* DUK_USE_PARANOID_ERRORS */ -#define DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, expectname, lowmemstr) \ - do { \ - duk_err_require_type_index((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO, (idx), (expectname)); \ - } while (0) -#endif /* DUK_USE_PARANOID_ERRORS */ - -#define DUK_ERROR_INTERNAL(thr) \ - do { \ - duk_err_error_internal((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO); \ - } while (0) -#define DUK_DCERROR_INTERNAL(thr) \ - do { \ - DUK_ERROR_INTERNAL((thr)); \ - return 0; \ - } while (0) -#define DUK_ERROR_ALLOC_FAILED(thr) \ - do { \ - duk_err_error_alloc_failed((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO); \ - } while (0) -#define DUK_ERROR_UNSUPPORTED(thr) \ - do { \ - DUK_ERROR((thr), DUK_ERR_ERROR, DUK_STR_UNSUPPORTED); \ - } while (0) -#define DUK_DCERROR_UNSUPPORTED(thr) \ - do { \ - DUK_ERROR_UNSUPPORTED((thr)); \ - return 0; \ - } while (0) -#define DUK_ERROR_ERROR(thr, msg) \ - do { \ - duk_err_error((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO, (msg)); \ - } while (0) -#define DUK_ERROR_RANGE_INDEX(thr, idx) \ - do { \ - duk_err_range_index((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO, (idx)); \ - } while (0) -#define DUK_ERROR_RANGE_PUSH_BEYOND(thr) \ - do { \ - duk_err_range_push_beyond((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO); \ - } while (0) -#define DUK_ERROR_RANGE_INVALID_ARGS(thr) \ - do { \ - DUK_ERROR_RANGE((thr), DUK_STR_INVALID_ARGS); \ - } while (0) -#define DUK_DCERROR_RANGE_INVALID_ARGS(thr) \ - do { \ - DUK_ERROR_RANGE_INVALID_ARGS((thr)); \ - return 0; \ - } while (0) -#define DUK_ERROR_RANGE_INVALID_COUNT(thr) \ - do { \ - DUK_ERROR_RANGE((thr), DUK_STR_INVALID_COUNT); \ - } while (0) -#define DUK_DCERROR_RANGE_INVALID_COUNT(thr) \ - do { \ - DUK_ERROR_RANGE_INVALID_COUNT((thr)); \ - return 0; \ - } while (0) -#define DUK_ERROR_RANGE_INVALID_LENGTH(thr) \ - do { \ - DUK_ERROR_RANGE((thr), DUK_STR_INVALID_LENGTH); \ - } while (0) -#define DUK_DCERROR_RANGE_INVALID_LENGTH(thr) \ - do { \ - DUK_ERROR_RANGE_INVALID_LENGTH((thr)); \ - return 0; \ - } while (0) -#define DUK_ERROR_RANGE(thr, msg) \ - do { \ - duk_err_range((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO, (msg)); \ - } while (0) -#define DUK_ERROR_EVAL(thr, msg) \ - do { \ - DUK_ERROR((thr), DUK_ERR_EVAL_ERROR, (msg)); \ - } while (0) -#define DUK_ERROR_REFERENCE(thr, msg) \ - do { \ - DUK_ERROR((thr), DUK_ERR_REFERENCE_ERROR, (msg)); \ - } while (0) -#define DUK_ERROR_SYNTAX(thr, msg) \ - do { \ - DUK_ERROR((thr), DUK_ERR_SYNTAX_ERROR, (msg)); \ - } while (0) -#define DUK_ERROR_TYPE_INVALID_ARGS(thr) \ - do { \ - duk_err_type_invalid_args((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO); \ - } while (0) -#define DUK_DCERROR_TYPE_INVALID_ARGS(thr) \ - do { \ - DUK_ERROR_TYPE_INVALID_ARGS((thr)); \ - return 0; \ - } while (0) -#define DUK_ERROR_TYPE_INVALID_STATE(thr) \ - do { \ - duk_err_type_invalid_state((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO); \ - } while (0) -#define DUK_DCERROR_TYPE_INVALID_STATE(thr) \ - do { \ - DUK_ERROR_TYPE_INVALID_STATE((thr)); \ - return 0; \ - } while (0) -#define DUK_ERROR_TYPE_INVALID_TRAP_RESULT(thr) \ - do { \ - duk_err_type_invalid_trap_result((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO); \ - } while (0) -#define DUK_DCERROR_TYPE_INVALID_TRAP_RESULT(thr) \ - do { \ - DUK_ERROR_TYPE((thr), DUK_STR_INVALID_TRAP_RESULT); \ - } while (0) -#define DUK_ERROR_TYPE(thr, msg) \ - do { \ - DUK_ERROR((thr), DUK_ERR_TYPE_ERROR, (msg)); \ - } while (0) -#define DUK_ERROR_URI(thr, msg) \ - do { \ - DUK_ERROR((thr), DUK_ERR_URI_ERROR, (msg)); \ - } while (0) -#else /* DUK_USE_VERBOSE_ERRORS */ -/* Non-verbose errors for low memory targets: no file, line, or message. */ - -#define DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, expectname, lowmemstr) \ - do { \ - duk_err_type((thr)); \ - } while (0) - -#define DUK_ERROR_INTERNAL(thr) \ - do { \ - duk_err_error((thr)); \ - } while (0) -#define DUK_DCERROR_INTERNAL(thr) \ - do { \ - DUK_UNREF((thr)); \ - return DUK_RET_ERROR; \ - } while (0) -#define DUK_ERROR_ALLOC_FAILED(thr) \ - do { \ - duk_err_error((thr)); \ - } while (0) -#define DUK_ERROR_UNSUPPORTED(thr) \ - do { \ - duk_err_error((thr)); \ - } while (0) -#define DUK_DCERROR_UNSUPPORTED(thr) \ - do { \ - DUK_UNREF((thr)); \ - return DUK_RET_ERROR; \ - } while (0) -#define DUK_ERROR_ERROR(thr, msg) \ - do { \ - duk_err_error((thr)); \ - } while (0) -#define DUK_ERROR_RANGE_INDEX(thr, idx) \ - do { \ - duk_err_range((thr)); \ - } while (0) -#define DUK_ERROR_RANGE_PUSH_BEYOND(thr) \ - do { \ - duk_err_range((thr)); \ - } while (0) -#define DUK_ERROR_RANGE_INVALID_ARGS(thr) \ - do { \ - duk_err_range((thr)); \ - } while (0) -#define DUK_DCERROR_RANGE_INVALID_ARGS(thr) \ - do { \ - DUK_UNREF((thr)); \ - return DUK_RET_RANGE_ERROR; \ - } while (0) -#define DUK_ERROR_RANGE_INVALID_COUNT(thr) \ - do { \ - duk_err_range((thr)); \ - } while (0) -#define DUK_DCERROR_RANGE_INVALID_COUNT(thr) \ - do { \ - DUK_UNREF((thr)); \ - return DUK_RET_RANGE_ERROR; \ - } while (0) -#define DUK_ERROR_RANGE_INVALID_LENGTH(thr) \ - do { \ - duk_err_range((thr)); \ - } while (0) -#define DUK_DCERROR_RANGE_INVALID_LENGTH(thr) \ - do { \ - DUK_UNREF((thr)); \ - return DUK_RET_RANGE_ERROR; \ - } while (0) -#define DUK_ERROR_RANGE(thr, msg) \ - do { \ - duk_err_range((thr)); \ - } while (0) -#define DUK_ERROR_EVAL(thr, msg) \ - do { \ - duk_err_eval((thr)); \ - } while (0) -#define DUK_ERROR_REFERENCE(thr, msg) \ - do { \ - duk_err_reference((thr)); \ - } while (0) -#define DUK_ERROR_SYNTAX(thr, msg) \ - do { \ - duk_err_syntax((thr)); \ - } while (0) -#define DUK_ERROR_TYPE_INVALID_ARGS(thr) \ - do { \ - duk_err_type((thr)); \ - } while (0) -#define DUK_DCERROR_TYPE_INVALID_ARGS(thr) \ - do { \ - DUK_UNREF((thr)); \ - return DUK_RET_TYPE_ERROR; \ - } while (0) -#define DUK_ERROR_TYPE_INVALID_STATE(thr) \ - do { \ - duk_err_type((thr)); \ - } while (0) -#define DUK_DCERROR_TYPE_INVALID_STATE(thr) \ - do { \ - duk_err_type((thr)); \ - } while (0) -#define DUK_ERROR_TYPE_INVALID_TRAP_RESULT(thr) \ - do { \ - duk_err_type((thr)); \ - } while (0) -#define DUK_DCERROR_TYPE_INVALID_TRAP_RESULT(thr) \ - do { \ - DUK_UNREF((thr)); \ - return DUK_RET_TYPE_ERROR; \ - } while (0) -#define DUK_ERROR_TYPE_INVALID_TRAP_RESULT(thr) \ - do { \ - duk_err_type((thr)); \ - } while (0) -#define DUK_ERROR_TYPE(thr, msg) \ - do { \ - duk_err_type((thr)); \ - } while (0) -#define DUK_ERROR_URI(thr, msg) \ - do { \ - duk_err_uri((thr)); \ - } while (0) -#endif /* DUK_USE_VERBOSE_ERRORS */ - -/* - * Assert macro: failure causes a fatal error. - * - * NOTE: since the assert macro doesn't take a heap/context argument, there's - * no way to look up a heap/context specific fatal error handler which may have - * been given by the application. Instead, assertion failures always use the - * internal default fatal error handler; it can be replaced via duk_config.h - * and then applies to all Duktape heaps. - */ - -#if defined(DUK_USE_ASSERTIONS) - -/* The message should be a compile time constant without formatting (less risk); - * we don't care about assertion text size because they're not used in production - * builds. - */ -#define DUK_ASSERT(x) \ - do { \ - if (!(x)) { \ - DUK_FATAL_WITHOUT_CONTEXT("assertion failed: " #x " (" DUK_FILE_MACRO \ - ":" DUK_MACRO_STRINGIFY(DUK_LINE_MACRO) ")"); \ - } \ - } while (0) - -/* Assertion compatible inside a comma expression, evaluates to void. */ -#define DUK_ASSERT_EXPR(x) \ - ((void) ((x) ? 0 : \ - (DUK_FATAL_WITHOUT_CONTEXT("assertion failed: " #x " (" DUK_FILE_MACRO \ - ":" DUK_MACRO_STRINGIFY(DUK_LINE_MACRO) ")"), \ - 0))) - -#else /* DUK_USE_ASSERTIONS */ - -#define DUK_ASSERT(x) \ - do { /* assertion omitted */ \ - } while (0) - -#define DUK_ASSERT_EXPR(x) ((void) 0) - -#endif /* DUK_USE_ASSERTIONS */ - -/* this variant is used when an assert would generate a compile warning by - * being always true (e.g. >= 0 comparison for an unsigned value - */ -#define DUK_ASSERT_DISABLE(x) \ - do { /* assertion disabled */ \ - } while (0) - -/* - * Assertion helpers - */ - -#if defined(DUK_USE_ASSERTIONS) && defined(DUK_USE_REFERENCE_COUNTING) -#define DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(h) \ - do { \ - DUK_ASSERT((h) == NULL || DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) (h)) > 0); \ - } while (0) -#define DUK_ASSERT_REFCOUNT_NONZERO_TVAL(tv) \ - do { \ - if ((tv) != NULL && DUK_TVAL_IS_HEAP_ALLOCATED((tv))) { \ - DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(DUK_TVAL_GET_HEAPHDR((tv))) > 0); \ - } \ - } while (0) -#else -#define DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(h) /* no refcount check */ -#define DUK_ASSERT_REFCOUNT_NONZERO_TVAL(tv) /* no refcount check */ -#endif - -#define DUK_ASSERT_TOP(ctx, n) DUK_ASSERT((duk_idx_t) duk_get_top((ctx)) == (duk_idx_t) (n)) - -#if defined(DUK_USE_ASSERTIONS) && defined(DUK_USE_PACKED_TVAL) -#define DUK_ASSERT_DOUBLE_IS_NORMALIZED(dval) \ - do { \ - duk_double_union duk__assert_tmp_du; \ - duk__assert_tmp_du.d = (dval); \ - DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&duk__assert_tmp_du)); \ - } while (0) -#else -#define DUK_ASSERT_DOUBLE_IS_NORMALIZED(dval) /* nop */ -#endif - -#define DUK_ASSERT_VS_SPACE(thr) DUK_ASSERT(thr->valstack_top < thr->valstack_end) - -/* - * Helper to initialize a memory area (e.g. struct) with garbage when - * assertions enabled. - */ - -#if defined(DUK_USE_ASSERTIONS) -#define DUK_ASSERT_SET_GARBAGE(ptr, size) \ - do { \ - duk_memset_unsafe((void *) (ptr), 0x5a, size); \ - } while (0) -#else -#define DUK_ASSERT_SET_GARBAGE(ptr, size) \ - do { \ - } while (0) -#endif - -/* - * Helper for valstack space - * - * Caller of DUK_ASSERT_VALSTACK_SPACE() estimates the number of free stack entries - * required for its own use, and any child calls which are not (a) Duktape API calls - * or (b) Duktape calls which involve extending the valstack (e.g. getter call). - */ - -#define DUK_VALSTACK_ASSERT_EXTRA \ - 5 /* this is added to checks to allow for Duktape \ - * API calls in addition to function's own use \ - */ -#if defined(DUK_USE_ASSERTIONS) -#define DUK_ASSERT_VALSTACK_SPACE(thr, n) \ - do { \ - DUK_ASSERT((thr) != NULL); \ - DUK_ASSERT((thr)->valstack_end - (thr)->valstack_top >= (n) + DUK_VALSTACK_ASSERT_EXTRA); \ - } while (0) -#else -#define DUK_ASSERT_VALSTACK_SPACE(thr, n) /* no valstack space check */ -#endif - -/* - * Prototypes - */ - -#if defined(DUK_USE_VERBOSE_ERRORS) -DUK_NORETURN( - DUK_INTERNAL_DECL void duk_err_handle_error(duk_hthread *thr, const char *filename, duk_uint_t line_and_code, const char *msg)); -DUK_NORETURN(DUK_INTERNAL_DECL void - duk_err_handle_error_fmt(duk_hthread *thr, const char *filename, duk_uint_t line_and_code, const char *fmt, ...)); -#else /* DUK_USE_VERBOSE_ERRORS */ -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_handle_error(duk_hthread *thr, duk_errcode_t code)); -#endif /* DUK_USE_VERBOSE_ERRORS */ - -#if defined(DUK_USE_VERBOSE_ERRORS) -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_create_and_throw(duk_hthread *thr, - duk_errcode_t code, - const char *msg, - const char *filename, - duk_int_t line)); -#else -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_create_and_throw(duk_hthread *thr, duk_errcode_t code)); -#endif - -DUK_NORETURN(DUK_INTERNAL_DECL void duk_error_throw_from_negative_rc(duk_hthread *thr, duk_ret_t rc)); - -#define DUK_AUGMENT_FLAG_NOBLAME_FILELINE (1U << 0) /* if set, don't blame C file/line for .fileName and .lineNumber */ -#define DUK_AUGMENT_FLAG_SKIP_ONE (1U << 1) /* if set, skip topmost activation in traceback construction */ - -#if defined(DUK_USE_AUGMENT_ERROR_CREATE) -DUK_INTERNAL_DECL void duk_err_augment_error_create(duk_hthread *thr, - duk_hthread *thr_callstack, - const char *filename, - duk_int_t line, - duk_small_uint_t flags); -#endif -#if defined(DUK_USE_AUGMENT_ERROR_THROW) -DUK_INTERNAL_DECL void duk_err_augment_error_throw(duk_hthread *thr); -#endif - -#if defined(DUK_USE_VERBOSE_ERRORS) -#if defined(DUK_USE_PARANOID_ERRORS) -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_require_type_index(duk_hthread *thr, - const char *filename, - duk_int_t linenumber, - duk_idx_t idx, - const char *expect_name)); -#else -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_require_type_index(duk_hthread *thr, - const char *filename, - duk_int_t linenumber, - duk_idx_t idx, - const char *expect_name)); -#endif -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_error_internal(duk_hthread *thr, const char *filename, duk_int_t linenumber)); -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_error_alloc_failed(duk_hthread *thr, const char *filename, duk_int_t linenumber)); -DUK_NORETURN( - DUK_INTERNAL_DECL void duk_err_error(duk_hthread *thr, const char *filename, duk_int_t linenumber, const char *message)); -DUK_NORETURN( - DUK_INTERNAL_DECL void duk_err_range_index(duk_hthread *thr, const char *filename, duk_int_t linenumber, duk_idx_t idx)); -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_range_push_beyond(duk_hthread *thr, const char *filename, duk_int_t linenumber)); -DUK_NORETURN( - DUK_INTERNAL_DECL void duk_err_range(duk_hthread *thr, const char *filename, duk_int_t linenumber, const char *message)); -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_type_invalid_args(duk_hthread *thr, const char *filename, duk_int_t linenumber)); -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_type_invalid_state(duk_hthread *thr, const char *filename, duk_int_t linenumber)); -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_type_invalid_trap_result(duk_hthread *thr, const char *filename, duk_int_t linenumber)); -#else /* DUK_VERBOSE_ERRORS */ -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_error(duk_hthread *thr)); -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_range(duk_hthread *thr)); -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_eval(duk_hthread *thr)); -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_reference(duk_hthread *thr)); -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_syntax(duk_hthread *thr)); -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_type(duk_hthread *thr)); -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_uri(duk_hthread *thr)); -#endif /* DUK_VERBOSE_ERRORS */ - -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_longjmp(duk_hthread *thr)); - -DUK_NORETURN(DUK_INTERNAL_DECL void duk_default_fatal_handler(void *udata, const char *msg)); - -DUK_INTERNAL_DECL void duk_err_setup_ljstate1(duk_hthread *thr, duk_small_uint_t lj_type, duk_tval *tv_val); -#if defined(DUK_USE_DEBUGGER_SUPPORT) -DUK_INTERNAL_DECL void duk_err_check_debugger_integration(duk_hthread *thr); -#endif - -DUK_INTERNAL_DECL duk_hobject *duk_error_prototype_from_code(duk_hthread *thr, duk_errcode_t err_code); - -#endif /* DUK_ERROR_H_INCLUDED */ -/* #include duk_unicode.h */ -/* - * Unicode helpers - */ - -#if !defined(DUK_UNICODE_H_INCLUDED) -#define DUK_UNICODE_H_INCLUDED - -/* - * UTF-8 / XUTF-8 / CESU-8 constants - */ - -#define DUK_UNICODE_MAX_XUTF8_LENGTH 7 /* up to 36 bit codepoints */ -#define DUK_UNICODE_MAX_XUTF8_BMP_LENGTH 3 /* all codepoints up to U+FFFF */ -#define DUK_UNICODE_MAX_CESU8_LENGTH 6 /* all codepoints up to U+10FFFF */ -#define DUK_UNICODE_MAX_CESU8_BMP_LENGTH 3 /* all codepoints up to U+FFFF */ - -/* - * Useful Unicode codepoints - * - * Integer constants must be signed to avoid unexpected coercions - * in comparisons. - */ - -#define DUK_UNICODE_CP_ZWNJ 0x200cL /* zero-width non-joiner */ -#define DUK_UNICODE_CP_ZWJ 0x200dL /* zero-width joiner */ -#define DUK_UNICODE_CP_REPLACEMENT_CHARACTER \ - 0xfffdL /* http://en.wikipedia.org/wiki/Replacement_character#Replacement_character \ - */ - -/* - * ASCII character constants - * - * C character literals like 'x' have a platform specific value and do - * not match ASCII (UTF-8) values on e.g. EBCDIC platforms. So, use - * these (admittedly awkward) constants instead. These constants must - * also have signed values to avoid unexpected coercions in comparisons. - * - * http://en.wikipedia.org/wiki/ASCII - */ - -#define DUK_ASC_NUL 0x00 -#define DUK_ASC_SOH 0x01 -#define DUK_ASC_STX 0x02 -#define DUK_ASC_ETX 0x03 -#define DUK_ASC_EOT 0x04 -#define DUK_ASC_ENQ 0x05 -#define DUK_ASC_ACK 0x06 -#define DUK_ASC_BEL 0x07 -#define DUK_ASC_BS 0x08 -#define DUK_ASC_HT 0x09 -#define DUK_ASC_LF 0x0a -#define DUK_ASC_VT 0x0b -#define DUK_ASC_FF 0x0c -#define DUK_ASC_CR 0x0d -#define DUK_ASC_SO 0x0e -#define DUK_ASC_SI 0x0f -#define DUK_ASC_DLE 0x10 -#define DUK_ASC_DC1 0x11 -#define DUK_ASC_DC2 0x12 -#define DUK_ASC_DC3 0x13 -#define DUK_ASC_DC4 0x14 -#define DUK_ASC_NAK 0x15 -#define DUK_ASC_SYN 0x16 -#define DUK_ASC_ETB 0x17 -#define DUK_ASC_CAN 0x18 -#define DUK_ASC_EM 0x19 -#define DUK_ASC_SUB 0x1a -#define DUK_ASC_ESC 0x1b -#define DUK_ASC_FS 0x1c -#define DUK_ASC_GS 0x1d -#define DUK_ASC_RS 0x1e -#define DUK_ASC_US 0x1f -#define DUK_ASC_SPACE 0x20 -#define DUK_ASC_EXCLAMATION 0x21 -#define DUK_ASC_DOUBLEQUOTE 0x22 -#define DUK_ASC_HASH 0x23 -#define DUK_ASC_DOLLAR 0x24 -#define DUK_ASC_PERCENT 0x25 -#define DUK_ASC_AMP 0x26 -#define DUK_ASC_SINGLEQUOTE 0x27 -#define DUK_ASC_LPAREN 0x28 -#define DUK_ASC_RPAREN 0x29 -#define DUK_ASC_STAR 0x2a -#define DUK_ASC_PLUS 0x2b -#define DUK_ASC_COMMA 0x2c -#define DUK_ASC_MINUS 0x2d -#define DUK_ASC_PERIOD 0x2e -#define DUK_ASC_SLASH 0x2f -#define DUK_ASC_0 0x30 -#define DUK_ASC_1 0x31 -#define DUK_ASC_2 0x32 -#define DUK_ASC_3 0x33 -#define DUK_ASC_4 0x34 -#define DUK_ASC_5 0x35 -#define DUK_ASC_6 0x36 -#define DUK_ASC_7 0x37 -#define DUK_ASC_8 0x38 -#define DUK_ASC_9 0x39 -#define DUK_ASC_COLON 0x3a -#define DUK_ASC_SEMICOLON 0x3b -#define DUK_ASC_LANGLE 0x3c -#define DUK_ASC_EQUALS 0x3d -#define DUK_ASC_RANGLE 0x3e -#define DUK_ASC_QUESTION 0x3f -#define DUK_ASC_ATSIGN 0x40 -#define DUK_ASC_UC_A 0x41 -#define DUK_ASC_UC_B 0x42 -#define DUK_ASC_UC_C 0x43 -#define DUK_ASC_UC_D 0x44 -#define DUK_ASC_UC_E 0x45 -#define DUK_ASC_UC_F 0x46 -#define DUK_ASC_UC_G 0x47 -#define DUK_ASC_UC_H 0x48 -#define DUK_ASC_UC_I 0x49 -#define DUK_ASC_UC_J 0x4a -#define DUK_ASC_UC_K 0x4b -#define DUK_ASC_UC_L 0x4c -#define DUK_ASC_UC_M 0x4d -#define DUK_ASC_UC_N 0x4e -#define DUK_ASC_UC_O 0x4f -#define DUK_ASC_UC_P 0x50 -#define DUK_ASC_UC_Q 0x51 -#define DUK_ASC_UC_R 0x52 -#define DUK_ASC_UC_S 0x53 -#define DUK_ASC_UC_T 0x54 -#define DUK_ASC_UC_U 0x55 -#define DUK_ASC_UC_V 0x56 -#define DUK_ASC_UC_W 0x57 -#define DUK_ASC_UC_X 0x58 -#define DUK_ASC_UC_Y 0x59 -#define DUK_ASC_UC_Z 0x5a -#define DUK_ASC_LBRACKET 0x5b -#define DUK_ASC_BACKSLASH 0x5c -#define DUK_ASC_RBRACKET 0x5d -#define DUK_ASC_CARET 0x5e -#define DUK_ASC_UNDERSCORE 0x5f -#define DUK_ASC_GRAVE 0x60 -#define DUK_ASC_LC_A 0x61 -#define DUK_ASC_LC_B 0x62 -#define DUK_ASC_LC_C 0x63 -#define DUK_ASC_LC_D 0x64 -#define DUK_ASC_LC_E 0x65 -#define DUK_ASC_LC_F 0x66 -#define DUK_ASC_LC_G 0x67 -#define DUK_ASC_LC_H 0x68 -#define DUK_ASC_LC_I 0x69 -#define DUK_ASC_LC_J 0x6a -#define DUK_ASC_LC_K 0x6b -#define DUK_ASC_LC_L 0x6c -#define DUK_ASC_LC_M 0x6d -#define DUK_ASC_LC_N 0x6e -#define DUK_ASC_LC_O 0x6f -#define DUK_ASC_LC_P 0x70 -#define DUK_ASC_LC_Q 0x71 -#define DUK_ASC_LC_R 0x72 -#define DUK_ASC_LC_S 0x73 -#define DUK_ASC_LC_T 0x74 -#define DUK_ASC_LC_U 0x75 -#define DUK_ASC_LC_V 0x76 -#define DUK_ASC_LC_W 0x77 -#define DUK_ASC_LC_X 0x78 -#define DUK_ASC_LC_Y 0x79 -#define DUK_ASC_LC_Z 0x7a -#define DUK_ASC_LCURLY 0x7b -#define DUK_ASC_PIPE 0x7c -#define DUK_ASC_RCURLY 0x7d -#define DUK_ASC_TILDE 0x7e -#define DUK_ASC_DEL 0x7f - -/* - * Miscellaneous - */ - -/* Uppercase A is 0x41, lowercase a is 0x61; OR 0x20 to convert uppercase - * to lowercase. - */ -#define DUK_LOWERCASE_CHAR_ASCII(x) ((x) | 0x20) - -/* - * Unicode tables - */ - -#if defined(DUK_USE_SOURCE_NONBMP) -/* - * Automatically generated by extract_chars.py, do not edit! - */ - -extern const duk_uint8_t duk_unicode_ids_noa[1116]; -#else -/* - * Automatically generated by extract_chars.py, do not edit! - */ - -extern const duk_uint8_t duk_unicode_ids_noabmp[625]; -#endif - -#if defined(DUK_USE_SOURCE_NONBMP) -/* - * Automatically generated by extract_chars.py, do not edit! - */ - -extern const duk_uint8_t duk_unicode_ids_m_let_noa[42]; -#else -/* - * Automatically generated by extract_chars.py, do not edit! - */ - -extern const duk_uint8_t duk_unicode_ids_m_let_noabmp[24]; -#endif - -#if defined(DUK_USE_SOURCE_NONBMP) -/* - * Automatically generated by extract_chars.py, do not edit! - */ - -extern const duk_uint8_t duk_unicode_idp_m_ids_noa[576]; -#else -/* - * Automatically generated by extract_chars.py, do not edit! - */ - -extern const duk_uint8_t duk_unicode_idp_m_ids_noabmp[358]; -#endif - -/* - * Automatically generated by extract_caseconv.py, do not edit! - */ - -extern const duk_uint8_t duk_unicode_caseconv_uc[1411]; -extern const duk_uint8_t duk_unicode_caseconv_lc[706]; - -#if defined(DUK_USE_REGEXP_CANON_WORKAROUND) -/* - * Automatically generated by extract_caseconv.py, do not edit! - */ - -extern const duk_uint16_t duk_unicode_re_canon_lookup[65536]; -#endif - -#if defined(DUK_USE_REGEXP_CANON_BITMAP) -/* - * Automatically generated by extract_caseconv.py, do not edit! - */ - -#define DUK_CANON_BITMAP_BLKSIZE 32 -#define DUK_CANON_BITMAP_BLKSHIFT 5 -#define DUK_CANON_BITMAP_BLKMASK 31 -extern const duk_uint8_t duk_unicode_re_canon_bitmap[256]; -#endif - -/* - * Extern - */ - -/* duk_unicode_support.c */ -#if !defined(DUK_SINGLE_FILE) -DUK_INTERNAL_DECL const duk_uint8_t duk_unicode_xutf8_markers[7]; -DUK_INTERNAL_DECL const duk_uint16_t duk_unicode_re_ranges_digit[2]; -DUK_INTERNAL_DECL const duk_uint16_t duk_unicode_re_ranges_white[22]; -DUK_INTERNAL_DECL const duk_uint16_t duk_unicode_re_ranges_wordchar[8]; -DUK_INTERNAL_DECL const duk_uint16_t duk_unicode_re_ranges_not_digit[4]; -DUK_INTERNAL_DECL const duk_uint16_t duk_unicode_re_ranges_not_white[24]; -DUK_INTERNAL_DECL const duk_uint16_t duk_unicode_re_ranges_not_wordchar[10]; -DUK_INTERNAL_DECL const duk_int8_t duk_is_idchar_tab[128]; -#endif /* !DUK_SINGLE_FILE */ - -/* - * Prototypes - */ - -DUK_INTERNAL_DECL duk_small_int_t duk_unicode_get_xutf8_length(duk_ucodepoint_t cp); -#if defined(DUK_USE_ASSERTIONS) -DUK_INTERNAL_DECL duk_small_int_t duk_unicode_get_cesu8_length(duk_ucodepoint_t cp); -#endif -DUK_INTERNAL_DECL duk_small_int_t duk_unicode_encode_xutf8(duk_ucodepoint_t cp, duk_uint8_t *out); -DUK_INTERNAL_DECL duk_small_int_t duk_unicode_encode_cesu8(duk_ucodepoint_t cp, duk_uint8_t *out); -DUK_INTERNAL_DECL duk_small_int_t duk_unicode_decode_xutf8(duk_hthread *thr, - const duk_uint8_t **ptr, - const duk_uint8_t *ptr_start, - const duk_uint8_t *ptr_end, - duk_ucodepoint_t *out_cp); -DUK_INTERNAL_DECL duk_ucodepoint_t duk_unicode_decode_xutf8_checked(duk_hthread *thr, - const duk_uint8_t **ptr, - const duk_uint8_t *ptr_start, - const duk_uint8_t *ptr_end); -DUK_INTERNAL_DECL duk_size_t duk_unicode_unvalidated_utf8_length(const duk_uint8_t *data, duk_size_t blen); -DUK_INTERNAL_DECL duk_bool_t duk_unicode_is_utf8_compatible(const duk_uint8_t *buf, duk_size_t len); -DUK_INTERNAL_DECL duk_small_int_t duk_unicode_is_whitespace(duk_codepoint_t cp); -DUK_INTERNAL_DECL duk_small_int_t duk_unicode_is_line_terminator(duk_codepoint_t cp); -DUK_INTERNAL_DECL duk_small_int_t duk_unicode_is_identifier_start(duk_codepoint_t cp); -DUK_INTERNAL_DECL duk_small_int_t duk_unicode_is_identifier_part(duk_codepoint_t cp); -DUK_INTERNAL_DECL duk_small_int_t duk_unicode_is_letter(duk_codepoint_t cp); -DUK_INTERNAL_DECL void duk_unicode_case_convert_string(duk_hthread *thr, duk_bool_t uppercase); -#if defined(DUK_USE_REGEXP_SUPPORT) -DUK_INTERNAL_DECL duk_codepoint_t duk_unicode_re_canonicalize_char(duk_hthread *thr, duk_codepoint_t cp); -DUK_INTERNAL_DECL duk_small_int_t duk_unicode_re_is_wordchar(duk_codepoint_t cp); -#endif - -#endif /* DUK_UNICODE_H_INCLUDED */ -/* #include duk_json.h */ -/* - * Defines for JSON, especially duk_bi_json.c. - */ - -#if !defined(DUK_JSON_H_INCLUDED) -#define DUK_JSON_H_INCLUDED - -/* Encoding/decoding flags */ -#define DUK_JSON_FLAG_ASCII_ONLY (1U << 0) /* escape any non-ASCII characters */ -#define DUK_JSON_FLAG_AVOID_KEY_QUOTES (1U << 1) /* avoid key quotes when key is an ASCII Identifier */ -#define DUK_JSON_FLAG_EXT_CUSTOM (1U << 2) /* extended types: custom encoding */ -#define DUK_JSON_FLAG_EXT_COMPATIBLE (1U << 3) /* extended types: compatible encoding */ - -/* How much stack to require on entry to object/array encode */ -#define DUK_JSON_ENC_REQSTACK 32 - -/* How much stack to require on entry to object/array decode */ -#define DUK_JSON_DEC_REQSTACK 32 - -/* How large a loop detection stack to use */ -#define DUK_JSON_ENC_LOOPARRAY 64 - -/* Encoding state. Heap object references are all borrowed. */ -typedef struct { - duk_hthread *thr; - duk_bufwriter_ctx bw; /* output bufwriter */ - duk_hobject *h_replacer; /* replacer function */ - duk_hstring *h_gap; /* gap (if empty string, NULL) */ - duk_idx_t idx_proplist; /* explicit PropertyList */ - duk_idx_t idx_loop; /* valstack index of loop detection object */ - duk_small_uint_t flags; - duk_small_uint_t flag_ascii_only; - duk_small_uint_t flag_avoid_key_quotes; -#if defined(DUK_USE_JX) || defined(DUK_USE_JC) - duk_small_uint_t flag_ext_custom; - duk_small_uint_t flag_ext_compatible; - duk_small_uint_t flag_ext_custom_or_compatible; -#endif - duk_uint_t recursion_depth; - duk_uint_t recursion_limit; - duk_uint_t mask_for_undefined; /* type bit mask: types which certainly produce 'undefined' */ -#if defined(DUK_USE_JX) || defined(DUK_USE_JC) - duk_small_uint_t stridx_custom_undefined; - duk_small_uint_t stridx_custom_nan; - duk_small_uint_t stridx_custom_neginf; - duk_small_uint_t stridx_custom_posinf; - duk_small_uint_t stridx_custom_function; -#endif - duk_hobject *visiting[DUK_JSON_ENC_LOOPARRAY]; /* indexed by recursion_depth */ -} duk_json_enc_ctx; - -typedef struct { - duk_hthread *thr; - const duk_uint8_t *p; - const duk_uint8_t *p_start; - const duk_uint8_t *p_end; - duk_idx_t idx_reviver; - duk_small_uint_t flags; -#if defined(DUK_USE_JX) || defined(DUK_USE_JC) - duk_small_uint_t flag_ext_custom; - duk_small_uint_t flag_ext_compatible; - duk_small_uint_t flag_ext_custom_or_compatible; -#endif - duk_int_t recursion_depth; - duk_int_t recursion_limit; -} duk_json_dec_ctx; - -#endif /* DUK_JSON_H_INCLUDED */ -/* #include duk_js.h */ -/* - * ECMAScript execution, support primitives. - */ - -#if !defined(DUK_JS_H_INCLUDED) -#define DUK_JS_H_INCLUDED - -/* Flags for call handling. Lowest flags must match bytecode DUK_BC_CALL_FLAG_xxx 1:1. */ -#define DUK_CALL_FLAG_TAILCALL (1U << 0) /* setup for a tail call */ -#define DUK_CALL_FLAG_CONSTRUCT (1U << 1) /* constructor call (i.e. called as 'new Foo()') */ -#define DUK_CALL_FLAG_CALLED_AS_EVAL (1U << 2) /* call was made using the identifier 'eval' */ -#define DUK_CALL_FLAG_ALLOW_ECMATOECMA (1U << 3) /* ecma-to-ecma call with executor reuse is possible */ -#define DUK_CALL_FLAG_DIRECT_EVAL (1U << 4) /* call is a direct eval call */ -#define DUK_CALL_FLAG_CONSTRUCT_PROXY (1U << 5) /* handled via 'construct' proxy trap, check return value invariant(s) */ -#define DUK_CALL_FLAG_DEFAULT_INSTANCE_UPDATED \ - (1U << 6) /* prototype of 'default instance' updated, temporary flag in call handling */ - -/* Flags for duk_js_equals_helper(). */ -#define DUK_EQUALS_FLAG_SAMEVALUE (1U << 0) /* use SameValue instead of non-strict equality */ -#define DUK_EQUALS_FLAG_STRICT (1U << 1) /* use strict equality instead of non-strict equality */ - -/* Flags for duk_js_compare_helper(). */ -#define DUK_COMPARE_FLAG_NEGATE (1U << 0) /* negate result */ -#define DUK_COMPARE_FLAG_EVAL_LEFT_FIRST (1U << 1) /* eval left argument first */ - -/* conversions, coercions, comparison, etc */ -DUK_INTERNAL_DECL duk_bool_t duk_js_toboolean(duk_tval *tv); -DUK_INTERNAL_DECL duk_double_t duk_js_tonumber(duk_hthread *thr, duk_tval *tv); -DUK_INTERNAL_DECL duk_double_t duk_js_tointeger_number(duk_double_t x); -DUK_INTERNAL_DECL duk_double_t duk_js_tointeger(duk_hthread *thr, duk_tval *tv); -DUK_INTERNAL_DECL duk_uint32_t duk_js_touint32(duk_hthread *thr, duk_tval *tv); -DUK_INTERNAL_DECL duk_int32_t duk_js_toint32(duk_hthread *thr, duk_tval *tv); -DUK_INTERNAL_DECL duk_uint16_t duk_js_touint16(duk_hthread *thr, duk_tval *tv); -DUK_INTERNAL_DECL duk_uarridx_t duk_js_to_arrayindex_string(const duk_uint8_t *str, duk_uint32_t blen); -#if !defined(DUK_USE_HSTRING_ARRIDX) -DUK_INTERNAL_DECL duk_uarridx_t duk_js_to_arrayindex_hstring_fast_known(duk_hstring *h); -DUK_INTERNAL_DECL duk_uarridx_t duk_js_to_arrayindex_hstring_fast(duk_hstring *h); -#endif -DUK_INTERNAL_DECL duk_bool_t duk_js_equals_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_uint_t flags); -DUK_INTERNAL_DECL duk_small_int_t duk_js_data_compare(const duk_uint8_t *buf1, - const duk_uint8_t *buf2, - duk_size_t len1, - duk_size_t len2); -DUK_INTERNAL_DECL duk_small_int_t duk_js_string_compare(duk_hstring *h1, duk_hstring *h2); -#if 0 /* unused */ -DUK_INTERNAL_DECL duk_small_int_t duk_js_buffer_compare(duk_heap *heap, duk_hbuffer *h1, duk_hbuffer *h2); -#endif -DUK_INTERNAL_DECL duk_bool_t duk_js_compare_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_uint_t flags); -DUK_INTERNAL_DECL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y); -#if defined(DUK_USE_SYMBOL_BUILTIN) -DUK_INTERNAL_DECL duk_bool_t duk_js_instanceof_ordinary(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y); -#endif -DUK_INTERNAL_DECL duk_bool_t duk_js_in(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y); -DUK_INTERNAL_DECL duk_small_uint_t duk_js_typeof_stridx(duk_tval *tv_x); -DUK_INTERNAL_DECL duk_bool_t duk_js_isarray_hobject(duk_hobject *h); -DUK_INTERNAL_DECL duk_bool_t duk_js_isarray(duk_tval *tv); - -/* arithmetic */ -DUK_INTERNAL_DECL double duk_js_arith_pow(double x, double y); -DUK_INTERNAL_DECL double duk_js_arith_mod(double x, double y); - -#define duk_js_equals(thr, tv_x, tv_y) duk_js_equals_helper((thr), (tv_x), (tv_y), 0) -#define duk_js_strict_equals(tv_x, tv_y) duk_js_equals_helper(NULL, (tv_x), (tv_y), DUK_EQUALS_FLAG_STRICT) -#define duk_js_samevalue(tv_x, tv_y) duk_js_equals_helper(NULL, (tv_x), (tv_y), DUK_EQUALS_FLAG_SAMEVALUE) - -/* E5 Sections 11.8.1, 11.8.5; x < y */ -#define duk_js_lessthan(thr, tv_x, tv_y) duk_js_compare_helper((thr), (tv_x), (tv_Y), DUK_COMPARE_FLAG_EVAL_LEFT_FIRST) - -/* E5 Sections 11.8.2, 11.8.5; x > y --> y < x */ -#define duk_js_greaterthan(thr, tv_x, tv_y) duk_js_compare_helper((thr), (tv_y), (tv_x), 0) - -/* E5 Sections 11.8.3, 11.8.5; x <= y --> not (x > y) --> not (y < x) */ -#define duk_js_lessthanorequal(thr, tv_x, tv_y) duk_js_compare_helper((thr), (tv_y), (tv_x), DUK_COMPARE_FLAG_NEGATE) - -/* E5 Sections 11.8.4, 11.8.5; x >= y --> not (x < y) */ -#define duk_js_greaterthanorequal(thr, tv_x, tv_y) \ - duk_js_compare_helper((thr), (tv_x), (tv_y), DUK_COMPARE_FLAG_EVAL_LEFT_FIRST | DUK_COMPARE_FLAG_NEGATE) - -/* identifiers and environment handling */ -#if 0 /*unused*/ -DUK_INTERNAL duk_bool_t duk_js_hasvar_envrec(duk_hthread *thr, duk_hobject *env, duk_hstring *name); -#endif -DUK_INTERNAL_DECL duk_bool_t duk_js_getvar_envrec(duk_hthread *thr, duk_hobject *env, duk_hstring *name, duk_bool_t throw_flag); -DUK_INTERNAL_DECL duk_bool_t duk_js_getvar_activation(duk_hthread *thr, - duk_activation *act, - duk_hstring *name, - duk_bool_t throw_flag); -DUK_INTERNAL_DECL void duk_js_putvar_envrec(duk_hthread *thr, - duk_hobject *env, - duk_hstring *name, - duk_tval *val, - duk_bool_t strict); -DUK_INTERNAL_DECL void duk_js_putvar_activation(duk_hthread *thr, - duk_activation *act, - duk_hstring *name, - duk_tval *val, - duk_bool_t strict); -#if 0 /*unused*/ -DUK_INTERNAL_DECL duk_bool_t duk_js_delvar_envrec(duk_hthread *thr, duk_hobject *env, duk_hstring *name); -#endif -DUK_INTERNAL_DECL duk_bool_t duk_js_delvar_activation(duk_hthread *thr, duk_activation *act, duk_hstring *name); -DUK_INTERNAL_DECL duk_bool_t duk_js_declvar_activation(duk_hthread *thr, - duk_activation *act, - duk_hstring *name, - duk_tval *val, - duk_small_uint_t prop_flags, - duk_bool_t is_func_decl); -DUK_INTERNAL_DECL void duk_js_init_activation_environment_records_delayed(duk_hthread *thr, duk_activation *act); -DUK_INTERNAL_DECL void duk_js_close_environment_record(duk_hthread *thr, duk_hobject *env); -DUK_INTERNAL_DECL duk_hobject *duk_create_activation_environment_record(duk_hthread *thr, - duk_hobject *func, - duk_size_t bottom_byteoff); -DUK_INTERNAL_DECL void duk_js_push_closure(duk_hthread *thr, - duk_hcompfunc *fun_temp, - duk_hobject *outer_var_env, - duk_hobject *outer_lex_env, - duk_bool_t add_auto_proto); - -/* call handling */ -DUK_INTERNAL_DECL void duk_native_stack_check(duk_hthread *thr); -DUK_INTERNAL_DECL duk_int_t duk_handle_call_unprotected(duk_hthread *thr, duk_idx_t idx_func, duk_small_uint_t call_flags); -DUK_INTERNAL_DECL duk_int_t duk_handle_call_unprotected_nargs(duk_hthread *thr, duk_idx_t nargs, duk_small_uint_t call_flags); -DUK_INTERNAL_DECL duk_int_t -duk_handle_safe_call(duk_hthread *thr, duk_safe_call_function func, void *udata, duk_idx_t num_stack_args, duk_idx_t num_stack_res); -DUK_INTERNAL_DECL void duk_call_construct_postprocess(duk_hthread *thr, duk_small_uint_t proxy_invariant); -#if defined(DUK_USE_VERBOSE_ERRORS) -DUK_INTERNAL_DECL void duk_call_setup_propcall_error(duk_hthread *thr, duk_tval *tv_base, duk_tval *tv_key); -#endif - -/* bytecode execution */ -DUK_INTERNAL_DECL void duk_js_execute_bytecode(duk_hthread *exec_thr); - -#endif /* DUK_JS_H_INCLUDED */ -/* #include duk_numconv.h */ -/* - * Number-to-string conversion. The semantics of these is very tightly - * bound with the ECMAScript semantics required for call sites. - */ - -#if !defined(DUK_NUMCONV_H_INCLUDED) -#define DUK_NUMCONV_H_INCLUDED - -/* Output a specified number of digits instead of using the shortest - * form. Used for toPrecision() and toFixed(). - */ -#define DUK_N2S_FLAG_FIXED_FORMAT (1U << 0) - -/* Force exponential format. Used for toExponential(). */ -#define DUK_N2S_FLAG_FORCE_EXP (1U << 1) - -/* If number would need zero padding (for whole number part), use - * exponential format instead. E.g. if input number is 12300, 3 - * digits are generated ("123"), output "1.23e+4" instead of "12300". - * Used for toPrecision(). - */ -#define DUK_N2S_FLAG_NO_ZERO_PAD (1U << 2) - -/* Digit count indicates number of fractions (i.e. an absolute - * digit index instead of a relative one). Used together with - * DUK_N2S_FLAG_FIXED_FORMAT for toFixed(). - */ -#define DUK_N2S_FLAG_FRACTION_DIGITS (1U << 3) - -/* - * String-to-number conversion - */ - -/* Maximum exponent value when parsing numbers. This is not strictly - * compliant as there should be no upper limit, but as we parse the - * exponent without a bigint, impose some limit. The limit should be - * small enough that multiplying it (or limit-1 to be precise) won't - * overflow signed 32-bit integer range. Exponent is only parsed with - * radix 10, but with maximum radix (36) a safe limit is: - * (10000000*36).toString(16) -> '15752a00' - */ -#define DUK_S2N_MAX_EXPONENT 10000000L - -/* Trim white space (= allow leading and trailing whitespace) */ -#define DUK_S2N_FLAG_TRIM_WHITE (1U << 0) - -/* Allow exponent */ -#define DUK_S2N_FLAG_ALLOW_EXP (1U << 1) - -/* Allow trailing garbage (e.g. treat "123foo" as "123) */ -#define DUK_S2N_FLAG_ALLOW_GARBAGE (1U << 2) - -/* Allow leading plus sign */ -#define DUK_S2N_FLAG_ALLOW_PLUS (1U << 3) - -/* Allow leading minus sign */ -#define DUK_S2N_FLAG_ALLOW_MINUS (1U << 4) - -/* Allow 'Infinity' */ -#define DUK_S2N_FLAG_ALLOW_INF (1U << 5) - -/* Allow fraction part */ -#define DUK_S2N_FLAG_ALLOW_FRAC (1U << 6) - -/* Allow naked fraction (e.g. ".123") */ -#define DUK_S2N_FLAG_ALLOW_NAKED_FRAC (1U << 7) - -/* Allow empty fraction (e.g. "123.") */ -#define DUK_S2N_FLAG_ALLOW_EMPTY_FRAC (1U << 8) - -/* Allow empty string to be interpreted as 0 */ -#define DUK_S2N_FLAG_ALLOW_EMPTY_AS_ZERO (1U << 9) - -/* Allow leading zeroes (e.g. "0123" -> "123") */ -#define DUK_S2N_FLAG_ALLOW_LEADING_ZERO (1U << 10) - -/* Allow automatic detection of hex base ("0x" or "0X" prefix), - * overrides radix argument and forces integer mode. - */ -#define DUK_S2N_FLAG_ALLOW_AUTO_HEX_INT (1U << 11) - -/* Allow automatic detection of legacy octal base ("0n"), - * overrides radix argument and forces integer mode. - */ -#define DUK_S2N_FLAG_ALLOW_AUTO_LEGACY_OCT_INT (1U << 12) - -/* Allow automatic detection of ES2015 octal base ("0o123"), - * overrides radix argument and forces integer mode. - */ -#define DUK_S2N_FLAG_ALLOW_AUTO_OCT_INT (1U << 13) - -/* Allow automatic detection of ES2015 binary base ("0b10001"), - * overrides radix argument and forces integer mode. - */ -#define DUK_S2N_FLAG_ALLOW_AUTO_BIN_INT (1U << 14) - -/* - * Prototypes - */ - -DUK_INTERNAL_DECL void duk_numconv_stringify(duk_hthread *thr, - duk_small_int_t radix, - duk_small_int_t digits, - duk_small_uint_t flags); -DUK_INTERNAL_DECL void duk_numconv_parse(duk_hthread *thr, duk_small_int_t radix, duk_small_uint_t flags); - -#endif /* DUK_NUMCONV_H_INCLUDED */ -/* #include duk_bi_protos.h */ -/* - * Prototypes for built-in functions not automatically covered by the - * header declarations emitted by genbuiltins.py. - */ - -#if !defined(DUK_BUILTIN_PROTOS_H_INCLUDED) -#define DUK_BUILTIN_PROTOS_H_INCLUDED - -/* Buffer size needed for ISO 8601 formatting. - * Accurate value is 32 + 1 for NUL termination: - * >>> len('+123456-01-23T12:34:56.123+12:34') - * 32 - * Include additional space to be safe. - */ -#define DUK_BI_DATE_ISO8601_BUFSIZE 40 - -/* Helpers exposed for internal use */ -DUK_INTERNAL_DECL void duk_bi_date_timeval_to_parts(duk_double_t d, duk_int_t *parts, duk_double_t *dparts, duk_small_uint_t flags); -DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_timeval_from_dparts(duk_double_t *dparts, duk_small_uint_t flags); -DUK_INTERNAL_DECL duk_bool_t duk_bi_date_is_leap_year(duk_int_t year); -DUK_INTERNAL_DECL duk_bool_t duk_bi_date_timeval_in_valid_range(duk_double_t x); -DUK_INTERNAL_DECL duk_bool_t duk_bi_date_year_in_valid_range(duk_double_t year); -DUK_INTERNAL_DECL duk_bool_t duk_bi_date_timeval_in_leeway_range(duk_double_t x); -/* Built-in providers */ -#if defined(DUK_USE_DATE_NOW_GETTIMEOFDAY) -DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_now_gettimeofday(void); -#endif -#if defined(DUK_USE_DATE_NOW_TIME) -DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_now_time(void); -#endif -#if defined(DUK_USE_DATE_NOW_WINDOWS) -DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_now_windows(void); -#endif -#if defined(DUK_USE_DATE_NOW_WINDOWS_SUBMS) -DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_now_windows_subms(void); -#endif -#if defined(DUK_USE_DATE_TZO_GMTIME_R) || defined(DUK_USE_DATE_TZO_GMTIME_S) || defined(DUK_USE_DATE_TZO_GMTIME) -DUK_INTERNAL_DECL duk_int_t duk_bi_date_get_local_tzoffset_gmtime(duk_double_t d); -#endif -#if defined(DUK_USE_DATE_TZO_WINDOWS) -DUK_INTERNAL_DECL duk_int_t duk_bi_date_get_local_tzoffset_windows(duk_double_t d); -#endif -#if defined(DUK_USE_DATE_TZO_WINDOWS_NO_DST) -DUK_INTERNAL_DECL duk_int_t duk_bi_date_get_local_tzoffset_windows_no_dst(duk_double_t d); -#endif -#if defined(DUK_USE_DATE_PRS_STRPTIME) -DUK_INTERNAL_DECL duk_bool_t duk_bi_date_parse_string_strptime(duk_hthread *thr, const char *str); -#endif -#if defined(DUK_USE_DATE_PRS_GETDATE) -DUK_INTERNAL_DECL duk_bool_t duk_bi_date_parse_string_getdate(duk_hthread *thr, const char *str); -#endif -#if defined(DUK_USE_DATE_FMT_STRFTIME) -DUK_INTERNAL_DECL duk_bool_t duk_bi_date_format_parts_strftime(duk_hthread *thr, - duk_int_t *parts, - duk_int_t tzoffset, - duk_small_uint_t flags); -#endif - -#if defined(DUK_USE_GET_MONOTONIC_TIME_CLOCK_GETTIME) -DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_monotonic_time_clock_gettime(void); -#endif -#if defined(DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC) -DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_monotonic_time_windows_qpc(void); -#endif - -DUK_INTERNAL_DECL -void duk_bi_json_parse_helper(duk_hthread *thr, duk_idx_t idx_value, duk_idx_t idx_reviver, duk_small_uint_t flags); -DUK_INTERNAL_DECL -void duk_bi_json_stringify_helper(duk_hthread *thr, - duk_idx_t idx_value, - duk_idx_t idx_replacer, - duk_idx_t idx_space, - duk_small_uint_t flags); - -DUK_INTERNAL_DECL duk_ret_t duk_textdecoder_decode_utf8_nodejs(duk_hthread *thr); - -#if defined(DUK_USE_ES6_PROXY) -DUK_INTERNAL_DECL void duk_proxy_ownkeys_postprocess(duk_hthread *thr, duk_hobject *h_proxy_target, duk_uint_t flags); -#endif - -#endif /* DUK_BUILTIN_PROTOS_H_INCLUDED */ -/* #include duk_selftest.h */ -/* - * Selftest code - */ - -#if !defined(DUK_SELFTEST_H_INCLUDED) -#define DUK_SELFTEST_H_INCLUDED - -#if defined(DUK_USE_SELF_TESTS) -DUK_INTERNAL_DECL duk_uint_t duk_selftest_run_tests(duk_alloc_function alloc_func, - duk_realloc_function realloc_func, - duk_free_function free_func, - void *udata); -#endif - -#endif /* DUK_SELFTEST_H_INCLUDED */ - -#endif /* DUK_INTERNAL_H_INCLUDED */ - -#if defined(DUK_USE_COMPUTED_NAN) -DUK_INTERNAL double duk_computed_nan; -#endif - -#if defined(DUK_USE_COMPUTED_INFINITY) -DUK_INTERNAL double duk_computed_infinity; -#endif - -#if defined(DUK_USE_REPL_FPCLASSIFY) -DUK_INTERNAL int duk_repl_fpclassify(double x) { - duk_double_union u; - duk_uint_fast16_t expt; - duk_small_int_t mzero; - - u.d = x; - expt = (duk_uint_fast16_t) (u.us[DUK_DBL_IDX_US0] & 0x7ff0UL); - if (expt > 0x0000UL && expt < 0x7ff0UL) { - /* expt values [0x001,0x7fe] = normal */ - return DUK_FP_NORMAL; - } - - mzero = (u.ui[DUK_DBL_IDX_UI1] == 0 && (u.ui[DUK_DBL_IDX_UI0] & 0x000fffffUL) == 0); - if (expt == 0x0000UL) { - /* expt 0x000 is zero/subnormal */ - if (mzero) { - return DUK_FP_ZERO; - } else { - return DUK_FP_SUBNORMAL; - } - } else { - /* expt 0xfff is infinite/nan */ - if (mzero) { - return DUK_FP_INFINITE; - } else { - return DUK_FP_NAN; - } - } -} -#endif - -#if defined(DUK_USE_REPL_SIGNBIT) -DUK_INTERNAL int duk_repl_signbit(double x) { - duk_double_union u; - u.d = x; - return (int) (u.uc[DUK_DBL_IDX_UC0] & 0x80UL); -} -#endif - -#if defined(DUK_USE_REPL_ISFINITE) -DUK_INTERNAL int duk_repl_isfinite(double x) { - int c = DUK_FPCLASSIFY(x); - if (c == DUK_FP_NAN || c == DUK_FP_INFINITE) { - return 0; - } else { - return 1; - } -} -#endif - -#if defined(DUK_USE_REPL_ISNAN) -DUK_INTERNAL int duk_repl_isnan(double x) { - int c = DUK_FPCLASSIFY(x); - return (c == DUK_FP_NAN); -} -#endif - -#if defined(DUK_USE_REPL_ISINF) -DUK_INTERNAL int duk_repl_isinf(double x) { - int c = DUK_FPCLASSIFY(x); - return (c == DUK_FP_INFINITE); -} -#endif -/* - * Debugging macro calls. - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_DEBUG) - -/* - * Debugging enabled - */ - -#include -#include -#include - -#if !defined(DUK_USE_DEBUG_WRITE) -#error debugging enabled (DUK_USE_DEBUG) but DUK_USE_DEBUG_WRITE not defined -#endif - -#define DUK__DEBUG_BUFSIZE DUK_USE_DEBUG_BUFSIZE - -#if defined(DUK_USE_VARIADIC_MACROS) - -DUK_INTERNAL void duk_debug_log(duk_int_t level, const char *file, duk_int_t line, const char *func, const char *fmt, ...) { - va_list ap; - long arg_level; - const char *arg_file; - long arg_line; - const char *arg_func; - const char *arg_msg; - char buf[DUK__DEBUG_BUFSIZE]; - - va_start(ap, fmt); - - duk_memzero((void *) buf, (size_t) DUK__DEBUG_BUFSIZE); - duk_debug_vsnprintf(buf, DUK__DEBUG_BUFSIZE - 1, fmt, ap); - - arg_level = (long) level; - arg_file = (const char *) file; - arg_line = (long) line; - arg_func = (const char *) func; - arg_msg = (const char *) buf; - DUK_USE_DEBUG_WRITE(arg_level, arg_file, arg_line, arg_func, arg_msg); - - va_end(ap); -} - -#else /* DUK_USE_VARIADIC_MACROS */ - -DUK_INTERNAL char duk_debug_file_stash[DUK_DEBUG_STASH_SIZE]; -DUK_INTERNAL duk_int_t duk_debug_line_stash; -DUK_INTERNAL char duk_debug_func_stash[DUK_DEBUG_STASH_SIZE]; -DUK_INTERNAL duk_int_t duk_debug_level_stash; - -DUK_INTERNAL void duk_debug_log(const char *fmt, ...) { - va_list ap; - long arg_level; - const char *arg_file; - long arg_line; - const char *arg_func; - const char *arg_msg; - char buf[DUK__DEBUG_BUFSIZE]; - - va_start(ap, fmt); - - duk_memzero((void *) buf, (size_t) DUK__DEBUG_BUFSIZE); - duk_debug_vsnprintf(buf, DUK__DEBUG_BUFSIZE - 1, fmt, ap); - - arg_level = (long) duk_debug_level_stash; - arg_file = (const char *) duk_debug_file_stash; - arg_line = (long) duk_debug_line_stash; - arg_func = (const char *) duk_debug_func_stash; - arg_msg = (const char *) buf; - DUK_USE_DEBUG_WRITE(arg_level, arg_file, arg_line, arg_func, arg_msg); - - va_end(ap); -} - -#endif /* DUK_USE_VARIADIC_MACROS */ - -#else /* DUK_USE_DEBUG */ - -/* - * Debugging disabled - */ - -#endif /* DUK_USE_DEBUG */ - -/* automatic undefs */ -#undef DUK__DEBUG_BUFSIZE -/* - * Automatically generated by genbuiltins.py, do not edit! - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_ASSERTIONS) -#define DUK__REFCINIT(refc) 0 /*h_assert_refcount*/, (refc) /*actual*/ -#else -#define DUK__REFCINIT(refc) (refc) /*actual*/ -#endif - -#if defined(DUK_USE_ROM_STRINGS) -#error ROM support not enabled, rerun configure.py with --rom-support -#else /* DUK_USE_ROM_STRINGS */ -DUK_INTERNAL const duk_uint8_t duk_strings_data[972] = { -79,40,209,144,168,105,6,78,54,139,89,185,44,48,46,90,120,8,154,140,35,103, -35,113,193,73,5,52,112,180,104,166,135,52,188,4,98,12,27,146,156,80,211,31, -129,115,150,64,52,220,109,24,18,68,156,24,38,67,114,36,55,9,119,151,132, -140,93,18,113,128,153,201,212,201,205,2,248,8,196,24,224,104,82,146,40,224, -193,48,114,168,37,147,196,54,123,28,4,98,12,43,148,67,103,177,192,70,32, -196,121,68,54,123,28,18,192,199,144,124,4,98,12,43,136,108,244,117,184,8, -196,24,95,40,134,207,71,91,128,140,65,133,113,13,158,158,151,1,24,131,11, -229,16,217,233,233,112,17,136,48,206,21,110,4,244,244,184,8,196,24,103,10, -183,2,122,218,156,4,98,12,24,203,112,64,179,113,193,79,8,218,155,131,32, -184,70,212,220,13,10,82,68,252,123,144,217,146,38,228,207,18,0,100,37,64, -178,212,11,161,17,104,162,96,10,200,193,57,165,65,169,16,5,100,81,27,70,18, -32,10,200,68,185,13,116,221,197,184,64,89,57,41,197,13,49,234,5,208,156, -113,87,55,118,147,20,187,56,161,166,92,221,212,73,210,236,226,134,153,115, -119,76,201,203,179,138,26,99,73,212,136,136,164,25,174,137,56,32,72,137, -101,23,52,45,13,34,86,9,79,136,104,201,114,149,96,52,138,134,140,151,75, -226,233,186,120,121,22,39,54,83,141,5,55,68,236,36,164,3,16,225,115,150,64, -52,205,163,2,72,154,83,138,26,99,75,12,11,150,103,5,36,20,211,70,140,133, -67,72,49,241,160,227,81,196,52,168,106,39,132,252,183,136,105,80,212,79,2, -249,110,128,126,88,95,133,109,237,237,237,151,235,127,46,249,119,203,190, -186,206,33,181,2,208,61,190,12,19,34,65,19,81,132,108,228,97,1,107,33,12, -32,45,100,137,64,247,175,9,19,155,41,198,130,155,134,69,146,100,227,226, -231,146,51,192,204,73,140,224,145,221,102,241,68,196,169,248,30,75,12,11, -151,242,233,187,143,138,24,137,162,164,255,253,63,3,201,97,129,114,254,92, -112,75,136,108,166,6,136,159,255,167,224,121,44,48,46,95,203,166,238,74, -113,67,77,201,128,223,255,223,224,121,44,48,46,95,203,145,46,9,205,16,39, -201,62,36,0,192,21,147,255,238,145,39,199,197,211,116,240,242,113,197,78, -214,211,226,233,187,107,105,19,119,37,56,161,166,52,221,212,201,205,36,240, -242,16,96,152,12,26,20,164,137,150,70,154,103,28,137,50,202,96,18,132,241, -41,104,105,56,218,48,36,138,183,57,56,128,68,24,38,2,52,12,34,10,133,147, -141,3,8,119,185,13,153,34,125,206,76,17,49,38,93,206,52,151,154,119,56,28, -76,130,112,200,141,206,21,209,96,23,35,238,114,160,139,0,243,238,114,78, -164,68,68,110,113,226,210,90,26,66,110,113,128,121,247,57,80,68,141,170, -183,56,84,52,11,70,73,19,110,114,160,93,8,113,57,143,66,200,84,53,244,154, -73,24,240,81,32,38,68,18,49,228,207,23,88,100,109,70,114,92,193,4,137,173, -168,36,220,73,19,247,247,182,168,209,144,187,223,58,156,104,79,190,183,127, -123,105,160,110,247,206,167,26,19,239,173,223,222,218,67,75,189,243,169, -198,132,251,235,183,247,182,154,134,151,123,231,83,141,9,247,215,111,239, -109,22,141,22,247,206,167,26,19,239,172,223,218,45,26,47,157,78,52,39,223, -74,24,144,10,32,129,34,20,64,152,142,129,57,179,67,104,68,12,129,161,140, -72,156,100,40,40,185,152,100,89,38,65,13,196,34,228,67,149,13,2,215,129, -149,209,65,104,209,77,14,104,144,81,33,170,67,101,48,52,68,113,70,210,88, -209,36,233,22,154,86,68,196,114,76,232,145,102,120,186,195,156,112,105,225, -228,113,71,80,68,162,115,101,50,85,200,25,108,116,44,132,178,38,114,137,96, -148,136,70,209,134,37,222,232,204,228,188,200,209,200,200,99,221,25,150,84, -121,34,70,209,107,36,227,66,20,160,92,136,164,49,235,35,8,217,201,40,108, -201,18,128,68,26,201,51,188,2,80,12,67,190,40,168,38,68,190,46,153,5,50,12, -207,160,86,129,26,83,4,208,34,225,4,88,192, -}; -#endif /* DUK_USE_ROM_STRINGS */ - -#if defined(DUK_USE_ROM_OBJECTS) -#error ROM support not enabled, rerun configure.py with --rom-support -#else /* DUK_USE_ROM_OBJECTS */ -/* native functions: 185 */ -DUK_INTERNAL const duk_c_function duk_bi_native_functions[185] = { - NULL, - duk_bi_array_constructor, - duk_bi_array_constructor_is_array, - duk_bi_array_prototype_concat, - duk_bi_array_prototype_indexof_shared, - duk_bi_array_prototype_iter_shared, - duk_bi_array_prototype_join_shared, - duk_bi_array_prototype_pop, - duk_bi_array_prototype_push, - duk_bi_array_prototype_reduce_shared, - duk_bi_array_prototype_reverse, - duk_bi_array_prototype_shift, - duk_bi_array_prototype_slice, - duk_bi_array_prototype_sort, - duk_bi_array_prototype_splice, - duk_bi_array_prototype_to_string, - duk_bi_array_prototype_unshift, - duk_bi_arraybuffer_constructor, - duk_bi_arraybuffer_isview, - duk_bi_boolean_constructor, - duk_bi_boolean_prototype_tostring_shared, - duk_bi_buffer_compare_shared, - duk_bi_buffer_readfield, - duk_bi_buffer_slice_shared, - duk_bi_buffer_writefield, - duk_bi_cbor_decode, - duk_bi_cbor_encode, - duk_bi_dataview_constructor, - duk_bi_date_constructor, - duk_bi_date_constructor_now, - duk_bi_date_constructor_parse, - duk_bi_date_constructor_utc, - duk_bi_date_prototype_get_shared, - duk_bi_date_prototype_get_timezone_offset, - duk_bi_date_prototype_set_shared, - duk_bi_date_prototype_set_time, - duk_bi_date_prototype_to_json, - duk_bi_date_prototype_toprimitive, - duk_bi_date_prototype_tostring_shared, - duk_bi_date_prototype_value_of, - duk_bi_duktape_object_act, - duk_bi_duktape_object_compact, - duk_bi_duktape_object_dec, - duk_bi_duktape_object_enc, - duk_bi_duktape_object_fin, - duk_bi_duktape_object_gc, - duk_bi_duktape_object_info, - duk_bi_error_constructor_shared, - duk_bi_error_prototype_filename_getter, - duk_bi_error_prototype_filename_setter, - duk_bi_error_prototype_linenumber_getter, - duk_bi_error_prototype_linenumber_setter, - duk_bi_error_prototype_stack_getter, - duk_bi_error_prototype_stack_setter, - duk_bi_error_prototype_to_string, - duk_bi_function_constructor, - duk_bi_function_prototype, - duk_bi_function_prototype_apply, - duk_bi_function_prototype_bind, - duk_bi_function_prototype_call, - duk_bi_function_prototype_hasinstance, - duk_bi_function_prototype_to_string, - duk_bi_global_object_decode_uri, - duk_bi_global_object_decode_uri_component, - duk_bi_global_object_encode_uri, - duk_bi_global_object_encode_uri_component, - duk_bi_global_object_escape, - duk_bi_global_object_eval, - duk_bi_global_object_is_finite, - duk_bi_global_object_is_nan, - duk_bi_global_object_parse_float, - duk_bi_global_object_parse_int, - duk_bi_global_object_unescape, - duk_bi_json_object_parse, - duk_bi_json_object_stringify, - duk_bi_math_object_clz32, - duk_bi_math_object_hypot, - duk_bi_math_object_imul, - duk_bi_math_object_max, - duk_bi_math_object_min, - duk_bi_math_object_onearg_shared, - duk_bi_math_object_random, - duk_bi_math_object_sign, - duk_bi_math_object_twoarg_shared, - duk_bi_native_function_length, - duk_bi_native_function_name, - duk_bi_nodejs_buffer_byte_length, - duk_bi_nodejs_buffer_concat, - duk_bi_nodejs_buffer_constructor, - duk_bi_nodejs_buffer_copy, - duk_bi_nodejs_buffer_fill, - duk_bi_nodejs_buffer_is_buffer, - duk_bi_nodejs_buffer_is_encoding, - duk_bi_nodejs_buffer_tojson, - duk_bi_nodejs_buffer_tostring, - duk_bi_nodejs_buffer_write, - duk_bi_number_check_shared, - duk_bi_number_constructor, - duk_bi_number_prototype_to_exponential, - duk_bi_number_prototype_to_fixed, - duk_bi_number_prototype_to_locale_string, - duk_bi_number_prototype_to_precision, - duk_bi_number_prototype_to_string, - duk_bi_number_prototype_value_of, - duk_bi_object_constructor, - duk_bi_object_constructor_assign, - duk_bi_object_constructor_create, - duk_bi_object_constructor_define_properties, - duk_bi_object_constructor_define_property, - duk_bi_object_constructor_get_own_property_descriptor, - duk_bi_object_constructor_is, - duk_bi_object_constructor_is_extensible, - duk_bi_object_constructor_is_sealed_frozen_shared, - duk_bi_object_constructor_keys_shared, - duk_bi_object_constructor_prevent_extensions, - duk_bi_object_constructor_seal_freeze_shared, - duk_bi_object_getprototype_shared, - duk_bi_object_prototype_defineaccessor, - duk_bi_object_prototype_has_own_property, - duk_bi_object_prototype_is_prototype_of, - duk_bi_object_prototype_lookupaccessor, - duk_bi_object_prototype_property_is_enumerable, - duk_bi_object_prototype_to_locale_string, - duk_bi_object_prototype_to_string, - duk_bi_object_prototype_value_of, - duk_bi_object_setprototype_shared, - duk_bi_performance_now, - duk_bi_pointer_constructor, - duk_bi_pointer_prototype_tostring_shared, - duk_bi_proxy_constructor, - duk_bi_reflect_apply, - duk_bi_reflect_construct, - duk_bi_reflect_object_delete_property, - duk_bi_reflect_object_get, - duk_bi_reflect_object_has, - duk_bi_reflect_object_set, - duk_bi_regexp_constructor, - duk_bi_regexp_prototype_exec, - duk_bi_regexp_prototype_flags, - duk_bi_regexp_prototype_shared_getter, - duk_bi_regexp_prototype_test, - duk_bi_regexp_prototype_tostring, - duk_bi_string_constructor, - duk_bi_string_constructor_from_char_code, - duk_bi_string_constructor_from_code_point, - duk_bi_string_prototype_caseconv_shared, - duk_bi_string_prototype_char_at, - duk_bi_string_prototype_char_code_at, - duk_bi_string_prototype_concat, - duk_bi_string_prototype_includes, - duk_bi_string_prototype_indexof_shared, - duk_bi_string_prototype_locale_compare, - duk_bi_string_prototype_match, - duk_bi_string_prototype_repeat, - duk_bi_string_prototype_replace, - duk_bi_string_prototype_search, - duk_bi_string_prototype_slice, - duk_bi_string_prototype_split, - duk_bi_string_prototype_startswith_endswith, - duk_bi_string_prototype_substr, - duk_bi_string_prototype_substring, - duk_bi_string_prototype_to_string, - duk_bi_string_prototype_trim, - duk_bi_symbol_constructor_shared, - duk_bi_symbol_key_for, - duk_bi_symbol_toprimitive, - duk_bi_symbol_tostring_shared, - duk_bi_textdecoder_constructor, - duk_bi_textdecoder_prototype_decode, - duk_bi_textdecoder_prototype_shared_getter, - duk_bi_textencoder_constructor, - duk_bi_textencoder_prototype_encode, - duk_bi_textencoder_prototype_encoding_getter, - duk_bi_thread_constructor, - duk_bi_thread_current, - duk_bi_thread_resume, - duk_bi_thread_yield, - duk_bi_type_error_thrower, - duk_bi_typedarray_buffer_getter, - duk_bi_typedarray_bytelength_getter, - duk_bi_typedarray_byteoffset_getter, - duk_bi_typedarray_constructor, - duk_bi_typedarray_set, - duk_bi_uint8array_allocplain, - duk_bi_uint8array_plainof, -}; -#if defined(DUK_USE_DOUBLE_LE) -DUK_INTERNAL const duk_uint8_t duk_builtins_data[4281] = { -144,148,105,226,32,68,52,228,254,12,104,202,37,132,52,167,194,138,105,245, -124,57,28,211,57,18,64,52,239,126,44,138,111,175,241,164,19,87,145,30,33, -167,22,145,159,8,211,139,9,225,42,5,240,145,139,163,163,8,211,139,10,228, -64,211,19,132,140,93,29,56,70,156,88,119,34,66,146,36,104,137,194,70,46, -142,172,35,78,44,47,146,195,102,11,240,145,139,163,175,8,211,139,9,228,240, -242,112,145,139,163,179,8,211,139,8,237,34,130,118,49,116,118,225,26,48,0, -1,98,29,201,158,46,183,39,135,147,132,140,93,16,132,76,66,33,8,66,16,132, -33,8,66,26,180,105,97,167,68,150,34,33,154,112,0,1,91,247,35,79,111,237, -198,174,232,47,31,23,95,17,13,31,249,96,211,49,50,53,214,77,141,24,0,0,181, -10,228,240,242,15,128,140,65,128,134,188,0,0,90,167,97,181,224,0,2,213,62, -53,224,0,2,213,66,237,120,0,0,181,81,204,107,192,0,5,170,150,67,94,0,0,45, -84,245,90,240,0,1,106,169,162,215,128,0,11,85,93,150,188,0,0,90,171,111,53, -109,22,162,26,48,0,1,84,23,201,146,243,225,26,39,12,145,136,104,192,0,5,61, -11,228,201,121,240,100,19,134,72,196,33,195,14,40,203,112,64,190,76,232, -145,153,136,0,0,0,0,0,0,31,15,249,152,0,0,0,0,0,0,30,15,249,120,144,13,96, -155,194,56,80,206,36,67,141,20,228,70,57,81,206,100,131,156,39,132,168,23, -194,70,46,137,208,21,200,129,166,39,9,24,186,39,72,119,34,66,146,36,104, -137,194,70,46,137,212,23,201,97,179,5,248,72,197,209,58,194,121,60,60,156, -36,98,232,157,129,29,164,80,78,198,46,137,218,146,121,25,71,146,9,209,5, -209,61,48,126,14,138,152,30,67,186,23,143,139,175,131,202,135,228,72,85, -144,83,60,179,30,94,209,233,102,30,98,105,230,103,30,114,121,231,104,30, -122,137,231,233,30,130,153,232,106,30,138,169,232,235,30,144,67,193,25,19, -136,108,207,30,41,224,140,137,194,173,192,153,228,5,242,100,188,248,70,137, -195,36,79,78,47,147,37,231,193,144,78,25,34,122,145,111,36,74,232,176,13, -17,61,234,226,93,207,148,160,84,75,141,7,27,161,32,33,18,225,80,212,76,154, -2,2,70,65,56,100,237,34,140,209,2,67,32,156,50,118,145,64,186,230,61,205, -35,103,155,32,36,141,19,134,78,210,40,206,16,36,70,137,195,39,105,20,11, -174,99,220,210,54,121,210,1,137,33,1,228,207,16,17,70,146,66,3,201,164,32, -0,65,112,152,56,196,159,31,23,77,211,195,201,199,23,160,72,214,246,81,6,12, -73,241,214,111,31,23,60,145,158,56,50,72,81,67,230,232,242,80,19,49,39,199, -89,188,124,92,242,70,120,227,64,194,75,154,72,12,9,73,6,111,21,120,12,40, -144,19,39,25,0,225,144,168,105,56,248,185,228,140,241,200,96,64,100,42,26, -78,62,46,121,35,52,18,92,116,1,36,64,47,158,64,49,98,66,100,156,242,65,23, -196,149,35,103,194,94,100,108,144,230,203,156,64,66,37,201,16,11,32,249, -132,4,34,92,44,93,146,55,152,72,24,137,112,151,153,27,36,5,100,229,144,8, -162,98,92,210,5,76,73,241,214,111,31,23,60,145,158,57,44,48,46,92,185,164, -160,72,151,41,0,50,107,179,244,59,36,93,127,92,6,19,172,3,11,216,0,56,224, -151,29,102,241,241,115,201,25,227,164,64,106,37,199,197,211,116,240,242, -113,197,233,144,40,248,185,228,140,241,196,75,132,109,24,72,128,43,39,84, -129,13,173,161,144,168,105,56,98,78,100,142,214,215,69,1,13,173,161,144, -168,105,57,34,78,100,142,214,215,69,16,67,107,105,110,114,168,254,24,147, -153,35,181,181,212,32,67,107,105,110,114,168,254,72,147,153,35,181,181,212, -36,65,130,3,144,8,26,252,200,13,30,85,16,16,64,90,242,231,192,64,161,163, -203,31,26,172,193,17,4,23,105,159,96,27,172,251,16,32,196,4,14,137,112,17, -136,48,164,28,134,80,215,202,1,132,130,8,12,39,52,64,155,31,24,56,36,1,189, -207,132,0,35,233,35,195,62,3,196,149,36,100,72,160,2,200,232,44,227,0,11, -37,160,68,142,128,36,157,25,200,32,26,79,90,4,73,43,192,122,54,71,65,103, -44,248,14,134,140,151,227,138,231,208,45,96,148,248,134,140,151,227,138, -231,240,1,255,254,10,74,146,56,128,104,4,147,152,72,6,144,28,174,143,8,1, -30,1,165,3,96,31,0,211,3,21,11,153,35,0,211,131,68,131,160,137,16,250,5, -196,131,160,137,200,160,199,156,67,248,0,255,255,65,140,10,48,177,115,56, -35,130,60,19,134,79,89,240,52,177,115,56,39,12,156,123,144,217,251,15,135, -34,167,30,20,170,154,255,232,12,47,244,0,97,28,17,224,39,238,32,40,71,4, -120,39,12,156,4,253,228,5,137,195,39,30,228,54,124,4,253,228,128,194,115, -68,9,252,15,128,232,104,201,126,56,191,35,64,90,193,41,241,13,25,47,199,23, -228,105,3,86,225,1,100,224,156,199,130,36,249,144,10,192,76,71,250,16,15, -18,61,96,17,62,200,3,72,128,136,143,247,32,22,75,64,137,248,64,22,79,90,39, -249,64,38,84,12,167,20,52,223,196,2,230,238,45,214,36,120,32,72,158,208,4, -102,238,45,194,2,201,197,186,196,143,4,9,19,218,0,92,221,202,61,228,143,4, -9,19,218,8,35,55,113,110,16,22,78,81,239,36,120,32,72,158,208,64,73,197,12, -255,0,13,18,60,128,159,212,128,169,76,17,156,185,100,76,255,163,64,65,26, -57,114,200,153,255,70,144,33,13,18,232,50,75,226,104,6,149,3,41,199,246, -130,12,128,28,142,156,120,203,175,158,8,194,207,1,6,81,20,79,88,11,237,84, -11,161,32,127,255,255,255,255,255,247,191,137,235,16,221,170,129,116,36,0, -16,0,0,0,0,0,0,12,196,0,0,0,0,0,0,15,135,242,61,123,164,137,162,164,218,67, -74,134,162,120,128,0,0,0,0,0,1,224,254,71,173,33,129,52,84,155,72,105,80, -212,79,16,0,0,0,0,0,0,60,63,195,244,143,146,22,230,192,0,0,0,0,0,0,176,60, -33,214,2,251,82,1,73,180,134,204,134,36,96,127,255,255,255,255,255,159,161, -144,235,16,221,169,0,164,218,67,102,67,18,48,63,255,255,255,255,255,207, -240,196,60,17,145,56,134,204,241,226,158,8,200,156,42,220,9,158,65,196,34, -92,42,26,137,147,120,64,74,37,196,54,100,49,35,188,36,5,68,184,208,113,187, -194,80,212,75,146,1,73,196,54,100,49,35,188,38,57,37,56,240,0,0,0,0,0,0,0, -0,32,235,248,68,48,156,2,24,94,24,0,243,119,10,139,144,123,242,3,102,238, -18,239,115,72,217,160,11,223,16,23,55,113,241,32,145,36,57,188,18,16,102,3, -5,120,35,34,89,32,15,180,152,173,127,0,218,235,88,0,228,180,227,200,0,0,0, -0,0,0,248,127,197,107,240,64,6,77,220,24,38,78,74,113,67,77,130,4,12,155, -185,52,48,156,148,226,134,155,4,10,194,96,129,132,166,238,45,194,2,201,193, -130,100,228,167,20,52,216,32,113,41,187,139,112,128,178,114,104,97,57,41, -197,13,54,8,32,48,216,32,130,195,224,130,19,97,124,134,23,6,0,57,137,62,77, -12,38,12,0,179,18,124,45,22,190,96,128,141,176,134,28,98,79,180,152,139, -218,45,124,193,1,27,97,16,32,196,159,24,230,204,246,194,40,89,137,62,210, -98,103,92,217,158,216,70,7,49,39,193,130,100,182,17,194,140,73,246,147,16, -250,9,146,216,72,6,49,39,193,131,22,194,72,73,137,62,210,98,31,65,139,97, -40,32,196,159,14,234,70,86,194,88,89,137,62,210,98,63,93,72,202,216,76,10, -49,39,198,33,180,153,37,108,38,134,152,147,237,38,38,117,13,164,201,43,97, -56,40,196,159,36,65,57,163,149,176,158,26,98,79,180,152,165,210,9,205,28, -173,133,0,243,18,124,98,22,180,72,130,115,71,43,97,68,72,196,159,105,49,51, -168,90,209,34,9,205,28,173,133,33,19,18,124,154,24,76,185,164,227,138,89, -18,119,0,7,145,39,201,161,132,188,64,124,137,62,49,11,90,36,65,57,163,149, -210,166,37,34,79,180,152,153,212,45,104,145,4,230,142,87,74,160,84,137,62, -72,130,115,71,43,171,234,134,200,147,237,38,41,116,130,115,71,43,171,235,5, -72,147,227,16,218,76,146,186,254,184,108,137,62,210,98,103,80,218,76,146, -186,254,192,68,137,62,29,212,140,174,207,178,23,34,79,180,152,143,215,82, -50,187,62,208,60,137,62,12,19,37,210,182,21,34,79,180,152,135,208,76,151, -74,224,68,137,62,49,205,153,238,175,186,23,34,79,180,152,153,215,54,103, -186,190,240,92,137,62,22,139,95,48,64,70,235,251,225,210,36,251,73,136,189, -162,215,204,16,17,186,255,2,14,98,79,152,32,35,108,48,64,242,36,249,130,2, -55,75,6,212,224,72,200,51,128,114,108,28,100,128,0,0,0,0,0,0,0,12,110,127, -48,98,115,249,201,117,243,249,195,21,159,206,38,47,63,156,86,8,75,144,94, -82,1,38,73,79,208,67,95,233,1,6,128,14,79,129,186,40,249,18,149,182,207, -144,200,155,188,248,204,105,184,207,142,199,137,175,201,0,159,72,10,5,21, -221,10,120,74,129,124,36,98,232,228,74,81,62,160,20,10,107,186,21,114,32, -105,137,194,70,46,142,68,165,19,235,1,64,170,187,161,119,34,66,146,36,104, -137,194,70,46,142,68,165,19,236,1,64,174,187,161,95,37,134,204,23,225,35, -23,71,34,82,137,246,128,160,89,93,208,167,147,195,201,194,70,46,142,68,165, -19,238,1,64,182,187,161,71,105,20,19,177,139,163,145,41,68,16,7,6,15,82,70, -72,115,96,0,0,0,0,0,27,234,32,91,60,165,195,201,194,8,134,149,216,162,0, -192,41,225,8,2,48,177,36,1,149,13,196,15,0,200,209,97,199,128,99,32,176, -195,192,113,57,143,0,167,133,32,230,80,28,202,139,175,238,2,48,189,192,20, -1,119,80,87,193,186,129,89,56,72,197,209,200,193,185,35,23,71,109,13,219, -36,98,232,237,156,13,26,208,211,14,102,19,87,137,91,95,128,0,10,96,24,92,0, -0,83,2,53,56,0,0,165,3,28,204,160,160,226,100,226,200,211,76,241,240,0,1, -102,8,22,75,64,137,73,20,230,105,133,7,19,39,22,70,154,103,143,128,0,11,48, -20,28,76,156,113,75,34,78,62,0,0,45,3,103,31,0,0,22,65,44,57,137,62,33,179, -216,162,152,192,131,18,124,162,27,61,138,41,108,32,196,159,16,217,232,235, -81,76,104,73,137,62,81,13,158,142,181,20,184,16,98,79,136,108,244,244,168, -166,56,36,196,159,40,134,207,79,74,138,93,10,49,39,194,173,192,158,158,149, -20,188,20,98,79,133,91,129,61,109,74,41,124,30,68,159,16,217,236,83,108,96, -68,137,62,81,13,158,197,54,182,17,34,79,136,108,244,117,169,182,52,38,68, -159,40,134,207,71,90,155,92,8,145,39,196,54,122,122,84,219,28,19,34,79,148, -67,103,167,165,77,174,133,72,147,225,86,224,79,79,74,155,94,10,145,39,194, -173,192,158,182,165,54,190,206,25,212,35,208,226,100,150,211,201,29,162,44, -140,35,103,0,0,0,0,0,0,3,192,252,206,25,228,35,208,226,100,150,211,201,29, -162,44,140,35,103,0,0,0,0,0,0,3,192,252,206,25,244,35,208,226,100,150,211, -201,29,162,44,140,35,103,0,0,0,0,0,0,3,192,252,206,26,4,35,208,226,100,150, -211,201,29,162,44,140,35,103,0,0,0,0,0,0,0,1,0,206,26,20,35,208,226,100, -150,211,201,29,162,44,140,35,103,0,0,0,0,0,0,0,1,0,206,26,36,35,208,226, -100,150,211,201,29,162,44,140,35,103,0,0,0,0,0,0,0,65,0,206,26,52,35,208, -226,100,150,211,201,29,162,44,140,35,103,0,0,0,0,0,0,0,65,0,206,26,68,35, -208,226,100,150,211,201,29,162,44,140,35,103,0,0,0,0,0,0,0,65,0,206,26,84, -35,208,226,100,150,211,201,29,162,44,140,35,103,0,0,0,0,0,0,0,129,0,195, -154,99,16,38,36,0,251,68,117,179,216,162,128,68,72,1,241,13,158,197,20,150, -25,18,0,125,162,58,217,232,235,117,100,162,136,25,18,0,125,162,58,217,232, -235,116,36,162,145,2,226,64,15,136,108,244,117,186,178,81,73,129,113,32,7, -196,54,122,58,221,9,40,165,64,200,144,3,237,17,214,207,79,75,171,37,20,80, -200,144,3,237,17,214,207,79,75,161,37,20,138,23,18,0,124,67,103,167,165, -213,146,138,77,11,137,0,62,33,179,211,210,232,73,69,42,133,196,128,31,10, -183,2,125,89,40,163,5,196,128,31,10,183,2,125,9,40,164,96,200,144,3,224, -221,64,172,157,89,40,163,134,68,128,31,6,234,5,100,232,73,69,35,133,68,128, -31,104,142,182,125,89,40,180,0,168,144,3,237,17,214,207,161,37,22,144,19, -18,0,124,67,103,213,146,139,80,9,137,0,62,33,179,232,73,69,172,5,90,40,153, -59,68,117,179,216,166,192,77,162,137,147,136,108,246,41,180,176,219,69,19, -39,104,142,182,122,58,221,89,41,178,6,218,40,153,59,68,117,179,209,214,232, -73,77,162,6,90,40,153,56,134,207,71,91,171,37,54,152,25,104,162,100,226,27, -61,29,110,132,148,218,160,109,162,137,147,180,71,91,61,61,46,172,148,217, -67,109,20,76,157,162,58,217,233,233,116,36,166,209,67,45,20,76,156,67,103, -167,165,213,146,155,77,12,180,81,50,113,13,158,158,151,66,74,109,84,50,209, -68,201,194,173,192,159,86,74,108,193,150,138,38,78,21,110,4,250,18,83,104, -193,182,138,38,78,13,212,10,201,213,146,155,56,109,162,137,147,131,117,2, -178,116,36,166,209,194,237,20,76,157,162,58,217,245,100,167,16,2,237,20,76, -157,162,58,217,244,36,167,18,2,173,20,76,156,67,103,213,146,156,80,10,180, -81,50,113,13,159,66,74,113,97,175,221,48,216,110,64,4,42,22,189,179,0,196, -133,0,185,80,32,28,78,99,193,18,80,36,4,19,159,141,172,0,178,90,4,74,73,0, -22,209,68,201,187,129,4,2,8,3,132,64,60,36,6,149,113,72,176,171,240,84,0, -157,91,116,116,32,11,42,218,221,216,181,129,32,3,234,219,165,3,188,231,235, -249,8,187,152,252,47,86,227,105,18,7,244,17,91,42,56,175,185,248,110,173, -198,209,208,36,0,238,82,97,87,188,189,179,240,93,122,32,12,22,162,42,125, -144,132,160,7,236,161,25,232,237,105,64,205,59,127,102,158,160,230,63,11, -217,66,51,210,129,154,118,254,205,61,65,236,127,171,197,34,168,48,6,90,194, -1,0,39,75,88,72,8,9,33,186,194,80,64,76,13,214,19,2,130,96,110,150,189,0, -65,6,51,214,20,128,65,17,11,214,19,130,137,121,211,210,211,144,6,39,75,88, -80,0,201,119,235,10,8,41,86,231,71,88,80,129,79,135,186,122,133,224,34,25, -69,234,80,3,91,141,172,40,96,139,113,180,181,133,36,21,110,54,142,134,176, -165,1,176,23,213,47,0,216,134,234,215,128,111,117,181,232,128,209,3,70,230, -107,64,5,139,168,209,235,10,32,36,144,102,235,136,3,146,27,172,40,160,146, -132,103,172,40,192,115,3,117,133,28,22,113,163,69,172,41,103,1,66,188,17, -145,52,168,4,202,113,67,76,130,227,76,194,13,240,108,0,0,83,224,0,2,193,0, -104,146,84,97,48,0,1,94,192,56,169,24,145,179,192,0,5,112,8,56,16,32,128, -56,18,52,125,230,86,147,190,140,28,50,21,13,39,31,23,60,145,158,57,12,141, -47,129,6,155,194,188,24,49,39,199,89,188,124,92,242,70,120,224,201,33,69, -15,155,163,201,68,14,49,39,199,197,211,116,240,242,113,197,232,18,180,254, -36,3,17,46,18,243,35,100,128,172,156,178,70,163,154,76,34,248,146,164,108, -248,75,204,141,146,28,217,115,137,27,95,27,241,173,236,162,160,224,200,2, -206,9,113,13,148,192,209,18,22,164,146,37,193,57,162,4,249,39,196,128,24,2, -178,66,213,136,68,201,16,77,209,131,31,192,242,88,96,92,191,151,34,100,136, -38,232,255,252,92,221,199,197,12,68,209,82,66,212,11,155,185,41,197,13,55, -38,3,66,213,47,135,254,72,12,162,99,133,116,112,0,1,72,66,14,16,16,50,37, -202,160,150,154,66,14,20,8,57,192,28,24,80,113,50,113,100,105,166,120,248, -0,0,179,1,65,196,201,199,20,178,36,227,224,0,2,208,54,113,240,0,1,100,11, -181,192,0,5,178,1,18,160,65,24,131,20,145,25,188,48,132,122,28,76,146,218, -121,35,180,69,145,132,108,224,0,0,0,0,0,0,120,31,153,188,56,132,122,28,76, -146,218,121,35,180,69,145,132,108,224,0,0,0,0,0,0,120,31,168,160,45,110,23, -30,176,33,184,0,0,183,32,29,235,2,27,199,23,0,0,23,4,51,120,129,8,244,56, -153,37,180,242,71,104,139,35,8,217,192,0,0,0,0,0,0,240,63,51,120,145,8,244, -56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,0,0,0,0,64,51,120,161,8, -244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,0,0,0,0,64,51,120,177, -8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,0,0,0,16,64,51,120, -193,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,0,0,0,16,64,51, -120,209,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,0,0,0,16,64, -51,120,225,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,0,0,0,32, -64,32,227,194,0,97,57,162,4,246,104,5,34,92,35,68,225,161,166,220,16,16, -137,112,52,41,73,29,185,1,65,196,201,197,145,166,153,246,72,3,137,204,120, -34,74,8,199,1,67,17,162,112,201,84,128,97,144,78,25,42,16,131,169,1,205,66, -8,35,68,225,161,166,239,128,0,10,192,64,196,104,156,50,96,0,2,172,73,240, -117,96,57,170,97,4,104,156,52,52,221,240,0,1,82,1,74,9,129,125,240,0,1,82, -32,148,25,174,137,58,23,51,190,0,0,42,69,64,195,32,156,50,96,0,2,160,81, -238,2,3,107,173,218,3,192, -}; -#elif defined(DUK_USE_DOUBLE_BE) -DUK_INTERNAL const duk_uint8_t duk_builtins_data[4281] = { -144,148,105,226,32,68,52,228,254,12,104,202,37,132,52,167,194,138,105,245, -124,57,28,211,57,18,64,52,239,126,44,138,111,175,241,164,19,87,145,30,33, -167,22,145,159,8,211,139,9,225,42,5,240,145,139,163,163,8,211,139,10,228, -64,211,19,132,140,93,29,56,70,156,88,119,34,66,146,36,104,137,194,70,46, -142,172,35,78,44,47,146,195,102,11,240,145,139,163,175,8,211,139,9,228,240, -242,112,145,139,163,179,8,211,139,8,237,34,130,118,49,116,118,225,26,48,0, -1,98,29,201,158,46,183,39,135,147,132,140,93,16,132,76,66,33,8,66,16,132, -33,8,66,26,180,105,97,167,68,150,34,33,154,112,0,1,91,247,35,79,111,237, -198,174,232,47,31,23,95,17,13,31,249,96,211,49,50,53,214,77,141,24,0,0,181, -10,228,240,242,15,128,140,65,128,134,188,0,0,90,167,97,181,224,0,2,213,62, -53,224,0,2,213,66,237,120,0,0,181,81,204,107,192,0,5,170,150,67,94,0,0,45, -84,245,90,240,0,1,106,169,162,215,128,0,11,85,93,150,188,0,0,90,171,111,53, -109,22,162,26,48,0,1,84,23,201,146,243,225,26,39,12,145,136,104,192,0,5,61, -11,228,201,121,240,100,19,134,72,196,33,195,14,40,203,112,64,190,76,232, -145,153,136,15,255,0,0,0,0,0,0,25,152,15,254,0,0,0,0,0,0,25,120,144,13,96, -155,194,56,80,206,36,67,141,20,228,70,57,81,206,100,131,156,39,132,168,23, -194,70,46,137,208,21,200,129,166,39,9,24,186,39,72,119,34,66,146,36,104, -137,194,70,46,137,212,23,201,97,179,5,248,72,197,209,58,194,121,60,60,156, -36,98,232,157,129,29,164,80,78,198,46,137,218,146,121,25,71,146,9,209,5, -209,61,48,126,14,138,152,30,67,186,23,143,139,175,131,202,135,228,72,85, -144,83,60,179,30,94,209,233,102,30,98,105,230,103,30,114,121,231,104,30, -122,137,231,233,30,130,153,232,106,30,138,169,232,235,30,144,67,193,25,19, -136,108,207,30,41,224,140,137,194,173,192,153,228,5,242,100,188,248,70,137, -195,36,79,78,47,147,37,231,193,144,78,25,34,122,145,111,36,74,232,176,13, -17,61,234,226,93,207,148,160,84,75,141,7,27,161,32,33,18,225,80,212,76,154, -2,2,70,65,56,100,237,34,140,209,2,67,32,156,50,118,145,64,186,230,61,205, -35,103,155,32,36,141,19,134,78,210,40,206,16,36,70,137,195,39,105,20,11, -174,99,220,210,54,121,210,1,137,33,1,228,207,16,17,70,146,66,3,201,164,32, -0,65,112,152,56,196,159,31,23,77,211,195,201,199,23,160,72,214,246,81,6,12, -73,241,214,111,31,23,60,145,158,56,50,72,81,67,230,232,242,80,19,49,39,199, -89,188,124,92,242,70,120,227,64,194,75,154,72,12,9,73,6,111,21,120,12,40, -144,19,39,25,0,225,144,168,105,56,248,185,228,140,241,200,96,64,100,42,26, -78,62,46,121,35,52,18,92,116,1,36,64,47,158,64,49,98,66,100,156,242,65,23, -196,149,35,103,194,94,100,108,144,230,203,156,64,66,37,201,16,11,32,249, -132,4,34,92,44,93,146,55,152,72,24,137,112,151,153,27,36,5,100,229,144,8, -162,98,92,210,5,76,73,241,214,111,31,23,60,145,158,57,44,48,46,92,185,164, -160,72,151,41,0,50,107,179,244,59,36,93,127,92,6,19,172,3,11,216,0,56,224, -151,29,102,241,241,115,201,25,227,164,64,106,37,199,197,211,116,240,242, -113,197,233,144,40,248,185,228,140,241,196,75,132,109,24,72,128,43,39,84, -129,13,173,161,144,168,105,56,98,78,100,142,214,215,69,1,13,173,161,144, -168,105,57,34,78,100,142,214,215,69,16,67,107,105,110,114,168,254,24,147, -153,35,181,181,212,32,67,107,105,110,114,168,254,72,147,153,35,181,181,212, -36,65,130,3,144,8,26,252,200,13,30,85,16,16,64,90,242,231,192,64,161,163, -203,31,26,172,193,17,4,23,105,159,96,27,172,251,16,32,196,4,14,137,112,17, -136,48,164,28,134,80,215,202,1,132,130,8,12,39,52,64,155,31,24,56,36,1,189, -207,132,0,35,233,35,195,62,3,196,149,36,100,72,160,2,200,232,44,227,0,11, -37,160,68,142,128,36,157,25,200,32,26,79,90,4,73,43,192,122,54,71,65,103, -44,248,14,134,140,151,227,138,231,208,45,96,148,248,134,140,151,227,138, -231,240,1,255,254,10,74,146,56,128,104,4,147,152,72,6,144,28,174,143,8,1, -30,1,165,3,96,31,0,211,3,21,11,153,35,0,211,131,68,131,160,137,16,250,5, -196,131,160,137,200,160,199,156,67,248,0,255,255,65,140,10,48,177,115,56, -35,130,60,19,134,79,89,240,52,177,115,56,39,12,156,123,144,217,251,15,135, -34,167,30,20,170,154,255,232,12,47,244,0,97,28,17,224,39,238,32,40,71,4, -120,39,12,156,4,253,228,5,137,195,39,30,228,54,124,4,253,228,128,194,115, -68,9,252,15,128,232,104,201,126,56,191,35,64,90,193,41,241,13,25,47,199,23, -228,105,3,86,225,1,100,224,156,199,130,36,249,144,10,192,76,71,250,16,15, -18,61,96,17,62,200,3,72,128,136,143,247,32,22,75,64,137,248,64,22,79,90,39, -249,64,38,84,12,167,20,52,223,196,2,230,238,45,214,36,120,32,72,158,208,4, -102,238,45,194,2,201,197,186,196,143,4,9,19,218,0,92,221,202,61,228,143,4, -9,19,218,8,35,55,113,110,16,22,78,81,239,36,120,32,72,158,208,64,73,197,12, -255,0,13,18,60,128,159,212,128,169,76,17,156,185,100,76,255,163,64,65,26, -57,114,200,153,255,70,144,33,13,18,232,50,75,226,104,6,149,3,41,199,246, -130,12,128,28,142,156,120,203,175,158,8,194,207,1,6,81,20,79,88,11,237,84, -11,161,32,63,247,255,255,255,255,255,255,137,235,16,221,170,129,116,36,0,0, -0,0,0,0,0,0,28,196,7,255,128,0,0,0,0,0,2,61,123,164,137,162,164,218,67,74, -134,162,120,128,255,224,0,0,0,0,0,0,71,173,33,129,52,84,155,72,105,80,212, -79,16,63,252,0,0,0,0,0,0,3,244,143,146,22,230,192,60,176,0,0,0,0,0,0,33, -214,2,251,82,1,73,180,134,204,134,36,96,33,159,255,255,255,255,255,255,144, -235,16,221,169,0,164,218,67,102,67,18,48,48,207,255,255,255,255,255,255, -196,60,17,145,56,134,204,241,226,158,8,200,156,42,220,9,158,65,196,34,92, -42,26,137,147,120,64,74,37,196,54,100,49,35,188,36,5,68,184,208,113,187, -194,80,212,75,146,1,73,196,54,100,49,35,188,38,57,37,56,240,0,0,0,0,0,0,0, -0,32,235,248,68,48,156,2,24,94,24,0,243,119,10,139,144,123,242,3,102,238, -18,239,115,72,217,160,11,223,16,23,55,113,241,32,145,36,57,188,18,16,102,3, -5,120,35,34,89,32,15,180,152,173,127,0,218,235,88,0,228,180,227,200,127, -248,0,0,0,0,0,0,197,107,240,64,6,77,220,24,38,78,74,113,67,77,130,4,12,155, -185,52,48,156,148,226,134,155,4,10,194,96,129,132,166,238,45,194,2,201,193, -130,100,228,167,20,52,216,32,113,41,187,139,112,128,178,114,104,97,57,41, -197,13,54,8,32,48,216,32,130,195,224,130,19,97,124,134,23,6,0,57,137,62,77, -12,38,12,0,179,18,124,45,22,190,96,128,141,176,134,28,98,79,180,152,139, -218,45,124,193,1,27,97,16,32,196,159,24,230,204,246,194,40,89,137,62,210, -98,103,92,217,158,216,70,7,49,39,193,130,100,182,17,194,140,73,246,147,16, -250,9,146,216,72,6,49,39,193,131,22,194,72,73,137,62,210,98,31,65,139,97, -40,32,196,159,14,234,70,86,194,88,89,137,62,210,98,63,93,72,202,216,76,10, -49,39,198,33,180,153,37,108,38,134,152,147,237,38,38,117,13,164,201,43,97, -56,40,196,159,36,65,57,163,149,176,158,26,98,79,180,152,165,210,9,205,28, -173,133,0,243,18,124,98,22,180,72,130,115,71,43,97,68,72,196,159,105,49,51, -168,90,209,34,9,205,28,173,133,33,19,18,124,154,24,76,185,164,227,138,89, -18,119,0,7,145,39,201,161,132,188,64,124,137,62,49,11,90,36,65,57,163,149, -210,166,37,34,79,180,152,153,212,45,104,145,4,230,142,87,74,160,84,137,62, -72,130,115,71,43,171,234,134,200,147,237,38,41,116,130,115,71,43,171,235,5, -72,147,227,16,218,76,146,186,254,184,108,137,62,210,98,103,80,218,76,146, -186,254,192,68,137,62,29,212,140,174,207,178,23,34,79,180,152,143,215,82, -50,187,62,208,60,137,62,12,19,37,210,182,21,34,79,180,152,135,208,76,151, -74,224,68,137,62,49,205,153,238,175,186,23,34,79,180,152,153,215,54,103, -186,190,240,92,137,62,22,139,95,48,64,70,235,251,225,210,36,251,73,136,189, -162,215,204,16,17,186,255,2,14,98,79,152,32,35,108,48,64,242,36,249,130,2, -55,75,6,212,224,72,200,51,128,114,108,28,100,128,0,0,0,0,0,0,0,12,110,127, -48,98,115,249,201,117,243,249,195,21,159,206,38,47,63,156,86,8,75,144,94, -82,1,38,73,79,208,67,95,233,1,6,128,14,79,129,186,40,249,18,149,182,207, -144,200,155,188,248,204,105,184,207,142,199,137,175,201,0,159,72,10,5,21, -221,10,120,74,129,124,36,98,232,228,74,81,62,160,20,10,107,186,21,114,32, -105,137,194,70,46,142,68,165,19,235,1,64,170,187,161,119,34,66,146,36,104, -137,194,70,46,142,68,165,19,236,1,64,174,187,161,95,37,134,204,23,225,35, -23,71,34,82,137,246,128,160,89,93,208,167,147,195,201,194,70,46,142,68,165, -19,238,1,64,182,187,161,71,105,20,19,177,139,163,145,41,68,16,7,6,15,82,70, -72,115,96,32,106,27,128,0,0,0,0,91,60,165,195,201,194,8,134,149,216,162,0, -192,41,225,8,2,48,177,36,1,149,13,196,15,0,200,209,97,199,128,99,32,176, -195,192,113,57,143,0,167,133,32,230,80,28,202,139,175,238,2,48,189,192,20, -1,119,80,87,193,186,129,89,56,72,197,209,200,193,185,35,23,71,109,13,219, -36,98,232,237,156,13,26,208,211,14,102,19,87,137,91,95,128,0,10,96,24,92,0, -0,83,2,53,56,0,0,165,3,28,204,160,160,226,100,226,200,211,76,241,240,0,1, -102,8,22,75,64,137,73,20,230,105,133,7,19,39,22,70,154,103,143,128,0,11,48, -20,28,76,156,113,75,34,78,62,0,0,45,3,103,31,0,0,22,65,44,57,137,62,33,179, -216,162,152,192,131,18,124,162,27,61,138,41,108,32,196,159,16,217,232,235, -81,76,104,73,137,62,81,13,158,142,181,20,184,16,98,79,136,108,244,244,168, -166,56,36,196,159,40,134,207,79,74,138,93,10,49,39,194,173,192,158,158,149, -20,188,20,98,79,133,91,129,61,109,74,41,124,30,68,159,16,217,236,83,108,96, -68,137,62,81,13,158,197,54,182,17,34,79,136,108,244,117,169,182,52,38,68, -159,40,134,207,71,90,155,92,8,145,39,196,54,122,122,84,219,28,19,34,79,148, -67,103,167,165,77,174,133,72,147,225,86,224,79,79,74,155,94,10,145,39,194, -173,192,158,182,165,54,190,206,25,212,35,208,226,100,150,211,201,29,162,44, -140,35,103,0,255,192,0,0,0,0,0,0,206,25,228,35,208,226,100,150,211,201,29, -162,44,140,35,103,0,255,192,0,0,0,0,0,0,206,25,244,35,208,226,100,150,211, -201,29,162,44,140,35,103,0,255,192,0,0,0,0,0,0,206,26,4,35,208,226,100,150, -211,201,29,162,44,140,35,103,1,0,0,0,0,0,0,0,0,206,26,20,35,208,226,100, -150,211,201,29,162,44,140,35,103,1,0,0,0,0,0,0,0,0,206,26,36,35,208,226, -100,150,211,201,29,162,44,140,35,103,1,0,64,0,0,0,0,0,0,206,26,52,35,208, -226,100,150,211,201,29,162,44,140,35,103,1,0,64,0,0,0,0,0,0,206,26,68,35, -208,226,100,150,211,201,29,162,44,140,35,103,1,0,64,0,0,0,0,0,0,206,26,84, -35,208,226,100,150,211,201,29,162,44,140,35,103,1,0,128,0,0,0,0,0,0,195, -154,99,16,38,36,0,251,68,117,179,216,162,128,68,72,1,241,13,158,197,20,150, -25,18,0,125,162,58,217,232,235,117,100,162,136,25,18,0,125,162,58,217,232, -235,116,36,162,145,2,226,64,15,136,108,244,117,186,178,81,73,129,113,32,7, -196,54,122,58,221,9,40,165,64,200,144,3,237,17,214,207,79,75,171,37,20,80, -200,144,3,237,17,214,207,79,75,161,37,20,138,23,18,0,124,67,103,167,165, -213,146,138,77,11,137,0,62,33,179,211,210,232,73,69,42,133,196,128,31,10, -183,2,125,89,40,163,5,196,128,31,10,183,2,125,9,40,164,96,200,144,3,224, -221,64,172,157,89,40,163,134,68,128,31,6,234,5,100,232,73,69,35,133,68,128, -31,104,142,182,125,89,40,180,0,168,144,3,237,17,214,207,161,37,22,144,19, -18,0,124,67,103,213,146,139,80,9,137,0,62,33,179,232,73,69,172,5,90,40,153, -59,68,117,179,216,166,192,77,162,137,147,136,108,246,41,180,176,219,69,19, -39,104,142,182,122,58,221,89,41,178,6,218,40,153,59,68,117,179,209,214,232, -73,77,162,6,90,40,153,56,134,207,71,91,171,37,54,152,25,104,162,100,226,27, -61,29,110,132,148,218,160,109,162,137,147,180,71,91,61,61,46,172,148,217, -67,109,20,76,157,162,58,217,233,233,116,36,166,209,67,45,20,76,156,67,103, -167,165,213,146,155,77,12,180,81,50,113,13,158,158,151,66,74,109,84,50,209, -68,201,194,173,192,159,86,74,108,193,150,138,38,78,21,110,4,250,18,83,104, -193,182,138,38,78,13,212,10,201,213,146,155,56,109,162,137,147,131,117,2, -178,116,36,166,209,194,237,20,76,157,162,58,217,245,100,167,16,2,237,20,76, -157,162,58,217,244,36,167,18,2,173,20,76,156,67,103,213,146,156,80,10,180, -81,50,113,13,159,66,74,113,97,175,221,48,216,110,64,4,42,22,189,179,0,196, -133,0,185,80,32,28,78,99,193,18,80,36,4,19,159,141,172,0,178,90,4,74,73,0, -22,209,68,201,187,129,4,2,8,3,132,64,60,36,4,0,91,240,168,177,69,118,144, -157,91,116,116,32,32,1,53,216,221,218,170,139,3,234,219,165,0,255,152,185, -11,251,232,231,188,47,86,227,105,18,1,255,184,170,59,41,92,23,240,110,173, -198,209,208,36,3,253,188,183,177,82,110,80,224,93,122,32,32,4,144,253,170, -34,22,140,7,236,161,25,232,237,105,64,63,230,160,158,102,127,59,205,11,217, -66,51,210,128,127,237,65,60,204,254,119,155,171,197,34,168,48,6,90,194,1,0, -39,75,88,72,8,9,33,186,194,80,64,76,13,214,19,2,130,96,110,150,189,0,65,6, -51,214,20,128,65,17,11,214,19,130,137,121,211,210,211,144,6,39,75,88,80,0, -201,119,235,10,8,41,86,231,71,88,80,129,79,135,186,122,133,224,34,25,69, -234,80,3,91,141,172,40,96,139,113,180,181,133,36,21,110,54,142,134,176,165, -1,176,23,213,47,0,216,134,234,215,128,111,117,181,232,128,209,3,70,230,107, -64,5,139,168,209,235,10,32,36,144,102,235,136,3,146,27,172,40,160,146,132, -103,172,40,192,115,3,117,133,28,22,113,163,69,172,41,103,1,66,188,17,145, -52,168,4,202,113,67,76,130,227,76,194,13,240,108,0,0,83,224,0,2,193,0,104, -146,84,97,48,0,1,94,192,56,169,24,145,179,192,0,5,112,8,56,16,32,128,56,18, -52,125,230,86,147,190,140,28,50,21,13,39,31,23,60,145,158,57,12,141,47,129, -6,155,194,188,24,49,39,199,89,188,124,92,242,70,120,224,201,33,69,15,155, -163,201,68,14,49,39,199,197,211,116,240,242,113,197,232,18,180,254,36,3,17, -46,18,243,35,100,128,172,156,178,70,163,154,76,34,248,146,164,108,248,75, -204,141,146,28,217,115,137,27,95,27,241,173,236,162,160,224,200,2,206,9, -113,13,148,192,209,18,22,164,146,37,193,57,162,4,249,39,196,128,24,2,178, -66,213,136,68,201,16,77,209,131,31,192,242,88,96,92,191,151,34,100,136,38, -232,255,252,92,221,199,197,12,68,209,82,66,212,11,155,185,41,197,13,55,38, -3,66,213,47,135,254,72,12,162,99,133,116,112,0,1,72,66,14,16,16,50,37,202, -160,150,154,66,14,20,8,57,192,28,24,80,113,50,113,100,105,166,120,248,0,0, -179,1,65,196,201,199,20,178,36,227,224,0,2,208,54,113,240,0,1,100,11,181, -192,0,5,178,1,18,160,65,24,131,20,145,25,188,48,132,122,28,76,146,218,121, -35,180,69,145,132,108,224,31,248,0,0,0,0,0,0,25,188,56,132,122,28,76,146, -218,121,35,180,69,145,132,108,224,31,248,0,0,0,0,0,0,40,160,45,110,23,30, -176,33,184,0,0,183,32,29,235,2,27,199,23,0,0,23,4,51,120,129,8,244,56,153, -37,180,242,71,104,139,35,8,217,192,63,240,0,0,0,0,0,0,51,120,145,8,244,56, -153,37,180,242,71,104,139,35,8,217,192,64,0,0,0,0,0,0,0,51,120,161,8,244, -56,153,37,180,242,71,104,139,35,8,217,192,64,0,0,0,0,0,0,0,51,120,177,8, -244,56,153,37,180,242,71,104,139,35,8,217,192,64,16,0,0,0,0,0,0,51,120,193, -8,244,56,153,37,180,242,71,104,139,35,8,217,192,64,16,0,0,0,0,0,0,51,120, -209,8,244,56,153,37,180,242,71,104,139,35,8,217,192,64,16,0,0,0,0,0,0,51, -120,225,8,244,56,153,37,180,242,71,104,139,35,8,217,192,64,32,0,0,0,0,0,0, -32,227,194,0,97,57,162,4,246,104,5,34,92,35,68,225,161,166,220,16,16,137, -112,52,41,73,29,185,1,65,196,201,197,145,166,153,246,72,3,137,204,120,34, -74,8,199,1,67,17,162,112,201,84,128,97,144,78,25,42,16,131,169,1,205,66,8, -35,68,225,161,166,239,128,0,10,192,64,196,104,156,50,96,0,2,172,73,240,117, -96,57,170,97,4,104,156,52,52,221,240,0,1,82,1,74,9,129,125,240,0,1,82,32, -148,25,174,137,58,23,51,190,0,0,42,69,64,195,32,156,50,96,0,2,160,81,238,2, -3,107,173,218,3,192, -}; -#elif defined(DUK_USE_DOUBLE_ME) -DUK_INTERNAL const duk_uint8_t duk_builtins_data[4281] = { -144,148,105,226,32,68,52,228,254,12,104,202,37,132,52,167,194,138,105,245, -124,57,28,211,57,18,64,52,239,126,44,138,111,175,241,164,19,87,145,30,33, -167,22,145,159,8,211,139,9,225,42,5,240,145,139,163,163,8,211,139,10,228, -64,211,19,132,140,93,29,56,70,156,88,119,34,66,146,36,104,137,194,70,46, -142,172,35,78,44,47,146,195,102,11,240,145,139,163,175,8,211,139,9,228,240, -242,112,145,139,163,179,8,211,139,8,237,34,130,118,49,116,118,225,26,48,0, -1,98,29,201,158,46,183,39,135,147,132,140,93,16,132,76,66,33,8,66,16,132, -33,8,66,26,180,105,97,167,68,150,34,33,154,112,0,1,91,247,35,79,111,237, -198,174,232,47,31,23,95,17,13,31,249,96,211,49,50,53,214,77,141,24,0,0,181, -10,228,240,242,15,128,140,65,128,134,188,0,0,90,167,97,181,224,0,2,213,62, -53,224,0,2,213,66,237,120,0,0,181,81,204,107,192,0,5,170,150,67,94,0,0,45, -84,245,90,240,0,1,106,169,162,215,128,0,11,85,93,150,188,0,0,90,171,111,53, -109,22,162,26,48,0,1,84,23,201,146,243,225,26,39,12,145,136,104,192,0,5,61, -11,228,201,121,240,100,19,134,72,196,33,195,14,40,203,112,64,190,76,232, -145,153,136,0,0,31,15,224,0,0,0,25,152,0,0,30,15,224,0,0,0,25,120,144,13, -96,155,194,56,80,206,36,67,141,20,228,70,57,81,206,100,131,156,39,132,168, -23,194,70,46,137,208,21,200,129,166,39,9,24,186,39,72,119,34,66,146,36,104, -137,194,70,46,137,212,23,201,97,179,5,248,72,197,209,58,194,121,60,60,156, -36,98,232,157,129,29,164,80,78,198,46,137,218,146,121,25,71,146,9,209,5, -209,61,48,126,14,138,152,30,67,186,23,143,139,175,131,202,135,228,72,85, -144,83,60,179,30,94,209,233,102,30,98,105,230,103,30,114,121,231,104,30, -122,137,231,233,30,130,153,232,106,30,138,169,232,235,30,144,67,193,25,19, -136,108,207,30,41,224,140,137,194,173,192,153,228,5,242,100,188,248,70,137, -195,36,79,78,47,147,37,231,193,144,78,25,34,122,145,111,36,74,232,176,13, -17,61,234,226,93,207,148,160,84,75,141,7,27,161,32,33,18,225,80,212,76,154, -2,2,70,65,56,100,237,34,140,209,2,67,32,156,50,118,145,64,186,230,61,205, -35,103,155,32,36,141,19,134,78,210,40,206,16,36,70,137,195,39,105,20,11, -174,99,220,210,54,121,210,1,137,33,1,228,207,16,17,70,146,66,3,201,164,32, -0,65,112,152,56,196,159,31,23,77,211,195,201,199,23,160,72,214,246,81,6,12, -73,241,214,111,31,23,60,145,158,56,50,72,81,67,230,232,242,80,19,49,39,199, -89,188,124,92,242,70,120,227,64,194,75,154,72,12,9,73,6,111,21,120,12,40, -144,19,39,25,0,225,144,168,105,56,248,185,228,140,241,200,96,64,100,42,26, -78,62,46,121,35,52,18,92,116,1,36,64,47,158,64,49,98,66,100,156,242,65,23, -196,149,35,103,194,94,100,108,144,230,203,156,64,66,37,201,16,11,32,249, -132,4,34,92,44,93,146,55,152,72,24,137,112,151,153,27,36,5,100,229,144,8, -162,98,92,210,5,76,73,241,214,111,31,23,60,145,158,57,44,48,46,92,185,164, -160,72,151,41,0,50,107,179,244,59,36,93,127,92,6,19,172,3,11,216,0,56,224, -151,29,102,241,241,115,201,25,227,164,64,106,37,199,197,211,116,240,242, -113,197,233,144,40,248,185,228,140,241,196,75,132,109,24,72,128,43,39,84, -129,13,173,161,144,168,105,56,98,78,100,142,214,215,69,1,13,173,161,144, -168,105,57,34,78,100,142,214,215,69,16,67,107,105,110,114,168,254,24,147, -153,35,181,181,212,32,67,107,105,110,114,168,254,72,147,153,35,181,181,212, -36,65,130,3,144,8,26,252,200,13,30,85,16,16,64,90,242,231,192,64,161,163, -203,31,26,172,193,17,4,23,105,159,96,27,172,251,16,32,196,4,14,137,112,17, -136,48,164,28,134,80,215,202,1,132,130,8,12,39,52,64,155,31,24,56,36,1,189, -207,132,0,35,233,35,195,62,3,196,149,36,100,72,160,2,200,232,44,227,0,11, -37,160,68,142,128,36,157,25,200,32,26,79,90,4,73,43,192,122,54,71,65,103, -44,248,14,134,140,151,227,138,231,208,45,96,148,248,134,140,151,227,138, -231,240,1,255,254,10,74,146,56,128,104,4,147,152,72,6,144,28,174,143,8,1, -30,1,165,3,96,31,0,211,3,21,11,153,35,0,211,131,68,131,160,137,16,250,5, -196,131,160,137,200,160,199,156,67,248,0,255,255,65,140,10,48,177,115,56, -35,130,60,19,134,79,89,240,52,177,115,56,39,12,156,123,144,217,251,15,135, -34,167,30,20,170,154,255,232,12,47,244,0,97,28,17,224,39,238,32,40,71,4, -120,39,12,156,4,253,228,5,137,195,39,30,228,54,124,4,253,228,128,194,115, -68,9,252,15,128,232,104,201,126,56,191,35,64,90,193,41,241,13,25,47,199,23, -228,105,3,86,225,1,100,224,156,199,130,36,249,144,10,192,76,71,250,16,15, -18,61,96,17,62,200,3,72,128,136,143,247,32,22,75,64,137,248,64,22,79,90,39, -249,64,38,84,12,167,20,52,223,196,2,230,238,45,214,36,120,32,72,158,208,4, -102,238,45,194,2,201,197,186,196,143,4,9,19,218,0,92,221,202,61,228,143,4, -9,19,218,8,35,55,113,110,16,22,78,81,239,36,120,32,72,158,208,64,73,197,12, -255,0,13,18,60,128,159,212,128,169,76,17,156,185,100,76,255,163,64,65,26, -57,114,200,153,255,70,144,33,13,18,232,50,75,226,104,6,149,3,41,199,246, -130,12,128,28,142,156,120,203,175,158,8,194,207,1,6,81,20,79,88,11,237,84, -11,161,32,127,255,247,191,255,255,255,255,137,235,16,221,170,129,116,36,0, -0,0,0,0,16,0,0,12,196,0,0,15,135,240,0,0,0,2,61,123,164,137,162,164,218,67, -74,134,162,120,128,0,1,224,254,0,0,0,0,71,173,33,129,52,84,155,72,105,80, -212,79,16,0,0,60,63,192,0,0,0,3,244,143,146,22,230,192,0,0,176,60,0,0,0,0, -33,214,2,251,82,1,73,180,134,204,134,36,96,127,255,159,161,255,255,255,255, -144,235,16,221,169,0,164,218,67,102,67,18,48,63,255,207,240,255,255,255, -255,196,60,17,145,56,134,204,241,226,158,8,200,156,42,220,9,158,65,196,34, -92,42,26,137,147,120,64,74,37,196,54,100,49,35,188,36,5,68,184,208,113,187, -194,80,212,75,146,1,73,196,54,100,49,35,188,38,57,37,56,240,0,0,0,0,0,0,0, -0,32,235,248,68,48,156,2,24,94,24,0,243,119,10,139,144,123,242,3,102,238, -18,239,115,72,217,160,11,223,16,23,55,113,241,32,145,36,57,188,18,16,102,3, -5,120,35,34,89,32,15,180,152,173,127,0,218,235,88,0,228,180,227,200,0,0, -248,127,0,0,0,0,197,107,240,64,6,77,220,24,38,78,74,113,67,77,130,4,12,155, -185,52,48,156,148,226,134,155,4,10,194,96,129,132,166,238,45,194,2,201,193, -130,100,228,167,20,52,216,32,113,41,187,139,112,128,178,114,104,97,57,41, -197,13,54,8,32,48,216,32,130,195,224,130,19,97,124,134,23,6,0,57,137,62,77, -12,38,12,0,179,18,124,45,22,190,96,128,141,176,134,28,98,79,180,152,139, -218,45,124,193,1,27,97,16,32,196,159,24,230,204,246,194,40,89,137,62,210, -98,103,92,217,158,216,70,7,49,39,193,130,100,182,17,194,140,73,246,147,16, -250,9,146,216,72,6,49,39,193,131,22,194,72,73,137,62,210,98,31,65,139,97, -40,32,196,159,14,234,70,86,194,88,89,137,62,210,98,63,93,72,202,216,76,10, -49,39,198,33,180,153,37,108,38,134,152,147,237,38,38,117,13,164,201,43,97, -56,40,196,159,36,65,57,163,149,176,158,26,98,79,180,152,165,210,9,205,28, -173,133,0,243,18,124,98,22,180,72,130,115,71,43,97,68,72,196,159,105,49,51, -168,90,209,34,9,205,28,173,133,33,19,18,124,154,24,76,185,164,227,138,89, -18,119,0,7,145,39,201,161,132,188,64,124,137,62,49,11,90,36,65,57,163,149, -210,166,37,34,79,180,152,153,212,45,104,145,4,230,142,87,74,160,84,137,62, -72,130,115,71,43,171,234,134,200,147,237,38,41,116,130,115,71,43,171,235,5, -72,147,227,16,218,76,146,186,254,184,108,137,62,210,98,103,80,218,76,146, -186,254,192,68,137,62,29,212,140,174,207,178,23,34,79,180,152,143,215,82, -50,187,62,208,60,137,62,12,19,37,210,182,21,34,79,180,152,135,208,76,151, -74,224,68,137,62,49,205,153,238,175,186,23,34,79,180,152,153,215,54,103, -186,190,240,92,137,62,22,139,95,48,64,70,235,251,225,210,36,251,73,136,189, -162,215,204,16,17,186,255,2,14,98,79,152,32,35,108,48,64,242,36,249,130,2, -55,75,6,212,224,72,200,51,128,114,108,28,100,128,0,0,0,0,0,0,0,12,110,127, -48,98,115,249,201,117,243,249,195,21,159,206,38,47,63,156,86,8,75,144,94, -82,1,38,73,79,208,67,95,233,1,6,128,14,79,129,186,40,249,18,149,182,207, -144,200,155,188,248,204,105,184,207,142,199,137,175,201,0,159,72,10,5,21, -221,10,120,74,129,124,36,98,232,228,74,81,62,160,20,10,107,186,21,114,32, -105,137,194,70,46,142,68,165,19,235,1,64,170,187,161,119,34,66,146,36,104, -137,194,70,46,142,68,165,19,236,1,64,174,187,161,95,37,134,204,23,225,35, -23,71,34,82,137,246,128,160,89,93,208,167,147,195,201,194,70,46,142,68,165, -19,238,1,64,182,187,161,71,105,20,19,177,139,163,145,41,68,16,7,6,15,82,70, -72,115,96,0,27,234,32,0,0,0,0,91,60,165,195,201,194,8,134,149,216,162,0, -192,41,225,8,2,48,177,36,1,149,13,196,15,0,200,209,97,199,128,99,32,176, -195,192,113,57,143,0,167,133,32,230,80,28,202,139,175,238,2,48,189,192,20, -1,119,80,87,193,186,129,89,56,72,197,209,200,193,185,35,23,71,109,13,219, -36,98,232,237,156,13,26,208,211,14,102,19,87,137,91,95,128,0,10,96,24,92,0, -0,83,2,53,56,0,0,165,3,28,204,160,160,226,100,226,200,211,76,241,240,0,1, -102,8,22,75,64,137,73,20,230,105,133,7,19,39,22,70,154,103,143,128,0,11,48, -20,28,76,156,113,75,34,78,62,0,0,45,3,103,31,0,0,22,65,44,57,137,62,33,179, -216,162,152,192,131,18,124,162,27,61,138,41,108,32,196,159,16,217,232,235, -81,76,104,73,137,62,81,13,158,142,181,20,184,16,98,79,136,108,244,244,168, -166,56,36,196,159,40,134,207,79,74,138,93,10,49,39,194,173,192,158,158,149, -20,188,20,98,79,133,91,129,61,109,74,41,124,30,68,159,16,217,236,83,108,96, -68,137,62,81,13,158,197,54,182,17,34,79,136,108,244,117,169,182,52,38,68, -159,40,134,207,71,90,155,92,8,145,39,196,54,122,122,84,219,28,19,34,79,148, -67,103,167,165,77,174,133,72,147,225,86,224,79,79,74,155,94,10,145,39,194, -173,192,158,182,165,54,190,206,25,212,35,208,226,100,150,211,201,29,162,44, -140,35,103,0,0,3,192,252,0,0,0,0,206,25,228,35,208,226,100,150,211,201,29, -162,44,140,35,103,0,0,3,192,252,0,0,0,0,206,25,244,35,208,226,100,150,211, -201,29,162,44,140,35,103,0,0,3,192,252,0,0,0,0,206,26,4,35,208,226,100,150, -211,201,29,162,44,140,35,103,0,0,0,1,0,0,0,0,0,206,26,20,35,208,226,100, -150,211,201,29,162,44,140,35,103,0,0,0,1,0,0,0,0,0,206,26,36,35,208,226, -100,150,211,201,29,162,44,140,35,103,0,0,0,65,0,0,0,0,0,206,26,52,35,208, -226,100,150,211,201,29,162,44,140,35,103,0,0,0,65,0,0,0,0,0,206,26,68,35, -208,226,100,150,211,201,29,162,44,140,35,103,0,0,0,65,0,0,0,0,0,206,26,84, -35,208,226,100,150,211,201,29,162,44,140,35,103,0,0,0,129,0,0,0,0,0,195, -154,99,16,38,36,0,251,68,117,179,216,162,128,68,72,1,241,13,158,197,20,150, -25,18,0,125,162,58,217,232,235,117,100,162,136,25,18,0,125,162,58,217,232, -235,116,36,162,145,2,226,64,15,136,108,244,117,186,178,81,73,129,113,32,7, -196,54,122,58,221,9,40,165,64,200,144,3,237,17,214,207,79,75,171,37,20,80, -200,144,3,237,17,214,207,79,75,161,37,20,138,23,18,0,124,67,103,167,165, -213,146,138,77,11,137,0,62,33,179,211,210,232,73,69,42,133,196,128,31,10, -183,2,125,89,40,163,5,196,128,31,10,183,2,125,9,40,164,96,200,144,3,224, -221,64,172,157,89,40,163,134,68,128,31,6,234,5,100,232,73,69,35,133,68,128, -31,104,142,182,125,89,40,180,0,168,144,3,237,17,214,207,161,37,22,144,19, -18,0,124,67,103,213,146,139,80,9,137,0,62,33,179,232,73,69,172,5,90,40,153, -59,68,117,179,216,166,192,77,162,137,147,136,108,246,41,180,176,219,69,19, -39,104,142,182,122,58,221,89,41,178,6,218,40,153,59,68,117,179,209,214,232, -73,77,162,6,90,40,153,56,134,207,71,91,171,37,54,152,25,104,162,100,226,27, -61,29,110,132,148,218,160,109,162,137,147,180,71,91,61,61,46,172,148,217, -67,109,20,76,157,162,58,217,233,233,116,36,166,209,67,45,20,76,156,67,103, -167,165,213,146,155,77,12,180,81,50,113,13,158,158,151,66,74,109,84,50,209, -68,201,194,173,192,159,86,74,108,193,150,138,38,78,21,110,4,250,18,83,104, -193,182,138,38,78,13,212,10,201,213,146,155,56,109,162,137,147,131,117,2, -178,116,36,166,209,194,237,20,76,157,162,58,217,245,100,167,16,2,237,20,76, -157,162,58,217,244,36,167,18,2,173,20,76,156,67,103,213,146,156,80,10,180, -81,50,113,13,159,66,74,113,97,175,221,48,216,110,64,4,42,22,189,179,0,196, -133,0,185,80,32,28,78,99,193,18,80,36,4,19,159,141,172,0,178,90,4,74,73,0, -22,209,68,201,187,129,4,2,8,3,132,64,60,36,0,171,240,84,6,149,113,72,176, -157,91,116,116,32,88,181,129,32,11,42,218,221,131,234,219,165,1,8,187,152, -255,188,231,235,248,47,86,227,105,18,2,56,175,185,255,244,17,91,40,110,173, -198,209,208,36,7,188,189,179,240,238,82,97,80,93,122,32,125,144,132,160,12, -22,162,42,7,236,161,25,232,237,105,64,158,160,230,63,205,59,127,102,11,217, -66,51,210,129,61,65,236,127,154,118,254,205,171,197,34,168,48,6,90,194,1,0, -39,75,88,72,8,9,33,186,194,80,64,76,13,214,19,2,130,96,110,150,189,0,65,6, -51,214,20,128,65,17,11,214,19,130,137,121,211,210,211,144,6,39,75,88,80,0, -201,119,235,10,8,41,86,231,71,88,80,129,79,135,186,122,133,224,34,25,69, -234,80,3,91,141,172,40,96,139,113,180,181,133,36,21,110,54,142,134,176,165, -1,176,23,213,47,0,216,134,234,215,128,111,117,181,232,128,209,3,70,230,107, -64,5,139,168,209,235,10,32,36,144,102,235,136,3,146,27,172,40,160,146,132, -103,172,40,192,115,3,117,133,28,22,113,163,69,172,41,103,1,66,188,17,145, -52,168,4,202,113,67,76,130,227,76,194,13,240,108,0,0,83,224,0,2,193,0,104, -146,84,97,48,0,1,94,192,56,169,24,145,179,192,0,5,112,8,56,16,32,128,56,18, -52,125,230,86,147,190,140,28,50,21,13,39,31,23,60,145,158,57,12,141,47,129, -6,155,194,188,24,49,39,199,89,188,124,92,242,70,120,224,201,33,69,15,155, -163,201,68,14,49,39,199,197,211,116,240,242,113,197,232,18,180,254,36,3,17, -46,18,243,35,100,128,172,156,178,70,163,154,76,34,248,146,164,108,248,75, -204,141,146,28,217,115,137,27,95,27,241,173,236,162,160,224,200,2,206,9, -113,13,148,192,209,18,22,164,146,37,193,57,162,4,249,39,196,128,24,2,178, -66,213,136,68,201,16,77,209,131,31,192,242,88,96,92,191,151,34,100,136,38, -232,255,252,92,221,199,197,12,68,209,82,66,212,11,155,185,41,197,13,55,38, -3,66,213,47,135,254,72,12,162,99,133,116,112,0,1,72,66,14,16,16,50,37,202, -160,150,154,66,14,20,8,57,192,28,24,80,113,50,113,100,105,166,120,248,0,0, -179,1,65,196,201,199,20,178,36,227,224,0,2,208,54,113,240,0,1,100,11,181, -192,0,5,178,1,18,160,65,24,131,20,145,25,188,48,132,122,28,76,146,218,121, -35,180,69,145,132,108,224,0,0,120,31,128,0,0,0,25,188,56,132,122,28,76,146, -218,121,35,180,69,145,132,108,224,0,0,120,31,128,0,0,0,40,160,45,110,23,30, -176,33,184,0,0,183,32,29,235,2,27,199,23,0,0,23,4,51,120,129,8,244,56,153, -37,180,242,71,104,139,35,8,217,192,0,0,240,63,0,0,0,0,51,120,145,8,244,56, -153,37,180,242,71,104,139,35,8,217,192,0,0,0,64,0,0,0,0,51,120,161,8,244, -56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,64,0,0,0,0,51,120,177,8, -244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,16,64,0,0,0,0,51,120,193, -8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,16,64,0,0,0,0,51,120, -209,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,16,64,0,0,0,0,51, -120,225,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,32,64,0,0,0,0, -32,227,194,0,97,57,162,4,246,104,5,34,92,35,68,225,161,166,220,16,16,137, -112,52,41,73,29,185,1,65,196,201,197,145,166,153,246,72,3,137,204,120,34, -74,8,199,1,67,17,162,112,201,84,128,97,144,78,25,42,16,131,169,1,205,66,8, -35,68,225,161,166,239,128,0,10,192,64,196,104,156,50,96,0,2,172,73,240,117, -96,57,170,97,4,104,156,52,52,221,240,0,1,82,1,74,9,129,125,240,0,1,82,32, -148,25,174,137,58,23,51,190,0,0,42,69,64,195,32,156,50,96,0,2,160,81,238,2, -3,107,173,218,3,192, -}; -#else -#error invalid endianness defines -#endif -#endif /* DUK_USE_ROM_OBJECTS */ - -/* automatic undefs */ -#undef DUK__REFCINIT -/* - * Error and fatal handling. - */ - -/* #include duk_internal.h -> already included */ - -#define DUK__ERRFMT_BUFSIZE 256 /* size for formatting buffers */ - -#if defined(DUK_USE_VERBOSE_ERRORS) - -DUK_INTERNAL DUK_COLD void duk_err_handle_error_fmt(duk_hthread *thr, - const char *filename, - duk_uint_t line_and_code, - const char *fmt, - ...) { - va_list ap; - char msg[DUK__ERRFMT_BUFSIZE]; - va_start(ap, fmt); - (void) DUK_VSNPRINTF(msg, sizeof(msg), fmt, ap); - msg[sizeof(msg) - 1] = (char) 0; - duk_err_create_and_throw(thr, - (duk_errcode_t) (line_and_code >> 24), - msg, - filename, - (duk_int_t) (line_and_code & 0x00ffffffL)); - va_end(ap); /* dead code, but ensures portability (see Linux man page notes) */ -} - -DUK_INTERNAL DUK_COLD void duk_err_handle_error(duk_hthread *thr, const char *filename, duk_uint_t line_and_code, const char *msg) { - duk_err_create_and_throw(thr, - (duk_errcode_t) (line_and_code >> 24), - msg, - filename, - (duk_int_t) (line_and_code & 0x00ffffffL)); -} - -#else /* DUK_USE_VERBOSE_ERRORS */ - -DUK_INTERNAL DUK_COLD void duk_err_handle_error(duk_hthread *thr, duk_errcode_t code) { - duk_err_create_and_throw(thr, code); -} - -#endif /* DUK_USE_VERBOSE_ERRORS */ - -/* - * Error throwing helpers - */ - -#if defined(DUK_USE_VERBOSE_ERRORS) -#if defined(DUK_USE_PARANOID_ERRORS) -DUK_INTERNAL DUK_COLD void duk_err_require_type_index(duk_hthread *thr, - const char *filename, - duk_int_t linenumber, - duk_idx_t idx, - const char *expect_name) { - DUK_ERROR_RAW_FMT3(thr, - filename, - linenumber, - DUK_ERR_TYPE_ERROR, - "%s required, found %s (stack index %ld)", - expect_name, - duk_get_type_name(thr, idx), - (long) idx); -} -#else -DUK_INTERNAL DUK_COLD void duk_err_require_type_index(duk_hthread *thr, - const char *filename, - duk_int_t linenumber, - duk_idx_t idx, - const char *expect_name) { - DUK_ERROR_RAW_FMT3(thr, - filename, - linenumber, - DUK_ERR_TYPE_ERROR, - "%s required, found %s (stack index %ld)", - expect_name, - duk_push_string_readable(thr, idx), - (long) idx); -} -#endif -DUK_INTERNAL DUK_COLD void duk_err_error_internal(duk_hthread *thr, const char *filename, duk_int_t linenumber) { - DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_ERROR, DUK_STR_INTERNAL_ERROR); -} -DUK_INTERNAL DUK_COLD void duk_err_error_alloc_failed(duk_hthread *thr, const char *filename, duk_int_t linenumber) { - DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_ERROR, DUK_STR_ALLOC_FAILED); -} -DUK_INTERNAL DUK_COLD void duk_err_error(duk_hthread *thr, const char *filename, duk_int_t linenumber, const char *message) { - DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_ERROR, message); -} -DUK_INTERNAL DUK_COLD void duk_err_range(duk_hthread *thr, const char *filename, duk_int_t linenumber, const char *message) { - DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_RANGE_ERROR, message); -} -DUK_INTERNAL DUK_COLD void duk_err_range_index(duk_hthread *thr, const char *filename, duk_int_t linenumber, duk_idx_t idx) { - DUK_ERROR_RAW_FMT1(thr, filename, linenumber, DUK_ERR_RANGE_ERROR, "invalid stack index %ld", (long) (idx)); -} -DUK_INTERNAL DUK_COLD void duk_err_range_push_beyond(duk_hthread *thr, const char *filename, duk_int_t linenumber) { - DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_RANGE_ERROR, DUK_STR_PUSH_BEYOND_ALLOC_STACK); -} -DUK_INTERNAL DUK_COLD void duk_err_type_invalid_args(duk_hthread *thr, const char *filename, duk_int_t linenumber) { - DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_TYPE_ERROR, DUK_STR_INVALID_ARGS); -} -DUK_INTERNAL DUK_COLD void duk_err_type_invalid_state(duk_hthread *thr, const char *filename, duk_int_t linenumber) { - DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_TYPE_ERROR, DUK_STR_INVALID_STATE); -} -DUK_INTERNAL DUK_COLD void duk_err_type_invalid_trap_result(duk_hthread *thr, const char *filename, duk_int_t linenumber) { - DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_TYPE_ERROR, DUK_STR_INVALID_TRAP_RESULT); -} -#else -/* The file/line arguments are NULL and 0, they're ignored by DUK_ERROR_RAW() - * when non-verbose errors are used. - */ - -DUK_NORETURN(DUK_LOCAL_DECL void duk__err_shared(duk_hthread *thr, duk_errcode_t code)); -DUK_LOCAL void duk__err_shared(duk_hthread *thr, duk_errcode_t code) { - DUK_ERROR_RAW(thr, NULL, 0, code, NULL); -} -DUK_INTERNAL DUK_COLD void duk_err_error(duk_hthread *thr) { - duk__err_shared(thr, DUK_ERR_ERROR); -} -DUK_INTERNAL DUK_COLD void duk_err_range(duk_hthread *thr) { - duk__err_shared(thr, DUK_ERR_RANGE_ERROR); -} -DUK_INTERNAL DUK_COLD void duk_err_eval(duk_hthread *thr) { - duk__err_shared(thr, DUK_ERR_EVAL_ERROR); -} -DUK_INTERNAL DUK_COLD void duk_err_reference(duk_hthread *thr) { - duk__err_shared(thr, DUK_ERR_REFERENCE_ERROR); -} -DUK_INTERNAL DUK_COLD void duk_err_syntax(duk_hthread *thr) { - duk__err_shared(thr, DUK_ERR_SYNTAX_ERROR); -} -DUK_INTERNAL DUK_COLD void duk_err_type(duk_hthread *thr) { - duk__err_shared(thr, DUK_ERR_TYPE_ERROR); -} -DUK_INTERNAL DUK_COLD void duk_err_uri(duk_hthread *thr) { - duk__err_shared(thr, DUK_ERR_URI_ERROR); -} -#endif - -/* - * Default fatal error handler - */ - -DUK_INTERNAL DUK_COLD void duk_default_fatal_handler(void *udata, const char *msg) { - DUK_UNREF(udata); - DUK_UNREF(msg); - - msg = msg ? msg : "NULL"; - -#if defined(DUK_USE_FATAL_HANDLER) - /* duk_config.h provided a custom default fatal handler. */ - DUK_D(DUK_DPRINT("custom default fatal error handler called: %s", msg)); - DUK_USE_FATAL_HANDLER(udata, msg); -#elif defined(DUK_USE_CPP_EXCEPTIONS) - /* With C++ use a duk_fatal_exception which user code can catch in - * a natural way. - */ - DUK_D(DUK_DPRINT("built-in default C++ fatal error handler called: %s", msg)); - throw duk_fatal_exception(msg); -#else - /* Default behavior is to abort() on error. There's no printout - * which makes this awkward, so it's always recommended to use an - * explicit fatal error handler. - * - * ==================================================================== - * NOTE: If you are seeing this, you are most likely dealing with an - * uncaught error. You should provide a fatal error handler in Duktape - * heap creation, and should consider using a protected call as your - * first call into an empty Duktape context to properly handle errors. - * See: - * - http://duktape.org/guide.html#error-handling - * - http://wiki.duktape.org/HowtoFatalErrors.html - * - http://duktape.org/api.html#taglist-protected - * ==================================================================== - */ - DUK_D(DUK_DPRINT("built-in default fatal error handler called: %s", msg)); - DUK_ABORT(); -#endif - - DUK_D(DUK_DPRINT("fatal error handler returned, enter forever loop")); - for (;;) { - /* Loop forever to ensure we don't return. */ - } -} - -/* automatic undefs */ -#undef DUK__ERRFMT_BUFSIZE -/* - * Various Unicode help functions for character classification predicates, - * case conversion, decoding, etc. - */ - -/* #include duk_internal.h -> already included */ - -/* - * Fast path tables - */ - -#if defined(DUK_USE_IDCHAR_FASTPATH) -DUK_INTERNAL const duk_int8_t duk_is_idchar_tab[128] = { - /* 0: not IdentifierStart or IdentifierPart - * 1: IdentifierStart and IdentifierPart - * -1: IdentifierPart only - */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00...0x0f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10...0x1f */ - 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20...0x2f */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, /* 0x30...0x3f */ - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40...0x4f */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x50...0x5f */ - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60...0x6f */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 /* 0x70...0x7f */ -}; -#endif - -/* - * XUTF-8 and CESU-8 encoding/decoding - */ - -DUK_INTERNAL duk_small_int_t duk_unicode_get_xutf8_length(duk_ucodepoint_t cp) { - duk_uint_fast32_t x = (duk_uint_fast32_t) cp; - if (x < 0x80UL) { - /* 7 bits */ - return 1; - } else if (x < 0x800UL) { - /* 11 bits */ - return 2; - } else if (x < 0x10000UL) { - /* 16 bits */ - return 3; - } else if (x < 0x200000UL) { - /* 21 bits */ - return 4; - } else if (x < 0x4000000UL) { - /* 26 bits */ - return 5; - } else if (x < (duk_ucodepoint_t) 0x80000000UL) { - /* 31 bits */ - return 6; - } else { - /* 36 bits */ - return 7; - } -} - -#if defined(DUK_USE_ASSERTIONS) -DUK_INTERNAL duk_small_int_t duk_unicode_get_cesu8_length(duk_ucodepoint_t cp) { - duk_uint_fast32_t x = (duk_uint_fast32_t) cp; - if (x < 0x80UL) { - /* 7 bits */ - return 1; - } else if (x < 0x800UL) { - /* 11 bits */ - return 2; - } else if (x < 0x10000UL) { - /* 16 bits */ - return 3; - } else { - /* Encoded as surrogate pair, each encoding to 3 bytes for - * 6 bytes total. Codepoints above U+10FFFF encode as 6 bytes - * too, see duk_unicode_encode_cesu8(). - */ - return 3 + 3; - } -} -#endif /* DUK_USE_ASSERTIONS */ - -DUK_INTERNAL const duk_uint8_t duk_unicode_xutf8_markers[7] = { 0x00, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe }; - -/* Encode to extended UTF-8; 'out' must have space for at least - * DUK_UNICODE_MAX_XUTF8_LENGTH bytes. Allows encoding of any - * 32-bit (unsigned) codepoint. - */ -DUK_INTERNAL duk_small_int_t duk_unicode_encode_xutf8(duk_ucodepoint_t cp, duk_uint8_t *out) { - duk_uint_fast32_t x = (duk_uint_fast32_t) cp; - duk_small_int_t len; - duk_uint8_t marker; - duk_small_int_t i; - - len = duk_unicode_get_xutf8_length(cp); - DUK_ASSERT(len > 0); - - marker = duk_unicode_xutf8_markers[len - 1]; /* 64-bit OK because always >= 0 */ - - i = len; - DUK_ASSERT(i > 0); - do { - i--; - if (i > 0) { - out[i] = (duk_uint8_t) (0x80 + (x & 0x3f)); - x >>= 6; - } else { - /* Note: masking of 'x' is not necessary because of - * range check and shifting -> no bits overlapping - * the marker should be set. - */ - out[0] = (duk_uint8_t) (marker + x); - } - } while (i > 0); - - return len; -} - -/* Encode to CESU-8; 'out' must have space for at least - * DUK_UNICODE_MAX_CESU8_LENGTH bytes; codepoints above U+10FFFF - * will encode to garbage but won't overwrite the output buffer. - */ -DUK_INTERNAL duk_small_int_t duk_unicode_encode_cesu8(duk_ucodepoint_t cp, duk_uint8_t *out) { - duk_uint_fast32_t x = (duk_uint_fast32_t) cp; - duk_small_int_t len; - - if (x < 0x80UL) { - out[0] = (duk_uint8_t) x; - len = 1; - } else if (x < 0x800UL) { - out[0] = (duk_uint8_t) (0xc0 + ((x >> 6) & 0x1f)); - out[1] = (duk_uint8_t) (0x80 + (x & 0x3f)); - len = 2; - } else if (x < 0x10000UL) { - /* surrogate pairs get encoded here */ - out[0] = (duk_uint8_t) (0xe0 + ((x >> 12) & 0x0f)); - out[1] = (duk_uint8_t) (0x80 + ((x >> 6) & 0x3f)); - out[2] = (duk_uint8_t) (0x80 + (x & 0x3f)); - len = 3; - } else { - /* - * Unicode codepoints above U+FFFF are encoded as surrogate - * pairs here. This ensures that all CESU-8 codepoints are - * 16-bit values as expected in ECMAScript. The surrogate - * pairs always get a 3-byte encoding (each) in CESU-8. - * See: http://en.wikipedia.org/wiki/Surrogate_pair - * - * 20-bit codepoint, 10 bits (A and B) per surrogate pair: - * - * x = 0b00000000 0000AAAA AAAAAABB BBBBBBBB - * sp1 = 0b110110AA AAAAAAAA (0xd800 + ((x >> 10) & 0x3ff)) - * sp2 = 0b110111BB BBBBBBBB (0xdc00 + (x & 0x3ff)) - * - * Encoded into CESU-8: - * - * sp1 -> 0b11101101 (0xe0 + ((sp1 >> 12) & 0x0f)) - * -> 0b1010AAAA (0x80 + ((sp1 >> 6) & 0x3f)) - * -> 0b10AAAAAA (0x80 + (sp1 & 0x3f)) - * sp2 -> 0b11101101 (0xe0 + ((sp2 >> 12) & 0x0f)) - * -> 0b1011BBBB (0x80 + ((sp2 >> 6) & 0x3f)) - * -> 0b10BBBBBB (0x80 + (sp2 & 0x3f)) - * - * Note that 0x10000 must be subtracted first. The code below - * avoids the sp1, sp2 temporaries which saves around 20 bytes - * of code. - */ - - x -= 0x10000UL; - - out[0] = (duk_uint8_t) (0xed); - out[1] = (duk_uint8_t) (0xa0 + ((x >> 16) & 0x0f)); - out[2] = (duk_uint8_t) (0x80 + ((x >> 10) & 0x3f)); - out[3] = (duk_uint8_t) (0xed); - out[4] = (duk_uint8_t) (0xb0 + ((x >> 6) & 0x0f)); - out[5] = (duk_uint8_t) (0x80 + (x & 0x3f)); - len = 6; - } - - return len; -} - -/* Decode helper. Return zero on error. */ -DUK_INTERNAL duk_small_int_t duk_unicode_decode_xutf8(duk_hthread *thr, - const duk_uint8_t **ptr, - const duk_uint8_t *ptr_start, - const duk_uint8_t *ptr_end, - duk_ucodepoint_t *out_cp) { - const duk_uint8_t *p; - duk_uint32_t res; - duk_uint_fast8_t ch; - duk_small_int_t n; - - DUK_UNREF(thr); - - p = *ptr; - if (p < ptr_start || p >= ptr_end) { - goto fail; - } - - /* - * UTF-8 decoder which accepts longer than standard byte sequences. - * This allows full 32-bit code points to be used. - */ - - ch = (duk_uint_fast8_t) (*p++); - if (ch < 0x80) { - /* 0xxx xxxx [7 bits] */ - res = (duk_uint32_t) (ch & 0x7f); - n = 0; - } else if (ch < 0xc0) { - /* 10xx xxxx -> invalid */ - goto fail; - } else if (ch < 0xe0) { - /* 110x xxxx 10xx xxxx [11 bits] */ - res = (duk_uint32_t) (ch & 0x1f); - n = 1; - } else if (ch < 0xf0) { - /* 1110 xxxx 10xx xxxx 10xx xxxx [16 bits] */ - res = (duk_uint32_t) (ch & 0x0f); - n = 2; - } else if (ch < 0xf8) { - /* 1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx [21 bits] */ - res = (duk_uint32_t) (ch & 0x07); - n = 3; - } else if (ch < 0xfc) { - /* 1111 10xx 10xx xxxx 10xx xxxx 10xx xxxx 10xx xxxx [26 bits] */ - res = (duk_uint32_t) (ch & 0x03); - n = 4; - } else if (ch < 0xfe) { - /* 1111 110x 10xx xxxx 10xx xxxx 10xx xxxx 10xx xxxx 10xx xxxx [31 bits] */ - res = (duk_uint32_t) (ch & 0x01); - n = 5; - } else if (ch < 0xff) { - /* 1111 1110 10xx xxxx 10xx xxxx 10xx xxxx 10xx xxxx 10xx xxxx 10xx xxxx [36 bits] */ - res = (duk_uint32_t) (0); - n = 6; - } else { - /* 8-byte format could be: - * 1111 1111 10xx xxxx 10xx xxxx 10xx xxxx 10xx xxxx 10xx xxxx 10xx xxxx 10xx xxxx [41 bits] - * - * However, this format would not have a zero bit following the - * leading one bits and would not allow 0xFF to be used as an - * "invalid xutf-8" marker for internal keys. Further, 8-byte - * encodings (up to 41 bit code points) are not currently needed. - */ - goto fail; - } - - DUK_ASSERT(p >= ptr_start); /* verified at beginning */ - if (p + n > ptr_end) { - /* check pointer at end */ - goto fail; - } - - while (n > 0) { - DUK_ASSERT(p >= ptr_start && p < ptr_end); - ch = (duk_uint_fast8_t) (*p++); -#if 0 - if (ch & 0xc0 != 0x80) { - /* not a continuation byte */ - p--; - *ptr = p; - *out_cp = DUK_UNICODE_CP_REPLACEMENT_CHARACTER; - return 1; - } -#endif - res = (res << 6) + (duk_uint32_t) (ch & 0x3f); - n--; - } - - *ptr = p; - *out_cp = res; - return 1; - -fail: - return 0; -} - -/* used by e.g. duk_regexp_executor.c, string built-ins */ -DUK_INTERNAL duk_ucodepoint_t duk_unicode_decode_xutf8_checked(duk_hthread *thr, - const duk_uint8_t **ptr, - const duk_uint8_t *ptr_start, - const duk_uint8_t *ptr_end) { - duk_ucodepoint_t cp; - - if (duk_unicode_decode_xutf8(thr, ptr, ptr_start, ptr_end, &cp)) { - return cp; - } - DUK_ERROR_INTERNAL(thr); - DUK_WO_NORETURN(return 0;); -} - -/* Compute (extended) utf-8 length without codepoint encoding validation, - * used for string interning. - * - * NOTE: This algorithm is performance critical, more so than string hashing - * in some cases. It is needed when interning a string and needs to scan - * every byte of the string with no skipping. Having an ASCII fast path - * is useful if possible in the algorithm. The current algorithms were - * chosen from several variants, based on x64 gcc -O2 testing. See: - * https://github.com/svaarala/duktape/pull/422 - * - * NOTE: must match tools/dukutil.py:duk_unicode_unvalidated_utf8_length(). - */ - -#if defined(DUK_USE_PREFER_SIZE) -/* Small variant; roughly 150 bytes smaller than the fast variant. */ -DUK_INTERNAL duk_size_t duk_unicode_unvalidated_utf8_length(const duk_uint8_t *data, duk_size_t blen) { - const duk_uint8_t *p; - const duk_uint8_t *p_end; - duk_size_t ncont; - duk_size_t clen; - - p = data; - p_end = data + blen; - ncont = 0; - while (p != p_end) { - duk_uint8_t x; - x = *p++; - if (DUK_UNLIKELY(x >= 0x80 && x <= 0xbf)) { - ncont++; - } - } - - DUK_ASSERT(ncont <= blen); - clen = blen - ncont; - DUK_ASSERT(clen <= blen); - return clen; -} -#else /* DUK_USE_PREFER_SIZE */ -/* This seems like a good overall approach. Fast path for ASCII in 4 byte - * blocks. - */ -DUK_INTERNAL duk_size_t duk_unicode_unvalidated_utf8_length(const duk_uint8_t *data, duk_size_t blen) { - const duk_uint8_t *p; - const duk_uint8_t *p_end; - const duk_uint32_t *p32_end; - const duk_uint32_t *p32; - duk_size_t ncont; - duk_size_t clen; - - ncont = 0; /* number of continuation (non-initial) bytes in [0x80,0xbf] */ - p = data; - p_end = data + blen; - if (blen < 16) { - goto skip_fastpath; - } - - /* Align 'p' to 4; the input data may have arbitrary alignment. - * End of string check not needed because blen >= 16. - */ - while (((duk_size_t) (const void *) p) & 0x03U) { - duk_uint8_t x; - x = *p++; - if (DUK_UNLIKELY(x >= 0x80 && x <= 0xbf)) { - ncont++; - } - } - - /* Full, aligned 4-byte reads. */ - p32_end = (const duk_uint32_t *) (const void *) (p + ((duk_size_t) (p_end - p) & (duk_size_t) (~0x03))); - p32 = (const duk_uint32_t *) (const void *) p; - while (p32 != (const duk_uint32_t *) p32_end) { - duk_uint32_t x; - x = *p32++; - if (DUK_LIKELY((x & 0x80808080UL) == 0)) { - ; /* ASCII fast path */ - } else { - /* Flip highest bit of each byte which changes - * the bit pattern 10xxxxxx into 00xxxxxx which - * allows an easy bit mask test. - */ - x ^= 0x80808080UL; - if (DUK_UNLIKELY(!(x & 0xc0000000UL))) { - ncont++; - } - if (DUK_UNLIKELY(!(x & 0x00c00000UL))) { - ncont++; - } - if (DUK_UNLIKELY(!(x & 0x0000c000UL))) { - ncont++; - } - if (DUK_UNLIKELY(!(x & 0x000000c0UL))) { - ncont++; - } - } - } - p = (const duk_uint8_t *) p32; - /* Fall through to handle the rest. */ - -skip_fastpath: - while (p != p_end) { - duk_uint8_t x; - x = *p++; - if (DUK_UNLIKELY(x >= 0x80 && x <= 0xbf)) { - ncont++; - } - } - - DUK_ASSERT(ncont <= blen); - clen = blen - ncont; - DUK_ASSERT(clen <= blen); - return clen; -} -#endif /* DUK_USE_PREFER_SIZE */ - -/* Check whether a string is UTF-8 compatible or not. */ -DUK_INTERNAL duk_bool_t duk_unicode_is_utf8_compatible(const duk_uint8_t *buf, duk_size_t len) { - duk_size_t i = 0; -#if !defined(DUK_USE_PREFER_SIZE) - duk_size_t len_safe; -#endif - - /* Many practical strings are ASCII only, so use a fast path check - * to check chunks of bytes at once with minimal branch cost. - */ -#if !defined(DUK_USE_PREFER_SIZE) - len_safe = len & ~0x03UL; - for (; i < len_safe; i += 4) { - duk_uint8_t t = buf[i] | buf[i + 1] | buf[i + 2] | buf[i + 3]; - if (DUK_UNLIKELY((t & 0x80U) != 0U)) { - /* At least one byte was outside 0x00-0x7f, break - * out to slow path (and remain there). - * - * XXX: We could also deal with the problem character - * and resume fast path later. - */ - break; - } - } -#endif - - for (; i < len;) { - duk_uint8_t t; - duk_size_t left; - duk_size_t ncont; - duk_uint32_t cp; - duk_uint32_t mincp; - - t = buf[i++]; - if (DUK_LIKELY((t & 0x80U) == 0U)) { - /* Fast path, ASCII. */ - continue; - } - - /* Non-ASCII start byte, slow path. - * - * 10xx xxxx -> continuation byte - * 110x xxxx + 1*CONT -> [0x80, 0x7ff] - * 1110 xxxx + 2*CONT -> [0x800, 0xffff], must reject [0xd800,0xdfff] - * 1111 0xxx + 3*CONT -> [0x10000, 0x10ffff] - */ - left = len - i; - if (t <= 0xdfU) { /* 1101 1111 = 0xdf */ - if (t <= 0xbfU) { /* 1011 1111 = 0xbf */ - return 0; - } - ncont = 1; - mincp = 0x80UL; - cp = t & 0x1fU; - } else if (t <= 0xefU) { /* 1110 1111 = 0xef */ - ncont = 2; - mincp = 0x800UL; - cp = t & 0x0fU; - } else if (t <= 0xf7U) { /* 1111 0111 = 0xf7 */ - ncont = 3; - mincp = 0x10000UL; - cp = t & 0x07U; - } else { - return 0; - } - if (left < ncont) { - return 0; - } - while (ncont > 0U) { - t = buf[i++]; - if ((t & 0xc0U) != 0x80U) { /* 10xx xxxx */ - return 0; - } - cp = (cp << 6) + (t & 0x3fU); - ncont--; - } - if (cp < mincp || cp > 0x10ffffUL || (cp >= 0xd800UL && cp <= 0xdfffUL)) { - return 0; - } - } - - return 1; -} - -/* - * Unicode range matcher - * - * Matches a codepoint against a packed bitstream of character ranges. - * Used for slow path Unicode matching. - */ - -/* Must match tools/extract_chars.py, generate_match_table3(). */ -DUK_LOCAL duk_uint32_t duk__uni_decode_value(duk_bitdecoder_ctx *bd_ctx) { - duk_uint32_t t; - - t = (duk_uint32_t) duk_bd_decode(bd_ctx, 4); - if (t <= 0x0eU) { - return t; - } - t = (duk_uint32_t) duk_bd_decode(bd_ctx, 8); - if (t <= 0xfdU) { - return t + 0x0f; - } - if (t == 0xfeU) { - t = (duk_uint32_t) duk_bd_decode(bd_ctx, 12); - return t + 0x0fU + 0xfeU; - } else { - t = (duk_uint32_t) duk_bd_decode(bd_ctx, 24); - return t + 0x0fU + 0xfeU + 0x1000UL; - } -} - -DUK_LOCAL duk_small_int_t duk__uni_range_match(const duk_uint8_t *unitab, duk_size_t unilen, duk_codepoint_t cp) { - duk_bitdecoder_ctx bd_ctx; - duk_codepoint_t prev_re; - - duk_memzero(&bd_ctx, sizeof(bd_ctx)); - bd_ctx.data = (const duk_uint8_t *) unitab; - bd_ctx.length = (duk_size_t) unilen; - - prev_re = 0; - for (;;) { - duk_codepoint_t r1, r2; - r1 = (duk_codepoint_t) duk__uni_decode_value(&bd_ctx); - if (r1 == 0) { - break; - } - r2 = (duk_codepoint_t) duk__uni_decode_value(&bd_ctx); - - r1 = prev_re + r1; - r2 = r1 + r2; - prev_re = r2; - - /* [r1,r2] is the range */ - - DUK_DDD(DUK_DDDPRINT("duk__uni_range_match: cp=%06lx range=[0x%06lx,0x%06lx]", - (unsigned long) cp, - (unsigned long) r1, - (unsigned long) r2)); - if (cp >= r1 && cp <= r2) { - return 1; - } - } - - return 0; -} - -/* - * "WhiteSpace" production check. - */ - -DUK_INTERNAL duk_small_int_t duk_unicode_is_whitespace(duk_codepoint_t cp) { - /* - * E5 Section 7.2 specifies six characters specifically as - * white space: - * - * 0009;;Cc;0;S;;;;;N;CHARACTER TABULATION;;;; - * 000B;;Cc;0;S;;;;;N;LINE TABULATION;;;; - * 000C;;Cc;0;WS;;;;;N;FORM FEED (FF);;;; - * 0020;SPACE;Zs;0;WS;;;;;N;;;;; - * 00A0;NO-BREAK SPACE;Zs;0;CS; 0020;;;;N;NON-BREAKING SPACE;;;; - * FEFF;ZERO WIDTH NO-BREAK SPACE;Cf;0;BN;;;;;N;BYTE ORDER MARK;;;; - * - * It also specifies any Unicode category 'Zs' characters as white - * space. These can be extracted with the "tools/extract_chars.py" script. - * Current result: - * - * RAW OUTPUT: - * =========== - * 0020;SPACE;Zs;0;WS;;;;;N;;;;; - * 00A0;NO-BREAK SPACE;Zs;0;CS; 0020;;;;N;NON-BREAKING SPACE;;;; - * 1680;OGHAM SPACE MARK;Zs;0;WS;;;;;N;;;;; - * 180E;MONGOLIAN VOWEL SEPARATOR;Zs;0;WS;;;;;N;;;;; - * 2000;EN QUAD;Zs;0;WS;2002;;;;N;;;;; - * 2001;EM QUAD;Zs;0;WS;2003;;;;N;;;;; - * 2002;EN SPACE;Zs;0;WS; 0020;;;;N;;;;; - * 2003;EM SPACE;Zs;0;WS; 0020;;;;N;;;;; - * 2004;THREE-PER-EM SPACE;Zs;0;WS; 0020;;;;N;;;;; - * 2005;FOUR-PER-EM SPACE;Zs;0;WS; 0020;;;;N;;;;; - * 2006;SIX-PER-EM SPACE;Zs;0;WS; 0020;;;;N;;;;; - * 2007;FIGURE SPACE;Zs;0;WS; 0020;;;;N;;;;; - * 2008;PUNCTUATION SPACE;Zs;0;WS; 0020;;;;N;;;;; - * 2009;THIN SPACE;Zs;0;WS; 0020;;;;N;;;;; - * 200A;HAIR SPACE;Zs;0;WS; 0020;;;;N;;;;; - * 202F;NARROW NO-BREAK SPACE;Zs;0;CS; 0020;;;;N;;;;; - * 205F;MEDIUM MATHEMATICAL SPACE;Zs;0;WS; 0020;;;;N;;;;; - * 3000;IDEOGRAPHIC SPACE;Zs;0;WS; 0020;;;;N;;;;; - * - * RANGES: - * ======= - * 0x0020 - * 0x00a0 - * 0x1680 - * 0x180e - * 0x2000 ... 0x200a - * 0x202f - * 0x205f - * 0x3000 - * - * A manual decoder (below) is probably most compact for this. - */ - - duk_uint_fast8_t lo; - duk_uint_fast32_t hi; - - /* cp == -1 (EOF) never matches and causes return value 0 */ - - lo = (duk_uint_fast8_t) (cp & 0xff); - hi = (duk_uint_fast32_t) (cp >> 8); /* does not fit into an uchar */ - - if (hi == 0x0000UL) { - if (lo == 0x09U || lo == 0x0bU || lo == 0x0cU || lo == 0x20U || lo == 0xa0U) { - return 1; - } - } else if (hi == 0x0020UL) { - if (lo <= 0x0aU || lo == 0x2fU || lo == 0x5fU) { - return 1; - } - } else if (cp == 0x1680L || cp == 0x180eL || cp == 0x3000L || cp == 0xfeffL) { - return 1; - } - - return 0; -} - -/* - * "LineTerminator" production check. - */ - -DUK_INTERNAL duk_small_int_t duk_unicode_is_line_terminator(duk_codepoint_t cp) { - /* - * E5 Section 7.3 - * - * A LineTerminatorSequence essentially merges sequences - * into a single line terminator. This must be handled by the caller. - */ - - if (cp == 0x000aL || cp == 0x000dL || cp == 0x2028L || cp == 0x2029L) { - return 1; - } - - return 0; -} - -/* - * "IdentifierStart" production check. - */ - -DUK_INTERNAL duk_small_int_t duk_unicode_is_identifier_start(duk_codepoint_t cp) { - /* - * E5 Section 7.6: - * - * IdentifierStart: - * UnicodeLetter - * $ - * _ - * \ UnicodeEscapeSequence - * - * IdentifierStart production has one multi-character production: - * - * \ UnicodeEscapeSequence - * - * The '\' character is -not- matched by this function. Rather, the caller - * should decode the escape and then call this function to check whether the - * decoded character is acceptable (see discussion in E5 Section 7.6). - * - * The "UnicodeLetter" alternative of the production allows letters - * from various Unicode categories. These can be extracted with the - * "tools/extract_chars.py" script. - * - * Because the result has hundreds of Unicode codepoint ranges, matching - * for any values >= 0x80 are done using a very slow range-by-range scan - * and a packed range format. - * - * The ASCII portion (codepoints 0x00 ... 0x7f) is fast-pathed below because - * it matters the most. The ASCII related ranges of IdentifierStart are: - * - * 0x0041 ... 0x005a ['A' ... 'Z'] - * 0x0061 ... 0x007a ['a' ... 'z'] - * 0x0024 ['$'] - * 0x005f ['_'] - */ - - /* ASCII (and EOF) fast path -- quick accept and reject */ - if (cp <= 0x7fL) { -#if defined(DUK_USE_IDCHAR_FASTPATH) - return (cp >= 0) && (duk_is_idchar_tab[cp] > 0); -#else - if ((cp >= 'a' && cp <= 'z') || (cp >= 'A' && cp <= 'Z') || cp == '_' || cp == '$') { - return 1; - } - return 0; -#endif - } - - /* Non-ASCII slow path (range-by-range linear comparison), very slow */ - -#if defined(DUK_USE_SOURCE_NONBMP) - if (duk__uni_range_match(duk_unicode_ids_noa, (duk_size_t) sizeof(duk_unicode_ids_noa), (duk_codepoint_t) cp)) { - return 1; - } - return 0; -#else - if (cp < 0x10000L) { - if (duk__uni_range_match(duk_unicode_ids_noabmp, sizeof(duk_unicode_ids_noabmp), (duk_codepoint_t) cp)) { - return 1; - } - return 0; - } else { - /* without explicit non-BMP support, assume non-BMP characters - * are always accepted as identifier characters. - */ - return 1; - } -#endif -} - -/* - * "IdentifierPart" production check. - */ - -DUK_INTERNAL duk_small_int_t duk_unicode_is_identifier_part(duk_codepoint_t cp) { - /* - * E5 Section 7.6: - * - * IdentifierPart: - * IdentifierStart - * UnicodeCombiningMark - * UnicodeDigit - * UnicodeConnectorPunctuation - * [U+200C] - * [U+200D] - * - * IdentifierPart production has one multi-character production - * as part of its IdentifierStart alternative. The '\' character - * of an escape sequence is not matched here, see discussion in - * duk_unicode_is_identifier_start(). - * - * To match non-ASCII characters (codepoints >= 0x80), a very slow - * linear range-by-range scan is used. The codepoint is first compared - * to the IdentifierStart ranges, and if it doesn't match, then to a - * set consisting of code points in IdentifierPart but not in - * IdentifierStart. This is done to keep the unicode range data small, - * at the expense of speed. - * - * The ASCII fast path consists of: - * - * 0x0030 ... 0x0039 ['0' ... '9', UnicodeDigit] - * 0x0041 ... 0x005a ['A' ... 'Z', IdentifierStart] - * 0x0061 ... 0x007a ['a' ... 'z', IdentifierStart] - * 0x0024 ['$', IdentifierStart] - * 0x005f ['_', IdentifierStart and - * UnicodeConnectorPunctuation] - * - * UnicodeCombiningMark has no code points <= 0x7f. - * - * The matching code reuses the "identifier start" tables, and then - * consults a separate range set for characters in "identifier part" - * but not in "identifier start". These can be extracted with the - * "tools/extract_chars.py" script. - * - * UnicodeCombiningMark -> categories Mn, Mc - * UnicodeDigit -> categories Nd - * UnicodeConnectorPunctuation -> categories Pc - */ - - /* ASCII (and EOF) fast path -- quick accept and reject */ - if (cp <= 0x7fL) { -#if defined(DUK_USE_IDCHAR_FASTPATH) - return (cp >= 0) && (duk_is_idchar_tab[cp] != 0); -#else - if ((cp >= 'a' && cp <= 'z') || (cp >= 'A' && cp <= 'Z') || (cp >= '0' && cp <= '9') || cp == '_' || cp == '$') { - return 1; - } - return 0; -#endif - } - - /* Non-ASCII slow path (range-by-range linear comparison), very slow */ - -#if defined(DUK_USE_SOURCE_NONBMP) - if (duk__uni_range_match(duk_unicode_ids_noa, sizeof(duk_unicode_ids_noa), (duk_codepoint_t) cp) || - duk__uni_range_match(duk_unicode_idp_m_ids_noa, sizeof(duk_unicode_idp_m_ids_noa), (duk_codepoint_t) cp)) { - return 1; - } - return 0; -#else - if (cp < 0x10000L) { - if (duk__uni_range_match(duk_unicode_ids_noabmp, sizeof(duk_unicode_ids_noabmp), (duk_codepoint_t) cp) || - duk__uni_range_match(duk_unicode_idp_m_ids_noabmp, - sizeof(duk_unicode_idp_m_ids_noabmp), - (duk_codepoint_t) cp)) { - return 1; - } - return 0; - } else { - /* without explicit non-BMP support, assume non-BMP characters - * are always accepted as identifier characters. - */ - return 1; - } -#endif -} - -/* - * Unicode letter check. - */ - -DUK_INTERNAL duk_small_int_t duk_unicode_is_letter(duk_codepoint_t cp) { - /* - * Unicode letter is now taken to be the categories: - * - * Lu, Ll, Lt, Lm, Lo - * - * (Not sure if this is exactly correct.) - * - * The ASCII fast path consists of: - * - * 0x0041 ... 0x005a ['A' ... 'Z'] - * 0x0061 ... 0x007a ['a' ... 'z'] - */ - - /* ASCII (and EOF) fast path -- quick accept and reject */ - if (cp <= 0x7fL) { - if ((cp >= 'a' && cp <= 'z') || (cp >= 'A' && cp <= 'Z')) { - return 1; - } - return 0; - } - - /* Non-ASCII slow path (range-by-range linear comparison), very slow */ - -#if defined(DUK_USE_SOURCE_NONBMP) - if (duk__uni_range_match(duk_unicode_ids_noa, sizeof(duk_unicode_ids_noa), (duk_codepoint_t) cp) && - !duk__uni_range_match(duk_unicode_ids_m_let_noa, sizeof(duk_unicode_ids_m_let_noa), (duk_codepoint_t) cp)) { - return 1; - } - return 0; -#else - if (cp < 0x10000L) { - if (duk__uni_range_match(duk_unicode_ids_noabmp, sizeof(duk_unicode_ids_noabmp), (duk_codepoint_t) cp) && - !duk__uni_range_match(duk_unicode_ids_m_let_noabmp, - sizeof(duk_unicode_ids_m_let_noabmp), - (duk_codepoint_t) cp)) { - return 1; - } - return 0; - } else { - /* without explicit non-BMP support, assume non-BMP characters - * are always accepted as letters. - */ - return 1; - } -#endif -} - -/* - * Complex case conversion helper which decodes a bit-packed conversion - * control stream generated by tools/extract_caseconv.py. The conversion - * is very slow because it runs through the conversion data in a linear - * fashion to save space (which is why ASCII characters have a special - * fast path before arriving here). - * - * The particular bit counts etc have been determined experimentally to - * be small but still sufficient, and must match the Python script - * (tools/extract_caseconv.py). - * - * The return value is the case converted codepoint or -1 if the conversion - * results in multiple characters (this is useful for regexp Canonicalization - * operation). If 'buf' is not NULL, the result codepoint(s) are also - * appended to the hbuffer. - * - * Context and locale specific rules must be checked before consulting - * this function. - */ - -DUK_LOCAL -duk_codepoint_t duk__slow_case_conversion(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_codepoint_t cp, duk_bitdecoder_ctx *bd_ctx) { - duk_small_int_t skip = 0; - duk_small_int_t n; - duk_small_int_t t; - duk_small_int_t count; - duk_codepoint_t tmp_cp; - duk_codepoint_t start_i; - duk_codepoint_t start_o; - - DUK_ASSERT(bd_ctx != NULL); - DUK_UNREF(thr); - - DUK_DDD(DUK_DDDPRINT("slow case conversion for codepoint: %ld", (long) cp)); - - /* range conversion with a "skip" */ - DUK_DDD(DUK_DDDPRINT("checking ranges")); - for (;;) { - skip++; - n = (duk_small_int_t) duk_bd_decode(bd_ctx, 6); - if (n == 0x3f) { - /* end marker */ - break; - } - DUK_DDD(DUK_DDDPRINT("skip=%ld, n=%ld", (long) skip, (long) n)); - - while (n--) { - start_i = (duk_codepoint_t) duk_bd_decode(bd_ctx, 16); - start_o = (duk_codepoint_t) duk_bd_decode(bd_ctx, 16); - count = (duk_small_int_t) duk_bd_decode(bd_ctx, 7); - DUK_DDD(DUK_DDDPRINT("range: start_i=%ld, start_o=%ld, count=%ld, skip=%ld", - (long) start_i, - (long) start_o, - (long) count, - (long) skip)); - - if (cp >= start_i) { - tmp_cp = cp - start_i; /* always >= 0 */ - if (tmp_cp < (duk_codepoint_t) count * (duk_codepoint_t) skip && - (tmp_cp % (duk_codepoint_t) skip) == 0) { - DUK_DDD(DUK_DDDPRINT("range matches input codepoint")); - cp = start_o + tmp_cp; - goto single; - } - } - } - } - - /* 1:1 conversion */ - n = (duk_small_int_t) duk_bd_decode(bd_ctx, 7); - DUK_DDD(DUK_DDDPRINT("checking 1:1 conversions (count %ld)", (long) n)); - while (n--) { - start_i = (duk_codepoint_t) duk_bd_decode(bd_ctx, 16); - start_o = (duk_codepoint_t) duk_bd_decode(bd_ctx, 16); - DUK_DDD(DUK_DDDPRINT("1:1 conversion %ld -> %ld", (long) start_i, (long) start_o)); - if (cp == start_i) { - DUK_DDD(DUK_DDDPRINT("1:1 matches input codepoint")); - cp = start_o; - goto single; - } - } - - /* complex, multicharacter conversion */ - n = (duk_small_int_t) duk_bd_decode(bd_ctx, 7); - DUK_DDD(DUK_DDDPRINT("checking 1:n conversions (count %ld)", (long) n)); - while (n--) { - start_i = (duk_codepoint_t) duk_bd_decode(bd_ctx, 16); - t = (duk_small_int_t) duk_bd_decode(bd_ctx, 2); - DUK_DDD(DUK_DDDPRINT("1:n conversion %ld -> %ld chars", (long) start_i, (long) t)); - if (cp == start_i) { - DUK_DDD(DUK_DDDPRINT("1:n matches input codepoint")); - if (bw != NULL) { - while (t--) { - tmp_cp = (duk_codepoint_t) duk_bd_decode(bd_ctx, 16); - DUK_BW_WRITE_RAW_XUTF8(thr, bw, (duk_ucodepoint_t) tmp_cp); - } - } - return -1; - } else { - while (t--) { - (void) duk_bd_decode(bd_ctx, 16); - } - } - } - - /* default: no change */ - DUK_DDD(DUK_DDDPRINT("no rule matches, output is same as input")); - /* fall through */ - -single: - if (bw != NULL) { - DUK_BW_WRITE_RAW_XUTF8(thr, bw, (duk_ucodepoint_t) cp); - } - return cp; -} - -/* - * Case conversion helper, with context/local sensitivity. - * For proper case conversion, one needs to know the character - * and the preceding and following characters, as well as - * locale/language. - */ - -/* XXX: add 'language' argument when locale/language sensitive rule - * support added. - */ -DUK_LOCAL -duk_codepoint_t duk__case_transform_helper(duk_hthread *thr, - duk_bufwriter_ctx *bw, - duk_codepoint_t cp, - duk_codepoint_t prev, - duk_codepoint_t next, - duk_bool_t uppercase) { - duk_bitdecoder_ctx bd_ctx; - - /* fast path for ASCII */ - if (cp < 0x80L) { - /* XXX: there are language sensitive rules for the ASCII range. - * If/when language/locale support is implemented, they need to - * be implemented here for the fast path. There are no context - * sensitive rules for ASCII range. - */ - - if (uppercase) { - if (cp >= 'a' && cp <= 'z') { - cp = cp - 'a' + 'A'; - } - } else { - if (cp >= 'A' && cp <= 'Z') { - cp = cp - 'A' + 'a'; - } - } - - if (bw != NULL) { - DUK_BW_WRITE_RAW_U8(thr, bw, (duk_uint8_t) cp); - } - return cp; - } - - /* context and locale specific rules which cannot currently be represented - * in the caseconv bitstream: hardcoded rules in C - */ - if (uppercase) { - /* XXX: turkish / azeri */ - } else { - /* - * Final sigma context specific rule. This is a rather tricky - * rule and this handling is probably not 100% correct now. - * The rule is not locale/language specific so it is supported. - */ - - if (cp == 0x03a3L && /* U+03A3 = GREEK CAPITAL LETTER SIGMA */ - duk_unicode_is_letter(prev) && /* prev exists and is not a letter */ - !duk_unicode_is_letter(next)) { /* next does not exist or next is not a letter */ - /* Capital sigma occurred at "end of word", lowercase to - * U+03C2 = GREEK SMALL LETTER FINAL SIGMA. Otherwise - * fall through and let the normal rules lowercase it to - * U+03C3 = GREEK SMALL LETTER SIGMA. - */ - cp = 0x03c2L; - goto singlechar; - } - - /* XXX: lithuanian not implemented */ - /* XXX: lithuanian, explicit dot rules */ - /* XXX: turkish / azeri, lowercase rules */ - } - - /* 1:1 or special conversions, but not locale/context specific: script generated rules */ - duk_memzero(&bd_ctx, sizeof(bd_ctx)); - if (uppercase) { - bd_ctx.data = (const duk_uint8_t *) duk_unicode_caseconv_uc; - bd_ctx.length = (duk_size_t) sizeof(duk_unicode_caseconv_uc); - } else { - bd_ctx.data = (const duk_uint8_t *) duk_unicode_caseconv_lc; - bd_ctx.length = (duk_size_t) sizeof(duk_unicode_caseconv_lc); - } - return duk__slow_case_conversion(thr, bw, cp, &bd_ctx); - -singlechar: - if (bw != NULL) { - DUK_BW_WRITE_RAW_XUTF8(thr, bw, (duk_ucodepoint_t) cp); - } - return cp; - - /* unused now, not needed until Turkish/Azeri */ -#if 0 - nochar: - return -1; -#endif -} - -/* - * Replace valstack top with case converted version. - */ - -DUK_INTERNAL void duk_unicode_case_convert_string(duk_hthread *thr, duk_bool_t uppercase) { - duk_hstring *h_input; - duk_bufwriter_ctx bw_alloc; - duk_bufwriter_ctx *bw; - const duk_uint8_t *p, *p_start, *p_end; - duk_codepoint_t prev, curr, next; - - h_input = duk_require_hstring(thr, -1); /* Accept symbols. */ - DUK_ASSERT(h_input != NULL); - - bw = &bw_alloc; - DUK_BW_INIT_PUSHBUF(thr, bw, DUK_HSTRING_GET_BYTELEN(h_input)); - - /* [ ... input buffer ] */ - - p_start = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_input); - p_end = p_start + DUK_HSTRING_GET_BYTELEN(h_input); - p = p_start; - - prev = -1; - DUK_UNREF(prev); - curr = -1; - next = -1; - for (;;) { - prev = curr; - curr = next; - next = -1; - if (p < p_end) { - next = (duk_codepoint_t) duk_unicode_decode_xutf8_checked(thr, &p, p_start, p_end); - } else { - /* end of input and last char has been processed */ - if (curr < 0) { - break; - } - } - - /* on first round, skip */ - if (curr >= 0) { - /* XXX: could add a fast path to process chunks of input codepoints, - * but relative benefit would be quite small. - */ - - /* Ensure space for maximum multi-character result; estimate is overkill. */ - DUK_BW_ENSURE(thr, bw, 8 * DUK_UNICODE_MAX_XUTF8_LENGTH); - - duk__case_transform_helper(thr, bw, (duk_codepoint_t) curr, prev, next, uppercase); - } - } - - DUK_BW_COMPACT(thr, bw); - (void) duk_buffer_to_string(thr, -1); /* Safe, output is encoded. */ - /* invalidates h_buf pointer */ - duk_remove_m2(thr); -} - -#if defined(DUK_USE_REGEXP_SUPPORT) - -/* - * Canonicalize() abstract operation needed for canonicalization of individual - * codepoints during regexp compilation and execution, see E5 Section 15.10.2.8. - * Note that codepoints are canonicalized one character at a time, so no context - * specific rules can apply. Locale specific rules can apply, though. - */ - -DUK_INTERNAL duk_codepoint_t duk_unicode_re_canonicalize_char(duk_hthread *thr, duk_codepoint_t cp) { -#if defined(DUK_USE_REGEXP_CANON_WORKAROUND) - /* Fast canonicalization lookup at the cost of 128kB footprint. */ - DUK_ASSERT(cp >= 0); - DUK_UNREF(thr); - if (DUK_LIKELY(cp < 0x10000L)) { - return (duk_codepoint_t) duk_unicode_re_canon_lookup[cp]; - } - return cp; -#else /* DUK_USE_REGEXP_CANON_WORKAROUND */ - duk_codepoint_t y; - - y = duk__case_transform_helper(thr, - NULL, /* NULL is allowed, no output */ - cp, /* curr char */ - -1, /* prev char */ - -1, /* next char */ - 1); /* uppercase */ - - if ((y < 0) || (cp >= 0x80 && y < 0x80)) { - /* multiple codepoint conversion or non-ASCII mapped to ASCII - * --> leave as is. - */ - return cp; - } - - return y; -#endif /* DUK_USE_REGEXP_CANON_WORKAROUND */ -} - -/* - * E5 Section 15.10.2.6 "IsWordChar" abstract operation. Assume - * x < 0 for characters read outside the string. - */ - -DUK_INTERNAL duk_small_int_t duk_unicode_re_is_wordchar(duk_codepoint_t x) { - /* - * Note: the description in E5 Section 15.10.2.6 has a typo, it - * contains 'A' twice and lacks 'a'; the intent is [0-9a-zA-Z_]. - */ - if ((x >= '0' && x <= '9') || (x >= 'a' && x <= 'z') || (x >= 'A' && x <= 'Z') || (x == '_')) { - return 1; - } - return 0; -} - -/* - * Regexp range tables - */ - -/* exposed because lexer needs these too */ -DUK_INTERNAL const duk_uint16_t duk_unicode_re_ranges_digit[2] = { - (duk_uint16_t) 0x0030UL, - (duk_uint16_t) 0x0039UL, -}; -DUK_INTERNAL const duk_uint16_t duk_unicode_re_ranges_white[22] = { - (duk_uint16_t) 0x0009UL, (duk_uint16_t) 0x000DUL, (duk_uint16_t) 0x0020UL, (duk_uint16_t) 0x0020UL, (duk_uint16_t) 0x00A0UL, - (duk_uint16_t) 0x00A0UL, (duk_uint16_t) 0x1680UL, (duk_uint16_t) 0x1680UL, (duk_uint16_t) 0x180EUL, (duk_uint16_t) 0x180EUL, - (duk_uint16_t) 0x2000UL, (duk_uint16_t) 0x200AUL, (duk_uint16_t) 0x2028UL, (duk_uint16_t) 0x2029UL, (duk_uint16_t) 0x202FUL, - (duk_uint16_t) 0x202FUL, (duk_uint16_t) 0x205FUL, (duk_uint16_t) 0x205FUL, (duk_uint16_t) 0x3000UL, (duk_uint16_t) 0x3000UL, - (duk_uint16_t) 0xFEFFUL, (duk_uint16_t) 0xFEFFUL, -}; -DUK_INTERNAL const duk_uint16_t duk_unicode_re_ranges_wordchar[8] = { - (duk_uint16_t) 0x0030UL, (duk_uint16_t) 0x0039UL, (duk_uint16_t) 0x0041UL, (duk_uint16_t) 0x005AUL, - (duk_uint16_t) 0x005FUL, (duk_uint16_t) 0x005FUL, (duk_uint16_t) 0x0061UL, (duk_uint16_t) 0x007AUL, -}; -DUK_INTERNAL const duk_uint16_t duk_unicode_re_ranges_not_digit[4] = { - (duk_uint16_t) 0x0000UL, - (duk_uint16_t) 0x002FUL, - (duk_uint16_t) 0x003AUL, - (duk_uint16_t) 0xFFFFUL, -}; -DUK_INTERNAL const duk_uint16_t duk_unicode_re_ranges_not_white[24] = { - (duk_uint16_t) 0x0000UL, (duk_uint16_t) 0x0008UL, (duk_uint16_t) 0x000EUL, (duk_uint16_t) 0x001FUL, (duk_uint16_t) 0x0021UL, - (duk_uint16_t) 0x009FUL, (duk_uint16_t) 0x00A1UL, (duk_uint16_t) 0x167FUL, (duk_uint16_t) 0x1681UL, (duk_uint16_t) 0x180DUL, - (duk_uint16_t) 0x180FUL, (duk_uint16_t) 0x1FFFUL, (duk_uint16_t) 0x200BUL, (duk_uint16_t) 0x2027UL, (duk_uint16_t) 0x202AUL, - (duk_uint16_t) 0x202EUL, (duk_uint16_t) 0x2030UL, (duk_uint16_t) 0x205EUL, (duk_uint16_t) 0x2060UL, (duk_uint16_t) 0x2FFFUL, - (duk_uint16_t) 0x3001UL, (duk_uint16_t) 0xFEFEUL, (duk_uint16_t) 0xFF00UL, (duk_uint16_t) 0xFFFFUL, -}; -DUK_INTERNAL const duk_uint16_t duk_unicode_re_ranges_not_wordchar[10] = { - (duk_uint16_t) 0x0000UL, (duk_uint16_t) 0x002FUL, (duk_uint16_t) 0x003AUL, (duk_uint16_t) 0x0040UL, (duk_uint16_t) 0x005BUL, - (duk_uint16_t) 0x005EUL, (duk_uint16_t) 0x0060UL, (duk_uint16_t) 0x0060UL, (duk_uint16_t) 0x007BUL, (duk_uint16_t) 0xFFFFUL, -}; - -#endif /* DUK_USE_REGEXP_SUPPORT */ -/* - * Macro support functions for reading/writing raw data. - * - * These are done using memcpy to ensure they're valid even for unaligned - * reads/writes on platforms where alignment counts. On x86 at least gcc - * is able to compile these into a bswap+mov. "Always inline" is used to - * ensure these macros compile to minimal code. - */ - -/* #include duk_internal.h -> already included */ - -union duk__u16_union { - duk_uint8_t b[2]; - duk_uint16_t x; -}; -typedef union duk__u16_union duk__u16_union; - -union duk__u32_union { - duk_uint8_t b[4]; - duk_uint32_t x; -}; -typedef union duk__u32_union duk__u32_union; - -#if defined(DUK_USE_64BIT_OPS) -union duk__u64_union { - duk_uint8_t b[8]; - duk_uint64_t x; -}; -typedef union duk__u64_union duk__u64_union; -#endif - -DUK_INTERNAL DUK_ALWAYS_INLINE duk_uint16_t duk_raw_read_u16_be(const duk_uint8_t *p) { - duk__u16_union u; - duk_memcpy((void *) u.b, (const void *) p, (size_t) 2); - u.x = DUK_NTOH16(u.x); - return u.x; -} - -DUK_INTERNAL DUK_ALWAYS_INLINE duk_uint32_t duk_raw_read_u32_be(const duk_uint8_t *p) { - duk__u32_union u; - duk_memcpy((void *) u.b, (const void *) p, (size_t) 4); - u.x = DUK_NTOH32(u.x); - return u.x; -} - -DUK_INTERNAL DUK_ALWAYS_INLINE duk_float_t duk_raw_read_float_be(const duk_uint8_t *p) { - duk_float_union fu; - duk_memcpy((void *) fu.uc, (const void *) p, (size_t) 4); - duk_fltunion_big_to_host(&fu); - return fu.f; -} - -DUK_INTERNAL DUK_ALWAYS_INLINE duk_double_t duk_raw_read_double_be(const duk_uint8_t *p) { - duk_double_union du; - duk_memcpy((void *) du.uc, (const void *) p, (size_t) 8); - duk_dblunion_big_to_host(&du); - return du.d; -} - -DUK_INTERNAL DUK_ALWAYS_INLINE duk_uint16_t duk_raw_readinc_u16_be(const duk_uint8_t **p) { - duk_uint16_t res = duk_raw_read_u16_be(*p); - *p += 2; - return res; -} - -DUK_INTERNAL DUK_ALWAYS_INLINE duk_uint32_t duk_raw_readinc_u32_be(const duk_uint8_t **p) { - duk_uint32_t res = duk_raw_read_u32_be(*p); - *p += 4; - return res; -} - -DUK_INTERNAL DUK_ALWAYS_INLINE duk_float_t duk_raw_readinc_float_be(const duk_uint8_t **p) { - duk_float_t res = duk_raw_read_float_be(*p); - *p += 4; - return res; -} - -DUK_INTERNAL DUK_ALWAYS_INLINE duk_double_t duk_raw_readinc_double_be(const duk_uint8_t **p) { - duk_double_t res = duk_raw_read_double_be(*p); - *p += 8; - return res; -} - -DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_write_u16_be(duk_uint8_t *p, duk_uint16_t val) { - duk__u16_union u; - u.x = DUK_HTON16(val); - duk_memcpy((void *) p, (const void *) u.b, (size_t) 2); -} - -DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_write_u32_be(duk_uint8_t *p, duk_uint32_t val) { - duk__u32_union u; - u.x = DUK_HTON32(val); - duk_memcpy((void *) p, (const void *) u.b, (size_t) 4); -} - -DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_write_float_be(duk_uint8_t *p, duk_float_t val) { - duk_float_union fu; - fu.f = val; - duk_fltunion_host_to_big(&fu); - duk_memcpy((void *) p, (const void *) fu.uc, (size_t) 4); -} - -DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_write_double_be(duk_uint8_t *p, duk_double_t val) { - duk_double_union du; - du.d = val; - duk_dblunion_host_to_big(&du); - duk_memcpy((void *) p, (const void *) du.uc, (size_t) 8); -} - -DUK_INTERNAL duk_small_int_t duk_raw_write_xutf8(duk_uint8_t *p, duk_ucodepoint_t val) { - duk_small_int_t len = duk_unicode_encode_xutf8(val, p); - return len; -} - -DUK_INTERNAL duk_small_int_t duk_raw_write_cesu8(duk_uint8_t *p, duk_ucodepoint_t val) { - duk_small_int_t len = duk_unicode_encode_cesu8(val, p); - return len; -} - -DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_writeinc_u16_be(duk_uint8_t **p, duk_uint16_t val) { - duk_raw_write_u16_be(*p, val); - *p += 2; -} - -DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_writeinc_u32_be(duk_uint8_t **p, duk_uint32_t val) { - duk_raw_write_u32_be(*p, val); - *p += 4; -} - -DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_writeinc_float_be(duk_uint8_t **p, duk_float_t val) { - duk_raw_write_float_be(*p, val); - *p += 4; -} - -DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_writeinc_double_be(duk_uint8_t **p, duk_double_t val) { - duk_raw_write_double_be(*p, val); - *p += 8; -} - -DUK_INTERNAL void duk_raw_writeinc_xutf8(duk_uint8_t **p, duk_ucodepoint_t val) { - duk_small_int_t len = duk_unicode_encode_xutf8(val, *p); - *p += len; -} - -DUK_INTERNAL void duk_raw_writeinc_cesu8(duk_uint8_t **p, duk_ucodepoint_t val) { - duk_small_int_t len = duk_unicode_encode_cesu8(val, *p); - *p += len; -} -/* - * Misc util stuff. - */ - -/* #include duk_internal.h -> already included */ - -/* - * Lowercase digits for radix values 2 to 36. Also doubles as lowercase - * hex nybble table. - */ - -DUK_INTERNAL const duk_uint8_t duk_lc_digits[36] = { - DUK_ASC_0, DUK_ASC_1, DUK_ASC_2, DUK_ASC_3, DUK_ASC_4, DUK_ASC_5, DUK_ASC_6, DUK_ASC_7, - DUK_ASC_8, DUK_ASC_9, DUK_ASC_LC_A, DUK_ASC_LC_B, DUK_ASC_LC_C, DUK_ASC_LC_D, DUK_ASC_LC_E, DUK_ASC_LC_F, - DUK_ASC_LC_G, DUK_ASC_LC_H, DUK_ASC_LC_I, DUK_ASC_LC_J, DUK_ASC_LC_K, DUK_ASC_LC_L, DUK_ASC_LC_M, DUK_ASC_LC_N, - DUK_ASC_LC_O, DUK_ASC_LC_P, DUK_ASC_LC_Q, DUK_ASC_LC_R, DUK_ASC_LC_S, DUK_ASC_LC_T, DUK_ASC_LC_U, DUK_ASC_LC_V, - DUK_ASC_LC_W, DUK_ASC_LC_X, DUK_ASC_LC_Y, DUK_ASC_LC_Z -}; - -DUK_INTERNAL const duk_uint8_t duk_uc_nybbles[16] = { DUK_ASC_0, DUK_ASC_1, DUK_ASC_2, DUK_ASC_3, - DUK_ASC_4, DUK_ASC_5, DUK_ASC_6, DUK_ASC_7, - DUK_ASC_8, DUK_ASC_9, DUK_ASC_UC_A, DUK_ASC_UC_B, - DUK_ASC_UC_C, DUK_ASC_UC_D, DUK_ASC_UC_E, DUK_ASC_UC_F }; - -/* - * Table for hex decoding ASCII hex digits - */ - -DUK_INTERNAL const duk_int8_t duk_hex_dectab[256] = { - /* -1 if invalid */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0f */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1f */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x20-0x2f */ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /* 0x30-0x3f */ - -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x40-0x4f */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x50-0x5f */ - -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x60-0x6f */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x70-0x7f */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x80-0x8f */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x90-0x9f */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xa0-0xaf */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xb0-0xbf */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xc0-0xcf */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xd0-0xdf */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xe0-0xef */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 /* 0xf0-0xff */ -}; - -#if defined(DUK_USE_HEX_FASTPATH) -/* Preshifted << 4. Must use 16-bit entry to allow negative value signaling. */ -DUK_INTERNAL const duk_int16_t duk_hex_dectab_shift4[256] = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0f */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1f */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x20-0x2f */ - 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, -1, -1, -1, -1, -1, -1, /* 0x30-0x3f */ - -1, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x40-0x4f */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x50-0x5f */ - -1, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x60-0x6f */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x70-0x7f */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x80-0x8f */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x90-0x9f */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xa0-0xaf */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xb0-0xbf */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xc0-0xcf */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xd0-0xdf */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xe0-0xef */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 /* 0xf0-0xff */ -}; -#endif - -/* - * Table for hex encoding bytes - */ - -#if defined(DUK_USE_HEX_FASTPATH) -/* Lookup to encode one byte directly into 2 characters: - * - * def genhextab(bswap): - * for i in xrange(256): - * t = chr(i).encode('hex') - * if bswap: - * t = t[1] + t[0] - * print('0x' + t.encode('hex') + 'U') - * print('big endian'); genhextab(False) - * print('little endian'); genhextab(True) - */ -DUK_INTERNAL const duk_uint16_t duk_hex_enctab[256] = { -#if defined(DUK_USE_INTEGER_BE) - 0x3030U, 0x3031U, 0x3032U, 0x3033U, 0x3034U, 0x3035U, 0x3036U, 0x3037U, 0x3038U, 0x3039U, 0x3061U, 0x3062U, 0x3063U, - 0x3064U, 0x3065U, 0x3066U, 0x3130U, 0x3131U, 0x3132U, 0x3133U, 0x3134U, 0x3135U, 0x3136U, 0x3137U, 0x3138U, 0x3139U, - 0x3161U, 0x3162U, 0x3163U, 0x3164U, 0x3165U, 0x3166U, 0x3230U, 0x3231U, 0x3232U, 0x3233U, 0x3234U, 0x3235U, 0x3236U, - 0x3237U, 0x3238U, 0x3239U, 0x3261U, 0x3262U, 0x3263U, 0x3264U, 0x3265U, 0x3266U, 0x3330U, 0x3331U, 0x3332U, 0x3333U, - 0x3334U, 0x3335U, 0x3336U, 0x3337U, 0x3338U, 0x3339U, 0x3361U, 0x3362U, 0x3363U, 0x3364U, 0x3365U, 0x3366U, 0x3430U, - 0x3431U, 0x3432U, 0x3433U, 0x3434U, 0x3435U, 0x3436U, 0x3437U, 0x3438U, 0x3439U, 0x3461U, 0x3462U, 0x3463U, 0x3464U, - 0x3465U, 0x3466U, 0x3530U, 0x3531U, 0x3532U, 0x3533U, 0x3534U, 0x3535U, 0x3536U, 0x3537U, 0x3538U, 0x3539U, 0x3561U, - 0x3562U, 0x3563U, 0x3564U, 0x3565U, 0x3566U, 0x3630U, 0x3631U, 0x3632U, 0x3633U, 0x3634U, 0x3635U, 0x3636U, 0x3637U, - 0x3638U, 0x3639U, 0x3661U, 0x3662U, 0x3663U, 0x3664U, 0x3665U, 0x3666U, 0x3730U, 0x3731U, 0x3732U, 0x3733U, 0x3734U, - 0x3735U, 0x3736U, 0x3737U, 0x3738U, 0x3739U, 0x3761U, 0x3762U, 0x3763U, 0x3764U, 0x3765U, 0x3766U, 0x3830U, 0x3831U, - 0x3832U, 0x3833U, 0x3834U, 0x3835U, 0x3836U, 0x3837U, 0x3838U, 0x3839U, 0x3861U, 0x3862U, 0x3863U, 0x3864U, 0x3865U, - 0x3866U, 0x3930U, 0x3931U, 0x3932U, 0x3933U, 0x3934U, 0x3935U, 0x3936U, 0x3937U, 0x3938U, 0x3939U, 0x3961U, 0x3962U, - 0x3963U, 0x3964U, 0x3965U, 0x3966U, 0x6130U, 0x6131U, 0x6132U, 0x6133U, 0x6134U, 0x6135U, 0x6136U, 0x6137U, 0x6138U, - 0x6139U, 0x6161U, 0x6162U, 0x6163U, 0x6164U, 0x6165U, 0x6166U, 0x6230U, 0x6231U, 0x6232U, 0x6233U, 0x6234U, 0x6235U, - 0x6236U, 0x6237U, 0x6238U, 0x6239U, 0x6261U, 0x6262U, 0x6263U, 0x6264U, 0x6265U, 0x6266U, 0x6330U, 0x6331U, 0x6332U, - 0x6333U, 0x6334U, 0x6335U, 0x6336U, 0x6337U, 0x6338U, 0x6339U, 0x6361U, 0x6362U, 0x6363U, 0x6364U, 0x6365U, 0x6366U, - 0x6430U, 0x6431U, 0x6432U, 0x6433U, 0x6434U, 0x6435U, 0x6436U, 0x6437U, 0x6438U, 0x6439U, 0x6461U, 0x6462U, 0x6463U, - 0x6464U, 0x6465U, 0x6466U, 0x6530U, 0x6531U, 0x6532U, 0x6533U, 0x6534U, 0x6535U, 0x6536U, 0x6537U, 0x6538U, 0x6539U, - 0x6561U, 0x6562U, 0x6563U, 0x6564U, 0x6565U, 0x6566U, 0x6630U, 0x6631U, 0x6632U, 0x6633U, 0x6634U, 0x6635U, 0x6636U, - 0x6637U, 0x6638U, 0x6639U, 0x6661U, 0x6662U, 0x6663U, 0x6664U, 0x6665U, 0x6666U -#else /* DUK_USE_INTEGER_BE */ - 0x3030U, 0x3130U, 0x3230U, 0x3330U, 0x3430U, 0x3530U, 0x3630U, 0x3730U, 0x3830U, 0x3930U, 0x6130U, 0x6230U, 0x6330U, - 0x6430U, 0x6530U, 0x6630U, 0x3031U, 0x3131U, 0x3231U, 0x3331U, 0x3431U, 0x3531U, 0x3631U, 0x3731U, 0x3831U, 0x3931U, - 0x6131U, 0x6231U, 0x6331U, 0x6431U, 0x6531U, 0x6631U, 0x3032U, 0x3132U, 0x3232U, 0x3332U, 0x3432U, 0x3532U, 0x3632U, - 0x3732U, 0x3832U, 0x3932U, 0x6132U, 0x6232U, 0x6332U, 0x6432U, 0x6532U, 0x6632U, 0x3033U, 0x3133U, 0x3233U, 0x3333U, - 0x3433U, 0x3533U, 0x3633U, 0x3733U, 0x3833U, 0x3933U, 0x6133U, 0x6233U, 0x6333U, 0x6433U, 0x6533U, 0x6633U, 0x3034U, - 0x3134U, 0x3234U, 0x3334U, 0x3434U, 0x3534U, 0x3634U, 0x3734U, 0x3834U, 0x3934U, 0x6134U, 0x6234U, 0x6334U, 0x6434U, - 0x6534U, 0x6634U, 0x3035U, 0x3135U, 0x3235U, 0x3335U, 0x3435U, 0x3535U, 0x3635U, 0x3735U, 0x3835U, 0x3935U, 0x6135U, - 0x6235U, 0x6335U, 0x6435U, 0x6535U, 0x6635U, 0x3036U, 0x3136U, 0x3236U, 0x3336U, 0x3436U, 0x3536U, 0x3636U, 0x3736U, - 0x3836U, 0x3936U, 0x6136U, 0x6236U, 0x6336U, 0x6436U, 0x6536U, 0x6636U, 0x3037U, 0x3137U, 0x3237U, 0x3337U, 0x3437U, - 0x3537U, 0x3637U, 0x3737U, 0x3837U, 0x3937U, 0x6137U, 0x6237U, 0x6337U, 0x6437U, 0x6537U, 0x6637U, 0x3038U, 0x3138U, - 0x3238U, 0x3338U, 0x3438U, 0x3538U, 0x3638U, 0x3738U, 0x3838U, 0x3938U, 0x6138U, 0x6238U, 0x6338U, 0x6438U, 0x6538U, - 0x6638U, 0x3039U, 0x3139U, 0x3239U, 0x3339U, 0x3439U, 0x3539U, 0x3639U, 0x3739U, 0x3839U, 0x3939U, 0x6139U, 0x6239U, - 0x6339U, 0x6439U, 0x6539U, 0x6639U, 0x3061U, 0x3161U, 0x3261U, 0x3361U, 0x3461U, 0x3561U, 0x3661U, 0x3761U, 0x3861U, - 0x3961U, 0x6161U, 0x6261U, 0x6361U, 0x6461U, 0x6561U, 0x6661U, 0x3062U, 0x3162U, 0x3262U, 0x3362U, 0x3462U, 0x3562U, - 0x3662U, 0x3762U, 0x3862U, 0x3962U, 0x6162U, 0x6262U, 0x6362U, 0x6462U, 0x6562U, 0x6662U, 0x3063U, 0x3163U, 0x3263U, - 0x3363U, 0x3463U, 0x3563U, 0x3663U, 0x3763U, 0x3863U, 0x3963U, 0x6163U, 0x6263U, 0x6363U, 0x6463U, 0x6563U, 0x6663U, - 0x3064U, 0x3164U, 0x3264U, 0x3364U, 0x3464U, 0x3564U, 0x3664U, 0x3764U, 0x3864U, 0x3964U, 0x6164U, 0x6264U, 0x6364U, - 0x6464U, 0x6564U, 0x6664U, 0x3065U, 0x3165U, 0x3265U, 0x3365U, 0x3465U, 0x3565U, 0x3665U, 0x3765U, 0x3865U, 0x3965U, - 0x6165U, 0x6265U, 0x6365U, 0x6465U, 0x6565U, 0x6665U, 0x3066U, 0x3166U, 0x3266U, 0x3366U, 0x3466U, 0x3566U, 0x3666U, - 0x3766U, 0x3866U, 0x3966U, 0x6166U, 0x6266U, 0x6366U, 0x6466U, 0x6566U, 0x6666U -#endif /* DUK_USE_INTEGER_BE */ -}; -#endif /* DUK_USE_HEX_FASTPATH */ - -/* - * Arbitrary byteswap for potentially unaligned values - * - * Used to byteswap pointers e.g. in debugger code. - */ - -#if defined(DUK_USE_DEBUGGER_SUPPORT) /* For now only needed by the debugger. */ -DUK_INTERNAL void duk_byteswap_bytes(duk_uint8_t *p, duk_small_uint_t len) { - duk_uint8_t tmp; - duk_uint8_t *q = p + len - 1; - - while (p - q < 0) { - tmp = *p; - *p = *q; - *q = tmp; - p++; - q--; - } -} -#endif - -/* - * Random - */ - -DUK_INTERNAL duk_double_t duk_util_get_random_double(duk_hthread *thr) { -#if defined(DUK_USE_GET_RANDOM_DOUBLE) - return DUK_USE_GET_RANDOM_DOUBLE(thr->heap->heap_udata); -#else - return duk_util_tinyrandom_get_double(thr); -#endif -} -/* - * Hobject ECMAScript [[Class]]. - */ - -/* #include duk_internal.h -> already included */ - -#if (DUK_STRIDX_UC_ARGUMENTS > 255) -#error constant too large -#endif -#if (DUK_STRIDX_UC_ARRAY > 255) -#error constant too large -#endif -#if (DUK_STRIDX_UC_BOOLEAN > 255) -#error constant too large -#endif -#if (DUK_STRIDX_UC_DATE > 255) -#error constant too large -#endif -#if (DUK_STRIDX_UC_ERROR > 255) -#error constant too large -#endif -#if (DUK_STRIDX_UC_FUNCTION > 255) -#error constant too large -#endif -#if (DUK_STRIDX_JSON > 255) -#error constant too large -#endif -#if (DUK_STRIDX_MATH > 255) -#error constant too large -#endif -#if (DUK_STRIDX_UC_NUMBER > 255) -#error constant too large -#endif -#if (DUK_STRIDX_UC_OBJECT > 255) -#error constant too large -#endif -#if (DUK_STRIDX_REG_EXP > 255) -#error constant too large -#endif -#if (DUK_STRIDX_UC_STRING > 255) -#error constant too large -#endif -#if (DUK_STRIDX_GLOBAL > 255) -#error constant too large -#endif -#if (DUK_STRIDX_OBJ_ENV > 255) -#error constant too large -#endif -#if (DUK_STRIDX_DEC_ENV > 255) -#error constant too large -#endif -#if (DUK_STRIDX_UC_POINTER > 255) -#error constant too large -#endif -#if (DUK_STRIDX_UC_THREAD > 255) -#error constant too large -#endif -#if (DUK_STRIDX_ARRAY_BUFFER > 255) -#error constant too large -#endif -#if (DUK_STRIDX_DATA_VIEW > 255) -#error constant too large -#endif -#if (DUK_STRIDX_INT8_ARRAY > 255) -#error constant too large -#endif -#if (DUK_STRIDX_UINT8_ARRAY > 255) -#error constant too large -#endif -#if (DUK_STRIDX_UINT8_CLAMPED_ARRAY > 255) -#error constant too large -#endif -#if (DUK_STRIDX_INT16_ARRAY > 255) -#error constant too large -#endif -#if (DUK_STRIDX_UINT16_ARRAY > 255) -#error constant too large -#endif -#if (DUK_STRIDX_INT32_ARRAY > 255) -#error constant too large -#endif -#if (DUK_STRIDX_UINT32_ARRAY > 255) -#error constant too large -#endif -#if (DUK_STRIDX_FLOAT32_ARRAY > 255) -#error constant too large -#endif -#if (DUK_STRIDX_FLOAT64_ARRAY > 255) -#error constant too large -#endif -#if (DUK_STRIDX_EMPTY_STRING > 255) -#error constant too large -#endif - -/* Note: assumes that these string indexes are 8-bit, genstrings.py must ensure that */ -DUK_INTERNAL duk_uint8_t duk_class_number_to_stridx[32] = { - DUK_STRIDX_EMPTY_STRING, /* NONE, intentionally empty */ - DUK_STRIDX_UC_OBJECT, - DUK_STRIDX_UC_ARRAY, - DUK_STRIDX_UC_FUNCTION, - DUK_STRIDX_UC_ARGUMENTS, - DUK_STRIDX_UC_BOOLEAN, - DUK_STRIDX_UC_DATE, - DUK_STRIDX_UC_ERROR, - DUK_STRIDX_JSON, - DUK_STRIDX_MATH, - DUK_STRIDX_UC_NUMBER, - DUK_STRIDX_REG_EXP, - DUK_STRIDX_UC_STRING, - DUK_STRIDX_GLOBAL, - DUK_STRIDX_UC_SYMBOL, - DUK_STRIDX_OBJ_ENV, - DUK_STRIDX_DEC_ENV, - DUK_STRIDX_UC_POINTER, - DUK_STRIDX_UC_THREAD, - DUK_STRIDX_ARRAY_BUFFER, - DUK_STRIDX_DATA_VIEW, - DUK_STRIDX_INT8_ARRAY, - DUK_STRIDX_UINT8_ARRAY, - DUK_STRIDX_UINT8_CLAMPED_ARRAY, - DUK_STRIDX_INT16_ARRAY, - DUK_STRIDX_UINT16_ARRAY, - DUK_STRIDX_INT32_ARRAY, - DUK_STRIDX_UINT32_ARRAY, - DUK_STRIDX_FLOAT32_ARRAY, - DUK_STRIDX_FLOAT64_ARRAY, - DUK_STRIDX_EMPTY_STRING, /* UNUSED, intentionally empty */ - DUK_STRIDX_EMPTY_STRING, /* UNUSED, intentionally empty */ -}; -/* - * Default allocation functions. - * - * Assumes behavior such as malloc allowing zero size, yielding - * a NULL or a unique pointer which is a no-op for free. - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_PROVIDE_DEFAULT_ALLOC_FUNCTIONS) -DUK_INTERNAL void *duk_default_alloc_function(void *udata, duk_size_t size) { - void *res; - DUK_UNREF(udata); - res = DUK_ANSI_MALLOC(size); - DUK_DDD(DUK_DDDPRINT("default alloc function: %lu -> %p", (unsigned long) size, (void *) res)); - return res; -} - -DUK_INTERNAL void *duk_default_realloc_function(void *udata, void *ptr, duk_size_t newsize) { - void *res; - DUK_UNREF(udata); - res = DUK_ANSI_REALLOC(ptr, newsize); - DUK_DDD(DUK_DDDPRINT("default realloc function: %p %lu -> %p", (void *) ptr, (unsigned long) newsize, (void *) res)); - return res; -} - -DUK_INTERNAL void duk_default_free_function(void *udata, void *ptr) { - DUK_DDD(DUK_DDDPRINT("default free function: %p", (void *) ptr)); - DUK_UNREF(udata); - DUK_ANSI_FREE(ptr); -} -#endif /* DUK_USE_PROVIDE_DEFAULT_ALLOC_FUNCTIONS */ -/* - * Buffer - */ - -/* #include duk_internal.h -> already included */ - -DUK_EXTERNAL void *duk_resize_buffer(duk_hthread *thr, duk_idx_t idx, duk_size_t new_size) { - duk_hbuffer_dynamic *h; - - DUK_ASSERT_API_ENTRY(thr); - - h = (duk_hbuffer_dynamic *) duk_require_hbuffer(thr, idx); - DUK_ASSERT(h != NULL); - - if (!(DUK_HBUFFER_HAS_DYNAMIC(h) && !DUK_HBUFFER_HAS_EXTERNAL(h))) { - DUK_ERROR_TYPE(thr, DUK_STR_WRONG_BUFFER_TYPE); - DUK_WO_NORETURN(return NULL;); - } - - /* Maximum size check is handled by callee. */ - duk_hbuffer_resize(thr, h, new_size); - - return DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, h); -} - -DUK_EXTERNAL void *duk_steal_buffer(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size) { - duk_hbuffer_dynamic *h; - void *ptr; - duk_size_t sz; - - DUK_ASSERT_API_ENTRY(thr); - - h = (duk_hbuffer_dynamic *) duk_require_hbuffer(thr, idx); - DUK_ASSERT(h != NULL); - - if (!(DUK_HBUFFER_HAS_DYNAMIC(h) && !DUK_HBUFFER_HAS_EXTERNAL(h))) { - DUK_ERROR_TYPE(thr, DUK_STR_WRONG_BUFFER_TYPE); - DUK_WO_NORETURN(return NULL;); - } - - /* Forget the previous allocation, setting size to 0 and alloc to - * NULL. Caller is responsible for freeing the previous allocation. - * Getting the allocation and clearing it is done in the same API - * call to avoid any chance of a realloc. - */ - ptr = DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, h); - sz = DUK_HBUFFER_DYNAMIC_GET_SIZE(h); - if (out_size) { - *out_size = sz; - } - DUK_HBUFFER_DYNAMIC_SET_DATA_PTR_NULL(thr->heap, h); - DUK_HBUFFER_DYNAMIC_SET_SIZE(h, 0); - - return ptr; -} - -DUK_EXTERNAL void duk_config_buffer(duk_hthread *thr, duk_idx_t idx, void *ptr, duk_size_t len) { - duk_hbuffer_external *h; - - DUK_ASSERT_API_ENTRY(thr); - - h = (duk_hbuffer_external *) duk_require_hbuffer(thr, idx); - DUK_ASSERT(h != NULL); - - if (!DUK_HBUFFER_HAS_EXTERNAL(h)) { - DUK_ERROR_TYPE(thr, DUK_STR_WRONG_BUFFER_TYPE); - DUK_WO_NORETURN(return;); - } - DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(h)); - - DUK_HBUFFER_EXTERNAL_SET_DATA_PTR(thr->heap, h, ptr); - DUK_HBUFFER_EXTERNAL_SET_SIZE(h, len); -} -/* - * Bytecode dump/load - * - * The bytecode load primitive is more important performance-wise than the - * dump primitive. - * - * Unlike most Duktape API calls, bytecode dump/load is not guaranteed to be - * memory safe for invalid arguments - caller beware! There's little point - * in trying to achieve memory safety unless bytecode instructions are also - * validated which is not easy to do with indirect register references etc. - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_BYTECODE_DUMP_SUPPORT) - -#define DUK__SER_MARKER 0xbf -#define DUK__SER_STRING 0x00 -#define DUK__SER_NUMBER 0x01 -#define DUK__BYTECODE_INITIAL_ALLOC 256 -#define DUK__NO_FORMALS 0xffffffffUL - -/* - * Dump/load helpers, xxx_raw() helpers do no buffer checks - */ - -DUK_LOCAL const duk_uint8_t *duk__load_string_raw(duk_hthread *thr, const duk_uint8_t *p) { - duk_uint32_t len; - - len = DUK_RAW_READINC_U32_BE(p); - duk_push_lstring(thr, (const char *) p, len); - p += len; - return p; -} - -DUK_LOCAL const duk_uint8_t *duk__load_buffer_raw(duk_hthread *thr, const duk_uint8_t *p) { - duk_uint32_t len; - duk_uint8_t *buf; - - len = DUK_RAW_READINC_U32_BE(p); - buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, (duk_size_t) len); - DUK_ASSERT(buf != NULL); - duk_memcpy((void *) buf, (const void *) p, (size_t) len); - p += len; - return p; -} - -DUK_LOCAL duk_uint8_t *duk__dump_hstring_raw(duk_uint8_t *p, duk_hstring *h) { - duk_size_t len; - duk_uint32_t tmp32; - - DUK_ASSERT(h != NULL); - - len = DUK_HSTRING_GET_BYTELEN(h); - DUK_ASSERT(len <= 0xffffffffUL); /* string limits */ - tmp32 = (duk_uint32_t) len; - DUK_RAW_WRITEINC_U32_BE(p, tmp32); - duk_memcpy((void *) p, (const void *) DUK_HSTRING_GET_DATA(h), len); - p += len; - return p; -} - -DUK_LOCAL duk_uint8_t *duk__dump_hbuffer_raw(duk_hthread *thr, duk_uint8_t *p, duk_hbuffer *h) { - duk_size_t len; - duk_uint32_t tmp32; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(h != NULL); - DUK_UNREF(thr); - - len = DUK_HBUFFER_GET_SIZE(h); - DUK_ASSERT(len <= 0xffffffffUL); /* buffer limits */ - tmp32 = (duk_uint32_t) len; - DUK_RAW_WRITEINC_U32_BE(p, tmp32); - /* When len == 0, buffer data pointer may be NULL. */ - duk_memcpy_unsafe((void *) p, (const void *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h), len); - p += len; - return p; -} - -DUK_LOCAL duk_uint8_t *duk__dump_string_prop(duk_hthread *thr, - duk_uint8_t *p, - duk_bufwriter_ctx *bw_ctx, - duk_hobject *func, - duk_small_uint_t stridx) { - duk_hstring *h_str; - duk_tval *tv; - - tv = duk_hobject_find_entry_tval_ptr_stridx(thr->heap, (duk_hobject *) func, stridx); - if (tv != NULL && DUK_TVAL_IS_STRING(tv)) { - h_str = DUK_TVAL_GET_STRING(tv); - DUK_ASSERT(h_str != NULL); - } else { - h_str = DUK_HTHREAD_STRING_EMPTY_STRING(thr); - DUK_ASSERT(h_str != NULL); - } - DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL); /* ensures no overflow */ - p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U + DUK_HSTRING_GET_BYTELEN(h_str), p); - p = duk__dump_hstring_raw(p, h_str); - return p; -} - -DUK_LOCAL duk_uint8_t *duk__dump_buffer_prop(duk_hthread *thr, - duk_uint8_t *p, - duk_bufwriter_ctx *bw_ctx, - duk_hobject *func, - duk_small_uint_t stridx) { - duk_tval *tv; - - tv = duk_hobject_find_entry_tval_ptr_stridx(thr->heap, (duk_hobject *) func, stridx); - if (tv != NULL && DUK_TVAL_IS_BUFFER(tv)) { - duk_hbuffer *h_buf; - h_buf = DUK_TVAL_GET_BUFFER(tv); - DUK_ASSERT(h_buf != NULL); - DUK_ASSERT(DUK_HBUFFER_MAX_BYTELEN <= 0x7fffffffUL); /* ensures no overflow */ - p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U + DUK_HBUFFER_GET_SIZE(h_buf), p); - p = duk__dump_hbuffer_raw(thr, p, h_buf); - } else { - p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U, p); - DUK_RAW_WRITEINC_U32_BE(p, 0); - } - return p; -} - -DUK_LOCAL duk_uint8_t *duk__dump_uint32_prop(duk_hthread *thr, - duk_uint8_t *p, - duk_bufwriter_ctx *bw_ctx, - duk_hobject *func, - duk_small_uint_t stridx, - duk_uint32_t def_value) { - duk_tval *tv; - duk_uint32_t val; - - tv = duk_hobject_find_entry_tval_ptr_stridx(thr->heap, (duk_hobject *) func, stridx); - if (tv != NULL && DUK_TVAL_IS_NUMBER(tv)) { - val = (duk_uint32_t) DUK_TVAL_GET_NUMBER(tv); - } else { - val = def_value; - } - p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U, p); - DUK_RAW_WRITEINC_U32_BE(p, val); - return p; -} - -DUK_LOCAL duk_uint8_t *duk__dump_varmap(duk_hthread *thr, duk_uint8_t *p, duk_bufwriter_ctx *bw_ctx, duk_hobject *func) { - duk_hobject *h; - - h = duk_hobject_get_varmap(thr, (duk_hobject *) func); - if (h != NULL) { - duk_uint_fast32_t i; - - /* We know _Varmap only has own properties so walk property - * table directly. We also know _Varmap is dense and all - * values are numbers; assert for these. GC and finalizers - * shouldn't affect _Varmap so side effects should be fine. - */ - for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(h); i++) { - duk_hstring *key; - duk_tval *tv_val; - duk_uint32_t val; - - key = DUK_HOBJECT_E_GET_KEY(thr->heap, h, i); - DUK_ASSERT(key != NULL); /* _Varmap is dense */ - DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, h, i)); - tv_val = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, h, i); - DUK_ASSERT(tv_val != NULL); - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_val)); /* known to be number; in fact an integer */ -#if defined(DUK_USE_FASTINT) - DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv_val)); - DUK_ASSERT(DUK_TVAL_GET_FASTINT(tv_val) == - (duk_int64_t) DUK_TVAL_GET_FASTINT_U32(tv_val)); /* known to be 32-bit */ - val = DUK_TVAL_GET_FASTINT_U32(tv_val); -#else - val = (duk_uint32_t) DUK_TVAL_GET_NUMBER(tv_val); -#endif - - DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL); /* ensures no overflow */ - p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U + DUK_HSTRING_GET_BYTELEN(key) + 4U, p); - p = duk__dump_hstring_raw(p, key); - DUK_RAW_WRITEINC_U32_BE(p, val); - } - } - p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U, p); - DUK_RAW_WRITEINC_U32_BE(p, 0); /* end of _Varmap */ - return p; -} - -DUK_LOCAL duk_uint8_t *duk__dump_formals(duk_hthread *thr, duk_uint8_t *p, duk_bufwriter_ctx *bw_ctx, duk_hobject *func) { - duk_harray *h; - - h = duk_hobject_get_formals(thr, (duk_hobject *) func); - if (h != NULL) { - duk_uint32_t i; - - /* Here we rely on _Formals being a dense array containing - * strings. This should be the case unless _Formals has been - * tweaked by the application (which we don't support right - * now). - */ - - p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U, p); - DUK_ASSERT(h->length != DUK__NO_FORMALS); /* limits */ - DUK_RAW_WRITEINC_U32_BE(p, h->length); - - for (i = 0; i < h->length; i++) { - duk_tval *tv_val; - duk_hstring *varname; - - tv_val = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, (duk_hobject *) h, i); - DUK_ASSERT(tv_val != NULL); - DUK_ASSERT(DUK_TVAL_IS_STRING(tv_val)); - - varname = DUK_TVAL_GET_STRING(tv_val); - DUK_ASSERT(varname != NULL); - DUK_ASSERT(DUK_HSTRING_GET_BYTELEN(varname) >= 1); - - DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL); /* ensures no overflow */ - p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U + DUK_HSTRING_GET_BYTELEN(varname), p); - p = duk__dump_hstring_raw(p, varname); - } - } else { - DUK_DD(DUK_DDPRINT("dumping function without _Formals, emit marker to indicate missing _Formals")); - p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U, p); - DUK_RAW_WRITEINC_U32_BE(p, DUK__NO_FORMALS); /* marker: no formals */ - } - return p; -} - -static duk_uint8_t *duk__dump_func(duk_hthread *thr, duk_hcompfunc *func, duk_bufwriter_ctx *bw_ctx, duk_uint8_t *p) { - duk_tval *tv, *tv_end; - duk_instr_t *ins, *ins_end; - duk_hobject **fn, **fn_end; - duk_hstring *h_str; - duk_uint32_t count_instr; - duk_uint32_t tmp32; - duk_uint16_t tmp16; - duk_double_t d; - - DUK_DD(DUK_DDPRINT("dumping function %p to %p: " - "consts=[%p,%p[ (%ld bytes, %ld items), " - "funcs=[%p,%p[ (%ld bytes, %ld items), " - "code=[%p,%p[ (%ld bytes, %ld items)", - (void *) func, - (void *) p, - (void *) DUK_HCOMPFUNC_GET_CONSTS_BASE(thr->heap, func), - (void *) DUK_HCOMPFUNC_GET_CONSTS_END(thr->heap, func), - (long) DUK_HCOMPFUNC_GET_CONSTS_SIZE(thr->heap, func), - (long) DUK_HCOMPFUNC_GET_CONSTS_COUNT(thr->heap, func), - (void *) DUK_HCOMPFUNC_GET_FUNCS_BASE(thr->heap, func), - (void *) DUK_HCOMPFUNC_GET_FUNCS_END(thr->heap, func), - (long) DUK_HCOMPFUNC_GET_FUNCS_SIZE(thr->heap, func), - (long) DUK_HCOMPFUNC_GET_FUNCS_COUNT(thr->heap, func), - (void *) DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, func), - (void *) DUK_HCOMPFUNC_GET_CODE_END(thr->heap, func), - (long) DUK_HCOMPFUNC_GET_CODE_SIZE(thr->heap, func), - (long) DUK_HCOMPFUNC_GET_CODE_COUNT(thr->heap, func))); - - DUK_ASSERT(DUK_USE_ESBC_MAX_BYTES <= 0x7fffffffUL); /* ensures no overflow */ - count_instr = (duk_uint32_t) DUK_HCOMPFUNC_GET_CODE_COUNT(thr->heap, func); - p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 3U * 4U + 2U * 2U + 3U * 4U + count_instr * 4U, p); - - /* Fixed header info. */ - tmp32 = count_instr; - DUK_RAW_WRITEINC_U32_BE(p, tmp32); - tmp32 = (duk_uint32_t) DUK_HCOMPFUNC_GET_CONSTS_COUNT(thr->heap, func); - DUK_RAW_WRITEINC_U32_BE(p, tmp32); - tmp32 = (duk_uint32_t) DUK_HCOMPFUNC_GET_FUNCS_COUNT(thr->heap, func); - DUK_RAW_WRITEINC_U32_BE(p, tmp32); - tmp16 = func->nregs; - DUK_RAW_WRITEINC_U16_BE(p, tmp16); - tmp16 = func->nargs; - DUK_RAW_WRITEINC_U16_BE(p, tmp16); -#if defined(DUK_USE_DEBUGGER_SUPPORT) - tmp32 = func->start_line; - DUK_RAW_WRITEINC_U32_BE(p, tmp32); - tmp32 = func->end_line; - DUK_RAW_WRITEINC_U32_BE(p, tmp32); -#else - DUK_RAW_WRITEINC_U32_BE(p, 0); - DUK_RAW_WRITEINC_U32_BE(p, 0); -#endif - tmp32 = DUK_HEAPHDR_GET_FLAGS((duk_heaphdr *) func); /* masks flags, only duk_hobject flags */ - tmp32 &= ~(DUK_HOBJECT_FLAG_HAVE_FINALIZER); /* finalizer flag is lost */ - DUK_RAW_WRITEINC_U32_BE(p, tmp32); - - /* Bytecode instructions: endian conversion needed unless - * platform is big endian. - */ - ins = DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, func); - ins_end = DUK_HCOMPFUNC_GET_CODE_END(thr->heap, func); - DUK_ASSERT((duk_size_t) (ins_end - ins) == (duk_size_t) count_instr); -#if defined(DUK_USE_INTEGER_BE) - duk_memcpy_unsafe((void *) p, (const void *) ins, count_instr * sizeof(duk_instr_t)); - p += count_instr * sizeof(duk_instr_t); - DUK_UNREF(ins_end); -#else - while (ins != ins_end) { - tmp32 = (duk_uint32_t) (*ins); - DUK_RAW_WRITEINC_U32_BE(p, tmp32); - ins++; - } -#endif - - /* Constants: variable size encoding. */ - tv = DUK_HCOMPFUNC_GET_CONSTS_BASE(thr->heap, func); - tv_end = DUK_HCOMPFUNC_GET_CONSTS_END(thr->heap, func); - while (tv != tv_end) { - /* constants are strings or numbers now */ - DUK_ASSERT(DUK_TVAL_IS_STRING(tv) || DUK_TVAL_IS_NUMBER(tv)); - - if (DUK_TVAL_IS_STRING(tv)) { - h_str = DUK_TVAL_GET_STRING(tv); - DUK_ASSERT(h_str != NULL); - DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL); /* ensures no overflow */ - p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 1U + 4U + DUK_HSTRING_GET_BYTELEN(h_str), p); - *p++ = DUK__SER_STRING; - p = duk__dump_hstring_raw(p, h_str); - } else { - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); - p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 1U + 8U, p); - *p++ = DUK__SER_NUMBER; - d = DUK_TVAL_GET_NUMBER(tv); - DUK_RAW_WRITEINC_DOUBLE_BE(p, d); - } - tv++; - } - - /* Inner functions recursively. */ - fn = (duk_hobject **) DUK_HCOMPFUNC_GET_FUNCS_BASE(thr->heap, func); - fn_end = (duk_hobject **) DUK_HCOMPFUNC_GET_FUNCS_END(thr->heap, func); - while (fn != fn_end) { - /* XXX: This causes recursion up to inner function depth - * which is normally not an issue, e.g. mark-and-sweep uses - * a recursion limiter to avoid C stack issues. Avoiding - * this would mean some sort of a work list or just refusing - * to serialize deep functions. - */ - DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(*fn)); - p = duk__dump_func(thr, (duk_hcompfunc *) *fn, bw_ctx, p); - fn++; - } - - /* Lexenv and varenv are not dumped. */ - - /* Object extra properties. - * - * There are some difference between function templates and functions. - * For example, function templates don't have .length and nargs is - * normally used to instantiate the functions. - */ - - p = duk__dump_uint32_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_LENGTH, (duk_uint32_t) func->nargs); -#if defined(DUK_USE_FUNC_NAME_PROPERTY) - p = duk__dump_string_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_NAME); -#endif -#if defined(DUK_USE_FUNC_FILENAME_PROPERTY) - p = duk__dump_string_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_FILE_NAME); -#endif -#if defined(DUK_USE_PC2LINE) - p = duk__dump_buffer_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_INT_PC2LINE); -#endif - p = duk__dump_varmap(thr, p, bw_ctx, (duk_hobject *) func); - p = duk__dump_formals(thr, p, bw_ctx, (duk_hobject *) func); - - DUK_DD(DUK_DDPRINT("serialized function %p -> final pointer %p", (void *) func, (void *) p)); - - return p; -} - -/* Load a function from bytecode. The function object returned here must - * match what is created by duk_js_push_closure() with respect to its flags, - * properties, etc. - * - * NOTE: there are intentionally no input buffer length / bound checks. - * Adding them would be easy but wouldn't ensure memory safety as untrusted - * or broken bytecode is unsafe during execution unless the opcodes themselves - * are validated (which is quite complex, especially for indirect opcodes). - */ - -#define DUK__ASSERT_LEFT(n) \ - do { \ - DUK_ASSERT((duk_size_t) (p_end - p) >= (duk_size_t) (n)); \ - } while (0) - -static const duk_uint8_t *duk__load_func(duk_hthread *thr, const duk_uint8_t *p, const duk_uint8_t *p_end) { - duk_hcompfunc *h_fun; - duk_hbuffer *h_data; - duk_size_t data_size; - duk_uint32_t count_instr, count_const, count_funcs; - duk_uint32_t n; - duk_uint32_t tmp32; - duk_small_uint_t const_type; - duk_uint8_t *fun_data; - duk_uint8_t *q; - duk_idx_t idx_base; - duk_tval *tv1; - duk_uarridx_t arr_idx; - duk_uarridx_t arr_limit; - duk_hobject *func_env; - duk_bool_t need_pop; - - /* XXX: There's some overlap with duk_js_closure() here, but - * seems difficult to share code. Ensure that the final function - * looks the same as created by duk_js_closure(). - */ - - DUK_ASSERT(thr != NULL); - - DUK_DD(DUK_DDPRINT("loading function, p=%p, p_end=%p", (const void *) p, (const void *) p_end)); - - DUK__ASSERT_LEFT(3 * 4); - count_instr = DUK_RAW_READINC_U32_BE(p); - count_const = DUK_RAW_READINC_U32_BE(p); - count_funcs = DUK_RAW_READINC_U32_BE(p); - - data_size = sizeof(duk_tval) * count_const + sizeof(duk_hobject *) * count_funcs + sizeof(duk_instr_t) * count_instr; - - DUK_DD(DUK_DDPRINT("instr=%ld, const=%ld, funcs=%ld, data_size=%ld", - (long) count_instr, - (long) count_const, - (long) count_const, - (long) data_size)); - - /* Value stack is used to ensure reachability of constants and - * inner functions being loaded. Require enough space to handle - * large functions correctly. - */ - duk_require_stack(thr, (duk_idx_t) (2 + count_const + count_funcs)); - idx_base = duk_get_top(thr); - - /* Push function object, init flags etc. This must match - * duk_js_push_closure() quite carefully. - */ - h_fun = duk_push_hcompfunc(thr); - DUK_ASSERT(h_fun != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC((duk_hobject *) h_fun)); - DUK_ASSERT(DUK_HCOMPFUNC_GET_DATA(thr->heap, h_fun) == NULL); - DUK_ASSERT(DUK_HCOMPFUNC_GET_FUNCS(thr->heap, h_fun) == NULL); - DUK_ASSERT(DUK_HCOMPFUNC_GET_BYTECODE(thr->heap, h_fun) == NULL); - DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) h_fun) == thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]); - - h_fun->nregs = DUK_RAW_READINC_U16_BE(p); - h_fun->nargs = DUK_RAW_READINC_U16_BE(p); -#if defined(DUK_USE_DEBUGGER_SUPPORT) - h_fun->start_line = DUK_RAW_READINC_U32_BE(p); - h_fun->end_line = DUK_RAW_READINC_U32_BE(p); -#else - p += 8; /* skip line info */ -#endif - - /* duk_hcompfunc flags; quite version specific */ - tmp32 = DUK_RAW_READINC_U32_BE(p); - DUK_HEAPHDR_SET_FLAGS((duk_heaphdr *) h_fun, tmp32); /* masks flags to only change duk_hobject flags */ - - /* standard prototype (no need to set here, already set) */ - DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) h_fun) == thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]); -#if 0 - DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, &h_fun->obj, thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]); -#endif - - /* assert just a few critical flags */ - DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) h_fun) == DUK_HTYPE_OBJECT); - DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(&h_fun->obj)); - DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(&h_fun->obj)); - DUK_ASSERT(!DUK_HOBJECT_HAS_NATFUNC(&h_fun->obj)); - DUK_ASSERT(!DUK_HOBJECT_IS_THREAD(&h_fun->obj)); - DUK_ASSERT(!DUK_HOBJECT_IS_PROXY(&h_fun->obj)); - DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARRAY(&h_fun->obj)); - DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(&h_fun->obj)); - DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(&h_fun->obj)); - - /* Create function 'data' buffer but don't attach it yet. */ - fun_data = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, data_size); - DUK_ASSERT(fun_data != NULL); - - /* Load bytecode instructions. */ - DUK_ASSERT(sizeof(duk_instr_t) == 4); - DUK__ASSERT_LEFT(count_instr * sizeof(duk_instr_t)); -#if defined(DUK_USE_INTEGER_BE) - q = fun_data + sizeof(duk_tval) * count_const + sizeof(duk_hobject *) * count_funcs; - duk_memcpy((void *) q, (const void *) p, sizeof(duk_instr_t) * count_instr); - p += sizeof(duk_instr_t) * count_instr; -#else - q = fun_data + sizeof(duk_tval) * count_const + sizeof(duk_hobject *) * count_funcs; - for (n = count_instr; n > 0; n--) { - *((duk_instr_t *) (void *) q) = DUK_RAW_READINC_U32_BE(p); - q += sizeof(duk_instr_t); - } -#endif - - /* Load constants onto value stack but don't yet copy to buffer. */ - for (n = count_const; n > 0; n--) { - DUK__ASSERT_LEFT(1); - const_type = DUK_RAW_READINC_U8(p); - switch (const_type) { - case DUK__SER_STRING: { - p = duk__load_string_raw(thr, p); - break; - } - case DUK__SER_NUMBER: { - /* Important to do a fastint check so that constants are - * properly read back as fastints. - */ - duk_tval tv_tmp; - duk_double_t val; - DUK__ASSERT_LEFT(8); - val = DUK_RAW_READINC_DOUBLE_BE(p); - DUK_TVAL_SET_NUMBER_CHKFAST_SLOW(&tv_tmp, val); - duk_push_tval(thr, &tv_tmp); - break; - } - default: { - goto format_error; - } - } - } - - /* Load inner functions to value stack, but don't yet copy to buffer. */ - for (n = count_funcs; n > 0; n--) { - p = duk__load_func(thr, p, p_end); - if (p == NULL) { - goto format_error; - } - } - - /* With constants and inner functions on value stack, we can now - * atomically finish the function 'data' buffer, bump refcounts, - * etc. - * - * Here we take advantage of the value stack being just a duk_tval - * array: we can just memcpy() the constants as long as we incref - * them afterwards. - */ - - h_data = (duk_hbuffer *) duk_known_hbuffer(thr, idx_base + 1); - DUK_ASSERT(!DUK_HBUFFER_HAS_DYNAMIC(h_data)); - DUK_HCOMPFUNC_SET_DATA(thr->heap, h_fun, h_data); - DUK_HBUFFER_INCREF(thr, h_data); - - tv1 = duk_get_tval(thr, idx_base + 2); /* may be NULL if no constants or inner funcs */ - DUK_ASSERT((count_const == 0 && count_funcs == 0) || tv1 != NULL); - - q = fun_data; - duk_memcpy_unsafe((void *) q, (const void *) tv1, sizeof(duk_tval) * count_const); - for (n = count_const; n > 0; n--) { - DUK_TVAL_INCREF_FAST(thr, (duk_tval *) (void *) q); /* no side effects */ - q += sizeof(duk_tval); - } - tv1 += count_const; - - DUK_HCOMPFUNC_SET_FUNCS(thr->heap, h_fun, (duk_hobject **) (void *) q); - for (n = count_funcs; n > 0; n--) { - duk_hobject *h_obj; - - DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv1)); - h_obj = DUK_TVAL_GET_OBJECT(tv1); - DUK_ASSERT(h_obj != NULL); - tv1++; - DUK_HOBJECT_INCREF(thr, h_obj); - - *((duk_hobject **) (void *) q) = h_obj; - q += sizeof(duk_hobject *); - } - - DUK_HCOMPFUNC_SET_BYTECODE(thr->heap, h_fun, (duk_instr_t *) (void *) q); - - /* The function object is now reachable and refcounts are fine, - * so we can pop off all the temporaries. - */ - DUK_DDD(DUK_DDDPRINT("function is reachable, reset top; func: %!iT", duk_get_tval(thr, idx_base))); - duk_set_top(thr, idx_base + 1); - - /* Setup function properties. */ - tmp32 = DUK_RAW_READINC_U32_BE(p); - duk_push_u32(thr, tmp32); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_C); - -#if defined(DUK_USE_FUNC_NAME_PROPERTY) - p = duk__load_string_raw(thr, p); /* -> [ func funcname ] */ - func_env = thr->builtins[DUK_BIDX_GLOBAL_ENV]; - DUK_ASSERT(func_env != NULL); - need_pop = 0; - if (DUK_HOBJECT_HAS_NAMEBINDING((duk_hobject *) h_fun)) { - /* Original function instance/template had NAMEBINDING. - * Must create a lexical environment on loading to allow - * recursive functions like 'function foo() { foo(); }'. - */ - duk_hdecenv *new_env; - - new_env = - duk_hdecenv_alloc(thr, DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV)); - DUK_ASSERT(new_env != NULL); - DUK_ASSERT(new_env->thread == NULL); /* Closed. */ - DUK_ASSERT(new_env->varmap == NULL); - DUK_ASSERT(new_env->regbase_byteoff == 0); - DUK_HDECENV_ASSERT_VALID(new_env); - DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) new_env) == NULL); - DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) new_env, func_env); - DUK_HOBJECT_INCREF(thr, func_env); - - func_env = (duk_hobject *) new_env; - - duk_push_hobject(thr, (duk_hobject *) new_env); - - duk_dup_m2(thr); /* -> [ func funcname env funcname ] */ - duk_dup(thr, idx_base); /* -> [ func funcname env funcname func ] */ - duk_xdef_prop(thr, -3, DUK_PROPDESC_FLAGS_NONE); /* -> [ func funcname env ] */ - - need_pop = 1; /* Need to pop env, but -after- updating h_fun and increfs. */ - } - DUK_ASSERT(func_env != NULL); - DUK_HCOMPFUNC_SET_LEXENV(thr->heap, h_fun, func_env); - DUK_HCOMPFUNC_SET_VARENV(thr->heap, h_fun, func_env); - DUK_HOBJECT_INCREF(thr, func_env); - DUK_HOBJECT_INCREF(thr, func_env); - if (need_pop) { - duk_pop(thr); - } - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C); -#endif /* DUK_USE_FUNC_NAME_PROPERTY */ - -#if defined(DUK_USE_FUNC_FILENAME_PROPERTY) - p = duk__load_string_raw(thr, p); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_C); -#endif /* DUK_USE_FUNC_FILENAME_PROPERTY */ - - if (DUK_HOBJECT_HAS_CONSTRUCTABLE((duk_hobject *) h_fun)) { - /* Restore empty external .prototype only for constructable - * functions. The prototype object should inherit from - * Object.prototype. - */ - duk_push_object(thr); - DUK_ASSERT(!duk_is_bare_object(thr, -1)); - duk_dup_m2(thr); - duk_xdef_prop_stridx_short(thr, - -2, - DUK_STRIDX_CONSTRUCTOR, - DUK_PROPDESC_FLAGS_WC); /* func.prototype.constructor = func */ - duk_compact_m1(thr); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_PROTOTYPE, DUK_PROPDESC_FLAGS_W); - } - -#if defined(DUK_USE_PC2LINE) - p = duk__load_buffer_raw(thr, p); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_PC2LINE, DUK_PROPDESC_FLAGS_WC); -#endif /* DUK_USE_PC2LINE */ - - duk_push_bare_object(thr); /* _Varmap */ - for (;;) { - /* XXX: awkward */ - p = duk__load_string_raw(thr, p); - if (duk_get_length(thr, -1) == 0) { - duk_pop(thr); - break; - } - tmp32 = DUK_RAW_READINC_U32_BE(p); - duk_push_u32(thr, tmp32); - duk_put_prop(thr, -3); - } - duk_compact_m1(thr); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VARMAP, DUK_PROPDESC_FLAGS_NONE); - - /* _Formals may have been missing in the original function, which is - * handled using a marker length. - */ - arr_limit = DUK_RAW_READINC_U32_BE(p); - if (arr_limit != DUK__NO_FORMALS) { - duk_push_bare_array(thr); /* _Formals */ - for (arr_idx = 0; arr_idx < arr_limit; arr_idx++) { - p = duk__load_string_raw(thr, p); - duk_put_prop_index(thr, -2, arr_idx); - } - duk_compact_m1(thr); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_FORMALS, DUK_PROPDESC_FLAGS_NONE); - } else { - DUK_DD(DUK_DDPRINT("no _Formals in dumped function")); - } - - /* Return with final function pushed on stack top. */ - DUK_DD(DUK_DDPRINT("final loaded function: %!iT", duk_get_tval(thr, -1))); - DUK_ASSERT_TOP(thr, idx_base + 1); - return p; - -format_error: - return NULL; -} - -DUK_EXTERNAL void duk_dump_function(duk_hthread *thr) { - duk_hcompfunc *func; - duk_bufwriter_ctx bw_ctx_alloc; - duk_bufwriter_ctx *bw_ctx = &bw_ctx_alloc; - duk_uint8_t *p; - - DUK_ASSERT_API_ENTRY(thr); - - /* Bound functions don't have all properties so we'd either need to - * lookup the non-bound target function or reject bound functions. - * For now, bound functions are rejected with TypeError. - */ - func = duk_require_hcompfunc(thr, -1); - DUK_ASSERT(func != NULL); - DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(&func->obj)); - - /* Estimating the result size beforehand would be costly, so - * start with a reasonable size and extend as needed. - */ - DUK_BW_INIT_PUSHBUF(thr, bw_ctx, DUK__BYTECODE_INITIAL_ALLOC); - p = DUK_BW_GET_PTR(thr, bw_ctx); - *p++ = DUK__SER_MARKER; - p = duk__dump_func(thr, func, bw_ctx, p); - DUK_BW_SET_PTR(thr, bw_ctx, p); - DUK_BW_COMPACT(thr, bw_ctx); - - DUK_DD(DUK_DDPRINT("serialized result: %!T", duk_get_tval(thr, -1))); - - duk_remove_m2(thr); /* [ ... func buf ] -> [ ... buf ] */ -} - -DUK_EXTERNAL void duk_load_function(duk_hthread *thr) { - const duk_uint8_t *p_buf, *p, *p_end; - duk_size_t sz; - - DUK_ASSERT_API_ENTRY(thr); - - p_buf = (duk_uint8_t *) duk_require_buffer(thr, -1, &sz); - DUK_ASSERT(p_buf != NULL); - - /* The caller is responsible for being sure that bytecode being loaded - * is valid and trusted. Invalid bytecode can cause memory unsafe - * behavior directly during loading or later during bytecode execution - * (instruction validation would be quite complex to implement). - * - * This signature check is the only sanity check for detecting - * accidental invalid inputs. The initial byte ensures no ordinary - * string or Symbol will be accepted by accident. - */ - p = p_buf; - p_end = p_buf + sz; - if (sz < 1 || p[0] != DUK__SER_MARKER) { - goto format_error; - } - p++; - - p = duk__load_func(thr, p, p_end); - if (p == NULL) { - goto format_error; - } - - duk_remove_m2(thr); /* [ ... buf func ] -> [ ... func ] */ - return; - -format_error: - DUK_ERROR_TYPE(thr, DUK_STR_INVALID_BYTECODE); - DUK_WO_NORETURN(return;); -} - -#else /* DUK_USE_BYTECODE_DUMP_SUPPORT */ - -DUK_EXTERNAL void duk_dump_function(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ERROR_UNSUPPORTED(thr); - DUK_WO_NORETURN(return;); -} - -DUK_EXTERNAL void duk_load_function(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ERROR_UNSUPPORTED(thr); - DUK_WO_NORETURN(return;); -} - -#endif /* DUK_USE_BYTECODE_DUMP_SUPPORT */ - -/* automatic undefs */ -#undef DUK__ASSERT_LEFT -#undef DUK__BYTECODE_INITIAL_ALLOC -#undef DUK__NO_FORMALS -#undef DUK__SER_MARKER -#undef DUK__SER_NUMBER -#undef DUK__SER_STRING -/* - * Calls. - * - * Protected variants should avoid ever throwing an error. Must be careful - * to catch errors related to value stack manipulation and property lookup, - * not just the call itself. - * - * The only exception is when arguments are insane, e.g. nargs/nrets are out - * of bounds; in such cases an error is thrown for two reasons. First, we - * can't always respect the value stack input/output guarantees in such cases - * so the caller would end up with the value stack in an unexpected state. - * Second, an attempt to create an error might itself fail (although this - * could be avoided by pushing a preallocated object/string or a primitive - * value). - */ - -/* #include duk_internal.h -> already included */ - -/* - * Helpers - */ - -struct duk__pcall_prop_args { - duk_idx_t obj_idx; - duk_idx_t nargs; - duk_small_uint_t call_flags; -}; -typedef struct duk__pcall_prop_args duk__pcall_prop_args; - -struct duk__pcall_method_args { - duk_idx_t nargs; - duk_small_uint_t call_flags; -}; -typedef struct duk__pcall_method_args duk__pcall_method_args; - -struct duk__pcall_args { - duk_idx_t nargs; - duk_small_uint_t call_flags; -}; -typedef struct duk__pcall_args duk__pcall_args; - -/* Compute and validate idx_func for a certain 'nargs' and 'other' - * parameter count (1 or 2, depending on whether 'this' binding is - * present). - */ -DUK_LOCAL duk_idx_t duk__call_get_idx_func(duk_hthread *thr, duk_idx_t nargs, duk_idx_t other) { - duk_idx_t idx_func; - - /* XXX: byte arithmetic? */ - - DUK_ASSERT(other >= 0); - - idx_func = duk_get_top(thr) - nargs - other; - if (DUK_UNLIKELY((idx_func | nargs) < 0)) { /* idx_func < 0 || nargs < 0; OR sign bits */ - DUK_ERROR_TYPE_INVALID_ARGS(thr); - DUK_WO_NORETURN(return 0;); - } - DUK_ASSERT(duk_is_valid_index(thr, idx_func)); - return idx_func; -} - -/* Compute idx_func, assume index will be valid. This is a valid assumption - * for protected calls: nargs < 0 is checked explicitly and duk_safe_call() - * validates the argument count. - */ -DUK_LOCAL duk_idx_t duk__call_get_idx_func_unvalidated(duk_hthread *thr, duk_idx_t nargs, duk_idx_t other) { - duk_idx_t idx_func; - - /* XXX: byte arithmetic? */ - - DUK_ASSERT(nargs >= 0); - DUK_ASSERT(other >= 0); - - idx_func = duk_get_top(thr) - nargs - other; - DUK_ASSERT(idx_func >= 0); - DUK_ASSERT(duk_is_valid_index(thr, idx_func)); - return idx_func; -} - -/* Prepare value stack for a method call through an object property. - * May currently throw an error e.g. when getting the property. - */ -DUK_LOCAL void duk__call_prop_prep_stack(duk_hthread *thr, duk_idx_t normalized_obj_idx, duk_idx_t nargs) { - DUK_CTX_ASSERT_VALID(thr); - DUK_ASSERT(nargs >= 0); - - DUK_DDD(DUK_DDDPRINT("duk__call_prop_prep_stack, normalized_obj_idx=%ld, nargs=%ld, stacktop=%ld", - (long) normalized_obj_idx, - (long) nargs, - (long) duk_get_top(thr))); - - /* [... key arg1 ... argN] */ - - /* duplicate key */ - duk_dup(thr, -nargs - 1); /* Note: -nargs alone would fail for nargs == 0, this is OK */ - (void) duk_get_prop(thr, normalized_obj_idx); - - DUK_DDD(DUK_DDDPRINT("func: %!T", (duk_tval *) duk_get_tval(thr, -1))); - -#if defined(DUK_USE_VERBOSE_ERRORS) - if (DUK_UNLIKELY(!duk_is_callable(thr, -1))) { - duk_tval *tv_base; - duk_tval *tv_key; - - /* tv_targ is passed on stack top (at index -1). */ - tv_base = DUK_GET_TVAL_POSIDX(thr, normalized_obj_idx); - tv_key = DUK_GET_TVAL_NEGIDX(thr, -nargs - 2); - DUK_ASSERT(tv_base >= thr->valstack_bottom && tv_base < thr->valstack_top); - DUK_ASSERT(tv_key >= thr->valstack_bottom && tv_key < thr->valstack_top); - - duk_call_setup_propcall_error(thr, tv_base, tv_key); - } -#endif - - /* [... key arg1 ... argN func] */ - - duk_replace(thr, -nargs - 2); - - /* [... func arg1 ... argN] */ - - duk_dup(thr, normalized_obj_idx); - duk_insert(thr, -nargs - 1); - - /* [... func this arg1 ... argN] */ -} - -DUK_EXTERNAL void duk_call(duk_hthread *thr, duk_idx_t nargs) { - duk_small_uint_t call_flags; - duk_idx_t idx_func; - - DUK_ASSERT_API_ENTRY(thr); - - idx_func = duk__call_get_idx_func(thr, nargs, 1); - DUK_ASSERT(duk_is_valid_index(thr, idx_func)); - - duk_insert_undefined(thr, idx_func + 1); - - call_flags = 0; /* not protected, respect reclimit, not constructor */ - duk_handle_call_unprotected(thr, idx_func, call_flags); -} - -DUK_EXTERNAL void duk_call_method(duk_hthread *thr, duk_idx_t nargs) { - duk_small_uint_t call_flags; - duk_idx_t idx_func; - - DUK_ASSERT_API_ENTRY(thr); - - idx_func = duk__call_get_idx_func(thr, nargs, 2); - DUK_ASSERT(duk_is_valid_index(thr, idx_func)); - - call_flags = 0; /* not protected, respect reclimit, not constructor */ - duk_handle_call_unprotected(thr, idx_func, call_flags); -} - -DUK_EXTERNAL void duk_call_prop(duk_hthread *thr, duk_idx_t obj_idx, duk_idx_t nargs) { - /* - * XXX: if duk_handle_call() took values through indices, this could be - * made much more sensible. However, duk_handle_call() needs to fudge - * the 'this' and 'func' values to handle bound functions, which is now - * done "in-place", so this is not a trivial change. - */ - - DUK_ASSERT_API_ENTRY(thr); - - obj_idx = duk_require_normalize_index(thr, obj_idx); /* make absolute */ - if (DUK_UNLIKELY(nargs < 0)) { - DUK_ERROR_TYPE_INVALID_ARGS(thr); - DUK_WO_NORETURN(return;); - } - - duk__call_prop_prep_stack(thr, obj_idx, nargs); - - duk_call_method(thr, nargs); -} - -DUK_LOCAL duk_ret_t duk__pcall_raw(duk_hthread *thr, void *udata) { - duk__pcall_args *args; - duk_idx_t idx_func; - duk_int_t ret; - - DUK_CTX_ASSERT_VALID(thr); - DUK_ASSERT(udata != NULL); - - args = (duk__pcall_args *) udata; - idx_func = duk__call_get_idx_func_unvalidated(thr, args->nargs, 1); - DUK_ASSERT(duk_is_valid_index(thr, idx_func)); - - duk_insert_undefined(thr, idx_func + 1); - - ret = duk_handle_call_unprotected(thr, idx_func, args->call_flags); - DUK_ASSERT(ret == 0); - DUK_UNREF(ret); - - return 1; -} - -DUK_EXTERNAL duk_int_t duk_pcall(duk_hthread *thr, duk_idx_t nargs) { - duk__pcall_args args; - - DUK_ASSERT_API_ENTRY(thr); - - args.nargs = nargs; - if (DUK_UNLIKELY(nargs < 0)) { - DUK_ERROR_TYPE_INVALID_ARGS(thr); - DUK_WO_NORETURN(return DUK_EXEC_ERROR;); - } - args.call_flags = 0; - - return duk_safe_call(thr, duk__pcall_raw, (void *) &args /*udata*/, nargs + 1 /*nargs*/, 1 /*nrets*/); -} - -DUK_LOCAL duk_ret_t duk__pcall_method_raw(duk_hthread *thr, void *udata) { - duk__pcall_method_args *args; - duk_idx_t idx_func; - duk_int_t ret; - - DUK_CTX_ASSERT_VALID(thr); - DUK_ASSERT(udata != NULL); - - args = (duk__pcall_method_args *) udata; - - idx_func = duk__call_get_idx_func_unvalidated(thr, args->nargs, 2); - DUK_ASSERT(duk_is_valid_index(thr, idx_func)); - - ret = duk_handle_call_unprotected(thr, idx_func, args->call_flags); - DUK_ASSERT(ret == 0); - DUK_UNREF(ret); - - return 1; -} - -DUK_INTERNAL duk_int_t duk_pcall_method_flags(duk_hthread *thr, duk_idx_t nargs, duk_small_uint_t call_flags) { - duk__pcall_method_args args; - - DUK_ASSERT_API_ENTRY(thr); - - args.nargs = nargs; - if (DUK_UNLIKELY(nargs < 0)) { - DUK_ERROR_TYPE_INVALID_ARGS(thr); - DUK_WO_NORETURN(return DUK_EXEC_ERROR;); - } - args.call_flags = call_flags; - - return duk_safe_call(thr, duk__pcall_method_raw, (void *) &args /*udata*/, nargs + 2 /*nargs*/, 1 /*nrets*/); -} - -DUK_EXTERNAL duk_int_t duk_pcall_method(duk_hthread *thr, duk_idx_t nargs) { - DUK_ASSERT_API_ENTRY(thr); - - return duk_pcall_method_flags(thr, nargs, 0); -} - -DUK_LOCAL duk_ret_t duk__pcall_prop_raw(duk_hthread *thr, void *udata) { - duk__pcall_prop_args *args; - duk_idx_t obj_idx; - duk_int_t ret; - - DUK_CTX_ASSERT_VALID(thr); - DUK_ASSERT(udata != NULL); - - args = (duk__pcall_prop_args *) udata; - - obj_idx = duk_require_normalize_index(thr, args->obj_idx); /* make absolute */ - duk__call_prop_prep_stack(thr, obj_idx, args->nargs); - - ret = duk_handle_call_unprotected_nargs(thr, args->nargs, args->call_flags); - DUK_ASSERT(ret == 0); - DUK_UNREF(ret); - return 1; -} - -DUK_EXTERNAL duk_int_t duk_pcall_prop(duk_hthread *thr, duk_idx_t obj_idx, duk_idx_t nargs) { - duk__pcall_prop_args args; - - DUK_ASSERT_API_ENTRY(thr); - - args.obj_idx = obj_idx; - args.nargs = nargs; - if (DUK_UNLIKELY(nargs < 0)) { - DUK_ERROR_TYPE_INVALID_ARGS(thr); - DUK_WO_NORETURN(return DUK_EXEC_ERROR;); - } - args.call_flags = 0; - - return duk_safe_call(thr, duk__pcall_prop_raw, (void *) &args /*udata*/, nargs + 1 /*nargs*/, 1 /*nrets*/); -} - -DUK_EXTERNAL duk_int_t duk_safe_call(duk_hthread *thr, duk_safe_call_function func, void *udata, duk_idx_t nargs, duk_idx_t nrets) { - duk_int_t rc; - - DUK_ASSERT_API_ENTRY(thr); - - /* nargs condition; fail if: top - bottom < nargs - * <=> top < bottom + nargs - * nrets condition; fail if: end - (top - nargs) < nrets - * <=> end - top + nargs < nrets - * <=> end + nargs < top + nrets - */ - /* XXX: check for any reserve? */ - - if (DUK_UNLIKELY((nargs | nrets) < 0 || /* nargs < 0 || nrets < 0; OR sign bits */ - thr->valstack_top < thr->valstack_bottom + nargs || /* nargs too large compared to top */ - thr->valstack_end + nargs < thr->valstack_top + nrets)) { /* nrets too large compared to reserve */ - DUK_D(DUK_DPRINT("not enough stack reserve for safe call or invalid arguments: " - "nargs=%ld < 0 (?), nrets=%ld < 0 (?), top=%ld < bottom=%ld + nargs=%ld (?), " - "end=%ld + nargs=%ld < top=%ld + nrets=%ld (?)", - (long) nargs, - (long) nrets, - (long) (thr->valstack_top - thr->valstack), - (long) (thr->valstack_bottom - thr->valstack), - (long) nargs, - (long) (thr->valstack_end - thr->valstack), - (long) nargs, - (long) (thr->valstack_top - thr->valstack), - (long) nrets)); - DUK_ERROR_TYPE_INVALID_ARGS(thr); - DUK_WO_NORETURN(return DUK_EXEC_ERROR;); - } - - rc = duk_handle_safe_call(thr, /* thread */ - func, /* func */ - udata, /* udata */ - nargs, /* num_stack_args */ - nrets); /* num_stack_res */ - - return rc; -} - -DUK_EXTERNAL void duk_new(duk_hthread *thr, duk_idx_t nargs) { - duk_idx_t idx_func; - - DUK_ASSERT_API_ENTRY(thr); - - idx_func = duk__call_get_idx_func(thr, nargs, 1); - DUK_ASSERT(duk_is_valid_index(thr, idx_func)); - - duk_push_object(thr); /* default instance; internal proto updated by call handling */ - duk_insert(thr, idx_func + 1); - - duk_handle_call_unprotected(thr, idx_func, DUK_CALL_FLAG_CONSTRUCT); -} - -DUK_LOCAL duk_ret_t duk__pnew_helper(duk_hthread *thr, void *udata) { - duk_idx_t nargs; - - DUK_ASSERT(udata != NULL); - nargs = *((duk_idx_t *) udata); - - duk_new(thr, nargs); - return 1; -} - -DUK_EXTERNAL duk_int_t duk_pnew(duk_hthread *thr, duk_idx_t nargs) { - duk_int_t rc; - - DUK_ASSERT_API_ENTRY(thr); - - /* For now, just use duk_safe_call() to wrap duk_new(). We can't - * simply use a protected duk_handle_call() because pushing the - * default instance might throw. - */ - - if (DUK_UNLIKELY(nargs < 0)) { - DUK_ERROR_TYPE_INVALID_ARGS(thr); - DUK_WO_NORETURN(return DUK_EXEC_ERROR;); - } - - rc = duk_safe_call(thr, duk__pnew_helper, (void *) &nargs /*udata*/, nargs + 1 /*nargs*/, 1 /*nrets*/); - return rc; -} - -DUK_EXTERNAL duk_bool_t duk_is_constructor_call(duk_hthread *thr) { - duk_activation *act; - - DUK_ASSERT_API_ENTRY(thr); - - act = thr->callstack_curr; - if (act != NULL) { - return ((act->flags & DUK_ACT_FLAG_CONSTRUCT) != 0 ? 1 : 0); - } - return 0; -} - -DUK_EXTERNAL void duk_require_constructor_call(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - - if (!duk_is_constructor_call(thr)) { - DUK_ERROR_TYPE(thr, DUK_STR_CONSTRUCT_ONLY); - DUK_WO_NORETURN(return;); - } -} - -DUK_EXTERNAL duk_bool_t duk_is_strict_call(duk_hthread *thr) { - duk_activation *act; - - /* For user code this could just return 1 (strict) always - * because all Duktape/C functions are considered strict, - * and strict is also the default when nothing is running. - * However, Duktape may call this function internally when - * the current activation is an ECMAScript function, so - * this cannot be replaced by a 'return 1' without fixing - * the internal call sites. - */ - - DUK_ASSERT_API_ENTRY(thr); - - act = thr->callstack_curr; - if (act != NULL) { - return ((act->flags & DUK_ACT_FLAG_STRICT) != 0 ? 1 : 0); - } else { - /* Strict by default. */ - return 1; - } -} - -/* - * Duktape/C function magic - */ - -DUK_EXTERNAL duk_int_t duk_get_current_magic(duk_hthread *thr) { - duk_activation *act; - duk_hobject *func; - - DUK_ASSERT_API_ENTRY(thr); - - act = thr->callstack_curr; - if (act) { - func = DUK_ACT_GET_FUNC(act); - if (!func) { - duk_tval *tv = &act->tv_func; - duk_small_uint_t lf_flags; - lf_flags = DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv); - return (duk_int_t) DUK_LFUNC_FLAGS_GET_MAGIC(lf_flags); - } - DUK_ASSERT(func != NULL); - - if (DUK_HOBJECT_IS_NATFUNC(func)) { - duk_hnatfunc *nf = (duk_hnatfunc *) func; - return (duk_int_t) nf->magic; - } - } - return 0; -} - -DUK_EXTERNAL duk_int_t duk_get_magic(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - duk_hobject *h; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_require_tval(thr, idx); - if (DUK_TVAL_IS_OBJECT(tv)) { - h = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - if (!DUK_HOBJECT_HAS_NATFUNC(h)) { - goto type_error; - } - return (duk_int_t) ((duk_hnatfunc *) h)->magic; - } else if (DUK_TVAL_IS_LIGHTFUNC(tv)) { - duk_small_uint_t lf_flags = DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv); - return (duk_int_t) DUK_LFUNC_FLAGS_GET_MAGIC(lf_flags); - } - - /* fall through */ -type_error: - DUK_ERROR_TYPE(thr, DUK_STR_UNEXPECTED_TYPE); - DUK_WO_NORETURN(return 0;); -} - -DUK_EXTERNAL void duk_set_magic(duk_hthread *thr, duk_idx_t idx, duk_int_t magic) { - duk_hnatfunc *nf; - - DUK_ASSERT_API_ENTRY(thr); - - nf = duk_require_hnatfunc(thr, idx); - DUK_ASSERT(nf != NULL); - nf->magic = (duk_int16_t) magic; -} - -/* - * Misc helpers - */ - -/* Resolve a bound function on value stack top to a non-bound target - * (leave other values as is). - */ -DUK_INTERNAL void duk_resolve_nonbound_function(duk_hthread *thr) { - duk_tval *tv; - - DUK_HTHREAD_ASSERT_VALID(thr); - - tv = DUK_GET_TVAL_NEGIDX(thr, -1); - if (DUK_TVAL_IS_OBJECT(tv)) { - duk_hobject *h; - - h = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - if (DUK_HOBJECT_HAS_BOUNDFUNC(h)) { - duk_push_tval(thr, &((duk_hboundfunc *) (void *) h)->target); - duk_replace(thr, -2); -#if 0 - DUK_TVAL_SET_TVAL(tv, &((duk_hboundfunc *) h)->target); - DUK_TVAL_INCREF(thr, tv); - DUK_HOBJECT_DECREF_NORZ(thr, h); -#endif - /* Rely on Function.prototype.bind() on never creating a bound - * function whose target is not proper. This is now safe - * because the target is not even an internal property but a - * struct member. - */ - DUK_ASSERT(duk_is_lightfunc(thr, -1) || duk_is_callable(thr, -1)); - } - } - - /* Lightfuncs cannot be bound but are always callable and - * constructable. - */ -} -/* - * Encoding and decoding basic formats: hex, base64. - * - * These are in-place operations which may allow an optimized implementation. - * - * Base-64: https://tools.ietf.org/html/rfc4648#section-4 - */ - -/* #include duk_internal.h -> already included */ - -/* - * Misc helpers - */ - -/* Shared handling for encode/decode argument. Fast path handling for - * buffer and string values because they're the most common. In particular, - * avoid creating a temporary string or buffer when possible. Return value - * is guaranteed to be non-NULL, even for zero length input. - */ -DUK_LOCAL const duk_uint8_t *duk__prep_codec_arg(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len) { - const void *def_ptr = (const void *) out_len; /* Any non-NULL pointer will do. */ - const void *ptr; - duk_bool_t isbuffer; - - DUK_ASSERT(out_len != NULL); - DUK_ASSERT(def_ptr != NULL); - DUK_ASSERT(duk_is_valid_index(thr, idx)); /* checked by caller */ - - ptr = (const void *) - duk_get_buffer_data_raw(thr, idx, out_len, NULL /*def_ptr*/, 0 /*def_size*/, 0 /*throw_flag*/, &isbuffer); - if (isbuffer) { - DUK_ASSERT(ptr != NULL || *out_len == 0U); - if (DUK_UNLIKELY(ptr == NULL)) { - ptr = def_ptr; - } - DUK_ASSERT(ptr != NULL); - } else { - /* For strings a non-NULL pointer is always guaranteed because - * at least a NUL will be present. - */ - ptr = (const void *) duk_to_lstring(thr, idx, out_len); - DUK_ASSERT(ptr != NULL); - } - DUK_ASSERT(ptr != NULL); - return (const duk_uint8_t *) ptr; -} - -/* - * Base64 - */ - -#if defined(DUK_USE_BASE64_SUPPORT) -/* Bytes emitted for number of padding characters in range [0,4]. */ -DUK_LOCAL const duk_int8_t duk__base64_decode_nequal_step[5] = { - 3, /* #### -> 24 bits, emit 3 bytes */ - 2, /* ###= -> 18 bits, emit 2 bytes */ - 1, /* ##== -> 12 bits, emit 1 byte */ - -1, /* #=== -> 6 bits, error */ - 0, /* ==== -> 0 bits, emit 0 bytes */ -}; - -#if defined(DUK_USE_BASE64_FASTPATH) -DUK_LOCAL const duk_uint8_t duk__base64_enctab_fast[64] = { - 0x41U, 0x42U, 0x43U, 0x44U, 0x45U, 0x46U, 0x47U, 0x48U, 0x49U, 0x4aU, 0x4bU, 0x4cU, 0x4dU, 0x4eU, 0x4fU, 0x50U, /* A...P */ - 0x51U, 0x52U, 0x53U, 0x54U, 0x55U, 0x56U, 0x57U, 0x58U, 0x59U, 0x5aU, 0x61U, 0x62U, 0x63U, 0x64U, 0x65U, 0x66U, /* Q...f */ - 0x67U, 0x68U, 0x69U, 0x6aU, 0x6bU, 0x6cU, 0x6dU, 0x6eU, 0x6fU, 0x70U, 0x71U, 0x72U, 0x73U, 0x74U, 0x75U, 0x76U, /* g...v */ - 0x77U, 0x78U, 0x79U, 0x7aU, 0x30U, 0x31U, 0x32U, 0x33U, 0x34U, 0x35U, 0x36U, 0x37U, 0x38U, 0x39U, 0x2bU, 0x2fU /* w.../ */ -}; -#endif /* DUK_USE_BASE64_FASTPATH */ - -#if defined(DUK_USE_BASE64_FASTPATH) -/* Decode table for one byte of input: - * -1 = allowed whitespace - * -2 = padding - * -3 = error - * 0...63 decoded bytes - */ -DUK_LOCAL const duk_int8_t duk__base64_dectab_fast[256] = { - -3, -3, -3, -3, -3, -3, -3, -3, -3, -1, -1, -3, -3, -1, -3, -3, /* 0x00...0x0f */ - -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, /* 0x10...0x1f */ - -1, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, 62, -3, -3, -3, 63, /* 0x20...0x2f */ - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -3, -3, -3, -2, -3, -3, /* 0x30...0x3f */ - -3, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40...0x4f */ - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -3, -3, -3, -3, -3, /* 0x50...0x5f */ - -3, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60...0x6f */ - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -3, -3, -3, -3, -3, /* 0x70...0x7f */ - -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, /* 0x80...0x8f */ - -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, /* 0x90...0x9f */ - -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, /* 0xa0...0xaf */ - -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, /* 0xb0...0xbf */ - -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, /* 0xc0...0xcf */ - -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, /* 0xd0...0xdf */ - -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, /* 0xe0...0xef */ - -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3 /* 0xf0...0xff */ -}; -#endif /* DUK_USE_BASE64_FASTPATH */ - -#if defined(DUK_USE_BASE64_FASTPATH) -DUK_LOCAL DUK_ALWAYS_INLINE void duk__base64_encode_fast_3(const duk_uint8_t *src, duk_uint8_t *dst) { - duk_uint_t t; - - t = (duk_uint_t) src[0]; - t = (t << 8) + (duk_uint_t) src[1]; - t = (t << 8) + (duk_uint_t) src[2]; - - dst[0] = duk__base64_enctab_fast[t >> 18]; - dst[1] = duk__base64_enctab_fast[(t >> 12) & 0x3fU]; - dst[2] = duk__base64_enctab_fast[(t >> 6) & 0x3fU]; - dst[3] = duk__base64_enctab_fast[t & 0x3fU]; - -#if 0 - /* Tested: not faster on x64, most likely due to aliasing between - * output and input index computation. - */ - /* aaaaaabb bbbbcccc ccdddddd */ - dst[0] = duk__base64_enctab_fast[(src[0] >> 2) & 0x3fU]; - dst[1] = duk__base64_enctab_fast[((src[0] << 4) & 0x30U) | ((src[1] >> 4) & 0x0fU)]; - dst[2] = duk__base64_enctab_fast[((src[1] << 2) & 0x3fU) | ((src[2] >> 6) & 0x03U)]; - dst[3] = duk__base64_enctab_fast[src[2] & 0x3fU]; -#endif -} - -DUK_LOCAL DUK_ALWAYS_INLINE void duk__base64_encode_fast_2(const duk_uint8_t *src, duk_uint8_t *dst) { - duk_uint_t t; - - t = (duk_uint_t) src[0]; - t = (t << 8) + (duk_uint_t) src[1]; - dst[0] = duk__base64_enctab_fast[t >> 10]; /* XXXXXX-- -------- */ - dst[1] = duk__base64_enctab_fast[(t >> 4) & 0x3fU]; /* ------XX XXXX---- */ - dst[2] = duk__base64_enctab_fast[(t << 2) & 0x3fU]; /* -------- ----XXXX */ - dst[3] = DUK_ASC_EQUALS; -} - -DUK_LOCAL DUK_ALWAYS_INLINE void duk__base64_encode_fast_1(const duk_uint8_t *src, duk_uint8_t *dst) { - duk_uint_t t; - - t = (duk_uint_t) src[0]; - dst[0] = duk__base64_enctab_fast[t >> 2]; /* XXXXXX-- */ - dst[1] = duk__base64_enctab_fast[(t << 4) & 0x3fU]; /* ------XX */ - dst[2] = DUK_ASC_EQUALS; - dst[3] = DUK_ASC_EQUALS; -} - -DUK_LOCAL void duk__base64_encode_helper(const duk_uint8_t *src, duk_size_t srclen, duk_uint8_t *dst) { - duk_size_t n; - const duk_uint8_t *p; - duk_uint8_t *q; - - n = srclen; - p = src; - q = dst; - - if (n >= 16U) { - /* Fast path, unrolled by 4, allows interleaving. Process - * 12-byte input chunks which encode to 16-char output chunks. - * Only enter when at least one block is emitted (avoids div+mul - * for short inputs too). - */ - const duk_uint8_t *p_end_fast; - - p_end_fast = p + ((n / 12U) * 12U); - DUK_ASSERT(p_end_fast >= p + 12); - do { - duk__base64_encode_fast_3(p, q); - duk__base64_encode_fast_3(p + 3, q + 4); - duk__base64_encode_fast_3(p + 6, q + 8); - duk__base64_encode_fast_3(p + 9, q + 12); - p += 12; - q += 16; - } while (DUK_LIKELY(p != p_end_fast)); - - DUK_ASSERT(src + srclen >= p); - n = (duk_size_t) (src + srclen - p); - DUK_ASSERT(n < 12U); - } - - /* Remainder. */ - while (n >= 3U) { - duk__base64_encode_fast_3(p, q); - p += 3; - q += 4; - n -= 3U; - } - DUK_ASSERT(n == 0U || n == 1U || n == 2U); - if (n == 1U) { - duk__base64_encode_fast_1(p, q); -#if 0 /* Unnecessary. */ - p += 1; - q += 4; - n -= 1U; -#endif - } else if (n == 2U) { - duk__base64_encode_fast_2(p, q); -#if 0 /* Unnecessary. */ - p += 2; - q += 4; - n -= 2U; -#endif - } else { - DUK_ASSERT(n == 0U); /* nothing to do */ - ; - } -} -#else /* DUK_USE_BASE64_FASTPATH */ -DUK_LOCAL void duk__base64_encode_helper(const duk_uint8_t *src, duk_size_t srclen, duk_uint8_t *dst) { - duk_small_uint_t i, npad; - duk_uint_t t, x, y; - const duk_uint8_t *p; - const duk_uint8_t *p_end; - duk_uint8_t *q; - - p = src; - p_end = src + srclen; - q = dst; - npad = 0U; - - while (p < p_end) { - /* Read 3 bytes into 't', padded by zero. */ - t = 0; - for (i = 0; i < 3; i++) { - t = t << 8; - if (p < p_end) { - t += (duk_uint_t) (*p++); - } else { - /* This only happens on the last loop and we're - * guaranteed to exit on the next loop. - */ - npad++; - } - } - DUK_ASSERT(npad <= 2U); - - /* Emit 4 encoded characters. If npad > 0, some of the - * chars will be incorrect (zero bits) but we fix up the - * padding after the loop. A straightforward 64-byte - * lookup would be faster and cleaner, but this is shorter. - */ - for (i = 0; i < 4; i++) { - x = ((t >> 18) & 0x3fU); - t = t << 6; - - if (x <= 51U) { - if (x <= 25) { - y = x + DUK_ASC_UC_A; - } else { - y = x - 26 + DUK_ASC_LC_A; - } - } else { - if (x <= 61U) { - y = x - 52 + DUK_ASC_0; - } else if (x == 62) { - y = DUK_ASC_PLUS; - } else { - DUK_ASSERT(x == 63); - y = DUK_ASC_SLASH; - } - } - - *q++ = (duk_uint8_t) y; - } - } - - /* Handle padding by rewriting 0-2 bogus characters at the end. - * - * Missing bytes npad base64 example - * 0 0 #### - * 1 1 ###= - * 2 2 ##== - */ - DUK_ASSERT(npad <= 2U); - while (npad > 0U) { - *(q - npad) = DUK_ASC_EQUALS; - npad--; - } -} -#endif /* DUK_USE_BASE64_FASTPATH */ - -#if defined(DUK_USE_BASE64_FASTPATH) -DUK_LOCAL duk_bool_t duk__base64_decode_helper(const duk_uint8_t *src, - duk_size_t srclen, - duk_uint8_t *dst, - duk_uint8_t **out_dst_final) { - duk_int_t x; - duk_uint_t t; - duk_small_uint_t n_equal; - duk_int8_t step; - const duk_uint8_t *p; - const duk_uint8_t *p_end; - const duk_uint8_t *p_end_safe; - duk_uint8_t *q; - - DUK_ASSERT(src != NULL); /* Required by pointer arithmetic below, which fails for NULL. */ - - p = src; - p_end = src + srclen; - p_end_safe = p_end - 8; /* If 'src <= src_end_safe', safe to read 8 bytes. */ - q = dst; - - /* Alternate between a fast path which processes clean groups with no - * padding or whitespace, and a slow path which processes one arbitrary - * group and then re-enters the fast path. This handles e.g. base64 - * with newlines reasonably well because the majority of a line is in - * the fast path. - */ - for (;;) { - /* Fast path, on each loop handle two 4-char input groups. - * If both are clean, emit 6 bytes and continue. If first - * is clean, emit 3 bytes and drop out; otherwise emit - * nothing and drop out. This approach could be extended to - * more groups per loop, but for inputs with e.g. periodic - * newlines (which are common) it might not be an improvement. - */ - while (DUK_LIKELY(p <= p_end_safe)) { - duk_int_t t1, t2; - - /* The lookup byte is intentionally sign extended to - * (at least) 32 bits and then ORed. This ensures - * that is at least 1 byte is negative, the highest - * bit of the accumulator will be set at the end and - * we don't need to check every byte. - * - * Read all input bytes first before writing output - * bytes to minimize aliasing. - */ - DUK_DDD(DUK_DDDPRINT("fast loop: p=%p, p_end_safe=%p, p_end=%p", - (const void *) p, - (const void *) p_end_safe, - (const void *) p_end)); - - t1 = (duk_int_t) duk__base64_dectab_fast[p[0]]; - t1 = (duk_int_t) ((duk_uint_t) t1 << 6) | (duk_int_t) duk__base64_dectab_fast[p[1]]; - t1 = (duk_int_t) ((duk_uint_t) t1 << 6) | (duk_int_t) duk__base64_dectab_fast[p[2]]; - t1 = (duk_int_t) ((duk_uint_t) t1 << 6) | (duk_int_t) duk__base64_dectab_fast[p[3]]; - - t2 = (duk_int_t) duk__base64_dectab_fast[p[4]]; - t2 = (duk_int_t) ((duk_uint_t) t2 << 6) | (duk_int_t) duk__base64_dectab_fast[p[5]]; - t2 = (duk_int_t) ((duk_uint_t) t2 << 6) | (duk_int_t) duk__base64_dectab_fast[p[6]]; - t2 = (duk_int_t) ((duk_uint_t) t2 << 6) | (duk_int_t) duk__base64_dectab_fast[p[7]]; - - q[0] = (duk_uint8_t) (((duk_uint_t) t1 >> 16) & 0xffU); - q[1] = (duk_uint8_t) (((duk_uint_t) t1 >> 8) & 0xffU); - q[2] = (duk_uint8_t) ((duk_uint_t) t1 & 0xffU); - - q[3] = (duk_uint8_t) (((duk_uint_t) t2 >> 16) & 0xffU); - q[4] = (duk_uint8_t) (((duk_uint_t) t2 >> 8) & 0xffU); - q[5] = (duk_uint8_t) ((duk_uint_t) t2 & 0xffU); - - /* Optimistic check using one branch. */ - if (DUK_LIKELY((t1 | t2) >= 0)) { - p += 8; - q += 6; - } else if (t1 >= 0) { - DUK_DDD( - DUK_DDDPRINT("fast loop first group was clean, second was not, process one slow path group")); - DUK_ASSERT(t2 < 0); - p += 4; - q += 3; - break; - } else { - DUK_DDD(DUK_DDDPRINT( - "fast loop first group was not clean, second does not matter, process one slow path group")); - DUK_ASSERT(t1 < 0); - break; - } - } /* fast path */ - - /* Slow path step 1: try to scan a 4-character encoded group, - * end-of-input, or start-of-padding. We exit with: - * 1. n_chars == 4: full group, no padding, no end-of-input. - * 2. n_chars < 4: partial group (may also be 0), encountered - * padding or end of input. - * - * The accumulator is initialized to 1; this allows us to detect - * a full group by comparing >= 0x1000000 without an extra - * counter variable. - */ - t = 1UL; - for (;;) { - DUK_DDD(DUK_DDDPRINT("slow loop: p=%p, p_end=%p, t=%lu", - (const void *) p, - (const void *) p_end, - (unsigned long) t)); - - if (DUK_LIKELY(p < p_end)) { - x = duk__base64_dectab_fast[*p++]; - if (DUK_LIKELY(x >= 0)) { - DUK_ASSERT(x >= 0 && x <= 63); - t = (t << 6) + (duk_uint_t) x; - if (t >= 0x1000000UL) { - break; - } - } else if (x == -1) { - continue; /* allowed ascii whitespace */ - } else if (x == -2) { - p--; - break; /* start of padding */ - } else { - DUK_ASSERT(x == -3); - goto decode_error; - } - } else { - break; /* end of input */ - } - } /* slow path step 1 */ - - /* Complete the padding by simulating pad characters, - * regardless of actual input padding chars. - */ - n_equal = 0; - while (t < 0x1000000UL) { - t = (t << 6) + 0U; - n_equal++; - } - - /* Slow path step 2: deal with full/partial group, padding, - * etc. Note that for num chars in [0,3] we intentionally emit - * 3 bytes but don't step forward that much, buffer space is - * guaranteed in setup. - * - * num chars: - * 0 #### no output (= step 0) - * 1 #=== reject, 6 bits of data - * 2 ##== 12 bits of data, output 1 byte (= step 1) - * 3 ###= 18 bits of data, output 2 bytes (= step 2) - * 4 #### 24 bits of data, output 3 bytes (= step 3) - */ - q[0] = (duk_uint8_t) ((t >> 16) & 0xffU); - q[1] = (duk_uint8_t) ((t >> 8) & 0xffU); - q[2] = (duk_uint8_t) (t & 0xffU); - - DUK_ASSERT(n_equal <= 4); - step = duk__base64_decode_nequal_step[n_equal]; - if (DUK_UNLIKELY(step < 0)) { - goto decode_error; - } - q += step; - - /* Slow path step 3: read and ignore padding and whitespace - * until (a) next non-padding and non-whitespace character - * after which we resume the fast path, or (b) end of input. - * This allows us to accept missing, partial, full, and extra - * padding cases uniformly. We also support concatenated - * base-64 documents because we resume scanning afterwards. - * - * Note that to support concatenated documents well, the '=' - * padding found inside the input must also allow for 'extra' - * padding. For example, 'Zm===' decodes to 'f' and has one - * extra padding char. So, 'Zm===Zm' should decode 'ff', even - * though the standard break-up would be 'Zm==' + '=Zm' which - * doesn't make sense. - * - * We also accept prepended padding like '==Zm9', because it - * is equivalent to an empty document with extra padding ('==') - * followed by a valid document. - */ - - for (;;) { - if (DUK_UNLIKELY(p >= p_end)) { - goto done; - } - x = duk__base64_dectab_fast[*p++]; - if (x == -1 || x == -2) { - ; /* padding or whitespace, keep eating */ - } else { - p--; - break; /* backtrack and go back to fast path, even for -1 */ - } - } /* slow path step 3 */ - } /* outer fast+slow path loop */ - -done: - DUK_DDD(DUK_DDDPRINT("done; p=%p, p_end=%p", (const void *) p, (const void *) p_end)); - - DUK_ASSERT(p == p_end); - - *out_dst_final = q; - return 1; - -decode_error: - return 0; -} -#else /* DUK_USE_BASE64_FASTPATH */ -DUK_LOCAL duk_bool_t duk__base64_decode_helper(const duk_uint8_t *src, - duk_size_t srclen, - duk_uint8_t *dst, - duk_uint8_t **out_dst_final) { - duk_uint_t t, x; - duk_int_t y; - duk_int8_t step; - const duk_uint8_t *p; - const duk_uint8_t *p_end; - duk_uint8_t *q; - /* 0x09, 0x0a, or 0x0d */ - duk_uint32_t mask_white = (1U << 9) | (1U << 10) | (1U << 13); - - /* 't' tracks progress of the decoded group: - * - * t == 1 no valid chars yet - * t >= 0x40 1x6 = 6 bits shifted in - * t >= 0x1000 2x6 = 12 bits shifted in - * t >= 0x40000 3x6 = 18 bits shifted in - * t >= 0x1000000 4x6 = 24 bits shifted in - * - * By initializing t=1 there's no need for a separate counter for - * the number of characters found so far. - */ - p = src; - p_end = src + srclen; - q = dst; - t = 1UL; - - for (;;) { - duk_small_uint_t n_equal; - - DUK_ASSERT(t >= 1U); - if (p >= p_end) { - /* End of input: if input exists, treat like - * start of padding, finish the block, then - * re-enter here to see we're done. - */ - if (t == 1U) { - break; - } else { - goto simulate_padding; - } - } - - x = *p++; - - if (x >= 0x41U) { - /* Valid: a-z and A-Z. */ - DUK_ASSERT(x >= 0x41U && x <= 0xffU); - if (x >= 0x61U && x <= 0x7aU) { - y = (duk_int_t) x - 0x61 + 26; - } else if (x <= 0x5aU) { - y = (duk_int_t) x - 0x41; - } else { - goto decode_error; - } - } else if (x >= 0x30U) { - /* Valid: 0-9 and =. */ - DUK_ASSERT(x >= 0x30U && x <= 0x40U); - if (x <= 0x39U) { - y = (duk_int_t) x - 0x30 + 52; - } else if (x == 0x3dU) { - /* Skip padding and whitespace unless we're in the - * middle of a block. Otherwise complete group by - * simulating shifting in the correct padding. - */ - if (t == 1U) { - continue; - } - goto simulate_padding; - } else { - goto decode_error; - } - } else if (x >= 0x20U) { - /* Valid: +, /, and 0x20 whitespace. */ - DUK_ASSERT(x >= 0x20U && x <= 0x2fU); - if (x == 0x2bU) { - y = 62; - } else if (x == 0x2fU) { - y = 63; - } else if (x == 0x20U) { - continue; - } else { - goto decode_error; - } - } else { - /* Valid: whitespace. */ - duk_uint32_t m; - DUK_ASSERT(x < 0x20U); /* 0x00 to 0x1f */ - m = (1U << x); - if (mask_white & m) { - /* Allow basic ASCII whitespace. */ - continue; - } else { - goto decode_error; - } - } - - DUK_ASSERT(y >= 0 && y <= 63); - t = (t << 6) + (duk_uint_t) y; - if (t < 0x1000000UL) { - continue; - } - /* fall through; no padding will be added */ - - simulate_padding: - n_equal = 0; - while (t < 0x1000000UL) { - t = (t << 6) + 0U; - n_equal++; - } - - /* Output 3 bytes from 't' and advance as needed. */ - q[0] = (duk_uint8_t) ((t >> 16) & 0xffU); - q[1] = (duk_uint8_t) ((t >> 8) & 0xffU); - q[2] = (duk_uint8_t) (t & 0xffU); - - DUK_ASSERT(n_equal <= 4U); - step = duk__base64_decode_nequal_step[n_equal]; - if (step < 0) { - goto decode_error; - } - q += step; - - /* Re-enter loop. The actual padding characters are skipped - * by the main loop. This handles cases like missing, partial, - * full, and extra padding, and allows parsing of concatenated - * documents (with extra padding) like: Zm===Zm. Also extra - * prepended padding is accepted: ===Zm9v. - */ - t = 1U; - } - DUK_ASSERT(t == 1UL); - - *out_dst_final = q; - return 1; - -decode_error: - return 0; -} -#endif /* DUK_USE_BASE64_FASTPATH */ - -DUK_EXTERNAL const char *duk_base64_encode(duk_hthread *thr, duk_idx_t idx) { - const duk_uint8_t *src; - duk_size_t srclen; - duk_size_t dstlen; - duk_uint8_t *dst; - const char *ret; - - DUK_ASSERT_API_ENTRY(thr); - - idx = duk_require_normalize_index(thr, idx); - src = duk__prep_codec_arg(thr, idx, &srclen); - DUK_ASSERT(src != NULL); - - /* Compute exact output length. Computation must not wrap; this - * limit works for 32-bit size_t: - * >>> srclen = 3221225469 - * >>> '%x' % ((srclen + 2) / 3 * 4) - * 'fffffffc' - */ - if (srclen > 3221225469UL) { - goto type_error; - } - dstlen = (srclen + 2U) / 3U * 4U; - dst = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, dstlen); - - duk__base64_encode_helper((const duk_uint8_t *) src, srclen, dst); - - ret = duk_buffer_to_string(thr, -1); /* Safe, result is ASCII. */ - duk_replace(thr, idx); - return ret; - -type_error: - DUK_ERROR_TYPE(thr, DUK_STR_BASE64_ENCODE_FAILED); - DUK_WO_NORETURN(return NULL;); -} - -DUK_EXTERNAL void duk_base64_decode(duk_hthread *thr, duk_idx_t idx) { - const duk_uint8_t *src; - duk_size_t srclen; - duk_size_t dstlen; - duk_uint8_t *dst; - duk_uint8_t *dst_final; - - DUK_ASSERT_API_ENTRY(thr); - - idx = duk_require_normalize_index(thr, idx); - src = duk__prep_codec_arg(thr, idx, &srclen); - DUK_ASSERT(src != NULL); - - /* Round up and add safety margin. Avoid addition before division to - * avoid possibility of wrapping. Margin includes +3 for rounding up, - * and +3 for one extra group: the decoder may emit and then backtrack - * a full group (3 bytes) from zero-sized input for technical reasons. - * Similarly, 'xx' may ecause 1+3 = bytes to be emitted and then - * backtracked. - */ - dstlen = (srclen / 4) * 3 + 6; /* upper limit, assuming no whitespace etc */ - dst = (duk_uint8_t *) duk_push_dynamic_buffer(thr, dstlen); - /* Note: for dstlen=0, dst may be NULL */ - - if (!duk__base64_decode_helper((const duk_uint8_t *) src, srclen, dst, &dst_final)) { - goto type_error; - } - - /* XXX: convert to fixed buffer? */ - (void) duk_resize_buffer(thr, -1, (duk_size_t) (dst_final - dst)); - duk_replace(thr, idx); - return; - -type_error: - DUK_ERROR_TYPE(thr, DUK_STR_BASE64_DECODE_FAILED); - DUK_WO_NORETURN(return;); -} -#else /* DUK_USE_BASE64_SUPPORT */ -DUK_EXTERNAL const char *duk_base64_encode(duk_hthread *thr, duk_idx_t idx) { - DUK_UNREF(idx); - DUK_ERROR_UNSUPPORTED(thr); - DUK_WO_NORETURN(return NULL;); -} - -DUK_EXTERNAL void duk_base64_decode(duk_hthread *thr, duk_idx_t idx) { - DUK_UNREF(idx); - DUK_ERROR_UNSUPPORTED(thr); - DUK_WO_NORETURN(return;); -} -#endif /* DUK_USE_BASE64_SUPPORT */ - -/* - * Hex - */ - -#if defined(DUK_USE_HEX_SUPPORT) -DUK_EXTERNAL const char *duk_hex_encode(duk_hthread *thr, duk_idx_t idx) { - const duk_uint8_t *inp; - duk_size_t len; - duk_size_t i; - duk_uint8_t *buf; - const char *ret; -#if defined(DUK_USE_HEX_FASTPATH) - duk_size_t len_safe; - duk_uint16_t *p16; -#endif - - DUK_ASSERT_API_ENTRY(thr); - - idx = duk_require_normalize_index(thr, idx); - inp = duk__prep_codec_arg(thr, idx, &len); - DUK_ASSERT(inp != NULL); - - /* Fixed buffer, no zeroing because we'll fill all the data. */ - buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, len * 2); - DUK_ASSERT(buf != NULL); - -#if defined(DUK_USE_HEX_FASTPATH) - DUK_ASSERT((((duk_size_t) buf) & 0x01U) == 0); /* pointer is aligned, guaranteed for fixed buffer */ - p16 = (duk_uint16_t *) (void *) buf; - len_safe = len & ~0x03U; - for (i = 0; i < len_safe; i += 4) { - p16[0] = duk_hex_enctab[inp[i]]; - p16[1] = duk_hex_enctab[inp[i + 1]]; - p16[2] = duk_hex_enctab[inp[i + 2]]; - p16[3] = duk_hex_enctab[inp[i + 3]]; - p16 += 4; - } - for (; i < len; i++) { - *p16++ = duk_hex_enctab[inp[i]]; - } -#else /* DUK_USE_HEX_FASTPATH */ - for (i = 0; i < len; i++) { - duk_small_uint_t t; - t = (duk_small_uint_t) inp[i]; - buf[i * 2 + 0] = duk_lc_digits[t >> 4]; - buf[i * 2 + 1] = duk_lc_digits[t & 0x0f]; - } -#endif /* DUK_USE_HEX_FASTPATH */ - - /* XXX: Using a string return value forces a string intern which is - * not always necessary. As a rough performance measure, hex encode - * time for tests/perf/test-hex-encode.js dropped from ~35s to ~15s - * without string coercion. Change to returning a buffer and let the - * caller coerce to string if necessary? - */ - - ret = duk_buffer_to_string(thr, -1); /* Safe, result is ASCII. */ - duk_replace(thr, idx); - return ret; -} - -DUK_EXTERNAL void duk_hex_decode(duk_hthread *thr, duk_idx_t idx) { - const duk_uint8_t *inp; - duk_size_t len; - duk_size_t i; - duk_int_t t; - duk_uint8_t *buf; -#if defined(DUK_USE_HEX_FASTPATH) - duk_int_t chk; - duk_uint8_t *p; - duk_size_t len_safe; -#endif - - DUK_ASSERT_API_ENTRY(thr); - - idx = duk_require_normalize_index(thr, idx); - inp = duk__prep_codec_arg(thr, idx, &len); - DUK_ASSERT(inp != NULL); - - if (len & 0x01) { - goto type_error; - } - - /* Fixed buffer, no zeroing because we'll fill all the data. */ - buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, len / 2); - DUK_ASSERT(buf != NULL); - -#if defined(DUK_USE_HEX_FASTPATH) - p = buf; - len_safe = len & ~0x07U; - for (i = 0; i < len_safe; i += 8) { - t = ((duk_int_t) duk_hex_dectab_shift4[inp[i]]) | ((duk_int_t) duk_hex_dectab[inp[i + 1]]); - chk = t; - p[0] = (duk_uint8_t) t; - t = ((duk_int_t) duk_hex_dectab_shift4[inp[i + 2]]) | ((duk_int_t) duk_hex_dectab[inp[i + 3]]); - chk |= t; - p[1] = (duk_uint8_t) t; - t = ((duk_int_t) duk_hex_dectab_shift4[inp[i + 4]]) | ((duk_int_t) duk_hex_dectab[inp[i + 5]]); - chk |= t; - p[2] = (duk_uint8_t) t; - t = ((duk_int_t) duk_hex_dectab_shift4[inp[i + 6]]) | ((duk_int_t) duk_hex_dectab[inp[i + 7]]); - chk |= t; - p[3] = (duk_uint8_t) t; - p += 4; - - /* Check if any lookup above had a negative result. */ - if (DUK_UNLIKELY(chk < 0)) { - goto type_error; - } - } - for (; i < len; i += 2) { - /* First cast to duk_int_t to sign extend, second cast to - * duk_uint_t to avoid signed left shift, and final cast to - * duk_int_t result type. - */ - t = (duk_int_t) ((((duk_uint_t) (duk_int_t) duk_hex_dectab[inp[i]]) << 4U) | - ((duk_uint_t) (duk_int_t) duk_hex_dectab[inp[i + 1]])); - if (DUK_UNLIKELY(t < 0)) { - goto type_error; - } - *p++ = (duk_uint8_t) t; - } -#else /* DUK_USE_HEX_FASTPATH */ - for (i = 0; i < len; i += 2) { - /* For invalid characters the value -1 gets extended to - * at least 16 bits. If either nybble is invalid, the - * resulting 't' will be < 0. - */ - t = (duk_int_t) ((((duk_uint_t) (duk_int_t) duk_hex_dectab[inp[i]]) << 4U) | - ((duk_uint_t) (duk_int_t) duk_hex_dectab[inp[i + 1]])); - if (DUK_UNLIKELY(t < 0)) { - goto type_error; - } - buf[i >> 1] = (duk_uint8_t) t; - } -#endif /* DUK_USE_HEX_FASTPATH */ - - duk_replace(thr, idx); - return; - -type_error: - DUK_ERROR_TYPE(thr, DUK_STR_HEX_DECODE_FAILED); - DUK_WO_NORETURN(return;); -} -#else /* DUK_USE_HEX_SUPPORT */ -DUK_EXTERNAL const char *duk_hex_encode(duk_hthread *thr, duk_idx_t idx) { - DUK_UNREF(idx); - DUK_ERROR_UNSUPPORTED(thr); - DUK_WO_NORETURN(return NULL;); -} -DUK_EXTERNAL void duk_hex_decode(duk_hthread *thr, duk_idx_t idx) { - DUK_UNREF(idx); - DUK_ERROR_UNSUPPORTED(thr); - DUK_WO_NORETURN(return;); -} -#endif /* DUK_USE_HEX_SUPPORT */ - -/* - * JSON - */ - -#if defined(DUK_USE_JSON_SUPPORT) -DUK_EXTERNAL const char *duk_json_encode(duk_hthread *thr, duk_idx_t idx) { -#if defined(DUK_USE_ASSERTIONS) - duk_idx_t top_at_entry; -#endif - const char *ret; - - DUK_ASSERT_API_ENTRY(thr); -#if defined(DUK_USE_ASSERTIONS) - top_at_entry = duk_get_top(thr); -#endif - - idx = duk_require_normalize_index(thr, idx); - duk_bi_json_stringify_helper(thr, - idx /*idx_value*/, - DUK_INVALID_INDEX /*idx_replacer*/, - DUK_INVALID_INDEX /*idx_space*/, - 0 /*flags*/); - DUK_ASSERT(duk_is_string(thr, -1)); - duk_replace(thr, idx); - ret = duk_get_string(thr, idx); - - DUK_ASSERT(duk_get_top(thr) == top_at_entry); - - return ret; -} - -DUK_EXTERNAL void duk_json_decode(duk_hthread *thr, duk_idx_t idx) { -#if defined(DUK_USE_ASSERTIONS) - duk_idx_t top_at_entry; -#endif - - DUK_ASSERT_API_ENTRY(thr); -#if defined(DUK_USE_ASSERTIONS) - top_at_entry = duk_get_top(thr); -#endif - - idx = duk_require_normalize_index(thr, idx); - duk_bi_json_parse_helper(thr, idx /*idx_value*/, DUK_INVALID_INDEX /*idx_reviver*/, 0 /*flags*/); - duk_replace(thr, idx); - - DUK_ASSERT(duk_get_top(thr) == top_at_entry); -} -#else /* DUK_USE_JSON_SUPPORT */ -DUK_EXTERNAL const char *duk_json_encode(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - DUK_UNREF(idx); - DUK_ERROR_UNSUPPORTED(thr); - DUK_WO_NORETURN(return NULL;); -} - -DUK_EXTERNAL void duk_json_decode(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - DUK_UNREF(idx); - DUK_ERROR_UNSUPPORTED(thr); - DUK_WO_NORETURN(return;); -} -#endif /* DUK_USE_JSON_SUPPORT */ -/* - * Compilation and evaluation - */ - -/* #include duk_internal.h -> already included */ - -typedef struct duk__compile_raw_args duk__compile_raw_args; -struct duk__compile_raw_args { - duk_size_t src_length; /* should be first on 64-bit platforms */ - const duk_uint8_t *src_buffer; - duk_uint_t flags; -}; - -/* Eval is just a wrapper now. */ -DUK_EXTERNAL duk_int_t duk_eval_raw(duk_hthread *thr, const char *src_buffer, duk_size_t src_length, duk_uint_t flags) { - duk_int_t rc; - - DUK_ASSERT_API_ENTRY(thr); - - /* Note: strictness is *not* inherited from the current Duktape/C. - * This would be confusing because the current strictness state - * depends on whether we're running inside a Duktape/C activation - * (= strict mode) or outside of any activation (= non-strict mode). - * See tests/api/test-eval-strictness.c for more discussion. - */ - - /* [ ... source? filename? ] (depends on flags) */ - - rc = duk_compile_raw(thr, - src_buffer, - src_length, - flags | DUK_COMPILE_EVAL); /* may be safe, or non-safe depending on flags */ - - /* [ ... closure/error ] */ - - if (rc != DUK_EXEC_SUCCESS) { - rc = DUK_EXEC_ERROR; - goto got_rc; - } - - duk_push_global_object(thr); /* explicit 'this' binding, see GH-164 */ - - if (flags & DUK_COMPILE_SAFE) { - rc = duk_pcall_method(thr, 0); - } else { - duk_call_method(thr, 0); - rc = DUK_EXEC_SUCCESS; - } - - /* [ ... result/error ] */ - -got_rc: - if (flags & DUK_COMPILE_NORESULT) { - duk_pop(thr); - } - - return rc; -} - -/* Helper which can be called both directly and with duk_safe_call(). */ -DUK_LOCAL duk_ret_t duk__do_compile(duk_hthread *thr, void *udata) { - duk__compile_raw_args *comp_args; - duk_uint_t flags; - duk_hcompfunc *h_templ; - - DUK_CTX_ASSERT_VALID(thr); - DUK_ASSERT(udata != NULL); - - /* Note: strictness is not inherited from the current Duktape/C - * context. Otherwise it would not be possible to compile - * non-strict code inside a Duktape/C activation (which is - * always strict now). See tests/api/test-eval-strictness.c - * for discussion. - */ - - /* [ ... source? filename? ] (depends on flags) */ - - comp_args = (duk__compile_raw_args *) udata; - flags = comp_args->flags; - - if (flags & DUK_COMPILE_NOFILENAME) { - /* Automatic filename: 'eval' or 'input'. */ - duk_push_hstring_stridx(thr, (flags & DUK_COMPILE_EVAL) ? DUK_STRIDX_EVAL : DUK_STRIDX_INPUT); - } - - /* [ ... source? filename ] */ - - if (!comp_args->src_buffer) { - duk_hstring *h_sourcecode; - - h_sourcecode = duk_get_hstring(thr, -2); - if ((flags & DUK_COMPILE_NOSOURCE) || /* args incorrect */ - (h_sourcecode == NULL)) { /* e.g. duk_push_string_file_raw() pushed undefined */ - DUK_ERROR_TYPE(thr, DUK_STR_NO_SOURCECODE); - DUK_WO_NORETURN(return 0;); - } - DUK_ASSERT(h_sourcecode != NULL); - comp_args->src_buffer = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_sourcecode); - comp_args->src_length = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h_sourcecode); - } - DUK_ASSERT(comp_args->src_buffer != NULL); - - if (flags & DUK_COMPILE_FUNCTION) { - flags |= DUK_COMPILE_EVAL | DUK_COMPILE_FUNCEXPR; - } - - /* [ ... source? filename ] */ - - duk_js_compile(thr, comp_args->src_buffer, comp_args->src_length, flags); - - /* [ ... source? func_template ] */ - - if (flags & DUK_COMPILE_NOSOURCE) { - ; - } else { - duk_remove_m2(thr); - } - - /* [ ... func_template ] */ - - h_templ = (duk_hcompfunc *) duk_known_hobject(thr, -1); - duk_js_push_closure(thr, - h_templ, - thr->builtins[DUK_BIDX_GLOBAL_ENV], - thr->builtins[DUK_BIDX_GLOBAL_ENV], - 1 /*add_auto_proto*/); - duk_remove_m2(thr); /* -> [ ... closure ] */ - - /* [ ... closure ] */ - - return 1; -} - -DUK_EXTERNAL duk_int_t duk_compile_raw(duk_hthread *thr, const char *src_buffer, duk_size_t src_length, duk_uint_t flags) { - duk__compile_raw_args comp_args_alloc; - duk__compile_raw_args *comp_args = &comp_args_alloc; - - DUK_ASSERT_API_ENTRY(thr); - - if ((flags & DUK_COMPILE_STRLEN) && (src_buffer != NULL)) { - /* String length is computed here to avoid multiple evaluation - * of a macro argument in the calling side. - */ - src_length = DUK_STRLEN(src_buffer); - } - - comp_args->src_buffer = (const duk_uint8_t *) src_buffer; - comp_args->src_length = src_length; - comp_args->flags = flags; - - /* [ ... source? filename? ] (depends on flags) */ - - if (flags & DUK_COMPILE_SAFE) { - duk_int_t rc; - duk_int_t nargs; - duk_int_t nrets = 1; - - /* Arguments can be: [ source? filename? &comp_args] so that - * nargs is 1 to 3. Call site encodes the correct nargs count - * directly into flags. - */ - nargs = flags & 0x07; - DUK_ASSERT(nargs == ((flags & DUK_COMPILE_NOSOURCE) ? 0 : 1) + ((flags & DUK_COMPILE_NOFILENAME) ? 0 : 1)); - rc = duk_safe_call(thr, duk__do_compile, (void *) comp_args, nargs, nrets); - - /* [ ... closure ] */ - return rc; - } - - (void) duk__do_compile(thr, (void *) comp_args); - - /* [ ... closure ] */ - return DUK_EXEC_SUCCESS; -} -/* - * Debugging related API calls - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_JSON_SUPPORT) -DUK_EXTERNAL void duk_push_context_dump(duk_hthread *thr) { - duk_idx_t idx; - duk_idx_t top; - - DUK_ASSERT_API_ENTRY(thr); - - /* We don't duk_require_stack() here now, but rely on the caller having - * enough space. - */ - - top = duk_get_top(thr); - duk_push_bare_array(thr); - for (idx = 0; idx < top; idx++) { - duk_dup(thr, idx); - duk_put_prop_index(thr, -2, (duk_uarridx_t) idx); - } - - /* XXX: conversion errors should not propagate outwards. - * Perhaps values need to be coerced individually? - */ - duk_bi_json_stringify_helper(thr, - duk_get_top_index(thr), /*idx_value*/ - DUK_INVALID_INDEX, /*idx_replacer*/ - DUK_INVALID_INDEX, /*idx_space*/ - DUK_JSON_FLAG_EXT_CUSTOM | DUK_JSON_FLAG_ASCII_ONLY | - DUK_JSON_FLAG_AVOID_KEY_QUOTES /*flags*/); - - duk_push_sprintf(thr, "ctx: top=%ld, stack=%s", (long) top, (const char *) duk_safe_to_string(thr, -1)); - duk_replace(thr, -3); /* [ ... arr jsonx(arr) res ] -> [ ... res jsonx(arr) ] */ - duk_pop(thr); - DUK_ASSERT(duk_is_string(thr, -1)); -} -#else /* DUK_USE_JSON_SUPPORT */ -DUK_EXTERNAL void duk_push_context_dump(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ERROR_UNSUPPORTED(thr); - DUK_WO_NORETURN(return;); -} -#endif /* DUK_USE_JSON_SUPPORT */ - -#if defined(DUK_USE_DEBUGGER_SUPPORT) - -DUK_EXTERNAL void duk_debugger_attach(duk_hthread *thr, - duk_debug_read_function read_cb, - duk_debug_write_function write_cb, - duk_debug_peek_function peek_cb, - duk_debug_read_flush_function read_flush_cb, - duk_debug_write_flush_function write_flush_cb, - duk_debug_request_function request_cb, - duk_debug_detached_function detached_cb, - void *udata) { - duk_heap *heap; - const char *str; - duk_size_t len; - - /* XXX: should there be an error or an automatic detach if - * already attached? - */ - - DUK_D(DUK_DPRINT("application called duk_debugger_attach()")); - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(read_cb != NULL); - DUK_ASSERT(write_cb != NULL); - /* Other callbacks are optional. */ - - heap = thr->heap; - heap->dbg_read_cb = read_cb; - heap->dbg_write_cb = write_cb; - heap->dbg_peek_cb = peek_cb; - heap->dbg_read_flush_cb = read_flush_cb; - heap->dbg_write_flush_cb = write_flush_cb; - heap->dbg_request_cb = request_cb; - heap->dbg_detached_cb = detached_cb; - heap->dbg_udata = udata; - heap->dbg_have_next_byte = 0; - - /* Start in paused state. */ - heap->dbg_processing = 0; - heap->dbg_state_dirty = 0; - heap->dbg_force_restart = 0; - heap->dbg_pause_flags = 0; - heap->dbg_pause_act = NULL; - heap->dbg_pause_startline = 0; - heap->dbg_exec_counter = 0; - heap->dbg_last_counter = 0; - heap->dbg_last_time = 0.0; - duk_debug_set_paused(heap); /* XXX: overlap with fields above */ - - /* Send version identification and flush right afterwards. Note that - * we must write raw, unframed bytes here. - */ - duk_push_sprintf(thr, - "%ld %ld %s %s\n", - (long) DUK_DEBUG_PROTOCOL_VERSION, - (long) DUK_VERSION, - (const char *) DUK_GIT_DESCRIBE, - (const char *) DUK_USE_TARGET_INFO); - str = duk_get_lstring(thr, -1, &len); - DUK_ASSERT(str != NULL); - duk_debug_write_bytes(thr, (const duk_uint8_t *) str, len); - duk_debug_write_flush(thr); - duk_pop(thr); -} - -DUK_EXTERNAL void duk_debugger_detach(duk_hthread *thr) { - DUK_D(DUK_DPRINT("application called duk_debugger_detach()")); - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(thr->heap != NULL); - - /* Can be called multiple times with no harm. */ - duk_debug_do_detach(thr->heap); -} - -DUK_EXTERNAL void duk_debugger_cooperate(duk_hthread *thr) { - duk_bool_t processed_messages; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(thr->heap != NULL); - - if (!duk_debug_is_attached(thr->heap)) { - return; - } - if (thr->callstack_curr != NULL || thr->heap->dbg_processing) { - /* Calling duk_debugger_cooperate() while Duktape is being - * called into is not supported. This is not a 100% check - * but prevents any damage in most cases. - */ - return; - } - - processed_messages = duk_debug_process_messages(thr, 1 /*no_block*/); - DUK_UNREF(processed_messages); -} - -DUK_EXTERNAL duk_bool_t duk_debugger_notify(duk_hthread *thr, duk_idx_t nvalues) { - duk_idx_t top; - duk_idx_t idx; - duk_bool_t ret = 0; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(thr->heap != NULL); - - DUK_D(DUK_DPRINT("application called duk_debugger_notify() with nvalues=%ld", (long) nvalues)); - - top = duk_get_top(thr); - if (top < nvalues) { - DUK_ERROR_RANGE(thr, "not enough stack values for notify"); - DUK_WO_NORETURN(return 0;); - } - if (duk_debug_is_attached(thr->heap)) { - duk_debug_write_notify(thr, DUK_DBG_CMD_APPNOTIFY); - for (idx = top - nvalues; idx < top; idx++) { - duk_tval *tv = DUK_GET_TVAL_POSIDX(thr, idx); - duk_debug_write_tval(thr, tv); - } - duk_debug_write_eom(thr); - - /* Return non-zero (true) if we have a good reason to believe - * the notify was delivered; if we're still attached at least - * a transport error was not indicated by the transport write - * callback. This is not a 100% guarantee of course. - */ - if (duk_debug_is_attached(thr->heap)) { - ret = 1; - } - } - duk_pop_n(thr, nvalues); - return ret; -} - -DUK_EXTERNAL void duk_debugger_pause(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(thr->heap != NULL); - - DUK_D(DUK_DPRINT("application called duk_debugger_pause()")); - - /* Treat like a debugger statement: ignore when not attached. */ - if (duk_debug_is_attached(thr->heap)) { - if (duk_debug_is_paused(thr->heap)) { - DUK_D(DUK_DPRINT("duk_debugger_pause() called when already paused; ignoring")); - } else { - duk_debug_set_paused(thr->heap); - - /* Pause on the next opcode executed. This is always safe to do even - * inside the debugger message loop: the interrupt counter will be reset - * to its proper value when the message loop exits. - */ - thr->interrupt_init = 1; - thr->interrupt_counter = 0; - } - } -} - -#else /* DUK_USE_DEBUGGER_SUPPORT */ - -DUK_EXTERNAL void duk_debugger_attach(duk_hthread *thr, - duk_debug_read_function read_cb, - duk_debug_write_function write_cb, - duk_debug_peek_function peek_cb, - duk_debug_read_flush_function read_flush_cb, - duk_debug_write_flush_function write_flush_cb, - duk_debug_request_function request_cb, - duk_debug_detached_function detached_cb, - void *udata) { - DUK_ASSERT_API_ENTRY(thr); - DUK_UNREF(read_cb); - DUK_UNREF(write_cb); - DUK_UNREF(peek_cb); - DUK_UNREF(read_flush_cb); - DUK_UNREF(write_flush_cb); - DUK_UNREF(request_cb); - DUK_UNREF(detached_cb); - DUK_UNREF(udata); - DUK_ERROR_TYPE(thr, "no debugger support"); - DUK_WO_NORETURN(return;); -} - -DUK_EXTERNAL void duk_debugger_detach(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ERROR_TYPE(thr, "no debugger support"); - DUK_WO_NORETURN(return;); -} - -DUK_EXTERNAL void duk_debugger_cooperate(duk_hthread *thr) { - /* nop */ - DUK_ASSERT_API_ENTRY(thr); - DUK_UNREF(thr); -} - -DUK_EXTERNAL duk_bool_t duk_debugger_notify(duk_hthread *thr, duk_idx_t nvalues) { - duk_idx_t top; - - DUK_ASSERT_API_ENTRY(thr); - - top = duk_get_top(thr); - if (top < nvalues) { - DUK_ERROR_RANGE_INVALID_COUNT(thr); - DUK_WO_NORETURN(return 0;); - } - - /* No debugger support, just pop values. */ - duk_pop_n(thr, nvalues); - return 0; -} - -DUK_EXTERNAL void duk_debugger_pause(duk_hthread *thr) { - /* Treat like debugger statement: nop */ - DUK_ASSERT_API_ENTRY(thr); - DUK_UNREF(thr); -} - -#endif /* DUK_USE_DEBUGGER_SUPPORT */ -/* - * Heap creation and destruction - */ - -/* #include duk_internal.h -> already included */ - -typedef struct duk_internal_thread_state duk_internal_thread_state; - -struct duk_internal_thread_state { - duk_ljstate lj; - duk_bool_t creating_error; - duk_hthread *curr_thread; - duk_uint8_t thread_state; - duk_int_t call_recursion_depth; -}; - -DUK_EXTERNAL duk_hthread *duk_create_heap(duk_alloc_function alloc_func, - duk_realloc_function realloc_func, - duk_free_function free_func, - void *heap_udata, - duk_fatal_function fatal_handler) { - duk_heap *heap = NULL; - duk_hthread *thr; - - /* Assume that either all memory funcs are NULL or non-NULL, mixed - * cases will now be unsafe. - */ - - /* XXX: just assert non-NULL values here and make caller arguments - * do the defaulting to the default implementations (smaller code)? - */ - - if (!alloc_func) { - DUK_ASSERT(realloc_func == NULL); - DUK_ASSERT(free_func == NULL); -#if defined(DUK_USE_PROVIDE_DEFAULT_ALLOC_FUNCTIONS) - alloc_func = duk_default_alloc_function; - realloc_func = duk_default_realloc_function; - free_func = duk_default_free_function; -#else - DUK_D(DUK_DPRINT("no allocation functions given and no default providers")); - return NULL; -#endif - } else { - DUK_ASSERT(realloc_func != NULL); - DUK_ASSERT(free_func != NULL); - } - - if (!fatal_handler) { - fatal_handler = duk_default_fatal_handler; - } - - DUK_ASSERT(alloc_func != NULL); - DUK_ASSERT(realloc_func != NULL); - DUK_ASSERT(free_func != NULL); - DUK_ASSERT(fatal_handler != NULL); - - heap = duk_heap_alloc(alloc_func, realloc_func, free_func, heap_udata, fatal_handler); - if (!heap) { - return NULL; - } - thr = heap->heap_thread; - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - return thr; -} - -DUK_EXTERNAL void duk_destroy_heap(duk_hthread *thr) { - duk_heap *heap; - - if (!thr) { - return; - } - DUK_ASSERT_API_ENTRY(thr); - heap = thr->heap; - DUK_ASSERT(heap != NULL); - - duk_heap_free(heap); -} - -DUK_EXTERNAL void duk_suspend(duk_hthread *thr, duk_thread_state *state) { - duk_internal_thread_state *snapshot = (duk_internal_thread_state *) (void *) state; - duk_heap *heap; - duk_ljstate *lj; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(thr->heap != NULL); - DUK_ASSERT(state != NULL); /* unvalidated */ - - /* Currently not supported when called from within a finalizer. - * If that is done, the finalizer will remain running indefinitely, - * preventing other finalizers from executing. The assert is a bit - * wider, checking that it would be OK to run pending finalizers. - */ - DUK_ASSERT(thr->heap->pf_prevent_count == 0); - - /* Currently not supported to duk_suspend() from an errCreate() - * call. - */ - DUK_ASSERT(thr->heap->creating_error == 0); - - heap = thr->heap; - lj = &heap->lj; - - duk_push_tval(thr, &lj->value1); - duk_push_tval(thr, &lj->value2); - - /* XXX: creating_error == 0 is asserted above, so no need to store. */ - duk_memcpy((void *) &snapshot->lj, (const void *) lj, sizeof(duk_ljstate)); - snapshot->creating_error = heap->creating_error; - snapshot->curr_thread = heap->curr_thread; - snapshot->thread_state = thr->state; - snapshot->call_recursion_depth = heap->call_recursion_depth; - - lj->jmpbuf_ptr = NULL; - lj->type = DUK_LJ_TYPE_UNKNOWN; - DUK_TVAL_SET_UNDEFINED(&lj->value1); - DUK_TVAL_SET_UNDEFINED(&lj->value2); - heap->creating_error = 0; - heap->curr_thread = NULL; - heap->call_recursion_depth = 0; - - thr->state = DUK_HTHREAD_STATE_INACTIVE; -} - -DUK_EXTERNAL void duk_resume(duk_hthread *thr, const duk_thread_state *state) { - const duk_internal_thread_state *snapshot = (const duk_internal_thread_state *) (const void *) state; - duk_heap *heap; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(thr->heap != NULL); - DUK_ASSERT(state != NULL); /* unvalidated */ - - /* Shouldn't be necessary if duk_suspend() is called before - * duk_resume(), but assert in case API sequence is incorrect. - */ - DUK_ASSERT(thr->heap->pf_prevent_count == 0); - DUK_ASSERT(thr->heap->creating_error == 0); - - thr->state = snapshot->thread_state; - - heap = thr->heap; - - duk_memcpy((void *) &heap->lj, (const void *) &snapshot->lj, sizeof(duk_ljstate)); - heap->creating_error = snapshot->creating_error; - heap->curr_thread = snapshot->curr_thread; - heap->call_recursion_depth = snapshot->call_recursion_depth; - - duk_pop_2(thr); -} - -/* XXX: better place for this */ -DUK_EXTERNAL void duk_set_global_object(duk_hthread *thr) { - duk_hobject *h_glob; - duk_hobject *h_prev_glob; - duk_hobjenv *h_env; - duk_hobject *h_prev_env; - - DUK_ASSERT_API_ENTRY(thr); - - DUK_D(DUK_DPRINT("replace global object with: %!T", duk_get_tval(thr, -1))); - - h_glob = duk_require_hobject(thr, -1); - DUK_ASSERT(h_glob != NULL); - - /* - * Replace global object. - */ - - h_prev_glob = thr->builtins[DUK_BIDX_GLOBAL]; - DUK_UNREF(h_prev_glob); - thr->builtins[DUK_BIDX_GLOBAL] = h_glob; - DUK_HOBJECT_INCREF(thr, h_glob); - DUK_HOBJECT_DECREF_ALLOWNULL(thr, h_prev_glob); /* side effects, in theory (referenced by global env) */ - - /* - * Replace lexical environment for global scope - * - * Create a new object environment for the global lexical scope. - * We can't just reset the _Target property of the current one, - * because the lexical scope is shared by other threads with the - * same (initial) built-ins. - */ - - h_env = duk_hobjenv_alloc(thr, DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJENV)); - DUK_ASSERT(h_env != NULL); - DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) h_env) == NULL); - - DUK_ASSERT(h_env->target == NULL); - DUK_ASSERT(h_glob != NULL); - h_env->target = h_glob; - DUK_HOBJECT_INCREF(thr, h_glob); - DUK_ASSERT(h_env->has_this == 0); - - /* [ ... new_glob ] */ - - h_prev_env = thr->builtins[DUK_BIDX_GLOBAL_ENV]; - thr->builtins[DUK_BIDX_GLOBAL_ENV] = (duk_hobject *) h_env; - DUK_HOBJECT_INCREF(thr, (duk_hobject *) h_env); - DUK_HOBJECT_DECREF_ALLOWNULL(thr, h_prev_env); /* side effects */ - DUK_UNREF(h_env); /* without refcounts */ - DUK_UNREF(h_prev_env); - - /* [ ... new_glob ] */ - - duk_pop(thr); - - /* [ ... ] */ -} -/* - * Inspection - */ - -/* #include duk_internal.h -> already included */ - -/* For footprint efficient multiple value setting: arrays are much better than - * varargs, format string with parsing is often better than string pointer arrays. - */ -DUK_LOCAL void duk__inspect_multiple_uint(duk_hthread *thr, const char *fmt, duk_int_t *vals) { - duk_int_t val; - const char *p; - const char *p_curr; - duk_size_t len; - - for (p = fmt;;) { - len = DUK_STRLEN(p); - p_curr = p; - p += len + 1; - if (len == 0) { - /* Double NUL (= empty key) terminates. */ - break; - } - val = *vals++; - if (val >= 0) { - /* Negative values are markers to skip key. */ - duk_push_string(thr, p_curr); - duk_push_int(thr, val); - duk_put_prop(thr, -3); - } - } -} - -/* Raw helper to extract internal information / statistics about a value. - * The return value is an object with properties that are version specific. - * The properties must not expose anything that would lead to security - * issues (e.g. exposing compiled function 'data' buffer might be an issue). - * Currently only counts and sizes and such are given so there shouldn't - * be security implications. - */ - -#define DUK__IDX_TYPE 0 -#define DUK__IDX_ITAG 1 -#define DUK__IDX_REFC 2 -#define DUK__IDX_HBYTES 3 -#define DUK__IDX_CLASS 4 -#define DUK__IDX_PBYTES 5 -#define DUK__IDX_ESIZE 6 -#define DUK__IDX_ENEXT 7 -#define DUK__IDX_ASIZE 8 -#define DUK__IDX_HSIZE 9 -#define DUK__IDX_BCBYTES 10 -#define DUK__IDX_DBYTES 11 -#define DUK__IDX_TSTATE 12 -#define DUK__IDX_VARIANT 13 - -DUK_EXTERNAL void duk_inspect_value(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - duk_heaphdr *h; - /* The temporary values should be in an array rather than individual - * variables which (in practice) ensures that the compiler won't map - * them to registers and emit a lot of unnecessary shuffling code. - */ - duk_int_t vals[14]; - - DUK_ASSERT_API_ENTRY(thr); - - /* Assume two's complement and set everything to -1. */ - duk_memset((void *) &vals, (int) 0xff, sizeof(vals)); - DUK_ASSERT(vals[DUK__IDX_TYPE] == -1); /* spot check one */ - - tv = duk_get_tval_or_unused(thr, idx); - h = (DUK_TVAL_IS_HEAP_ALLOCATED(tv) ? DUK_TVAL_GET_HEAPHDR(tv) : NULL); - - vals[DUK__IDX_TYPE] = duk_get_type_tval(tv); - vals[DUK__IDX_ITAG] = (duk_int_t) DUK_TVAL_GET_TAG(tv); - - duk_push_bare_object(thr); /* Invalidates 'tv'. */ - tv = NULL; - - if (h == NULL) { - goto finish; - } - duk_push_pointer(thr, (void *) h); - duk_put_prop_literal(thr, -2, "hptr"); - -#if 0 - /* Covers a lot of information, e.g. buffer and string variants. */ - duk_push_uint(thr, (duk_uint_t) DUK_HEAPHDR_GET_FLAGS(h)); - duk_put_prop_literal(thr, -2, "hflags"); -#endif - -#if defined(DUK_USE_REFERENCE_COUNTING) - vals[DUK__IDX_REFC] = (duk_int_t) DUK_HEAPHDR_GET_REFCOUNT(h); -#endif - vals[DUK__IDX_VARIANT] = 0; - - /* Heaphdr size and additional allocation size, followed by - * type specific stuff (with varying value count). - */ - switch ((duk_small_int_t) DUK_HEAPHDR_GET_TYPE(h)) { - case DUK_HTYPE_STRING: { - duk_hstring *h_str = (duk_hstring *) h; - vals[DUK__IDX_HBYTES] = (duk_int_t) (sizeof(duk_hstring) + DUK_HSTRING_GET_BYTELEN(h_str) + 1); -#if defined(DUK_USE_HSTRING_EXTDATA) - if (DUK_HSTRING_HAS_EXTDATA(h_str)) { - vals[DUK__IDX_VARIANT] = 1; - } -#endif - break; - } - case DUK_HTYPE_OBJECT: { - duk_hobject *h_obj = (duk_hobject *) h; - - /* XXX: variants here are maybe pointless; class is enough? */ - if (DUK_HOBJECT_IS_ARRAY(h_obj)) { - vals[DUK__IDX_HBYTES] = sizeof(duk_harray); - } else if (DUK_HOBJECT_IS_COMPFUNC(h_obj)) { - vals[DUK__IDX_HBYTES] = sizeof(duk_hcompfunc); - } else if (DUK_HOBJECT_IS_NATFUNC(h_obj)) { - vals[DUK__IDX_HBYTES] = sizeof(duk_hnatfunc); - } else if (DUK_HOBJECT_IS_THREAD(h_obj)) { - vals[DUK__IDX_HBYTES] = sizeof(duk_hthread); - vals[DUK__IDX_TSTATE] = ((duk_hthread *) h_obj)->state; -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) - } else if (DUK_HOBJECT_IS_BUFOBJ(h_obj)) { - vals[DUK__IDX_HBYTES] = sizeof(duk_hbufobj); - /* XXX: some size information */ -#endif - } else { - vals[DUK__IDX_HBYTES] = (duk_small_uint_t) sizeof(duk_hobject); - } - - vals[DUK__IDX_CLASS] = (duk_int_t) DUK_HOBJECT_GET_CLASS_NUMBER(h_obj); - vals[DUK__IDX_PBYTES] = (duk_int_t) DUK_HOBJECT_P_ALLOC_SIZE(h_obj); - vals[DUK__IDX_ESIZE] = (duk_int_t) DUK_HOBJECT_GET_ESIZE(h_obj); - vals[DUK__IDX_ENEXT] = (duk_int_t) DUK_HOBJECT_GET_ENEXT(h_obj); - vals[DUK__IDX_ASIZE] = (duk_int_t) DUK_HOBJECT_GET_ASIZE(h_obj); - vals[DUK__IDX_HSIZE] = (duk_int_t) DUK_HOBJECT_GET_HSIZE(h_obj); - - /* Note: e_next indicates the number of gc-reachable entries - * in the entry part, and also indicates the index where the - * next new property would be inserted. It does *not* indicate - * the number of non-NULL keys present in the object. That - * value could be counted separately but requires a pass through - * the key list. - */ - - if (DUK_HOBJECT_IS_COMPFUNC(h_obj)) { - duk_hbuffer *h_data = (duk_hbuffer *) DUK_HCOMPFUNC_GET_DATA(thr->heap, (duk_hcompfunc *) h_obj); - vals[DUK__IDX_BCBYTES] = (duk_int_t) (h_data ? DUK_HBUFFER_GET_SIZE(h_data) : 0); - } - break; - } - case DUK_HTYPE_BUFFER: { - duk_hbuffer *h_buf = (duk_hbuffer *) h; - - if (DUK_HBUFFER_HAS_DYNAMIC(h_buf)) { - if (DUK_HBUFFER_HAS_EXTERNAL(h_buf)) { - vals[DUK__IDX_VARIANT] = 2; /* buffer variant 2: external */ - vals[DUK__IDX_HBYTES] = (duk_uint_t) (sizeof(duk_hbuffer_external)); - } else { - /* When alloc_size == 0 the second allocation may not - * actually exist. - */ - vals[DUK__IDX_VARIANT] = 1; /* buffer variant 1: dynamic */ - vals[DUK__IDX_HBYTES] = (duk_uint_t) (sizeof(duk_hbuffer_dynamic)); - } - vals[DUK__IDX_DBYTES] = (duk_int_t) (DUK_HBUFFER_GET_SIZE(h_buf)); - } else { - DUK_ASSERT(vals[DUK__IDX_VARIANT] == 0); /* buffer variant 0: fixed */ - vals[DUK__IDX_HBYTES] = (duk_int_t) (sizeof(duk_hbuffer_fixed) + DUK_HBUFFER_GET_SIZE(h_buf)); - } - break; - } - } - -finish: - duk__inspect_multiple_uint(thr, - "type" - "\x00" - "itag" - "\x00" - "refc" - "\x00" - "hbytes" - "\x00" - "class" - "\x00" - "pbytes" - "\x00" - "esize" - "\x00" - "enext" - "\x00" - "asize" - "\x00" - "hsize" - "\x00" - "bcbytes" - "\x00" - "dbytes" - "\x00" - "tstate" - "\x00" - "variant" - "\x00" - "\x00", - (duk_int_t *) &vals); -} - -DUK_EXTERNAL void duk_inspect_callstack_entry(duk_hthread *thr, duk_int_t level) { - duk_activation *act; - duk_uint_fast32_t pc; - duk_uint_fast32_t line; - - DUK_ASSERT_API_ENTRY(thr); - - /* -1 = top callstack entry - * -2 = caller of level -1 - * etc - */ - act = duk_hthread_get_activation_for_level(thr, level); - if (act == NULL) { - duk_push_undefined(thr); - return; - } - duk_push_bare_object(thr); - - /* Relevant PC is just before current one because PC is - * post-incremented. This should match what error augment - * code does. - */ - pc = duk_hthread_get_act_prev_pc(thr, act); - - duk_push_tval(thr, &act->tv_func); - - duk_push_uint(thr, (duk_uint_t) pc); - duk_put_prop_stridx_short(thr, -3, DUK_STRIDX_PC); - -#if defined(DUK_USE_PC2LINE) - line = duk_hobject_pc2line_query(thr, -1, pc); -#else - line = 0; -#endif - duk_push_uint(thr, (duk_uint_t) line); - duk_put_prop_stridx_short(thr, -3, DUK_STRIDX_LINE_NUMBER); - - duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_LC_FUNCTION); - /* Providing access to e.g. act->lex_env would be dangerous: these - * internal structures must never be accessible to the application. - * Duktape relies on them having consistent data, and this consistency - * is only asserted for, not checked for. - */ -} - -/* automatic undefs */ -#undef DUK__IDX_ASIZE -#undef DUK__IDX_BCBYTES -#undef DUK__IDX_CLASS -#undef DUK__IDX_DBYTES -#undef DUK__IDX_ENEXT -#undef DUK__IDX_ESIZE -#undef DUK__IDX_HBYTES -#undef DUK__IDX_HSIZE -#undef DUK__IDX_ITAG -#undef DUK__IDX_PBYTES -#undef DUK__IDX_REFC -#undef DUK__IDX_TSTATE -#undef DUK__IDX_TYPE -#undef DUK__IDX_VARIANT -/* - * Memory calls. - */ - -/* #include duk_internal.h -> already included */ - -DUK_EXTERNAL void *duk_alloc_raw(duk_hthread *thr, duk_size_t size) { - DUK_ASSERT_API_ENTRY(thr); - - return DUK_ALLOC_RAW(thr->heap, size); -} - -DUK_EXTERNAL void duk_free_raw(duk_hthread *thr, void *ptr) { - DUK_ASSERT_API_ENTRY(thr); - - DUK_FREE_RAW(thr->heap, ptr); -} - -DUK_EXTERNAL void *duk_realloc_raw(duk_hthread *thr, void *ptr, duk_size_t size) { - DUK_ASSERT_API_ENTRY(thr); - - return DUK_REALLOC_RAW(thr->heap, ptr, size); -} - -DUK_EXTERNAL void *duk_alloc(duk_hthread *thr, duk_size_t size) { - DUK_ASSERT_API_ENTRY(thr); - - return DUK_ALLOC(thr->heap, size); -} - -DUK_EXTERNAL void duk_free(duk_hthread *thr, void *ptr) { - DUK_ASSERT_API_ENTRY(thr); - - DUK_FREE_CHECKED(thr, ptr); -} - -DUK_EXTERNAL void *duk_realloc(duk_hthread *thr, void *ptr, duk_size_t size) { - DUK_ASSERT_API_ENTRY(thr); - - /* - * Note: since this is an exposed API call, there should be - * no way a mark-and-sweep could have a side effect on the - * memory allocation behind 'ptr'; the pointer should never - * be something that Duktape wants to change. - * - * Thus, no need to use DUK_REALLOC_INDIRECT (and we don't - * have the storage location here anyway). - */ - - return DUK_REALLOC(thr->heap, ptr, size); -} - -DUK_EXTERNAL void duk_get_memory_functions(duk_hthread *thr, duk_memory_functions *out_funcs) { - duk_heap *heap; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(out_funcs != NULL); - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - - heap = thr->heap; - out_funcs->alloc_func = heap->alloc_func; - out_funcs->realloc_func = heap->realloc_func; - out_funcs->free_func = heap->free_func; - out_funcs->udata = heap->heap_udata; -} - -DUK_EXTERNAL void duk_gc(duk_hthread *thr, duk_uint_t flags) { - duk_heap *heap; - duk_small_uint_t ms_flags; - - DUK_ASSERT_API_ENTRY(thr); - heap = thr->heap; - DUK_ASSERT(heap != NULL); - - DUK_D(DUK_DPRINT("mark-and-sweep requested by application")); - DUK_ASSERT(DUK_GC_COMPACT == DUK_MS_FLAG_EMERGENCY); /* Compact flag is 1:1 with emergency flag which forces compaction. */ - ms_flags = (duk_small_uint_t) flags; - duk_heap_mark_and_sweep(heap, ms_flags); -} -/* - * Object handling: property access and other support functions. - */ - -/* #include duk_internal.h -> already included */ - -/* - * Property handling - * - * The API exposes only the most common property handling functions. - * The caller can invoke ECMAScript built-ins for full control (e.g. - * defineProperty, getOwnPropertyDescriptor). - */ - -DUK_EXTERNAL duk_bool_t duk_get_prop(duk_hthread *thr, duk_idx_t obj_idx) { - duk_tval *tv_obj; - duk_tval *tv_key; - duk_bool_t rc; - - DUK_ASSERT_API_ENTRY(thr); - - /* Note: copying tv_obj and tv_key to locals to shield against a valstack - * resize is not necessary for a property get right now. - */ - - tv_obj = duk_require_tval(thr, obj_idx); - tv_key = duk_require_tval(thr, -1); - - rc = duk_hobject_getprop(thr, tv_obj, tv_key); - DUK_ASSERT(rc == 0 || rc == 1); - /* a value is left on stack regardless of rc */ - - duk_remove_m2(thr); /* remove key */ - DUK_ASSERT(duk_is_undefined(thr, -1) || rc == 1); - return rc; /* 1 if property found, 0 otherwise */ -} - -DUK_EXTERNAL duk_bool_t duk_get_prop_string(duk_hthread *thr, duk_idx_t obj_idx, const char *key) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(key != NULL); - - obj_idx = duk_require_normalize_index(thr, obj_idx); - (void) duk_push_string(thr, key); - return duk_get_prop(thr, obj_idx); -} - -DUK_EXTERNAL duk_bool_t duk_get_prop_lstring(duk_hthread *thr, duk_idx_t obj_idx, const char *key, duk_size_t key_len) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(key != NULL); - - obj_idx = duk_require_normalize_index(thr, obj_idx); - (void) duk_push_lstring(thr, key, key_len); - return duk_get_prop(thr, obj_idx); -} - -#if !defined(DUK_USE_PREFER_SIZE) -DUK_EXTERNAL duk_bool_t duk_get_prop_literal_raw(duk_hthread *thr, duk_idx_t obj_idx, const char *key, duk_size_t key_len) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(key != NULL); - DUK_ASSERT(key[key_len] == (char) 0); - - obj_idx = duk_require_normalize_index(thr, obj_idx); - (void) duk_push_literal_raw(thr, key, key_len); - return duk_get_prop(thr, obj_idx); -} -#endif - -DUK_EXTERNAL duk_bool_t duk_get_prop_index(duk_hthread *thr, duk_idx_t obj_idx, duk_uarridx_t arr_idx) { - DUK_ASSERT_API_ENTRY(thr); - - obj_idx = duk_require_normalize_index(thr, obj_idx); - duk_push_uarridx(thr, arr_idx); - return duk_get_prop(thr, obj_idx); -} - -DUK_EXTERNAL duk_bool_t duk_get_prop_heapptr(duk_hthread *thr, duk_idx_t obj_idx, void *ptr) { - DUK_ASSERT_API_ENTRY(thr); - - obj_idx = duk_require_normalize_index(thr, obj_idx); - (void) duk_push_heapptr(thr, ptr); /* NULL -> 'undefined' */ - return duk_get_prop(thr, obj_idx); -} - -DUK_INTERNAL duk_bool_t duk_get_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT_STRIDX_VALID(stridx); - - obj_idx = duk_require_normalize_index(thr, obj_idx); - (void) duk_push_hstring(thr, DUK_HTHREAD_GET_STRING(thr, stridx)); - return duk_get_prop(thr, obj_idx); -} - -DUK_INTERNAL duk_bool_t duk_get_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args) { - return duk_get_prop_stridx(thr, (duk_idx_t) (duk_int16_t) (packed_args >> 16), (duk_small_uint_t) (packed_args & 0xffffUL)); -} - -DUK_INTERNAL duk_bool_t duk_get_prop_stridx_boolean(duk_hthread *thr, - duk_idx_t obj_idx, - duk_small_uint_t stridx, - duk_bool_t *out_has_prop) { - duk_bool_t rc; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT_STRIDX_VALID(stridx); - - rc = duk_get_prop_stridx(thr, obj_idx, stridx); - if (out_has_prop) { - *out_has_prop = rc; - } - return duk_to_boolean_top_pop(thr); -} - -/* This get variant is for internal use, it differs from standard - * duk_get_prop() in that: - * - Object argument must be an object (primitive values not supported). - * - Key argument must be a string (no coercion). - * - Only own properties are checked (no inheritance). Only "entry part" - * properties are checked (not array index properties). - * - Property must be a plain data property, not a getter. - * - Proxy traps are not triggered. - */ -DUK_INTERNAL duk_bool_t duk_xget_owndataprop(duk_hthread *thr, duk_idx_t obj_idx) { - duk_hobject *h_obj; - duk_hstring *h_key; - duk_tval *tv_val; - - DUK_ASSERT_API_ENTRY(thr); - - /* Note: copying tv_obj and tv_key to locals to shield against a valstack - * resize is not necessary for a property get right now. - */ - - h_obj = duk_get_hobject(thr, obj_idx); - if (h_obj == NULL) { - return 0; - } - h_key = duk_require_hstring(thr, -1); - - tv_val = duk_hobject_find_entry_tval_ptr(thr->heap, h_obj, h_key); - if (tv_val == NULL) { - return 0; - } - - duk_push_tval(thr, tv_val); - duk_remove_m2(thr); /* remove key */ - - return 1; -} - -DUK_INTERNAL duk_bool_t duk_xget_owndataprop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT_STRIDX_VALID(stridx); - - obj_idx = duk_require_normalize_index(thr, obj_idx); - (void) duk_push_hstring(thr, DUK_HTHREAD_GET_STRING(thr, stridx)); - return duk_xget_owndataprop(thr, obj_idx); -} - -DUK_INTERNAL duk_bool_t duk_xget_owndataprop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args) { - return duk_xget_owndataprop_stridx(thr, - (duk_idx_t) (duk_int16_t) (packed_args >> 16), - (duk_small_uint_t) (packed_args & 0xffffUL)); -} - -DUK_LOCAL duk_bool_t duk__put_prop_shared(duk_hthread *thr, duk_idx_t obj_idx, duk_idx_t idx_key) { - duk_tval *tv_obj; - duk_tval *tv_key; - duk_tval *tv_val; - duk_bool_t throw_flag; - duk_bool_t rc; - - /* Note: copying tv_obj and tv_key to locals to shield against a valstack - * resize is not necessary for a property put right now (putprop protects - * against it internally). - */ - - /* Key and value indices are either (-2, -1) or (-1, -2). Given idx_key, - * idx_val is always (idx_key ^ 0x01). - */ - DUK_ASSERT((idx_key == -2 && (idx_key ^ 1) == -1) || (idx_key == -1 && (idx_key ^ 1) == -2)); - /* XXX: Direct access; faster validation. */ - tv_obj = duk_require_tval(thr, obj_idx); - tv_key = duk_require_tval(thr, idx_key); - tv_val = duk_require_tval(thr, idx_key ^ 1); - throw_flag = duk_is_strict_call(thr); - - rc = duk_hobject_putprop(thr, tv_obj, tv_key, tv_val, throw_flag); - DUK_ASSERT(rc == 0 || rc == 1); - - duk_pop_2(thr); /* remove key and value */ - return rc; /* 1 if property found, 0 otherwise */ -} - -DUK_EXTERNAL duk_bool_t duk_put_prop(duk_hthread *thr, duk_idx_t obj_idx) { - DUK_ASSERT_API_ENTRY(thr); - return duk__put_prop_shared(thr, obj_idx, -2); -} - -DUK_EXTERNAL duk_bool_t duk_put_prop_string(duk_hthread *thr, duk_idx_t obj_idx, const char *key) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(key != NULL); - - /* Careful here and with other duk_put_prop_xxx() helpers: the - * target object and the property value may be in the same value - * stack slot (unusual, but still conceptually clear). - */ - obj_idx = duk_normalize_index(thr, obj_idx); - (void) duk_push_string(thr, key); - return duk__put_prop_shared(thr, obj_idx, -1); -} - -DUK_EXTERNAL duk_bool_t duk_put_prop_lstring(duk_hthread *thr, duk_idx_t obj_idx, const char *key, duk_size_t key_len) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(key != NULL); - - obj_idx = duk_normalize_index(thr, obj_idx); - (void) duk_push_lstring(thr, key, key_len); - return duk__put_prop_shared(thr, obj_idx, -1); -} - -#if !defined(DUK_USE_PREFER_SIZE) -DUK_EXTERNAL duk_bool_t duk_put_prop_literal_raw(duk_hthread *thr, duk_idx_t obj_idx, const char *key, duk_size_t key_len) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(key != NULL); - DUK_ASSERT(key[key_len] == (char) 0); - - obj_idx = duk_normalize_index(thr, obj_idx); - (void) duk_push_literal_raw(thr, key, key_len); - return duk__put_prop_shared(thr, obj_idx, -1); -} -#endif - -DUK_EXTERNAL duk_bool_t duk_put_prop_index(duk_hthread *thr, duk_idx_t obj_idx, duk_uarridx_t arr_idx) { - DUK_ASSERT_API_ENTRY(thr); - - obj_idx = duk_require_normalize_index(thr, obj_idx); - duk_push_uarridx(thr, arr_idx); - return duk__put_prop_shared(thr, obj_idx, -1); -} - -DUK_EXTERNAL duk_bool_t duk_put_prop_heapptr(duk_hthread *thr, duk_idx_t obj_idx, void *ptr) { - DUK_ASSERT_API_ENTRY(thr); - - obj_idx = duk_require_normalize_index(thr, obj_idx); - (void) duk_push_heapptr(thr, ptr); /* NULL -> 'undefined' */ - return duk__put_prop_shared(thr, obj_idx, -1); -} - -DUK_INTERNAL duk_bool_t duk_put_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT_STRIDX_VALID(stridx); - - obj_idx = duk_require_normalize_index(thr, obj_idx); - duk_push_hstring(thr, DUK_HTHREAD_GET_STRING(thr, stridx)); - return duk__put_prop_shared(thr, obj_idx, -1); -} - -DUK_INTERNAL duk_bool_t duk_put_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args) { - return duk_put_prop_stridx(thr, (duk_idx_t) (duk_int16_t) (packed_args >> 16), (duk_small_uint_t) (packed_args & 0xffffUL)); -} - -DUK_EXTERNAL duk_bool_t duk_del_prop(duk_hthread *thr, duk_idx_t obj_idx) { - duk_tval *tv_obj; - duk_tval *tv_key; - duk_bool_t throw_flag; - duk_bool_t rc; - - DUK_ASSERT_API_ENTRY(thr); - - /* Note: copying tv_obj and tv_key to locals to shield against a valstack - * resize is not necessary for a property delete right now. - */ - - tv_obj = duk_require_tval(thr, obj_idx); - tv_key = duk_require_tval(thr, -1); - throw_flag = duk_is_strict_call(thr); - - rc = duk_hobject_delprop(thr, tv_obj, tv_key, throw_flag); - DUK_ASSERT(rc == 0 || rc == 1); - - duk_pop(thr); /* remove key */ - return rc; -} - -DUK_EXTERNAL duk_bool_t duk_del_prop_string(duk_hthread *thr, duk_idx_t obj_idx, const char *key) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(key != NULL); - - obj_idx = duk_require_normalize_index(thr, obj_idx); - (void) duk_push_string(thr, key); - return duk_del_prop(thr, obj_idx); -} - -DUK_EXTERNAL duk_bool_t duk_del_prop_lstring(duk_hthread *thr, duk_idx_t obj_idx, const char *key, duk_size_t key_len) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(key != NULL); - - obj_idx = duk_require_normalize_index(thr, obj_idx); - (void) duk_push_lstring(thr, key, key_len); - return duk_del_prop(thr, obj_idx); -} - -#if !defined(DUK_USE_PREFER_SIZE) -DUK_EXTERNAL duk_bool_t duk_del_prop_literal_raw(duk_hthread *thr, duk_idx_t obj_idx, const char *key, duk_size_t key_len) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(key != NULL); - DUK_ASSERT(key[key_len] == (char) 0); - - obj_idx = duk_require_normalize_index(thr, obj_idx); - (void) duk_push_literal_raw(thr, key, key_len); - return duk_del_prop(thr, obj_idx); -} -#endif - -DUK_EXTERNAL duk_bool_t duk_del_prop_index(duk_hthread *thr, duk_idx_t obj_idx, duk_uarridx_t arr_idx) { - DUK_ASSERT_API_ENTRY(thr); - - obj_idx = duk_require_normalize_index(thr, obj_idx); - duk_push_uarridx(thr, arr_idx); - return duk_del_prop(thr, obj_idx); -} - -DUK_EXTERNAL duk_bool_t duk_del_prop_heapptr(duk_hthread *thr, duk_idx_t obj_idx, void *ptr) { - DUK_ASSERT_API_ENTRY(thr); - - obj_idx = duk_require_normalize_index(thr, obj_idx); - (void) duk_push_heapptr(thr, ptr); /* NULL -> 'undefined' */ - return duk_del_prop(thr, obj_idx); -} - -DUK_INTERNAL duk_bool_t duk_del_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT_STRIDX_VALID(stridx); - - obj_idx = duk_require_normalize_index(thr, obj_idx); - duk_push_hstring(thr, DUK_HTHREAD_GET_STRING(thr, stridx)); - return duk_del_prop(thr, obj_idx); -} - -#if 0 -DUK_INTERNAL duk_bool_t duk_del_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args) { - return duk_del_prop_stridx(thr, (duk_idx_t) (duk_int16_t) (packed_args >> 16), - (duk_small_uint_t) (packed_args & 0xffffUL)); -} -#endif - -DUK_EXTERNAL duk_bool_t duk_has_prop(duk_hthread *thr, duk_idx_t obj_idx) { - duk_tval *tv_obj; - duk_tval *tv_key; - duk_bool_t rc; - - DUK_ASSERT_API_ENTRY(thr); - - /* Note: copying tv_obj and tv_key to locals to shield against a valstack - * resize is not necessary for a property existence check right now. - */ - - tv_obj = duk_require_tval(thr, obj_idx); - tv_key = duk_require_tval(thr, -1); - - rc = duk_hobject_hasprop(thr, tv_obj, tv_key); - DUK_ASSERT(rc == 0 || rc == 1); - - duk_pop(thr); /* remove key */ - return rc; /* 1 if property found, 0 otherwise */ -} - -DUK_EXTERNAL duk_bool_t duk_has_prop_string(duk_hthread *thr, duk_idx_t obj_idx, const char *key) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(key != NULL); - - obj_idx = duk_require_normalize_index(thr, obj_idx); - (void) duk_push_string(thr, key); - return duk_has_prop(thr, obj_idx); -} - -DUK_EXTERNAL duk_bool_t duk_has_prop_lstring(duk_hthread *thr, duk_idx_t obj_idx, const char *key, duk_size_t key_len) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(key != NULL); - - obj_idx = duk_require_normalize_index(thr, obj_idx); - (void) duk_push_lstring(thr, key, key_len); - return duk_has_prop(thr, obj_idx); -} - -#if !defined(DUK_USE_PREFER_SIZE) -DUK_EXTERNAL duk_bool_t duk_has_prop_literal_raw(duk_hthread *thr, duk_idx_t obj_idx, const char *key, duk_size_t key_len) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(key != NULL); - DUK_ASSERT(key[key_len] == (char) 0); - - obj_idx = duk_require_normalize_index(thr, obj_idx); - (void) duk_push_literal_raw(thr, key, key_len); - return duk_has_prop(thr, obj_idx); -} -#endif - -DUK_EXTERNAL duk_bool_t duk_has_prop_index(duk_hthread *thr, duk_idx_t obj_idx, duk_uarridx_t arr_idx) { - DUK_ASSERT_API_ENTRY(thr); - - obj_idx = duk_require_normalize_index(thr, obj_idx); - duk_push_uarridx(thr, arr_idx); - return duk_has_prop(thr, obj_idx); -} - -DUK_EXTERNAL duk_bool_t duk_has_prop_heapptr(duk_hthread *thr, duk_idx_t obj_idx, void *ptr) { - DUK_ASSERT_API_ENTRY(thr); - - obj_idx = duk_require_normalize_index(thr, obj_idx); - (void) duk_push_heapptr(thr, ptr); /* NULL -> 'undefined' */ - return duk_has_prop(thr, obj_idx); -} - -DUK_INTERNAL duk_bool_t duk_has_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT_STRIDX_VALID(stridx); - - obj_idx = duk_require_normalize_index(thr, obj_idx); - duk_push_hstring(thr, DUK_HTHREAD_GET_STRING(thr, stridx)); - return duk_has_prop(thr, obj_idx); -} - -#if 0 -DUK_INTERNAL duk_bool_t duk_has_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args) { - return duk_has_prop_stridx(thr, (duk_idx_t) (duk_int16_t) (packed_args >> 16), - (duk_small_uint_t) (packed_args & 0xffffUL)); -} -#endif - -/* Define own property without inheritance lookups and such. This differs from - * [[DefineOwnProperty]] because special behaviors (like Array 'length') are - * not invoked by this method. The caller must be careful to invoke any such - * behaviors if necessary. - */ -DUK_INTERNAL void duk_xdef_prop(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t desc_flags) { - duk_hobject *obj; - duk_hstring *key; - - DUK_ASSERT_API_ENTRY(thr); - - obj = duk_require_hobject(thr, obj_idx); - DUK_ASSERT(obj != NULL); - key = duk_to_property_key_hstring(thr, -2); - DUK_ASSERT(key != NULL); - DUK_ASSERT(duk_require_tval(thr, -1) != NULL); - - duk_hobject_define_property_internal(thr, obj, key, desc_flags); - - duk_pop(thr); /* pop key */ -} - -DUK_INTERNAL void duk_xdef_prop_index(duk_hthread *thr, duk_idx_t obj_idx, duk_uarridx_t arr_idx, duk_small_uint_t desc_flags) { - duk_hobject *obj; - - DUK_ASSERT_API_ENTRY(thr); - - obj = duk_require_hobject(thr, obj_idx); - DUK_ASSERT(obj != NULL); - - duk_hobject_define_property_internal_arridx(thr, obj, arr_idx, desc_flags); - /* value popped by call */ -} - -DUK_INTERNAL void duk_xdef_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx, duk_small_uint_t desc_flags) { - duk_hobject *obj; - duk_hstring *key; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT_STRIDX_VALID(stridx); - - obj = duk_require_hobject(thr, obj_idx); - DUK_ASSERT(obj != NULL); - key = DUK_HTHREAD_GET_STRING(thr, stridx); - DUK_ASSERT(key != NULL); - DUK_ASSERT(duk_require_tval(thr, -1) != NULL); - - duk_hobject_define_property_internal(thr, obj, key, desc_flags); - /* value popped by call */ -} - -DUK_INTERNAL void duk_xdef_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args) { - duk_xdef_prop_stridx(thr, - (duk_idx_t) (duk_int8_t) (packed_args >> 24), - (duk_small_uint_t) (packed_args >> 8) & 0xffffUL, - (duk_small_uint_t) (packed_args & 0xffL)); -} - -#if 0 /*unused*/ -DUK_INTERNAL void duk_xdef_prop_stridx_builtin(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx, duk_small_int_t builtin_idx, duk_small_uint_t desc_flags) { - duk_hobject *obj; - duk_hstring *key; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT_STRIDX_VALID(stridx); - DUK_ASSERT_BIDX_VALID(builtin_idx); - - obj = duk_require_hobject(thr, obj_idx); - DUK_ASSERT(obj != NULL); - key = DUK_HTHREAD_GET_STRING(thr, stridx); - DUK_ASSERT(key != NULL); - - duk_push_hobject(thr, thr->builtins[builtin_idx]); - duk_hobject_define_property_internal(thr, obj, key, desc_flags); - /* value popped by call */ -} -#endif - -/* This is a rare property helper; it sets the global thrower (E5 Section 13.2.3) - * setter/getter into an object property. This is needed by the 'arguments' - * object creation code, function instance creation code, and Function.prototype.bind(). - */ - -DUK_INTERNAL void duk_xdef_prop_stridx_thrower(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx) { - DUK_ASSERT_API_ENTRY(thr); - - obj_idx = duk_require_normalize_index(thr, obj_idx); - duk_push_hstring_stridx(thr, stridx); - duk_push_hobject_bidx(thr, DUK_BIDX_TYPE_ERROR_THROWER); - duk_dup_top(thr); - duk_def_prop(thr, obj_idx, DUK_DEFPROP_HAVE_SETTER | DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_FORCE); /* attributes always 0 */ -} - -/* Object.getOwnPropertyDescriptor() equivalent C binding. */ -DUK_EXTERNAL void duk_get_prop_desc(duk_hthread *thr, duk_idx_t obj_idx, duk_uint_t flags) { - DUK_ASSERT_API_ENTRY(thr); - DUK_UNREF(flags); /* no flags defined yet */ - - duk_hobject_object_get_own_property_descriptor(thr, obj_idx); /* [ ... key ] -> [ ... desc ] */ -} - -/* Object.defineProperty() equivalent C binding. */ -DUK_EXTERNAL void duk_def_prop(duk_hthread *thr, duk_idx_t obj_idx, duk_uint_t flags) { - duk_idx_t idx_base; - duk_hobject *obj; - duk_hstring *key; - duk_idx_t idx_value; - duk_hobject *get; - duk_hobject *set; - duk_uint_t is_data_desc; - duk_uint_t is_acc_desc; - - DUK_ASSERT_API_ENTRY(thr); - - obj = duk_require_hobject(thr, obj_idx); - - is_data_desc = flags & (DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_HAVE_WRITABLE); - is_acc_desc = flags & (DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER); - if (is_data_desc && is_acc_desc) { - /* "Have" flags must not be conflicting so that they would - * apply to both a plain property and an accessor at the same - * time. - */ - goto fail_invalid_desc; - } - - idx_base = duk_get_top_index(thr); - if (flags & DUK_DEFPROP_HAVE_SETTER) { - duk_require_type_mask(thr, idx_base, DUK_TYPE_MASK_UNDEFINED | DUK_TYPE_MASK_OBJECT | DUK_TYPE_MASK_LIGHTFUNC); - set = duk_get_hobject_promote_lfunc(thr, idx_base); - if (set != NULL && !DUK_HOBJECT_IS_CALLABLE(set)) { - goto fail_not_callable; - } - idx_base--; - } else { - set = NULL; - } - if (flags & DUK_DEFPROP_HAVE_GETTER) { - duk_require_type_mask(thr, idx_base, DUK_TYPE_MASK_UNDEFINED | DUK_TYPE_MASK_OBJECT | DUK_TYPE_MASK_LIGHTFUNC); - get = duk_get_hobject_promote_lfunc(thr, idx_base); - if (get != NULL && !DUK_HOBJECT_IS_CALLABLE(get)) { - goto fail_not_callable; - } - idx_base--; - } else { - get = NULL; - } - if (flags & DUK_DEFPROP_HAVE_VALUE) { - idx_value = idx_base; - idx_base--; - } else { - idx_value = (duk_idx_t) -1; - } - key = duk_to_property_key_hstring(thr, idx_base); - DUK_ASSERT(key != NULL); - - duk_require_valid_index(thr, idx_base); - - duk_hobject_define_property_helper(thr, flags /*defprop_flags*/, obj, key, idx_value, get, set, 1 /*throw_flag*/); - - /* Clean up stack */ - - duk_set_top(thr, idx_base); - - /* [ ... obj ... ] */ - - return; - -fail_invalid_desc: - DUK_ERROR_TYPE(thr, DUK_STR_INVALID_DESCRIPTOR); - DUK_WO_NORETURN(return;); - -fail_not_callable: - DUK_ERROR_TYPE(thr, DUK_STR_NOT_CALLABLE); - DUK_WO_NORETURN(return;); -} - -/* - * Object related - */ - -DUK_EXTERNAL void duk_compact(duk_hthread *thr, duk_idx_t obj_idx) { - duk_hobject *obj; - - DUK_ASSERT_API_ENTRY(thr); - - obj = duk_get_hobject(thr, obj_idx); - if (obj) { - /* Note: this may fail, caller should protect the call if necessary */ - duk_hobject_compact_props(thr, obj); - } -} - -DUK_INTERNAL void duk_compact_m1(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - - duk_compact(thr, -1); -} - -/* XXX: the duk_hobject_enum.c stack APIs should be reworked */ - -DUK_EXTERNAL void duk_enum(duk_hthread *thr, duk_idx_t obj_idx, duk_uint_t enum_flags) { - DUK_ASSERT_API_ENTRY(thr); - - duk_dup(thr, obj_idx); - duk_require_hobject_promote_mask(thr, -1, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER); - duk_hobject_enumerator_create(thr, enum_flags); /* [target] -> [enum] */ -} - -DUK_EXTERNAL duk_bool_t duk_next(duk_hthread *thr, duk_idx_t enum_index, duk_bool_t get_value) { - DUK_ASSERT_API_ENTRY(thr); - - duk_require_hobject(thr, enum_index); - duk_dup(thr, enum_index); - return duk_hobject_enumerator_next(thr, get_value); -} - -DUK_INTERNAL void duk_seal_freeze_raw(duk_hthread *thr, duk_idx_t obj_idx, duk_bool_t is_freeze) { - duk_tval *tv; - duk_hobject *h; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_require_tval(thr, obj_idx); - DUK_ASSERT(tv != NULL); - - /* Seal/freeze are quite rare in practice so it'd be nice to get the - * correct behavior simply via automatic promotion (at the cost of some - * memory churn). However, the promoted objects don't behave the same, - * e.g. promoted lightfuncs are extensible. - */ - - switch (DUK_TVAL_GET_TAG(tv)) { - case DUK_TAG_BUFFER: - /* Plain buffer: already sealed, but not frozen (and can't be frozen - * because index properties can't be made non-writable. - */ - if (is_freeze) { - goto fail_cannot_freeze; - } - break; - case DUK_TAG_LIGHTFUNC: - /* Lightfunc: already sealed and frozen, success. */ - break; - case DUK_TAG_OBJECT: - h = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - if (is_freeze && DUK_HOBJECT_IS_BUFOBJ(h)) { - /* Buffer objects cannot be frozen because there's no internal - * support for making virtual array indices non-writable. - */ - DUK_DD(DUK_DDPRINT("cannot freeze a buffer object")); - goto fail_cannot_freeze; - } - duk_hobject_object_seal_freeze_helper(thr, h, is_freeze); - - /* Sealed and frozen objects cannot gain any more properties, - * so this is a good time to compact them. - */ - duk_hobject_compact_props(thr, h); - break; - default: - /* ES2015 Sections 19.1.2.5, 19.1.2.17 */ - break; - } - return; - -fail_cannot_freeze: - DUK_ERROR_TYPE_INVALID_ARGS(thr); /* XXX: proper error message */ - DUK_WO_NORETURN(return;); -} - -DUK_EXTERNAL void duk_seal(duk_hthread *thr, duk_idx_t obj_idx) { - DUK_ASSERT_API_ENTRY(thr); - - duk_seal_freeze_raw(thr, obj_idx, 0 /*is_freeze*/); -} - -DUK_EXTERNAL void duk_freeze(duk_hthread *thr, duk_idx_t obj_idx) { - DUK_ASSERT_API_ENTRY(thr); - - duk_seal_freeze_raw(thr, obj_idx, 1 /*is_freeze*/); -} - -/* - * Helpers for writing multiple properties - */ - -DUK_EXTERNAL void duk_put_function_list(duk_hthread *thr, duk_idx_t obj_idx, const duk_function_list_entry *funcs) { - const duk_function_list_entry *ent = funcs; - - DUK_ASSERT_API_ENTRY(thr); - - obj_idx = duk_require_normalize_index(thr, obj_idx); - if (ent != NULL) { - while (ent->key != NULL) { - duk_push_c_function(thr, ent->value, ent->nargs); - duk_put_prop_string(thr, obj_idx, ent->key); - ent++; - } - } -} - -DUK_EXTERNAL void duk_put_number_list(duk_hthread *thr, duk_idx_t obj_idx, const duk_number_list_entry *numbers) { - const duk_number_list_entry *ent = numbers; - duk_tval *tv; - - DUK_ASSERT_API_ENTRY(thr); - - obj_idx = duk_require_normalize_index(thr, obj_idx); - if (ent != NULL) { - while (ent->key != NULL) { - tv = thr->valstack_top++; - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv)); /* value stack init policy */ - DUK_TVAL_SET_NUMBER_CHKFAST_SLOW(tv, ent->value); /* no need for decref/incref */ - duk_put_prop_string(thr, obj_idx, ent->key); - ent++; - } - } -} - -/* - * Shortcut for accessing global object properties - */ - -DUK_EXTERNAL duk_bool_t duk_get_global_string(duk_hthread *thr, const char *key) { - duk_bool_t ret; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL); - - /* XXX: direct implementation */ - - duk_push_hobject(thr, thr->builtins[DUK_BIDX_GLOBAL]); - ret = duk_get_prop_string(thr, -1, key); - duk_remove_m2(thr); - return ret; -} - -DUK_EXTERNAL duk_bool_t duk_get_global_lstring(duk_hthread *thr, const char *key, duk_size_t key_len) { - duk_bool_t ret; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL); - - /* XXX: direct implementation */ - - duk_push_hobject(thr, thr->builtins[DUK_BIDX_GLOBAL]); - ret = duk_get_prop_lstring(thr, -1, key, key_len); - duk_remove_m2(thr); - return ret; -} - -#if !defined(DUK_USE_PREFER_SIZE) -DUK_EXTERNAL duk_bool_t duk_get_global_literal_raw(duk_hthread *thr, const char *key, duk_size_t key_len) { - duk_bool_t ret; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL); - DUK_ASSERT(key[key_len] == (char) 0); - - /* XXX: direct implementation */ - - duk_push_hobject(thr, thr->builtins[DUK_BIDX_GLOBAL]); - ret = duk_get_prop_literal_raw(thr, -1, key, key_len); - duk_remove_m2(thr); - return ret; -} -#endif - -DUK_EXTERNAL duk_bool_t duk_get_global_heapptr(duk_hthread *thr, void *ptr) { - duk_bool_t ret; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL); - - /* XXX: direct implementation */ - - duk_push_hobject(thr, thr->builtins[DUK_BIDX_GLOBAL]); - ret = duk_get_prop_heapptr(thr, -1, ptr); - duk_remove_m2(thr); - return ret; -} - -DUK_EXTERNAL duk_bool_t duk_put_global_string(duk_hthread *thr, const char *key) { - duk_bool_t ret; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL); - - /* XXX: direct implementation */ - - duk_push_hobject(thr, thr->builtins[DUK_BIDX_GLOBAL]); - duk_insert(thr, -2); - ret = duk_put_prop_string(thr, -2, key); /* [ ... global val ] -> [ ... global ] */ - duk_pop(thr); - return ret; -} - -DUK_EXTERNAL duk_bool_t duk_put_global_lstring(duk_hthread *thr, const char *key, duk_size_t key_len) { - duk_bool_t ret; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL); - - /* XXX: direct implementation */ - - duk_push_hobject(thr, thr->builtins[DUK_BIDX_GLOBAL]); - duk_insert(thr, -2); - ret = duk_put_prop_lstring(thr, -2, key, key_len); /* [ ... global val ] -> [ ... global ] */ - duk_pop(thr); - return ret; -} - -#if !defined(DUK_USE_PREFER_SIZE) -DUK_EXTERNAL duk_bool_t duk_put_global_literal_raw(duk_hthread *thr, const char *key, duk_size_t key_len) { - duk_bool_t ret; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL); - DUK_ASSERT(key[key_len] == (char) 0); - - /* XXX: direct implementation */ - - duk_push_hobject(thr, thr->builtins[DUK_BIDX_GLOBAL]); - duk_insert(thr, -2); - ret = duk_put_prop_literal_raw(thr, -2, key, key_len); /* [ ... global val ] -> [ ... global ] */ - duk_pop(thr); - return ret; -} -#endif - -DUK_EXTERNAL duk_bool_t duk_put_global_heapptr(duk_hthread *thr, void *ptr) { - duk_bool_t ret; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL); - - /* XXX: direct implementation */ - - duk_push_hobject(thr, thr->builtins[DUK_BIDX_GLOBAL]); - duk_insert(thr, -2); - ret = duk_put_prop_heapptr(thr, -2, ptr); /* [ ... global val ] -> [ ... global ] */ - duk_pop(thr); - return ret; -} - -/* - * ES2015 GetMethod() - */ - -DUK_INTERNAL duk_bool_t duk_get_method_stridx(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t stridx) { - (void) duk_get_prop_stridx(thr, idx, stridx); - if (duk_is_null_or_undefined(thr, -1)) { - duk_pop_nodecref_unsafe(thr); - return 0; - } - if (!duk_is_callable(thr, -1)) { - DUK_ERROR_TYPE(thr, DUK_STR_NOT_CALLABLE); - DUK_WO_NORETURN(return 0;); - } - return 1; -} - -/* - * Object prototype - */ - -DUK_EXTERNAL void duk_get_prototype(duk_hthread *thr, duk_idx_t idx) { - duk_hobject *obj; - duk_hobject *proto; - - DUK_ASSERT_API_ENTRY(thr); - - obj = duk_require_hobject(thr, idx); - DUK_ASSERT(obj != NULL); - - /* XXX: shared helper for duk_push_hobject_or_undefined()? */ - proto = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, obj); - if (proto) { - duk_push_hobject(thr, proto); - } else { - duk_push_undefined(thr); - } -} - -DUK_EXTERNAL void duk_set_prototype(duk_hthread *thr, duk_idx_t idx) { - duk_hobject *obj; - duk_hobject *proto; - - DUK_ASSERT_API_ENTRY(thr); - - obj = duk_require_hobject(thr, idx); - DUK_ASSERT(obj != NULL); - duk_require_type_mask(thr, -1, DUK_TYPE_MASK_UNDEFINED | DUK_TYPE_MASK_OBJECT); - proto = duk_get_hobject(thr, -1); - /* proto can also be NULL here (allowed explicitly) */ - -#if defined(DUK_USE_ROM_OBJECTS) - if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)) { - DUK_ERROR_TYPE(thr, DUK_STR_NOT_CONFIGURABLE); /* XXX: "read only object"? */ - DUK_WO_NORETURN(return;); - } -#endif - - DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, obj, proto); - - duk_pop(thr); -} - -DUK_INTERNAL void duk_clear_prototype(duk_hthread *thr, duk_idx_t idx) { - duk_hobject *obj; - - DUK_ASSERT_API_ENTRY(thr); - - obj = duk_require_hobject(thr, idx); - DUK_ASSERT(obj != NULL); - -#if defined(DUK_USE_ROM_OBJECTS) - if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)) { - DUK_ERROR_TYPE(thr, DUK_STR_NOT_CONFIGURABLE); /* XXX: "read only object"? */ - DUK_WO_NORETURN(return;); - } -#endif - - DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, obj, NULL); -} - -DUK_INTERNAL duk_bool_t duk_is_bare_object(duk_hthread *thr, duk_idx_t idx) { - duk_hobject *obj; - duk_hobject *proto; - - DUK_ASSERT_API_ENTRY(thr); - - obj = duk_require_hobject(thr, idx); - DUK_ASSERT(obj != NULL); - - proto = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, obj); - return (proto == NULL); -} - -/* - * Object finalizer - */ - -#if defined(DUK_USE_FINALIZER_SUPPORT) -/* XXX: these could be implemented as macros calling an internal function - * directly. - * XXX: same issue as with Duktape.fin: there's no way to delete the property - * now (just set it to undefined). - */ -DUK_EXTERNAL void duk_get_finalizer(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - - /* This get intentionally walks the inheritance chain at present, - * which matches how the effective finalizer property is also - * looked up in GC. - */ - duk_get_prop_stridx(thr, idx, DUK_STRIDX_INT_FINALIZER); -} - -DUK_EXTERNAL void duk_set_finalizer(duk_hthread *thr, duk_idx_t idx) { - duk_hobject *h; - duk_bool_t callable; - - DUK_ASSERT_API_ENTRY(thr); - - h = duk_require_hobject(thr, idx); /* Get before 'put' so that 'idx' is correct. */ - callable = duk_is_callable(thr, -1); - - /* At present finalizer is stored as a hidden Symbol, with normal - * inheritance and access control. As a result, finalizer cannot - * currently be set on a non-extensible (sealed or frozen) object. - * It might be useful to allow it. - */ - duk_put_prop_stridx(thr, idx, DUK_STRIDX_INT_FINALIZER); - - /* In addition to setting the finalizer property, keep a "have - * finalizer" flag in duk_hobject in sync so that refzero can do - * a very quick finalizer check by walking the prototype chain - * and checking the flag alone. (Note that this means that just - * setting _Finalizer on an object won't affect finalizer checks.) - * - * NOTE: if the argument is a Proxy object, this flag will be set - * on the Proxy, not the target. As a result, the target won't get - * a finalizer flag and the Proxy also won't be finalized as there's - * an explicit Proxy check in finalization now. - */ - if (callable) { - DUK_HOBJECT_SET_HAVE_FINALIZER(h); - } else { - DUK_HOBJECT_CLEAR_HAVE_FINALIZER(h); - } -} -#else /* DUK_USE_FINALIZER_SUPPORT */ -DUK_EXTERNAL void duk_get_finalizer(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - DUK_UNREF(idx); - DUK_ERROR_UNSUPPORTED(thr); - DUK_WO_NORETURN(return;); -} - -DUK_EXTERNAL void duk_set_finalizer(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - DUK_UNREF(idx); - DUK_ERROR_UNSUPPORTED(thr); - DUK_WO_NORETURN(return;); -} -#endif /* DUK_USE_FINALIZER_SUPPORT */ -/* - * Random numbers - */ - -/* #include duk_internal.h -> already included */ - -DUK_EXTERNAL duk_double_t duk_random(duk_hthread *thr) { - return (duk_double_t) duk_util_get_random_double(thr); -} -/* - * API calls related to general value stack manipulation: resizing the value - * stack, pushing and popping values, type checking and reading values, - * coercing values, etc. - * - * Also contains internal functions (such as duk_get_tval()), defined - * in duk_api_internal.h, with semantics similar to the public API. - */ - -/* XXX: repetition of stack pre-checks -> helper or macro or inline */ -/* XXX: shared api error strings, and perhaps even throw code for rare cases? */ - -/* #include duk_internal.h -> already included */ - -/* - * Forward declarations - */ - -DUK_LOCAL_DECL duk_idx_t -duk__push_c_function_raw(duk_hthread *thr, duk_c_function func, duk_idx_t nargs, duk_uint_t flags, duk_small_uint_t proto_bidx); - -/* - * Global state for working around missing variadic macros - */ - -#if !defined(DUK_USE_VARIADIC_MACROS) -DUK_EXTERNAL const char *duk_api_global_filename = NULL; -DUK_EXTERNAL duk_int_t duk_api_global_line = 0; -#endif - -/* - * Misc helpers - */ - -DUK_LOCAL const char * const duk__symbol_type_strings[4] = { "hidden", "global", "local", "wellknown" }; - -#if !defined(DUK_USE_PACKED_TVAL) -DUK_LOCAL const duk_uint_t duk__type_from_tag[] = { - DUK_TYPE_NUMBER, DUK_TYPE_NUMBER, /* fastint */ - DUK_TYPE_UNDEFINED, DUK_TYPE_NULL, DUK_TYPE_BOOLEAN, DUK_TYPE_POINTER, DUK_TYPE_LIGHTFUNC, - DUK_TYPE_NONE, DUK_TYPE_STRING, DUK_TYPE_OBJECT, DUK_TYPE_BUFFER, -}; -DUK_LOCAL const duk_uint_t duk__type_mask_from_tag[] = { - DUK_TYPE_MASK_NUMBER, DUK_TYPE_MASK_NUMBER, /* fastint */ - DUK_TYPE_MASK_UNDEFINED, DUK_TYPE_MASK_NULL, DUK_TYPE_MASK_BOOLEAN, DUK_TYPE_MASK_POINTER, DUK_TYPE_MASK_LIGHTFUNC, - DUK_TYPE_MASK_NONE, DUK_TYPE_MASK_STRING, DUK_TYPE_MASK_OBJECT, DUK_TYPE_MASK_BUFFER, -}; -#endif /* !DUK_USE_PACKED_TVAL */ - -/* Assert that there's room for one value. */ -#define DUK__ASSERT_SPACE() \ - do { \ - DUK_ASSERT(!(thr->valstack_top >= thr->valstack_end)); \ - } while (0) - -/* Check that there's room to push one value. */ -#if defined(DUK_USE_VALSTACK_UNSAFE) -/* Faster but value stack overruns are memory unsafe. */ -#define DUK__CHECK_SPACE() DUK__ASSERT_SPACE() -#else -#define DUK__CHECK_SPACE() \ - do { \ - if (DUK_UNLIKELY(thr->valstack_top >= thr->valstack_end)) { \ - DUK_ERROR_RANGE_PUSH_BEYOND(thr); \ - } \ - } while (0) -#endif - -DUK_LOCAL duk_small_uint_t duk__get_symbol_type(duk_hstring *h) { - const duk_uint8_t *data; - duk_size_t len; - - DUK_ASSERT(h != NULL); - DUK_ASSERT(DUK_HSTRING_HAS_SYMBOL(h)); - DUK_ASSERT(DUK_HSTRING_GET_BYTELEN(h) >= 1); /* always true, symbol prefix */ - - data = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h); - len = DUK_HSTRING_GET_BYTELEN(h); - DUK_ASSERT(len >= 1); - - /* XXX: differentiate between 0x82 and 0xff (hidden vs. internal?)? */ - - if (data[0] == 0xffU) { - return DUK_SYMBOL_TYPE_HIDDEN; - } else if (data[0] == 0x82U) { - return DUK_SYMBOL_TYPE_HIDDEN; - } else if (data[0] == 0x80U) { - return DUK_SYMBOL_TYPE_GLOBAL; - } else if (data[len - 1] != 0xffU) { - return DUK_SYMBOL_TYPE_LOCAL; - } else { - return DUK_SYMBOL_TYPE_WELLKNOWN; - } -} - -DUK_LOCAL const char *duk__get_symbol_type_string(duk_hstring *h) { - duk_small_uint_t idx; - idx = duk__get_symbol_type(h); - DUK_ASSERT(idx < sizeof(duk__symbol_type_strings)); - return duk__symbol_type_strings[idx]; -} - -DUK_LOCAL_DECL duk_heaphdr *duk__get_tagged_heaphdr_raw(duk_hthread *thr, duk_idx_t idx, duk_uint_t tag); - -DUK_LOCAL duk_int_t duk__api_coerce_d2i(duk_hthread *thr, duk_idx_t idx, duk_int_t def_value, duk_bool_t require) { - duk_tval *tv; - duk_small_int_t c; - duk_double_t d; - - tv = duk_get_tval_or_unused(thr, idx); - DUK_ASSERT(tv != NULL); - - /* - * Special cases like NaN and +/- Infinity are handled explicitly - * because a plain C coercion from double to int handles these cases - * in undesirable ways. For instance, NaN may coerce to INT_MIN - * (not zero), and INT_MAX + 1 may coerce to INT_MIN (not INT_MAX). - * - * This double-to-int coercion differs from ToInteger() because it - * has a finite range (ToInteger() allows e.g. +/- Infinity). It - * also differs from ToInt32() because the INT_MIN/INT_MAX clamping - * depends on the size of the int type on the platform. In particular, - * on platforms with a 64-bit int type, the full range is allowed. - */ - -#if defined(DUK_USE_FASTINT) - if (DUK_TVAL_IS_FASTINT(tv)) { - duk_int64_t t = DUK_TVAL_GET_FASTINT(tv); -#if (DUK_INT_MAX <= 0x7fffffffL) - /* Clamping only necessary for 32-bit ints. */ - if (t < DUK_INT_MIN) { - t = DUK_INT_MIN; - } else if (t > DUK_INT_MAX) { - t = DUK_INT_MAX; - } -#endif - return (duk_int_t) t; - } -#endif - - if (DUK_TVAL_IS_NUMBER(tv)) { - d = DUK_TVAL_GET_NUMBER(tv); - c = (duk_small_int_t) DUK_FPCLASSIFY(d); - if (c == DUK_FP_NAN) { - return 0; - } else if (d < (duk_double_t) DUK_INT_MIN) { - /* covers -Infinity */ - return DUK_INT_MIN; - } else if (d > (duk_double_t) DUK_INT_MAX) { - /* covers +Infinity */ - return DUK_INT_MAX; - } else { - /* coerce towards zero */ - return (duk_int_t) d; - } - } - - if (require) { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "number", DUK_STR_NOT_NUMBER); - DUK_WO_NORETURN(return 0;); - } - - return def_value; -} - -DUK_LOCAL duk_uint_t duk__api_coerce_d2ui(duk_hthread *thr, duk_idx_t idx, duk_uint_t def_value, duk_bool_t require) { - duk_tval *tv; - duk_small_int_t c; - duk_double_t d; - - /* Same as above but for unsigned int range. */ - - tv = duk_get_tval_or_unused(thr, idx); - DUK_ASSERT(tv != NULL); - -#if defined(DUK_USE_FASTINT) - if (DUK_TVAL_IS_FASTINT(tv)) { - duk_int64_t t = DUK_TVAL_GET_FASTINT(tv); - if (t < 0) { - t = 0; - } -#if (DUK_UINT_MAX <= 0xffffffffUL) - /* Clamping only necessary for 32-bit ints. */ - else if (t > DUK_UINT_MAX) { - t = DUK_UINT_MAX; - } -#endif - return (duk_uint_t) t; - } -#endif - - if (DUK_TVAL_IS_NUMBER(tv)) { - d = DUK_TVAL_GET_NUMBER(tv); - c = (duk_small_int_t) DUK_FPCLASSIFY(d); - if (c == DUK_FP_NAN) { - return 0; - } else if (d < 0.0) { - /* covers -Infinity */ - return (duk_uint_t) 0; - } else if (d > (duk_double_t) DUK_UINT_MAX) { - /* covers +Infinity */ - return (duk_uint_t) DUK_UINT_MAX; - } else { - /* coerce towards zero */ - return (duk_uint_t) d; - } - } - - if (require) { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "number", DUK_STR_NOT_NUMBER); - DUK_WO_NORETURN(return 0;); - } - - return def_value; -} - -/* - * Stack index validation/normalization and getting a stack duk_tval ptr. - * - * These are called by many API entrypoints so the implementations must be - * fast and "inlined". - * - * There's some repetition because of this; keep the functions in sync. - */ - -DUK_EXTERNAL duk_idx_t duk_normalize_index(duk_hthread *thr, duk_idx_t idx) { - duk_uidx_t vs_size; - duk_uidx_t uidx; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(DUK_INVALID_INDEX < 0); - - /* Care must be taken to avoid pointer wrapping in the index - * validation. For instance, on a 32-bit platform with 8-byte - * duk_tval the index 0x20000000UL would wrap the memory space - * once. - */ - - /* Assume value stack sizes (in elements) fits into duk_idx_t. */ - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - vs_size = (duk_uidx_t) (thr->valstack_top - thr->valstack_bottom); - DUK_ASSERT_DISABLE(vs_size >= 0); /* unsigned */ - - if (idx < 0) { - uidx = vs_size + (duk_uidx_t) idx; - } else { - /* since index non-negative */ - DUK_ASSERT(idx != DUK_INVALID_INDEX); - uidx = (duk_uidx_t) idx; - } - - /* DUK_INVALID_INDEX won't be accepted as a valid index. */ - DUK_ASSERT(vs_size + (duk_uidx_t) DUK_INVALID_INDEX >= vs_size); - - if (DUK_LIKELY(uidx < vs_size)) { - return (duk_idx_t) uidx; - } - return DUK_INVALID_INDEX; -} - -DUK_EXTERNAL duk_idx_t duk_require_normalize_index(duk_hthread *thr, duk_idx_t idx) { - duk_uidx_t vs_size; - duk_uidx_t uidx; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(DUK_INVALID_INDEX < 0); - - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - vs_size = (duk_uidx_t) (thr->valstack_top - thr->valstack_bottom); - DUK_ASSERT_DISABLE(vs_size >= 0); /* unsigned */ - - if (idx < 0) { - uidx = vs_size + (duk_uidx_t) idx; - } else { - DUK_ASSERT(idx != DUK_INVALID_INDEX); - uidx = (duk_uidx_t) idx; - } - - /* DUK_INVALID_INDEX won't be accepted as a valid index. */ - DUK_ASSERT(vs_size + (duk_uidx_t) DUK_INVALID_INDEX >= vs_size); - - if (DUK_LIKELY(uidx < vs_size)) { - return (duk_idx_t) uidx; - } - DUK_ERROR_RANGE_INDEX(thr, idx); - DUK_WO_NORETURN(return 0;); -} - -DUK_INTERNAL duk_tval *duk_get_tval(duk_hthread *thr, duk_idx_t idx) { - duk_uidx_t vs_size; - duk_uidx_t uidx; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(DUK_INVALID_INDEX < 0); - - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - vs_size = (duk_uidx_t) (thr->valstack_top - thr->valstack_bottom); - DUK_ASSERT_DISABLE(vs_size >= 0); /* unsigned */ - - if (idx < 0) { - uidx = vs_size + (duk_uidx_t) idx; - } else { - DUK_ASSERT(idx != DUK_INVALID_INDEX); - uidx = (duk_uidx_t) idx; - } - - /* DUK_INVALID_INDEX won't be accepted as a valid index. */ - DUK_ASSERT(vs_size + (duk_uidx_t) DUK_INVALID_INDEX >= vs_size); - - if (DUK_LIKELY(uidx < vs_size)) { - return thr->valstack_bottom + uidx; - } - return NULL; -} - -/* Variant of duk_get_tval() which is guaranteed to return a valid duk_tval - * pointer. When duk_get_tval() would return NULL, this variant returns a - * pointer to a duk_tval with tag DUK_TAG_UNUSED. This allows the call site - * to avoid an unnecessary NULL check which sometimes leads to better code. - * The return duk_tval is read only (at least for the UNUSED value). - */ -DUK_LOCAL const duk_tval_unused duk__const_tval_unused = DUK_TVAL_UNUSED_INITIALIZER(); - -DUK_INTERNAL duk_tval *duk_get_tval_or_unused(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_get_tval(thr, idx); - if (tv != NULL) { - return tv; - } - return (duk_tval *) DUK_LOSE_CONST(&duk__const_tval_unused); -} - -DUK_INTERNAL duk_tval *duk_require_tval(duk_hthread *thr, duk_idx_t idx) { - duk_uidx_t vs_size; - duk_uidx_t uidx; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(DUK_INVALID_INDEX < 0); - - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - vs_size = (duk_uidx_t) (thr->valstack_top - thr->valstack_bottom); - DUK_ASSERT_DISABLE(vs_size >= 0); /* unsigned */ - - /* Use unsigned arithmetic to optimize comparison. */ - if (idx < 0) { - uidx = vs_size + (duk_uidx_t) idx; - } else { - DUK_ASSERT(idx != DUK_INVALID_INDEX); - uidx = (duk_uidx_t) idx; - } - - /* DUK_INVALID_INDEX won't be accepted as a valid index. */ - DUK_ASSERT(vs_size + (duk_uidx_t) DUK_INVALID_INDEX >= vs_size); - - if (DUK_LIKELY(uidx < vs_size)) { - return thr->valstack_bottom + uidx; - } - DUK_ERROR_RANGE_INDEX(thr, idx); - DUK_WO_NORETURN(return NULL;); -} - -/* Non-critical. */ -DUK_EXTERNAL duk_bool_t duk_is_valid_index(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(DUK_INVALID_INDEX < 0); - - return (duk_normalize_index(thr, idx) >= 0); -} - -/* Non-critical. */ -DUK_EXTERNAL void duk_require_valid_index(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(DUK_INVALID_INDEX < 0); - - if (DUK_UNLIKELY(duk_normalize_index(thr, idx) < 0)) { - DUK_ERROR_RANGE_INDEX(thr, idx); - DUK_WO_NORETURN(return;); - } -} - -/* - * Value stack top handling - */ - -DUK_EXTERNAL duk_idx_t duk_get_top(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - - return (duk_idx_t) (thr->valstack_top - thr->valstack_bottom); -} - -/* Internal helper to get current top but to require a minimum top value - * (TypeError if not met). - */ -DUK_INTERNAL duk_idx_t duk_get_top_require_min(duk_hthread *thr, duk_idx_t min_top) { - duk_idx_t ret; - - DUK_ASSERT_API_ENTRY(thr); - - ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom); - if (DUK_UNLIKELY(ret < min_top)) { - DUK_ERROR_TYPE_INVALID_ARGS(thr); - DUK_WO_NORETURN(return 0;); - } - return ret; -} - -/* Set stack top within currently allocated range, but don't reallocate. - * This is performance critical especially for call handling, so whenever - * changing, profile and look at generated code. - */ -DUK_EXTERNAL void duk_set_top(duk_hthread *thr, duk_idx_t idx) { - duk_uidx_t vs_size; - duk_uidx_t vs_limit; - duk_uidx_t uidx; - duk_tval *tv; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(DUK_INVALID_INDEX < 0); - - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - DUK_ASSERT(thr->valstack_end >= thr->valstack_bottom); - vs_size = (duk_uidx_t) (thr->valstack_top - thr->valstack_bottom); - vs_limit = (duk_uidx_t) (thr->valstack_end - thr->valstack_bottom); - - if (idx < 0) { - /* Negative indices are always within allocated stack but - * must not go below zero index. - */ - uidx = vs_size + (duk_uidx_t) idx; - } else { - /* Positive index can be higher than valstack top but must - * not go above allocated stack (equality is OK). - */ - uidx = (duk_uidx_t) idx; - } - - /* DUK_INVALID_INDEX won't be accepted as a valid index. */ - DUK_ASSERT(vs_size + (duk_uidx_t) DUK_INVALID_INDEX >= vs_size); - DUK_ASSERT(vs_size + (duk_uidx_t) DUK_INVALID_INDEX >= vs_limit); - -#if defined(DUK_USE_VALSTACK_UNSAFE) - DUK_ASSERT(uidx <= vs_limit); - DUK_UNREF(vs_limit); -#else - if (DUK_UNLIKELY(uidx > vs_limit)) { - DUK_ERROR_RANGE_INDEX(thr, idx); - DUK_WO_NORETURN(return;); - } -#endif - DUK_ASSERT(uidx <= vs_limit); - - /* Handle change in value stack top. Respect value stack - * initialization policy: 'undefined' above top. Note that - * DECREF may cause a side effect that reallocates valstack, - * so must relookup after DECREF. - */ - - if (uidx >= vs_size) { - /* Stack size increases or stays the same. */ -#if defined(DUK_USE_ASSERTIONS) - duk_uidx_t count; - - count = uidx - vs_size; - while (count != 0) { - count--; - tv = thr->valstack_top + count; - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv)); - } -#endif - thr->valstack_top = thr->valstack_bottom + uidx; - } else { - /* Stack size decreases. */ -#if defined(DUK_USE_REFERENCE_COUNTING) - duk_uidx_t count; - duk_tval *tv_end; - - count = vs_size - uidx; - DUK_ASSERT(count > 0); - tv = thr->valstack_top; - tv_end = tv - count; - DUK_ASSERT(tv > tv_end); /* Because count > 0. */ - do { - tv--; - DUK_ASSERT(tv >= thr->valstack_bottom); - DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ(thr, tv); - } while (tv != tv_end); - thr->valstack_top = tv_end; - DUK_REFZERO_CHECK_FAST(thr); -#else /* DUK_USE_REFERENCE_COUNTING */ - duk_uidx_t count; - duk_tval *tv_end; - - count = vs_size - uidx; - tv = thr->valstack_top; - tv_end = tv - count; - DUK_ASSERT(tv > tv_end); - do { - tv--; - DUK_TVAL_SET_UNDEFINED(tv); - } while (tv != tv_end); - thr->valstack_top = tv_end; -#endif /* DUK_USE_REFERENCE_COUNTING */ - } -} - -/* Internal variant with a non-negative index and no runtime size checks. */ -#if defined(DUK_USE_PREFER_SIZE) -DUK_INTERNAL void duk_set_top_unsafe(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - - duk_set_top(thr, idx); -} -#else /* DUK_USE_PREFER_SIZE */ -DUK_INTERNAL void duk_set_top_unsafe(duk_hthread *thr, duk_idx_t idx) { - duk_uidx_t uidx; - duk_uidx_t vs_size; - duk_tval *tv; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - DUK_ASSERT(thr->valstack_end >= thr->valstack_bottom); - DUK_ASSERT(idx >= 0); - DUK_ASSERT(idx <= (duk_idx_t) (thr->valstack_end - thr->valstack_bottom)); - - /* XXX: byte arithmetic */ - uidx = (duk_uidx_t) idx; - vs_size = (duk_uidx_t) (thr->valstack_top - thr->valstack_bottom); - - if (uidx >= vs_size) { - /* Stack size increases or stays the same. */ -#if defined(DUK_USE_ASSERTIONS) - duk_uidx_t count; - - count = uidx - vs_size; - while (count != 0) { - count--; - tv = thr->valstack_top + count; - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv)); - } -#endif - thr->valstack_top = thr->valstack_bottom + uidx; - } else { - /* Stack size decreases. */ -#if defined(DUK_USE_REFERENCE_COUNTING) - duk_uidx_t count; - duk_tval *tv_end; - - count = vs_size - uidx; - DUK_ASSERT(count > 0); - tv = thr->valstack_top; - tv_end = tv - count; - DUK_ASSERT(tv > tv_end); /* Because count > 0. */ - do { - tv--; - DUK_ASSERT(tv >= thr->valstack_bottom); - DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ(thr, tv); - } while (tv != tv_end); - thr->valstack_top = tv_end; - DUK_REFZERO_CHECK_FAST(thr); -#else /* DUK_USE_REFERENCE_COUNTING */ - duk_uidx_t count; - duk_tval *tv_end; - - count = vs_size - uidx; - tv = thr->valstack_top; - tv_end = tv - count; - DUK_ASSERT(tv > tv_end); - do { - tv--; - DUK_TVAL_SET_UNDEFINED(tv); - } while (tv != tv_end); - thr->valstack_top = tv_end; -#endif /* DUK_USE_REFERENCE_COUNTING */ - } -} -#endif /* DUK_USE_PREFER_SIZE */ - -/* Internal helper: set top to 'top', and set [idx_wipe_start,top[ to - * 'undefined' (doing nothing if idx_wipe_start == top). Indices are - * positive and within value stack reserve. This is used by call handling. - */ -DUK_INTERNAL void duk_set_top_and_wipe(duk_hthread *thr, duk_idx_t top, duk_idx_t idx_wipe_start) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(top >= 0); - DUK_ASSERT(idx_wipe_start >= 0); - DUK_ASSERT(idx_wipe_start <= top); - DUK_ASSERT(thr->valstack_bottom + top <= thr->valstack_end); - DUK_ASSERT(thr->valstack_bottom + idx_wipe_start <= thr->valstack_end); - - duk_set_top_unsafe(thr, idx_wipe_start); - duk_set_top_unsafe(thr, top); -} - -DUK_EXTERNAL duk_idx_t duk_get_top_index(duk_hthread *thr) { - duk_idx_t ret; - - DUK_ASSERT_API_ENTRY(thr); - - ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom) - 1; - if (DUK_UNLIKELY(ret < 0)) { - /* Return invalid index; if caller uses this without checking - * in another API call, the index won't map to a valid stack - * entry. - */ - return DUK_INVALID_INDEX; - } - return ret; -} - -/* Internal variant: call assumes there is at least one element on the value - * stack frame; this is only asserted for. - */ -DUK_INTERNAL duk_idx_t duk_get_top_index_unsafe(duk_hthread *thr) { - duk_idx_t ret; - - DUK_ASSERT_API_ENTRY(thr); - - ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom) - 1; - return ret; -} - -DUK_EXTERNAL duk_idx_t duk_require_top_index(duk_hthread *thr) { - duk_idx_t ret; - - DUK_ASSERT_API_ENTRY(thr); - - ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom) - 1; - if (DUK_UNLIKELY(ret < 0)) { - DUK_ERROR_RANGE_INDEX(thr, -1); - DUK_WO_NORETURN(return 0;); - } - return ret; -} - -/* - * Value stack resizing. - * - * This resizing happens above the current "top": the value stack can be - * grown or shrunk, but the "top" is not affected. The value stack cannot - * be resized to a size below the current reserve. - * - * The low level reallocation primitive must carefully recompute all value - * stack pointers, and must also work if ALL pointers are NULL. The resize - * is quite tricky because the valstack realloc may cause a mark-and-sweep, - * which may run finalizers. Running finalizers may resize the valstack - * recursively (the same value stack we're working on). So, after realloc - * returns, we know that the valstack bottom, top, and reserve should still - * be the same (there should not be live values above the "top"), but its - * underlying size, alloc_end, and base pointer may have changed. - * - * 'new_size' is known to be <= DUK_USE_VALSTACK_LIMIT, which ensures that - * size_t and pointer arithmetic won't wrap in duk__resize_valstack(). - */ - -/* Low level valstack resize primitive, used for both grow and shrink. All - * adjustments for slack etc have already been done. Doesn't throw but does - * have allocation side effects. - */ -DUK_LOCAL DUK_COLD DUK_NOINLINE duk_bool_t duk__resize_valstack(duk_hthread *thr, duk_size_t new_size) { - duk_tval *pre_valstack; - duk_tval *pre_bottom; - duk_tval *pre_top; - duk_tval *pre_end; - duk_tval *pre_alloc_end; - duk_ptrdiff_t ptr_diff; - duk_tval *new_valstack; - duk_size_t new_alloc_size; - duk_tval *tv_prev_alloc_end; - duk_tval *p; - - DUK_HTHREAD_ASSERT_VALID(thr); - DUK_ASSERT(thr->valstack_bottom >= thr->valstack); - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - DUK_ASSERT(thr->valstack_end >= thr->valstack_top); - DUK_ASSERT(thr->valstack_alloc_end >= thr->valstack_end); - DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack) <= new_size); /* can't resize below 'top' */ - DUK_ASSERT(new_size <= DUK_USE_VALSTACK_LIMIT); /* valstack limit caller has check, prevents wrapping */ - DUK_ASSERT(new_size <= DUK_SIZE_MAX / sizeof(duk_tval)); /* specific assert for wrapping */ - - /* Pre-realloc pointer copies for asserts and debug logs. */ - pre_valstack = thr->valstack; - pre_bottom = thr->valstack_bottom; - pre_top = thr->valstack_top; - pre_end = thr->valstack_end; - pre_alloc_end = thr->valstack_alloc_end; - - DUK_UNREF(pre_valstack); - DUK_UNREF(pre_bottom); - DUK_UNREF(pre_top); - DUK_UNREF(pre_end); - DUK_UNREF(pre_alloc_end); - - /* If finalizer torture enabled, force base pointer change every time - * when it would be allowed. - */ -#if defined(DUK_USE_FINALIZER_TORTURE) - if (thr->heap->pf_prevent_count == 0) { - duk_hthread_valstack_torture_realloc(thr); - } -#endif - - /* Allocate a new valstack using DUK_REALLOC_DIRECT() to deal with - * a side effect changing the base pointer. - */ - new_alloc_size = sizeof(duk_tval) * new_size; - new_valstack = (duk_tval *) DUK_REALLOC_INDIRECT(thr->heap, duk_hthread_get_valstack_ptr, (void *) thr, new_alloc_size); - if (DUK_UNLIKELY(new_valstack == NULL)) { - /* Because new_size != 0, if condition doesn't need to be - * (new_valstack != NULL || new_size == 0). - */ - DUK_ASSERT(new_size != 0); - DUK_D(DUK_DPRINT("failed to resize valstack to %lu entries (%lu bytes)", - (unsigned long) new_size, - (unsigned long) new_alloc_size)); - return 0; - } - - /* Debug log any changes in pointer(s) by side effects. These don't - * necessarily imply any incorrect behavior, but should be rare in - * practice. - */ -#if defined(DUK_USE_DEBUG) - if (thr->valstack != pre_valstack) { - DUK_D(DUK_DPRINT("valstack base pointer changed during valstack resize: %p -> %p", - (void *) pre_valstack, - (void *) thr->valstack)); - } - if (thr->valstack_bottom != pre_bottom) { - DUK_D(DUK_DPRINT("valstack bottom pointer changed during valstack resize: %p -> %p", - (void *) pre_bottom, - (void *) thr->valstack_bottom)); - } - if (thr->valstack_top != pre_top) { - DUK_D(DUK_DPRINT("valstack top pointer changed during valstack resize: %p -> %p", - (void *) pre_top, - (void *) thr->valstack_top)); - } - if (thr->valstack_end != pre_end) { - DUK_D(DUK_DPRINT("valstack end pointer changed during valstack resize: %p -> %p", - (void *) pre_end, - (void *) thr->valstack_end)); - } - if (thr->valstack_alloc_end != pre_alloc_end) { - DUK_D(DUK_DPRINT("valstack alloc_end pointer changed during valstack resize: %p -> %p", - (void *) pre_alloc_end, - (void *) thr->valstack_alloc_end)); - } -#endif - - /* Assertions: offsets for bottom, top, and end (reserve) must not - * have changed even with side effects because they are always - * restored in unwind. For alloc_end there's no guarantee: it may - * have grown or shrunk (but remain above 'end'). - */ - DUK_ASSERT(thr->valstack_bottom - thr->valstack == pre_bottom - pre_valstack); - DUK_ASSERT(thr->valstack_top - thr->valstack == pre_top - pre_valstack); - DUK_ASSERT(thr->valstack_end - thr->valstack == pre_end - pre_valstack); - DUK_ASSERT(thr->valstack_alloc_end >= thr->valstack_end); - - /* Write new pointers. Most pointers can be handled as a pointer - * difference. - */ - ptr_diff = (duk_ptrdiff_t) ((duk_uint8_t *) new_valstack - (duk_uint8_t *) thr->valstack); - tv_prev_alloc_end = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack_alloc_end + ptr_diff); - thr->valstack = new_valstack; - thr->valstack_bottom = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack_bottom + ptr_diff); - thr->valstack_top = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack_top + ptr_diff); - thr->valstack_end = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack_end + ptr_diff); - thr->valstack_alloc_end = (duk_tval *) (void *) ((duk_uint8_t *) new_valstack + new_alloc_size); - - /* Assertions: pointer sanity after pointer updates. */ - DUK_ASSERT(thr->valstack_bottom >= thr->valstack); - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - DUK_ASSERT(thr->valstack_end >= thr->valstack_top); - DUK_ASSERT(thr->valstack_alloc_end >= thr->valstack_end); - - DUK_D(DUK_DPRINT("resized valstack %lu -> %lu elements (%lu -> %lu bytes): " - "base=%p -> %p, bottom=%p -> %p (%ld), top=%p -> %p (%ld), " - "end=%p -> %p (%ld), alloc_end=%p -> %p (%ld);" - " tv_prev_alloc_end=%p (-> %ld inits; <0 means shrink)", - (unsigned long) (pre_alloc_end - pre_valstack), - (unsigned long) new_size, - (unsigned long) ((duk_uint8_t *) pre_alloc_end - (duk_uint8_t *) pre_valstack), - (unsigned long) new_alloc_size, - (void *) pre_valstack, - (void *) thr->valstack, - (void *) pre_bottom, - (void *) thr->valstack_bottom, - (long) (thr->valstack_bottom - thr->valstack), - (void *) pre_top, - (void *) thr->valstack_top, - (long) (thr->valstack_top - thr->valstack), - (void *) pre_end, - (void *) thr->valstack_end, - (long) (thr->valstack_end - thr->valstack), - (void *) pre_alloc_end, - (void *) thr->valstack_alloc_end, - (long) (thr->valstack_alloc_end - thr->valstack), - (void *) tv_prev_alloc_end, - (long) (thr->valstack_alloc_end - tv_prev_alloc_end))); - - /* If allocation grew, init any new slots to 'undefined'. */ - p = tv_prev_alloc_end; - while (p < thr->valstack_alloc_end) { - /* Never executed if new size is smaller. */ - DUK_TVAL_SET_UNDEFINED(p); - p++; - } - - /* Assert for value stack initialization policy. */ -#if defined(DUK_USE_ASSERTIONS) - p = thr->valstack_top; - while (p < thr->valstack_alloc_end) { - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(p)); - p++; - } -#endif - - return 1; -} - -DUK_LOCAL DUK_COLD DUK_NOINLINE duk_bool_t duk__valstack_grow(duk_hthread *thr, duk_size_t min_bytes, duk_bool_t throw_on_error) { - duk_size_t min_size; - duk_size_t new_size; - - DUK_ASSERT(min_bytes / sizeof(duk_tval) * sizeof(duk_tval) == min_bytes); - min_size = min_bytes / sizeof(duk_tval); /* from bytes to slots */ - -#if defined(DUK_USE_VALSTACK_GROW_SHIFT) - /* New size is minimum size plus a proportional slack, e.g. shift of - * 2 means a 25% slack. - */ - new_size = min_size + (min_size >> DUK_USE_VALSTACK_GROW_SHIFT); -#else - /* New size is tight with no slack. This is sometimes preferred in - * low memory environments. - */ - new_size = min_size; -#endif - - if (DUK_UNLIKELY(new_size > DUK_USE_VALSTACK_LIMIT || new_size < min_size /*wrap*/)) { - /* Note: may be triggered even if minimal new_size would not reach the limit, - * plan limit accordingly. - */ - if (throw_on_error) { - DUK_ERROR_RANGE(thr, DUK_STR_VALSTACK_LIMIT); - DUK_WO_NORETURN(return 0;); - } - return 0; - } - - if (duk__resize_valstack(thr, new_size) == 0) { - if (throw_on_error) { - DUK_ERROR_ALLOC_FAILED(thr); - DUK_WO_NORETURN(return 0;); - } - return 0; - } - - thr->valstack_end = thr->valstack + min_size; - DUK_ASSERT(thr->valstack_alloc_end >= thr->valstack_end); - - return 1; -} - -/* Hot, inlined value stack grow check. Because value stack almost never - * grows, the actual resize call is in a NOINLINE helper. - */ -DUK_INTERNAL DUK_INLINE void duk_valstack_grow_check_throw(duk_hthread *thr, duk_size_t min_bytes) { - duk_tval *tv; - - tv = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + min_bytes); - if (DUK_LIKELY(thr->valstack_end >= tv)) { - return; - } - if (DUK_LIKELY(thr->valstack_alloc_end >= tv)) { - /* Values in [valstack_top,valstack_alloc_end[ are initialized - * to 'undefined' so we can just move the end pointer. - */ - thr->valstack_end = tv; - return; - } - (void) duk__valstack_grow(thr, min_bytes, 1 /*throw_on_error*/); -} - -/* Hot, inlined value stack grow check which doesn't throw. */ -DUK_INTERNAL DUK_INLINE duk_bool_t duk_valstack_grow_check_nothrow(duk_hthread *thr, duk_size_t min_bytes) { - duk_tval *tv; - - tv = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + min_bytes); - if (DUK_LIKELY(thr->valstack_end >= tv)) { - return 1; - } - if (DUK_LIKELY(thr->valstack_alloc_end >= tv)) { - thr->valstack_end = tv; - return 1; - } - return duk__valstack_grow(thr, min_bytes, 0 /*throw_on_error*/); -} - -/* Value stack shrink check, called from mark-and-sweep. */ -DUK_INTERNAL void duk_valstack_shrink_check_nothrow(duk_hthread *thr, duk_bool_t snug) { - duk_size_t alloc_bytes; - duk_size_t reserve_bytes; - duk_size_t shrink_bytes; - - alloc_bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_alloc_end - (duk_uint8_t *) thr->valstack); - reserve_bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack); - DUK_ASSERT(alloc_bytes >= reserve_bytes); - - /* We're free to shrink the value stack allocation down to - * reserve_bytes but not more. If 'snug' (emergency GC) - * shrink whatever we can. Otherwise only shrink if the new - * size would be considerably smaller. - */ - -#if defined(DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT) - if (snug) { - shrink_bytes = reserve_bytes; - } else { - duk_size_t proportion, slack; - - /* Require that value stack shrinks by at least X% of its - * current size. For example, shift of 2 means at least - * 25%. The proportion is computed as bytes and may not - * be a multiple of sizeof(duk_tval); that's OK here. - */ - proportion = alloc_bytes >> DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT; - if (alloc_bytes - reserve_bytes < proportion) { - /* Too little would be freed, do nothing. */ - return; - } - - /* Keep a slack after shrinking. The slack is again a - * proportion of the current size (the proportion should - * of course be smaller than the check proportion above). - */ -#if defined(DUK_USE_VALSTACK_SHRINK_SLACK_SHIFT) - DUK_ASSERT(DUK_USE_VALSTACK_SHRINK_SLACK_SHIFT > DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT); - slack = alloc_bytes >> DUK_USE_VALSTACK_SHRINK_SLACK_SHIFT; -#else - slack = 0; -#endif - shrink_bytes = reserve_bytes + slack / sizeof(duk_tval) * sizeof(duk_tval); /* multiple of duk_tval */ - } -#else /* DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT */ - /* Always snug, useful in some low memory environments. */ - DUK_UNREF(snug); - shrink_bytes = reserve_bytes; -#endif /* DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT */ - - DUK_D(DUK_DPRINT("valstack shrink check: alloc_bytes=%ld, reserve_bytes=%ld, shrink_bytes=%ld (unvalidated)", - (long) alloc_bytes, - (long) reserve_bytes, - (long) shrink_bytes)); - DUK_ASSERT(shrink_bytes >= reserve_bytes); - if (shrink_bytes >= alloc_bytes) { - /* Skip if shrink target is same as current one (or higher, - * though that shouldn't happen in practice). - */ - return; - } - DUK_ASSERT(shrink_bytes / sizeof(duk_tval) * sizeof(duk_tval) == shrink_bytes); - - DUK_D(DUK_DPRINT("valstack shrink check: decided to shrink, snug: %ld", (long) snug)); - - duk__resize_valstack(thr, shrink_bytes / sizeof(duk_tval)); -} - -DUK_EXTERNAL duk_bool_t duk_check_stack(duk_hthread *thr, duk_idx_t extra) { - duk_size_t min_new_bytes; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(thr != NULL); - - if (DUK_UNLIKELY(extra < 0 || extra > DUK_USE_VALSTACK_LIMIT)) { - if (extra < 0) { - /* Clamping to zero makes the API more robust to calling code - * calculation errors. - */ - extra = 0; - } else { - /* Cause grow check to fail without wrapping arithmetic. */ - extra = DUK_USE_VALSTACK_LIMIT; - } - } - - min_new_bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_top - (duk_uint8_t *) thr->valstack) + - sizeof(duk_tval) * ((duk_size_t) extra + DUK_VALSTACK_INTERNAL_EXTRA); - return duk_valstack_grow_check_nothrow(thr, min_new_bytes); -} - -DUK_EXTERNAL void duk_require_stack(duk_hthread *thr, duk_idx_t extra) { - duk_size_t min_new_bytes; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(thr != NULL); - - if (DUK_UNLIKELY(extra < 0 || extra > DUK_USE_VALSTACK_LIMIT)) { - if (extra < 0) { - /* Clamping to zero makes the API more robust to calling code - * calculation errors. - */ - extra = 0; - } else { - /* Cause grow check to fail without wrapping arithmetic. */ - extra = DUK_USE_VALSTACK_LIMIT; - } - } - - min_new_bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_top - (duk_uint8_t *) thr->valstack) + - sizeof(duk_tval) * ((duk_size_t) extra + DUK_VALSTACK_INTERNAL_EXTRA); - duk_valstack_grow_check_throw(thr, min_new_bytes); -} - -DUK_EXTERNAL duk_bool_t duk_check_stack_top(duk_hthread *thr, duk_idx_t top) { - duk_size_t min_new_bytes; - - DUK_ASSERT_API_ENTRY(thr); - - if (DUK_UNLIKELY(top < 0 || top > DUK_USE_VALSTACK_LIMIT)) { - if (top < 0) { - /* Clamping to zero makes the API more robust to calling code - * calculation errors. - */ - top = 0; - } else { - /* Cause grow check to fail without wrapping arithmetic. */ - top = DUK_USE_VALSTACK_LIMIT; - } - } - - DUK_ASSERT(top >= 0); - min_new_bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_bottom - (duk_uint8_t *) thr->valstack) + - sizeof(duk_tval) * ((duk_size_t) top + DUK_VALSTACK_INTERNAL_EXTRA); - return duk_valstack_grow_check_nothrow(thr, min_new_bytes); -} - -DUK_EXTERNAL void duk_require_stack_top(duk_hthread *thr, duk_idx_t top) { - duk_size_t min_new_bytes; - - DUK_ASSERT_API_ENTRY(thr); - - if (DUK_UNLIKELY(top < 0 || top > DUK_USE_VALSTACK_LIMIT)) { - if (top < 0) { - /* Clamping to zero makes the API more robust to calling code - * calculation errors. - */ - top = 0; - } else { - /* Cause grow check to fail without wrapping arithmetic. */ - top = DUK_USE_VALSTACK_LIMIT; - } - } - - DUK_ASSERT(top >= 0); - min_new_bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_bottom - (duk_uint8_t *) thr->valstack) + - sizeof(duk_tval) * ((duk_size_t) top + DUK_VALSTACK_INTERNAL_EXTRA); - duk_valstack_grow_check_throw(thr, min_new_bytes); -} - -/* - * Basic stack manipulation: swap, dup, insert, replace, etc - */ - -DUK_EXTERNAL void duk_swap(duk_hthread *thr, duk_idx_t idx1, duk_idx_t idx2) { - duk_tval *tv1; - duk_tval *tv2; - duk_tval tv_tmp; - - DUK_ASSERT_API_ENTRY(thr); - - tv1 = duk_require_tval(thr, idx1); - DUK_ASSERT(tv1 != NULL); - tv2 = duk_require_tval(thr, idx2); - DUK_ASSERT(tv2 != NULL); - - /* If tv1==tv2 this is a NOP, no check is needed */ - DUK_TVAL_SET_TVAL(&tv_tmp, tv1); - DUK_TVAL_SET_TVAL(tv1, tv2); - DUK_TVAL_SET_TVAL(tv2, &tv_tmp); -} - -DUK_EXTERNAL void duk_swap_top(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - - duk_swap(thr, idx, -1); -} - -DUK_EXTERNAL void duk_dup(duk_hthread *thr, duk_idx_t from_idx) { - duk_tval *tv_from; - duk_tval *tv_to; - - DUK_ASSERT_API_ENTRY(thr); - DUK__CHECK_SPACE(); - - tv_from = duk_require_tval(thr, from_idx); - tv_to = thr->valstack_top++; - DUK_ASSERT(tv_from != NULL); - DUK_ASSERT(tv_to != NULL); - DUK_TVAL_SET_TVAL(tv_to, tv_from); - DUK_TVAL_INCREF(thr, tv_to); /* no side effects */ -} - -DUK_EXTERNAL void duk_dup_top(duk_hthread *thr) { -#if defined(DUK_USE_PREFER_SIZE) - duk_dup(thr, -1); -#else - duk_tval *tv_from; - duk_tval *tv_to; - - DUK_ASSERT_API_ENTRY(thr); - DUK__CHECK_SPACE(); - - if (DUK_UNLIKELY(thr->valstack_top - thr->valstack_bottom <= 0)) { - DUK_ERROR_RANGE_INDEX(thr, -1); - DUK_WO_NORETURN(return;); - } - tv_from = thr->valstack_top - 1; - tv_to = thr->valstack_top++; - DUK_ASSERT(tv_from != NULL); - DUK_ASSERT(tv_to != NULL); - DUK_TVAL_SET_TVAL(tv_to, tv_from); - DUK_TVAL_INCREF(thr, tv_to); /* no side effects */ -#endif -} - -DUK_INTERNAL void duk_dup_0(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - duk_dup(thr, 0); -} -DUK_INTERNAL void duk_dup_1(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - duk_dup(thr, 1); -} -DUK_INTERNAL void duk_dup_2(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - duk_dup(thr, 2); -} -DUK_INTERNAL void duk_dup_m2(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - duk_dup(thr, -2); -} -DUK_INTERNAL void duk_dup_m3(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - duk_dup(thr, -3); -} -DUK_INTERNAL void duk_dup_m4(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - duk_dup(thr, -4); -} - -DUK_EXTERNAL void duk_insert(duk_hthread *thr, duk_idx_t to_idx) { - duk_tval *p; - duk_tval *q; - duk_tval tv_tmp; - duk_size_t nbytes; - - DUK_ASSERT_API_ENTRY(thr); - - p = duk_require_tval(thr, to_idx); - DUK_ASSERT(p != NULL); - q = duk_require_tval(thr, -1); - DUK_ASSERT(q != NULL); - - DUK_ASSERT(q >= p); - - /* nbytes - * <---------> - * [ ... | p | x | x | q ] - * => [ ... | q | p | x | x ] - */ - - nbytes = (duk_size_t) (((duk_uint8_t *) q) - ((duk_uint8_t *) p)); - - DUK_DDD(DUK_DDDPRINT("duk_insert: to_idx=%ld, p=%p, q=%p, nbytes=%lu", - (long) to_idx, - (void *) p, - (void *) q, - (unsigned long) nbytes)); - - /* No net refcount changes. No need to special case nbytes == 0 - * (p == q). - */ - DUK_TVAL_SET_TVAL(&tv_tmp, q); - duk_memmove((void *) (p + 1), (const void *) p, (size_t) nbytes); - DUK_TVAL_SET_TVAL(p, &tv_tmp); -} - -DUK_INTERNAL void duk_insert_undefined(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(idx >= 0); /* Doesn't support negative indices. */ - - duk_push_undefined(thr); - duk_insert(thr, idx); -} - -DUK_INTERNAL void duk_insert_undefined_n(duk_hthread *thr, duk_idx_t idx, duk_idx_t count) { - duk_tval *tv, *tv_end; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(idx >= 0); /* Doesn't support negative indices or count. */ - DUK_ASSERT(count >= 0); - - tv = duk_reserve_gap(thr, idx, count); - tv_end = tv + count; - while (tv != tv_end) { - DUK_TVAL_SET_UNDEFINED(tv); - tv++; - } -} - -DUK_EXTERNAL void duk_pull(duk_hthread *thr, duk_idx_t from_idx) { - duk_tval *p; - duk_tval *q; - duk_tval tv_tmp; - duk_size_t nbytes; - - DUK_ASSERT_API_ENTRY(thr); - - /* nbytes - * <---------> - * [ ... | x | x | p | y | y | q ] - * => [ ... | x | x | y | y | q | p ] - */ - - p = duk_require_tval(thr, from_idx); - DUK_ASSERT(p != NULL); - q = duk_require_tval(thr, -1); - DUK_ASSERT(q != NULL); - - DUK_ASSERT(q >= p); - - nbytes = (duk_size_t) (((duk_uint8_t *) q) - ((duk_uint8_t *) p)); - - DUK_DDD(DUK_DDDPRINT("duk_pull: from_idx=%ld, p=%p, q=%p, nbytes=%lu", - (long) from_idx, - (void *) p, - (void *) q, - (unsigned long) nbytes)); - - /* No net refcount changes. No need to special case nbytes == 0 - * (p == q). - */ - DUK_TVAL_SET_TVAL(&tv_tmp, p); - duk_memmove((void *) p, (const void *) (p + 1), (size_t) nbytes); - DUK_TVAL_SET_TVAL(q, &tv_tmp); -} - -DUK_EXTERNAL void duk_replace(duk_hthread *thr, duk_idx_t to_idx) { - duk_tval *tv1; - duk_tval *tv2; - duk_tval tv_tmp; - - DUK_ASSERT_API_ENTRY(thr); - - tv1 = duk_require_tval(thr, -1); - DUK_ASSERT(tv1 != NULL); - tv2 = duk_require_tval(thr, to_idx); - DUK_ASSERT(tv2 != NULL); - - /* For tv1 == tv2, both pointing to stack top, the end result - * is same as duk_pop(thr). - */ - DUK_TVAL_SET_TVAL(&tv_tmp, tv2); - DUK_TVAL_SET_TVAL(tv2, tv1); - DUK_TVAL_SET_UNDEFINED(tv1); - thr->valstack_top--; - DUK_TVAL_DECREF(thr, &tv_tmp); /* side effects */ -} - -DUK_EXTERNAL void duk_copy(duk_hthread *thr, duk_idx_t from_idx, duk_idx_t to_idx) { - duk_tval *tv1; - duk_tval *tv2; - - DUK_ASSERT_API_ENTRY(thr); - - tv1 = duk_require_tval(thr, from_idx); - DUK_ASSERT(tv1 != NULL); - tv2 = duk_require_tval(thr, to_idx); - DUK_ASSERT(tv2 != NULL); - - /* For tv1 == tv2, this is a no-op (no explicit check needed). */ - DUK_TVAL_SET_TVAL_UPDREF(thr, tv2, tv1); /* side effects */ -} - -DUK_EXTERNAL void duk_remove(duk_hthread *thr, duk_idx_t idx) { - duk_tval *p; - duk_tval *q; -#if defined(DUK_USE_REFERENCE_COUNTING) - duk_tval tv_tmp; -#endif - duk_size_t nbytes; - - DUK_ASSERT_API_ENTRY(thr); - - p = duk_require_tval(thr, idx); - DUK_ASSERT(p != NULL); - q = duk_require_tval(thr, -1); - DUK_ASSERT(q != NULL); - - DUK_ASSERT(q >= p); - - /* nbytes zero size case - * <---------> - * [ ... | p | x | x | q ] [ ... | p==q ] - * => [ ... | x | x | q ] [ ... ] - */ - -#if defined(DUK_USE_REFERENCE_COUNTING) - /* use a temp: decref only when valstack reachable values are correct */ - DUK_TVAL_SET_TVAL(&tv_tmp, p); -#endif - - nbytes = (duk_size_t) (((duk_uint8_t *) q) - ((duk_uint8_t *) p)); /* Note: 'q' is top-1 */ - duk_memmove((void *) p, (const void *) (p + 1), (size_t) nbytes); - - DUK_TVAL_SET_UNDEFINED(q); - thr->valstack_top--; - -#if defined(DUK_USE_REFERENCE_COUNTING) - DUK_TVAL_DECREF(thr, &tv_tmp); /* side effects */ -#endif -} - -DUK_INTERNAL void duk_remove_unsafe(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - - duk_remove(thr, idx); /* XXX: no optimization for now */ -} - -DUK_INTERNAL void duk_remove_m2(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - - duk_remove(thr, -2); -} - -DUK_INTERNAL void duk_remove_n(duk_hthread *thr, duk_idx_t idx, duk_idx_t count) { -#if defined(DUK_USE_PREFER_SIZE) - /* XXX: maybe too slow even when preferring size? */ - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(count >= 0); - DUK_ASSERT(idx >= 0); - - while (count-- > 0) { - duk_remove(thr, idx); - } -#else /* DUK_USE_PREFER_SIZE */ - duk_tval *tv_src; - duk_tval *tv_dst; - duk_tval *tv_newtop; - duk_tval *tv; - duk_size_t bytes; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(count >= 0); - DUK_ASSERT(idx >= 0); - - tv_dst = thr->valstack_bottom + idx; - DUK_ASSERT(tv_dst <= thr->valstack_top); - tv_src = tv_dst + count; - DUK_ASSERT(tv_src <= thr->valstack_top); - bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_top - (duk_uint8_t *) tv_src); - - for (tv = tv_dst; tv < tv_src; tv++) { - DUK_TVAL_DECREF_NORZ(thr, tv); - } - - duk_memmove((void *) tv_dst, (const void *) tv_src, bytes); - - tv_newtop = thr->valstack_top - count; - for (tv = tv_newtop; tv < thr->valstack_top; tv++) { - DUK_TVAL_SET_UNDEFINED(tv); - } - thr->valstack_top = tv_newtop; - - /* When not preferring size, only NORZ macros are used; caller - * is expected to DUK_REFZERO_CHECK(). - */ -#endif /* DUK_USE_PREFER_SIZE */ -} - -DUK_INTERNAL void duk_remove_n_unsafe(duk_hthread *thr, duk_idx_t idx, duk_idx_t count) { - DUK_ASSERT_API_ENTRY(thr); - - duk_remove_n(thr, idx, count); /* XXX: no optimization for now */ -} - -/* - * Stack slice primitives - */ - -DUK_EXTERNAL void duk_xcopymove_raw(duk_hthread *to_thr, duk_hthread *from_thr, duk_idx_t count, duk_bool_t is_copy) { - void *src; - duk_size_t nbytes; - duk_tval *p; - duk_tval *q; - - /* XXX: several pointer comparison issues here */ - - DUK_ASSERT_API_ENTRY(to_thr); - DUK_CTX_ASSERT_VALID(to_thr); - DUK_CTX_ASSERT_VALID(from_thr); - DUK_ASSERT(to_thr->heap == from_thr->heap); - - if (DUK_UNLIKELY(to_thr == from_thr)) { - DUK_ERROR_TYPE(to_thr, DUK_STR_INVALID_CONTEXT); - DUK_WO_NORETURN(return;); - } - if (DUK_UNLIKELY((duk_uidx_t) count > (duk_uidx_t) DUK_USE_VALSTACK_LIMIT)) { - /* Maximum value check ensures 'nbytes' won't wrap below. - * Also handles negative count. - */ - DUK_ERROR_RANGE_INVALID_COUNT(to_thr); - DUK_WO_NORETURN(return;); - } - DUK_ASSERT(count >= 0); - - nbytes = sizeof(duk_tval) * (duk_size_t) count; - if (DUK_UNLIKELY(nbytes == 0)) { - return; - } - DUK_ASSERT(to_thr->valstack_top <= to_thr->valstack_end); - if (DUK_UNLIKELY((duk_size_t) ((duk_uint8_t *) to_thr->valstack_end - (duk_uint8_t *) to_thr->valstack_top) < nbytes)) { - DUK_ERROR_RANGE_PUSH_BEYOND(to_thr); - DUK_WO_NORETURN(return;); - } - src = (void *) ((duk_uint8_t *) from_thr->valstack_top - nbytes); - if (DUK_UNLIKELY(src < (void *) from_thr->valstack_bottom)) { - DUK_ERROR_RANGE_INVALID_COUNT(to_thr); - DUK_WO_NORETURN(return;); - } - - /* Copy values (no overlap even if to_thr == from_thr; that's not - * allowed now anyway). - */ - DUK_ASSERT(nbytes > 0); - duk_memcpy((void *) to_thr->valstack_top, (const void *) src, (size_t) nbytes); - - p = to_thr->valstack_top; - to_thr->valstack_top = (duk_tval *) (void *) (((duk_uint8_t *) p) + nbytes); - - if (is_copy) { - /* Incref copies, keep originals. */ - q = to_thr->valstack_top; - while (p < q) { - DUK_TVAL_INCREF(to_thr, p); /* no side effects */ - p++; - } - } else { - /* No net refcount change. */ - p = from_thr->valstack_top; - q = (duk_tval *) (void *) (((duk_uint8_t *) p) - nbytes); - from_thr->valstack_top = q; - - while (p > q) { - p--; - DUK_TVAL_SET_UNDEFINED(p); - /* XXX: fast primitive to set a bunch of values to UNDEFINED */ - } - } -} - -/* Internal helper: reserve a gap of 'count' elements at 'idx_base' and return a - * pointer to the gap. Values in the gap are garbage and MUST be initialized by - * the caller before any side effects may occur. The caller must ensure there's - * enough stack reserve for 'count' values. - */ -DUK_INTERNAL duk_tval *duk_reserve_gap(duk_hthread *thr, duk_idx_t idx_base, duk_idx_t count) { - duk_tval *tv_src; - duk_tval *tv_dst; - duk_size_t gap_bytes; - duk_size_t copy_bytes; - - /* Caller is responsible for ensuring there's enough preallocated - * value stack. - */ - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(count >= 0); - DUK_ASSERT((duk_size_t) (thr->valstack_end - thr->valstack_top) >= (duk_size_t) count); - - tv_src = thr->valstack_bottom + idx_base; - gap_bytes = (duk_size_t) count * sizeof(duk_tval); - tv_dst = (duk_tval *) (void *) ((duk_uint8_t *) tv_src + gap_bytes); - copy_bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_top - (duk_uint8_t *) tv_src); - thr->valstack_top = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack_top + gap_bytes); - duk_memmove((void *) tv_dst, (const void *) tv_src, copy_bytes); - - /* Values in the gap are left as garbage: caller must fill them in - * and INCREF them before any side effects. - */ - return tv_src; -} - -/* - * Get/opt/require - */ - -DUK_EXTERNAL void duk_require_undefined(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_get_tval_or_unused(thr, idx); - DUK_ASSERT(tv != NULL); - if (DUK_UNLIKELY(!DUK_TVAL_IS_UNDEFINED(tv))) { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "undefined", DUK_STR_NOT_UNDEFINED); - DUK_WO_NORETURN(return;); - } -} - -DUK_EXTERNAL void duk_require_null(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_get_tval_or_unused(thr, idx); - DUK_ASSERT(tv != NULL); - if (DUK_UNLIKELY(!DUK_TVAL_IS_NULL(tv))) { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "null", DUK_STR_NOT_NULL); - DUK_WO_NORETURN(return;); - } -} - -DUK_LOCAL DUK_ALWAYS_INLINE duk_bool_t duk__get_boolean_raw(duk_hthread *thr, duk_idx_t idx, duk_bool_t def_value) { - duk_bool_t ret; - duk_tval *tv; - - DUK_CTX_ASSERT_VALID(thr); - - tv = duk_get_tval_or_unused(thr, idx); - DUK_ASSERT(tv != NULL); - if (DUK_TVAL_IS_BOOLEAN(tv)) { - ret = DUK_TVAL_GET_BOOLEAN(tv); - DUK_ASSERT(ret == 0 || ret == 1); - } else { - ret = def_value; - /* Not guaranteed to be 0 or 1. */ - } - - return ret; -} - -DUK_EXTERNAL duk_bool_t duk_get_boolean(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - - return duk__get_boolean_raw(thr, idx, 0); /* default: false */ -} - -DUK_EXTERNAL duk_bool_t duk_get_boolean_default(duk_hthread *thr, duk_idx_t idx, duk_bool_t def_value) { - DUK_ASSERT_API_ENTRY(thr); - - return duk__get_boolean_raw(thr, idx, def_value); -} - -DUK_EXTERNAL duk_bool_t duk_require_boolean(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - duk_bool_t ret; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_get_tval_or_unused(thr, idx); - DUK_ASSERT(tv != NULL); - if (DUK_LIKELY(DUK_TVAL_IS_BOOLEAN(tv))) { - ret = DUK_TVAL_GET_BOOLEAN(tv); - DUK_ASSERT(ret == 0 || ret == 1); - return ret; - } else { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "boolean", DUK_STR_NOT_BOOLEAN); - DUK_WO_NORETURN(return 0;); - } -} - -DUK_EXTERNAL duk_bool_t duk_opt_boolean(duk_hthread *thr, duk_idx_t idx, duk_bool_t def_value) { - DUK_ASSERT_API_ENTRY(thr); - - if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { - return def_value; - } - return duk_require_boolean(thr, idx); -} - -DUK_LOCAL DUK_ALWAYS_INLINE duk_double_t duk__get_number_raw(duk_hthread *thr, duk_idx_t idx, duk_double_t def_value) { - duk_double_union ret; - duk_tval *tv; - - DUK_CTX_ASSERT_VALID(thr); - - tv = duk_get_tval_or_unused(thr, idx); - DUK_ASSERT(tv != NULL); -#if defined(DUK_USE_FASTINT) - if (DUK_TVAL_IS_FASTINT(tv)) { - ret.d = (duk_double_t) DUK_TVAL_GET_FASTINT(tv); /* XXX: cast trick */ - } else -#endif - if (DUK_TVAL_IS_DOUBLE(tv)) { - /* When using packed duk_tval, number must be in NaN-normalized form - * for it to be a duk_tval, so no need to normalize. NOP for unpacked - * duk_tval. - */ - ret.d = DUK_TVAL_GET_DOUBLE(tv); - DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&ret)); - } else { - ret.d = def_value; - /* Default value (including NaN) may not be normalized. */ - } - - return ret.d; -} - -DUK_EXTERNAL duk_double_t duk_get_number(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - return duk__get_number_raw(thr, idx, DUK_DOUBLE_NAN); /* default: NaN */ -} - -DUK_EXTERNAL duk_double_t duk_get_number_default(duk_hthread *thr, duk_idx_t idx, duk_double_t def_value) { - DUK_ASSERT_API_ENTRY(thr); - return duk__get_number_raw(thr, idx, def_value); -} - -DUK_EXTERNAL duk_double_t duk_require_number(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - duk_double_union ret; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_get_tval_or_unused(thr, idx); - DUK_ASSERT(tv != NULL); - if (DUK_UNLIKELY(!DUK_TVAL_IS_NUMBER(tv))) { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "number", DUK_STR_NOT_NUMBER); - DUK_WO_NORETURN(return 0.0;); - } - - ret.d = DUK_TVAL_GET_NUMBER(tv); - - /* When using packed duk_tval, number must be in NaN-normalized form - * for it to be a duk_tval, so no need to normalize. NOP for unpacked - * duk_tval. - */ - DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&ret)); - return ret.d; -} - -DUK_EXTERNAL duk_double_t duk_opt_number(duk_hthread *thr, duk_idx_t idx, duk_double_t def_value) { - DUK_ASSERT_API_ENTRY(thr); - - if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { - /* User provided default is not NaN normalized. */ - return def_value; - } - return duk_require_number(thr, idx); -} - -DUK_EXTERNAL duk_int_t duk_get_int(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - - return (duk_int_t) duk__api_coerce_d2i(thr, idx, 0 /*def_value*/, 0 /*require*/); -} - -DUK_EXTERNAL duk_uint_t duk_get_uint(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - - return (duk_uint_t) duk__api_coerce_d2ui(thr, idx, 0 /*def_value*/, 0 /*require*/); -} - -DUK_EXTERNAL duk_int_t duk_get_int_default(duk_hthread *thr, duk_idx_t idx, duk_int_t def_value) { - DUK_ASSERT_API_ENTRY(thr); - - return (duk_int_t) duk__api_coerce_d2i(thr, idx, def_value, 0 /*require*/); -} - -DUK_EXTERNAL duk_uint_t duk_get_uint_default(duk_hthread *thr, duk_idx_t idx, duk_uint_t def_value) { - DUK_ASSERT_API_ENTRY(thr); - - return (duk_uint_t) duk__api_coerce_d2ui(thr, idx, def_value, 0 /*require*/); -} - -DUK_EXTERNAL duk_int_t duk_require_int(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - - return (duk_int_t) duk__api_coerce_d2i(thr, idx, 0 /*def_value*/, 1 /*require*/); -} - -DUK_EXTERNAL duk_uint_t duk_require_uint(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - - return (duk_uint_t) duk__api_coerce_d2ui(thr, idx, 0 /*def_value*/, 1 /*require*/); -} - -DUK_EXTERNAL duk_int_t duk_opt_int(duk_hthread *thr, duk_idx_t idx, duk_int_t def_value) { - DUK_ASSERT_API_ENTRY(thr); - - if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { - return def_value; - } - return duk_require_int(thr, idx); -} - -DUK_EXTERNAL duk_uint_t duk_opt_uint(duk_hthread *thr, duk_idx_t idx, duk_uint_t def_value) { - DUK_ASSERT_API_ENTRY(thr); - - if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { - return def_value; - } - return duk_require_uint(thr, idx); -} - -DUK_EXTERNAL const char *duk_get_lstring(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len) { - duk_hstring *h; - const char *ret; - duk_size_t len; - - DUK_ASSERT_API_ENTRY(thr); - - h = duk_get_hstring(thr, idx); - if (h != NULL) { - len = DUK_HSTRING_GET_BYTELEN(h); - ret = (const char *) DUK_HSTRING_GET_DATA(h); - } else { - len = 0; - ret = NULL; - } - - if (out_len != NULL) { - *out_len = len; - } - return ret; -} - -DUK_EXTERNAL const char *duk_require_lstring(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len) { - duk_hstring *h; - - DUK_ASSERT_API_ENTRY(thr); - - h = duk_require_hstring(thr, idx); - DUK_ASSERT(h != NULL); - if (out_len) { - *out_len = DUK_HSTRING_GET_BYTELEN(h); - } - return (const char *) DUK_HSTRING_GET_DATA(h); -} - -DUK_INTERNAL const char *duk_require_lstring_notsymbol(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len) { - duk_hstring *h; - - DUK_ASSERT_API_ENTRY(thr); - - h = duk_require_hstring_notsymbol(thr, idx); - DUK_ASSERT(h != NULL); - if (out_len) { - *out_len = DUK_HSTRING_GET_BYTELEN(h); - } - return (const char *) DUK_HSTRING_GET_DATA(h); -} - -DUK_EXTERNAL const char *duk_get_string(duk_hthread *thr, duk_idx_t idx) { - duk_hstring *h; - - DUK_ASSERT_API_ENTRY(thr); - - h = duk_get_hstring(thr, idx); - if (h != NULL) { - return (const char *) DUK_HSTRING_GET_DATA(h); - } else { - return NULL; - } -} - -DUK_EXTERNAL const char *duk_opt_lstring(duk_hthread *thr, - duk_idx_t idx, - duk_size_t *out_len, - const char *def_ptr, - duk_size_t def_len) { - DUK_ASSERT_API_ENTRY(thr); - - if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { - if (out_len != NULL) { - *out_len = def_len; - } - return def_ptr; - } - return duk_require_lstring(thr, idx, out_len); -} - -DUK_EXTERNAL const char *duk_opt_string(duk_hthread *thr, duk_idx_t idx, const char *def_ptr) { - DUK_ASSERT_API_ENTRY(thr); - - if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { - return def_ptr; - } - return duk_require_string(thr, idx); -} - -DUK_EXTERNAL const char *duk_get_lstring_default(duk_hthread *thr, - duk_idx_t idx, - duk_size_t *out_len, - const char *def_ptr, - duk_size_t def_len) { - duk_hstring *h; - const char *ret; - duk_size_t len; - - DUK_ASSERT_API_ENTRY(thr); - - h = duk_get_hstring(thr, idx); - if (h != NULL) { - len = DUK_HSTRING_GET_BYTELEN(h); - ret = (const char *) DUK_HSTRING_GET_DATA(h); - } else { - len = def_len; - ret = def_ptr; - } - - if (out_len != NULL) { - *out_len = len; - } - return ret; -} - -DUK_EXTERNAL const char *duk_get_string_default(duk_hthread *thr, duk_idx_t idx, const char *def_value) { - duk_hstring *h; - - DUK_ASSERT_API_ENTRY(thr); - - h = duk_get_hstring(thr, idx); - if (h != NULL) { - return (const char *) DUK_HSTRING_GET_DATA(h); - } else { - return def_value; - } -} - -DUK_INTERNAL const char *duk_get_string_notsymbol(duk_hthread *thr, duk_idx_t idx) { - duk_hstring *h; - - DUK_ASSERT_API_ENTRY(thr); - - h = duk_get_hstring_notsymbol(thr, idx); - if (h) { - return (const char *) DUK_HSTRING_GET_DATA(h); - } else { - return NULL; - } -} - -DUK_EXTERNAL const char *duk_require_string(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - - return duk_require_lstring(thr, idx, NULL); -} - -DUK_INTERNAL const char *duk_require_string_notsymbol(duk_hthread *thr, duk_idx_t idx) { - duk_hstring *h; - - DUK_ASSERT_API_ENTRY(thr); - - h = duk_require_hstring_notsymbol(thr, idx); - DUK_ASSERT(h != NULL); - return (const char *) DUK_HSTRING_GET_DATA(h); -} - -DUK_EXTERNAL void duk_require_object(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_get_tval_or_unused(thr, idx); - DUK_ASSERT(tv != NULL); - if (DUK_UNLIKELY(!DUK_TVAL_IS_OBJECT(tv))) { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "object", DUK_STR_NOT_OBJECT); - DUK_WO_NORETURN(return;); - } -} - -DUK_LOCAL void *duk__get_pointer_raw(duk_hthread *thr, duk_idx_t idx, void *def_value) { - duk_tval *tv; - void *p; - - DUK_CTX_ASSERT_VALID(thr); - - tv = duk_get_tval_or_unused(thr, idx); - DUK_ASSERT(tv != NULL); - if (!DUK_TVAL_IS_POINTER(tv)) { - return def_value; - } - - p = DUK_TVAL_GET_POINTER(tv); /* may be NULL */ - return p; -} - -DUK_EXTERNAL void *duk_get_pointer(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - return duk__get_pointer_raw(thr, idx, NULL /*def_value*/); -} - -DUK_EXTERNAL void *duk_opt_pointer(duk_hthread *thr, duk_idx_t idx, void *def_value) { - DUK_ASSERT_API_ENTRY(thr); - - if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { - return def_value; - } - return duk_require_pointer(thr, idx); -} - -DUK_EXTERNAL void *duk_get_pointer_default(duk_hthread *thr, duk_idx_t idx, void *def_value) { - DUK_ASSERT_API_ENTRY(thr); - return duk__get_pointer_raw(thr, idx, def_value); -} - -DUK_EXTERNAL void *duk_require_pointer(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - void *p; - - DUK_ASSERT_API_ENTRY(thr); - - /* Note: here we must be wary of the fact that a pointer may be - * valid and be a NULL. - */ - tv = duk_get_tval_or_unused(thr, idx); - DUK_ASSERT(tv != NULL); - if (DUK_UNLIKELY(!DUK_TVAL_IS_POINTER(tv))) { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "pointer", DUK_STR_NOT_POINTER); - DUK_WO_NORETURN(return NULL;); - } - p = DUK_TVAL_GET_POINTER(tv); /* may be NULL */ - return p; -} - -#if 0 /*unused*/ -DUK_INTERNAL void *duk_get_voidptr(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - duk_heaphdr *h; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_get_tval_or_unused(thr, idx); - DUK_ASSERT(tv != NULL); - if (!DUK_TVAL_IS_HEAP_ALLOCATED(tv)) { - return NULL; - } - - h = DUK_TVAL_GET_HEAPHDR(tv); - DUK_ASSERT(h != NULL); - return (void *) h; -} -#endif - -DUK_LOCAL void *duk__get_buffer_helper(duk_hthread *thr, - duk_idx_t idx, - duk_size_t *out_size, - void *def_ptr, - duk_size_t def_size, - duk_bool_t throw_flag) { - duk_hbuffer *h; - void *ret; - duk_size_t len; - duk_tval *tv; - - DUK_CTX_ASSERT_VALID(thr); - - if (out_size != NULL) { - *out_size = 0; - } - - tv = duk_get_tval_or_unused(thr, idx); - DUK_ASSERT(tv != NULL); - if (DUK_LIKELY(DUK_TVAL_IS_BUFFER(tv))) { - h = DUK_TVAL_GET_BUFFER(tv); - DUK_ASSERT(h != NULL); - - len = DUK_HBUFFER_GET_SIZE(h); - ret = DUK_HBUFFER_GET_DATA_PTR(thr->heap, h); - } else { - if (throw_flag) { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "buffer", DUK_STR_NOT_BUFFER); - DUK_WO_NORETURN(return NULL;); - } - len = def_size; - ret = def_ptr; - } - - if (out_size != NULL) { - *out_size = len; - } - return ret; -} - -DUK_EXTERNAL void *duk_get_buffer(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size) { - DUK_ASSERT_API_ENTRY(thr); - - return duk__get_buffer_helper(thr, idx, out_size, NULL /*def_ptr*/, 0 /*def_size*/, 0 /*throw_flag*/); -} - -DUK_EXTERNAL void *duk_opt_buffer(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size) { - DUK_ASSERT_API_ENTRY(thr); - - if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { - if (out_size != NULL) { - *out_size = def_size; - } - return def_ptr; - } - return duk_require_buffer(thr, idx, out_size); -} - -DUK_EXTERNAL void *duk_get_buffer_default(duk_hthread *thr, - duk_idx_t idx, - duk_size_t *out_size, - void *def_ptr, - duk_size_t def_len) { - DUK_ASSERT_API_ENTRY(thr); - - return duk__get_buffer_helper(thr, idx, out_size, def_ptr, def_len, 0 /*throw_flag*/); -} - -DUK_EXTERNAL void *duk_require_buffer(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size) { - DUK_ASSERT_API_ENTRY(thr); - - return duk__get_buffer_helper(thr, idx, out_size, NULL /*def_ptr*/, 0 /*def_size*/, 1 /*throw_flag*/); -} - -/* Get the active buffer data area for a plain buffer or a buffer object. - * Return NULL if the the value is not a buffer. Note that a buffer may - * have a NULL data pointer when its size is zero, the optional 'out_isbuffer' - * argument allows caller to detect this reliably. - */ -DUK_INTERNAL void *duk_get_buffer_data_raw(duk_hthread *thr, - duk_idx_t idx, - duk_size_t *out_size, - void *def_ptr, - duk_size_t def_size, - duk_bool_t throw_flag, - duk_bool_t *out_isbuffer) { - duk_tval *tv; - - DUK_ASSERT_API_ENTRY(thr); - - if (out_isbuffer != NULL) { - *out_isbuffer = 0; - } - if (out_size != NULL) { - *out_size = def_size; - } - - tv = duk_get_tval_or_unused(thr, idx); - DUK_ASSERT(tv != NULL); - - if (DUK_TVAL_IS_BUFFER(tv)) { - duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv); - DUK_ASSERT(h != NULL); - if (out_size != NULL) { - *out_size = DUK_HBUFFER_GET_SIZE(h); - } - if (out_isbuffer != NULL) { - *out_isbuffer = 1; - } - return (void *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h); /* may be NULL (but only if size is 0) */ - } -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) - else if (DUK_TVAL_IS_OBJECT(tv)) { - duk_hobject *h = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - if (DUK_HOBJECT_IS_BUFOBJ(h)) { - /* XXX: this is probably a useful shared helper: for a - * duk_hbufobj, get a validated buffer pointer/length. - */ - duk_hbufobj *h_bufobj = (duk_hbufobj *) h; - DUK_HBUFOBJ_ASSERT_VALID(h_bufobj); - - if (h_bufobj->buf != NULL && DUK_HBUFOBJ_VALID_SLICE(h_bufobj)) { - duk_uint8_t *p; - - p = (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_bufobj->buf); - if (out_size != NULL) { - *out_size = (duk_size_t) h_bufobj->length; - } - if (out_isbuffer != NULL) { - *out_isbuffer = 1; - } - return (void *) (p + h_bufobj->offset); - } - /* if slice not fully valid, treat as error */ - } - } -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - - if (throw_flag) { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "buffer", DUK_STR_NOT_BUFFER); - DUK_WO_NORETURN(return NULL;); - } - return def_ptr; -} - -DUK_EXTERNAL void *duk_get_buffer_data(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size) { - DUK_ASSERT_API_ENTRY(thr); - return duk_get_buffer_data_raw(thr, idx, out_size, NULL /*def_ptr*/, 0 /*def_size*/, 0 /*throw_flag*/, NULL); -} - -DUK_EXTERNAL void *duk_get_buffer_data_default(duk_hthread *thr, - duk_idx_t idx, - duk_size_t *out_size, - void *def_ptr, - duk_size_t def_size) { - DUK_ASSERT_API_ENTRY(thr); - return duk_get_buffer_data_raw(thr, idx, out_size, def_ptr, def_size, 0 /*throw_flag*/, NULL); -} - -DUK_EXTERNAL void *duk_opt_buffer_data(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size) { - DUK_ASSERT_API_ENTRY(thr); - - if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { - if (out_size != NULL) { - *out_size = def_size; - } - return def_ptr; - } - return duk_require_buffer_data(thr, idx, out_size); -} - -DUK_EXTERNAL void *duk_require_buffer_data(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size) { - DUK_ASSERT_API_ENTRY(thr); - return duk_get_buffer_data_raw(thr, idx, out_size, NULL /*def_ptr*/, 0 /*def_size*/, 1 /*throw_flag*/, NULL); -} - -/* Raw helper for getting a value from the stack, checking its tag. - * The tag cannot be a number because numbers don't have an internal - * tag in the packed representation. - */ - -DUK_LOCAL duk_heaphdr *duk__get_tagged_heaphdr_raw(duk_hthread *thr, duk_idx_t idx, duk_uint_t tag) { - duk_tval *tv; - duk_heaphdr *ret; - - DUK_CTX_ASSERT_VALID(thr); - - tv = duk_get_tval_or_unused(thr, idx); - DUK_ASSERT(tv != NULL); - if (DUK_TVAL_GET_TAG(tv) != tag) { - return (duk_heaphdr *) NULL; - } - - ret = DUK_TVAL_GET_HEAPHDR(tv); - DUK_ASSERT(ret != NULL); /* tagged null pointers should never occur */ - return ret; -} - -DUK_INTERNAL duk_hstring *duk_get_hstring(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - return (duk_hstring *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_STRING); -} - -DUK_INTERNAL duk_hstring *duk_get_hstring_notsymbol(duk_hthread *thr, duk_idx_t idx) { - duk_hstring *h; - - DUK_ASSERT_API_ENTRY(thr); - - h = (duk_hstring *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_STRING); - if (DUK_UNLIKELY(h && DUK_HSTRING_HAS_SYMBOL(h))) { - return NULL; - } - return h; -} - -DUK_INTERNAL duk_hstring *duk_require_hstring(duk_hthread *thr, duk_idx_t idx) { - duk_hstring *h; - - DUK_ASSERT_API_ENTRY(thr); - - h = (duk_hstring *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_STRING); - if (DUK_UNLIKELY(h == NULL)) { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "string", DUK_STR_NOT_STRING); - DUK_WO_NORETURN(return NULL;); - } - return h; -} - -DUK_INTERNAL duk_hstring *duk_require_hstring_notsymbol(duk_hthread *thr, duk_idx_t idx) { - duk_hstring *h; - - DUK_ASSERT_API_ENTRY(thr); - - h = (duk_hstring *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_STRING); - if (DUK_UNLIKELY(h == NULL || DUK_HSTRING_HAS_SYMBOL(h))) { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "string", DUK_STR_NOT_STRING); - DUK_WO_NORETURN(return NULL;); - } - return h; -} - -DUK_INTERNAL duk_hobject *duk_get_hobject(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - return (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT); -} - -DUK_INTERNAL duk_hobject *duk_require_hobject(duk_hthread *thr, duk_idx_t idx) { - duk_hobject *h; - - DUK_ASSERT_API_ENTRY(thr); - - h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT); - if (DUK_UNLIKELY(h == NULL)) { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "object", DUK_STR_NOT_OBJECT); - DUK_WO_NORETURN(return NULL;); - } - return h; -} - -DUK_INTERNAL duk_hbuffer *duk_get_hbuffer(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - return (duk_hbuffer *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_BUFFER); -} - -DUK_INTERNAL duk_hbuffer *duk_require_hbuffer(duk_hthread *thr, duk_idx_t idx) { - duk_hbuffer *h; - - DUK_ASSERT_API_ENTRY(thr); - - h = (duk_hbuffer *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_BUFFER); - if (DUK_UNLIKELY(h == NULL)) { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "buffer", DUK_STR_NOT_BUFFER); - DUK_WO_NORETURN(return NULL;); - } - return h; -} - -DUK_INTERNAL duk_hthread *duk_get_hthread(duk_hthread *thr, duk_idx_t idx) { - duk_hobject *h; - - DUK_ASSERT_API_ENTRY(thr); - - h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT); - if (DUK_UNLIKELY(h != NULL && !DUK_HOBJECT_IS_THREAD(h))) { - h = NULL; - } - return (duk_hthread *) h; -} - -DUK_INTERNAL duk_hthread *duk_require_hthread(duk_hthread *thr, duk_idx_t idx) { - duk_hobject *h; - - DUK_ASSERT_API_ENTRY(thr); - - h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT); - if (DUK_UNLIKELY(!(h != NULL && DUK_HOBJECT_IS_THREAD(h)))) { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "thread", DUK_STR_NOT_THREAD); - DUK_WO_NORETURN(return NULL;); - } - return (duk_hthread *) h; -} - -DUK_INTERNAL duk_hcompfunc *duk_get_hcompfunc(duk_hthread *thr, duk_idx_t idx) { - duk_hobject *h; - - DUK_ASSERT_API_ENTRY(thr); - - h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT); - if (DUK_UNLIKELY(h != NULL && !DUK_HOBJECT_IS_COMPFUNC(h))) { - h = NULL; - } - return (duk_hcompfunc *) h; -} - -DUK_INTERNAL duk_hcompfunc *duk_require_hcompfunc(duk_hthread *thr, duk_idx_t idx) { - duk_hobject *h; - - DUK_ASSERT_API_ENTRY(thr); - - h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT); - if (DUK_UNLIKELY(!(h != NULL && DUK_HOBJECT_IS_COMPFUNC(h)))) { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "compiledfunction", DUK_STR_NOT_COMPFUNC); - DUK_WO_NORETURN(return NULL;); - } - return (duk_hcompfunc *) h; -} - -DUK_INTERNAL duk_hnatfunc *duk_get_hnatfunc(duk_hthread *thr, duk_idx_t idx) { - duk_hobject *h; - - DUK_ASSERT_API_ENTRY(thr); - - h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT); - if (DUK_UNLIKELY(h != NULL && !DUK_HOBJECT_IS_NATFUNC(h))) { - h = NULL; - } - return (duk_hnatfunc *) h; -} - -DUK_INTERNAL duk_hnatfunc *duk_require_hnatfunc(duk_hthread *thr, duk_idx_t idx) { - duk_hobject *h; - - DUK_ASSERT_API_ENTRY(thr); - - h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT); - if (DUK_UNLIKELY(!(h != NULL && DUK_HOBJECT_IS_NATFUNC(h)))) { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "nativefunction", DUK_STR_NOT_NATFUNC); - DUK_WO_NORETURN(return NULL;); - } - return (duk_hnatfunc *) h; -} - -DUK_EXTERNAL duk_c_function duk_get_c_function(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - duk_hobject *h; - duk_hnatfunc *f; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_get_tval_or_unused(thr, idx); - DUK_ASSERT(tv != NULL); - if (DUK_UNLIKELY(!DUK_TVAL_IS_OBJECT(tv))) { - return NULL; - } - h = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - - if (DUK_UNLIKELY(!DUK_HOBJECT_IS_NATFUNC(h))) { - return NULL; - } - DUK_ASSERT(DUK_HOBJECT_HAS_NATFUNC(h)); - f = (duk_hnatfunc *) h; - - return f->func; -} - -DUK_EXTERNAL duk_c_function duk_opt_c_function(duk_hthread *thr, duk_idx_t idx, duk_c_function def_value) { - DUK_ASSERT_API_ENTRY(thr); - - if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { - return def_value; - } - return duk_require_c_function(thr, idx); -} - -DUK_EXTERNAL duk_c_function duk_get_c_function_default(duk_hthread *thr, duk_idx_t idx, duk_c_function def_value) { - duk_c_function ret; - - DUK_ASSERT_API_ENTRY(thr); - - ret = duk_get_c_function(thr, idx); - if (ret != NULL) { - return ret; - } - - return def_value; -} - -DUK_EXTERNAL duk_c_function duk_require_c_function(duk_hthread *thr, duk_idx_t idx) { - duk_c_function ret; - - DUK_ASSERT_API_ENTRY(thr); - - ret = duk_get_c_function(thr, idx); - if (DUK_UNLIKELY(!ret)) { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "nativefunction", DUK_STR_NOT_NATFUNC); - DUK_WO_NORETURN(return ret;); - } - return ret; -} - -DUK_EXTERNAL void duk_require_function(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - if (DUK_UNLIKELY(!duk_is_function(thr, idx))) { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "function", DUK_STR_NOT_FUNCTION); - DUK_WO_NORETURN(return;); - } -} - -DUK_EXTERNAL void duk_require_constructable(duk_hthread *thr, duk_idx_t idx) { - duk_hobject *h; - - DUK_ASSERT_API_ENTRY(thr); - - h = duk_require_hobject_accept_mask(thr, idx, DUK_TYPE_MASK_LIGHTFUNC); - if (DUK_UNLIKELY(h != NULL && !DUK_HOBJECT_HAS_CONSTRUCTABLE(h))) { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "constructable", DUK_STR_NOT_CONSTRUCTABLE); - DUK_WO_NORETURN(return;); - } - /* Lightfuncs (h == NULL) are constructable. */ -} - -DUK_EXTERNAL duk_hthread *duk_get_context(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - - return duk_get_hthread(thr, idx); -} - -DUK_EXTERNAL duk_hthread *duk_require_context(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - - return duk_require_hthread(thr, idx); -} - -DUK_EXTERNAL duk_hthread *duk_opt_context(duk_hthread *thr, duk_idx_t idx, duk_hthread *def_value) { - DUK_ASSERT_API_ENTRY(thr); - - if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { - return def_value; - } - return duk_require_context(thr, idx); -} - -DUK_EXTERNAL duk_hthread *duk_get_context_default(duk_hthread *thr, duk_idx_t idx, duk_hthread *def_value) { - duk_hthread *ret; - - DUK_ASSERT_API_ENTRY(thr); - - ret = duk_get_context(thr, idx); - if (ret != NULL) { - return ret; - } - - return def_value; -} - -DUK_EXTERNAL void *duk_get_heapptr(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - void *ret; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_get_tval_or_unused(thr, idx); - DUK_ASSERT(tv != NULL); - if (DUK_UNLIKELY(!DUK_TVAL_IS_HEAP_ALLOCATED(tv))) { - return (void *) NULL; - } - - ret = (void *) DUK_TVAL_GET_HEAPHDR(tv); - DUK_ASSERT(ret != NULL); - return ret; -} - -DUK_EXTERNAL void *duk_opt_heapptr(duk_hthread *thr, duk_idx_t idx, void *def_value) { - DUK_ASSERT_API_ENTRY(thr); - - if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { - return def_value; - } - return duk_require_heapptr(thr, idx); -} - -DUK_EXTERNAL void *duk_get_heapptr_default(duk_hthread *thr, duk_idx_t idx, void *def_value) { - void *ret; - - DUK_ASSERT_API_ENTRY(thr); - - ret = duk_get_heapptr(thr, idx); - if (ret != NULL) { - return ret; - } - - return def_value; -} - -DUK_EXTERNAL void *duk_require_heapptr(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - void *ret; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_get_tval_or_unused(thr, idx); - DUK_ASSERT(tv != NULL); - if (DUK_UNLIKELY(!DUK_TVAL_IS_HEAP_ALLOCATED(tv))) { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "heapobject", DUK_STR_UNEXPECTED_TYPE); - DUK_WO_NORETURN(return NULL;); - } - - ret = (void *) DUK_TVAL_GET_HEAPHDR(tv); - DUK_ASSERT(ret != NULL); - return ret; -} - -/* Internal helper for getting/requiring a duk_hobject with possible promotion. */ -DUK_LOCAL duk_hobject *duk__get_hobject_promote_mask_raw(duk_hthread *thr, duk_idx_t idx, duk_uint_t type_mask) { - duk_uint_t val_mask; - duk_hobject *res; - - DUK_CTX_ASSERT_VALID(thr); - - res = duk_get_hobject(thr, idx); /* common case, not promoted */ - if (DUK_LIKELY(res != NULL)) { - DUK_ASSERT(res != NULL); - return res; - } - - val_mask = duk_get_type_mask(thr, idx); - if (val_mask & type_mask) { - if (type_mask & DUK_TYPE_MASK_PROMOTE) { - res = duk_to_hobject(thr, idx); - DUK_ASSERT(res != NULL); - return res; - } else { - return NULL; /* accept without promoting */ - } - } - - if (type_mask & DUK_TYPE_MASK_THROW) { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "object", DUK_STR_NOT_OBJECT); - DUK_WO_NORETURN(return NULL;); - } - return NULL; -} - -/* Get a duk_hobject * at 'idx'; if the value is not an object but matches the - * supplied 'type_mask', promote it to an object and return the duk_hobject *. - * This is useful for call sites which want an object but also accept a plain - * buffer and/or a lightfunc which gets automatically promoted to an object. - * Return value is NULL if value is neither an object nor a plain type allowed - * by the mask. - */ -DUK_INTERNAL duk_hobject *duk_get_hobject_promote_mask(duk_hthread *thr, duk_idx_t idx, duk_uint_t type_mask) { - DUK_ASSERT_API_ENTRY(thr); - return duk__get_hobject_promote_mask_raw(thr, idx, type_mask | DUK_TYPE_MASK_PROMOTE); -} - -/* Like duk_get_hobject_promote_mask() but throw a TypeError instead of - * returning a NULL. - */ -DUK_INTERNAL duk_hobject *duk_require_hobject_promote_mask(duk_hthread *thr, duk_idx_t idx, duk_uint_t type_mask) { - DUK_ASSERT_API_ENTRY(thr); - return duk__get_hobject_promote_mask_raw(thr, idx, type_mask | DUK_TYPE_MASK_THROW | DUK_TYPE_MASK_PROMOTE); -} - -/* Require a duk_hobject * at 'idx'; if the value is not an object but matches the - * supplied 'type_mask', return a NULL instead. Otherwise throw a TypeError. - */ -DUK_INTERNAL duk_hobject *duk_require_hobject_accept_mask(duk_hthread *thr, duk_idx_t idx, duk_uint_t type_mask) { - DUK_ASSERT_API_ENTRY(thr); - return duk__get_hobject_promote_mask_raw(thr, idx, type_mask | DUK_TYPE_MASK_THROW); -} - -DUK_INTERNAL duk_hobject *duk_get_hobject_with_class(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t classnum) { - duk_hobject *h; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT_DISABLE(classnum >= 0); /* unsigned */ - DUK_ASSERT(classnum <= DUK_HOBJECT_CLASS_MAX); - - h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT); - if (DUK_UNLIKELY(h != NULL && DUK_HOBJECT_GET_CLASS_NUMBER(h) != classnum)) { - h = NULL; - } - return h; -} - -DUK_INTERNAL duk_hobject *duk_require_hobject_with_class(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t classnum) { - duk_hobject *h; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT_DISABLE(classnum >= 0); /* unsigned */ - DUK_ASSERT(classnum <= DUK_HOBJECT_CLASS_MAX); - - h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT); - if (DUK_UNLIKELY(!(h != NULL && DUK_HOBJECT_GET_CLASS_NUMBER(h) == classnum))) { - duk_hstring *h_class; - h_class = DUK_HTHREAD_GET_STRING(thr, DUK_HOBJECT_CLASS_NUMBER_TO_STRIDX(classnum)); - DUK_UNREF(h_class); - - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, (const char *) DUK_HSTRING_GET_DATA(h_class), DUK_STR_UNEXPECTED_TYPE); - DUK_WO_NORETURN(return NULL;); - } - return h; -} - -DUK_EXTERNAL duk_size_t duk_get_length(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_get_tval_or_unused(thr, idx); - DUK_ASSERT(tv != NULL); - - switch (DUK_TVAL_GET_TAG(tv)) { - case DUK_TAG_UNDEFINED: - case DUK_TAG_NULL: - case DUK_TAG_BOOLEAN: - case DUK_TAG_POINTER: - return 0; -#if defined(DUK_USE_PREFER_SIZE) - /* String and buffer have a virtual non-configurable .length property - * which is within size_t range so it can be looked up without specific - * type checks. Lightfuncs inherit from %NativeFunctionPrototype% - * which provides an inherited .length accessor; it could be overwritten - * to produce unexpected types or values, but just number convert and - * duk_size_t cast for now. - */ - case DUK_TAG_STRING: - case DUK_TAG_BUFFER: - case DUK_TAG_LIGHTFUNC: { - duk_size_t ret; - duk_get_prop_stridx(thr, idx, DUK_STRIDX_LENGTH); - ret = (duk_size_t) duk_to_number_m1(thr); - duk_pop_unsafe(thr); - return ret; - } -#else /* DUK_USE_PREFER_SIZE */ - case DUK_TAG_STRING: { - duk_hstring *h = DUK_TVAL_GET_STRING(tv); - DUK_ASSERT(h != NULL); - if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) { - return 0; - } - return (duk_size_t) DUK_HSTRING_GET_CHARLEN(h); - } - case DUK_TAG_BUFFER: { - duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv); - DUK_ASSERT(h != NULL); - return (duk_size_t) DUK_HBUFFER_GET_SIZE(h); - } - case DUK_TAG_LIGHTFUNC: { - /* We could look up the length from the lightfunc duk_tval, - * but since Duktape 2.2 lightfunc .length comes from - * %NativeFunctionPrototype% which can be overridden, so - * look up the property explicitly. - */ - duk_size_t ret; - duk_get_prop_stridx(thr, idx, DUK_STRIDX_LENGTH); - ret = (duk_size_t) duk_to_number_m1(thr); - duk_pop_unsafe(thr); - return ret; - } -#endif /* DUK_USE_PREFER_SIZE */ - case DUK_TAG_OBJECT: { - duk_hobject *h = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - return (duk_size_t) duk_hobject_get_length(thr, h); - } -#if defined(DUK_USE_FASTINT) - case DUK_TAG_FASTINT: -#endif - default: - /* number or 'unused' */ - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv) || DUK_TVAL_IS_UNUSED(tv)); - return 0; - } - - DUK_UNREACHABLE(); -} - -/* - * duk_known_xxx() helpers - * - * Used internally when we're 100% sure that a certain index is valid and - * contains an object of a certain type. For example, if we duk_push_object() - * we can then safely duk_known_hobject(thr, -1). These helpers just assert - * for the index and type, and if the assumptions are not valid, memory unsafe - * behavior happens. - */ - -DUK_LOCAL duk_heaphdr *duk__known_heaphdr(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - duk_heaphdr *h; - - DUK_CTX_ASSERT_VALID(thr); - if (idx < 0) { - tv = thr->valstack_top + idx; - } else { - tv = thr->valstack_bottom + idx; - } - DUK_ASSERT(tv >= thr->valstack_bottom); - DUK_ASSERT(tv < thr->valstack_top); - h = DUK_TVAL_GET_HEAPHDR(tv); - DUK_ASSERT(h != NULL); - return h; -} - -DUK_INTERNAL duk_hstring *duk_known_hstring(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(duk_get_hstring(thr, idx) != NULL); - return (duk_hstring *) duk__known_heaphdr(thr, idx); -} - -DUK_INTERNAL duk_hobject *duk_known_hobject(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(duk_get_hobject(thr, idx) != NULL); - return (duk_hobject *) duk__known_heaphdr(thr, idx); -} - -DUK_INTERNAL duk_hbuffer *duk_known_hbuffer(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(duk_get_hbuffer(thr, idx) != NULL); - return (duk_hbuffer *) duk__known_heaphdr(thr, idx); -} - -DUK_INTERNAL duk_hcompfunc *duk_known_hcompfunc(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(duk_get_hcompfunc(thr, idx) != NULL); - return (duk_hcompfunc *) duk__known_heaphdr(thr, idx); -} - -DUK_INTERNAL duk_hnatfunc *duk_known_hnatfunc(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(duk_get_hnatfunc(thr, idx) != NULL); - return (duk_hnatfunc *) duk__known_heaphdr(thr, idx); -} - -DUK_EXTERNAL void duk_set_length(duk_hthread *thr, duk_idx_t idx, duk_size_t len) { - DUK_ASSERT_API_ENTRY(thr); - - idx = duk_normalize_index(thr, idx); - duk_push_uint(thr, (duk_uint_t) len); - duk_put_prop_stridx(thr, idx, DUK_STRIDX_LENGTH); -} - -/* - * Conversions and coercions - * - * The conversion/coercions are in-place operations on the value stack. - * Some operations are implemented here directly, while others call a - * helper in duk_js_ops.c after validating arguments. - */ - -/* E5 Section 8.12.8 */ - -DUK_LOCAL duk_bool_t duk__defaultvalue_coerce_attempt(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t func_stridx) { - if (duk_get_prop_stridx(thr, idx, func_stridx)) { - /* [ ... func ] */ - if (duk_is_callable(thr, -1)) { - duk_dup(thr, idx); /* -> [ ... func this ] */ - duk_call_method(thr, 0); /* -> [ ... retval ] */ - if (duk_is_primitive(thr, -1)) { - duk_replace(thr, idx); - return 1; - } - /* [ ... retval ]; popped below */ - } - } - duk_pop_unsafe(thr); /* [ ... func/retval ] -> [ ... ] */ - return 0; -} - -DUK_EXTERNAL void duk_to_undefined(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_require_tval(thr, idx); - DUK_ASSERT(tv != NULL); - DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv); /* side effects */ -} - -DUK_EXTERNAL void duk_to_null(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_require_tval(thr, idx); - DUK_ASSERT(tv != NULL); - DUK_TVAL_SET_NULL_UPDREF(thr, tv); /* side effects */ -} - -/* E5 Section 9.1 */ -DUK_LOCAL const char * const duk__toprim_hint_strings[3] = { "default", "string", "number" }; -DUK_LOCAL void duk__to_primitive_helper(duk_hthread *thr, duk_idx_t idx, duk_int_t hint, duk_bool_t check_symbol) { - /* Inline initializer for coercers[] is not allowed by old compilers like BCC. */ - duk_small_uint_t coercers[2]; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(hint == DUK_HINT_NONE || hint == DUK_HINT_NUMBER || hint == DUK_HINT_STRING); - - idx = duk_require_normalize_index(thr, idx); - - /* If already primitive, return as is. */ - if (!duk_check_type_mask(thr, idx, DUK_TYPE_MASK_OBJECT | DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER)) { - DUK_ASSERT(!duk_is_buffer(thr, idx)); /* duk_to_string() relies on this behavior */ - return; - } - - /* @@toPrimitive lookup. Also do for plain buffers and lightfuncs - * which mimic objects. - */ - if (check_symbol && duk_get_method_stridx(thr, idx, DUK_STRIDX_WELLKNOWN_SYMBOL_TO_PRIMITIVE)) { - DUK_ASSERT(hint >= 0 && (duk_size_t) hint < sizeof(duk__toprim_hint_strings) / sizeof(const char *)); - duk_dup(thr, idx); - duk_push_string(thr, duk__toprim_hint_strings[hint]); - duk_call_method(thr, 1); /* [ ... method value hint ] -> [ ... res] */ - if (duk_check_type_mask(thr, -1, DUK_TYPE_MASK_OBJECT | DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER)) { - goto fail; - } - duk_replace(thr, idx); - return; - } - - /* Objects are coerced based on E5 specification. - * Lightfuncs are coerced because they behave like - * objects even if they're internally a primitive - * type. Same applies to plain buffers, which behave - * like ArrayBuffer objects since Duktape 2.x. - */ - - /* Hint magic for Date is unnecessary in ES2015 because of - * Date.prototype[@@toPrimitive]. However, it is needed if - * symbol support is not enabled. - */ -#if defined(DUK_USE_SYMBOL_BUILTIN) - if (hint == DUK_HINT_NONE) { - hint = DUK_HINT_NUMBER; - } -#else /* DUK_USE_SYMBOL_BUILTIN */ - if (hint == DUK_HINT_NONE) { - duk_small_uint_t class_number; - - class_number = duk_get_class_number(thr, idx); - if (class_number == DUK_HOBJECT_CLASS_DATE) { - hint = DUK_HINT_STRING; - } else { - hint = DUK_HINT_NUMBER; - } - } -#endif /* DUK_USE_SYMBOL_BUILTIN */ - - coercers[0] = DUK_STRIDX_VALUE_OF; - coercers[1] = DUK_STRIDX_TO_STRING; - if (hint == DUK_HINT_STRING) { - coercers[0] = DUK_STRIDX_TO_STRING; - coercers[1] = DUK_STRIDX_VALUE_OF; - } - - if (duk__defaultvalue_coerce_attempt(thr, idx, coercers[0])) { - DUK_ASSERT(!duk_is_buffer(thr, idx)); /* duk_to_string() relies on this behavior */ - return; - } - - if (duk__defaultvalue_coerce_attempt(thr, idx, coercers[1])) { - DUK_ASSERT(!duk_is_buffer(thr, idx)); /* duk_to_string() relies on this behavior */ - return; - } - -fail: - DUK_ERROR_TYPE(thr, DUK_STR_TOPRIMITIVE_FAILED); - DUK_WO_NORETURN(return;); -} - -DUK_EXTERNAL void duk_to_primitive(duk_hthread *thr, duk_idx_t idx, duk_int_t hint) { - duk__to_primitive_helper(thr, idx, hint, 1 /*check_symbol*/); -} - -#if defined(DUK_USE_SYMBOL_BUILTIN) -DUK_INTERNAL void duk_to_primitive_ordinary(duk_hthread *thr, duk_idx_t idx, duk_int_t hint) { - duk__to_primitive_helper(thr, idx, hint, 0 /*check_symbol*/); -} -#endif - -/* E5 Section 9.2 */ -DUK_EXTERNAL duk_bool_t duk_to_boolean(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - duk_bool_t val; - - DUK_ASSERT_API_ENTRY(thr); - - idx = duk_require_normalize_index(thr, idx); - tv = DUK_GET_TVAL_POSIDX(thr, idx); - DUK_ASSERT(tv != NULL); - - val = duk_js_toboolean(tv); - DUK_ASSERT(val == 0 || val == 1); - - /* Note: no need to re-lookup tv, conversion is side effect free. */ - DUK_ASSERT(tv != NULL); - DUK_TVAL_SET_BOOLEAN_UPDREF(thr, tv, val); /* side effects */ - return val; -} - -DUK_INTERNAL duk_bool_t duk_to_boolean_top_pop(duk_hthread *thr) { - duk_tval *tv; - duk_bool_t val; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_require_tval(thr, -1); - DUK_ASSERT(tv != NULL); - - val = duk_js_toboolean(tv); - DUK_ASSERT(val == 0 || val == 1); - - duk_pop_unsafe(thr); - return val; -} - -DUK_EXTERNAL duk_double_t duk_to_number(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - duk_double_t d; - - DUK_ASSERT_API_ENTRY(thr); - - /* XXX: No need to normalize; the whole operation could be inlined here to - * avoid 'tv' re-lookup. - */ - idx = duk_require_normalize_index(thr, idx); - tv = DUK_GET_TVAL_POSIDX(thr, idx); - DUK_ASSERT(tv != NULL); - d = duk_js_tonumber(thr, tv); /* XXX: fastint coercion? now result will always be a non-fastint */ - - /* ToNumber() may have side effects so must relookup 'tv'. */ - tv = DUK_GET_TVAL_POSIDX(thr, idx); - DUK_TVAL_SET_NUMBER_UPDREF(thr, tv, d); /* side effects */ - return d; -} - -DUK_INTERNAL duk_double_t duk_to_number_m1(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - return duk_to_number(thr, -1); -} -DUK_INTERNAL duk_double_t duk_to_number_m2(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - return duk_to_number(thr, -2); -} - -DUK_INTERNAL duk_double_t duk_to_number_tval(duk_hthread *thr, duk_tval *tv) { -#if defined(DUK_USE_PREFER_SIZE) - duk_double_t res; - - DUK_ASSERT_API_ENTRY(thr); - - duk_push_tval(thr, tv); - res = duk_to_number_m1(thr); - duk_pop_unsafe(thr); - return res; -#else - duk_double_t res; - duk_tval *tv_dst; - - DUK_ASSERT_API_ENTRY(thr); - DUK__ASSERT_SPACE(); - - tv_dst = thr->valstack_top++; - DUK_TVAL_SET_TVAL(tv_dst, tv); - DUK_TVAL_INCREF(thr, tv_dst); /* decref not necessary */ - res = duk_to_number_m1(thr); /* invalidates tv_dst */ - - tv_dst = --thr->valstack_top; - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_dst)); - DUK_ASSERT(!DUK_TVAL_NEEDS_REFCOUNT_UPDATE(tv_dst)); /* plain number */ - DUK_TVAL_SET_UNDEFINED(tv_dst); /* valstack init policy */ - - return res; -#endif -} - -/* XXX: combine all the integer conversions: they share everything - * but the helper function for coercion. - */ - -typedef duk_double_t (*duk__toint_coercer)(duk_hthread *thr, duk_tval *tv); - -DUK_LOCAL duk_double_t duk__to_int_uint_helper(duk_hthread *thr, duk_idx_t idx, duk__toint_coercer coerce_func) { - duk_tval *tv; - duk_double_t d; - - DUK_CTX_ASSERT_VALID(thr); - - tv = duk_require_tval(thr, idx); - DUK_ASSERT(tv != NULL); - -#if defined(DUK_USE_FASTINT) - /* If argument is a fastint, guarantee that it remains one. - * There's no downgrade check for other cases. - */ - if (DUK_TVAL_IS_FASTINT(tv)) { - /* XXX: Unnecessary conversion back and forth. */ - return (duk_double_t) DUK_TVAL_GET_FASTINT(tv); - } -#endif - d = coerce_func(thr, tv); - - /* XXX: fastint? */ - - /* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */ - tv = duk_require_tval(thr, idx); - DUK_TVAL_SET_NUMBER_UPDREF(thr, tv, d); /* side effects */ - return d; -} - -DUK_EXTERNAL duk_int_t duk_to_int(duk_hthread *thr, duk_idx_t idx) { - /* Value coercion (in stack): ToInteger(), E5 Section 9.4, - * API return value coercion: custom. - */ - DUK_ASSERT_API_ENTRY(thr); - (void) duk__to_int_uint_helper(thr, idx, duk_js_tointeger); - return (duk_int_t) duk__api_coerce_d2i(thr, idx, 0 /*def_value*/, 0 /*require*/); -} - -DUK_EXTERNAL duk_uint_t duk_to_uint(duk_hthread *thr, duk_idx_t idx) { - /* Value coercion (in stack): ToInteger(), E5 Section 9.4, - * API return value coercion: custom. - */ - DUK_ASSERT_API_ENTRY(thr); - (void) duk__to_int_uint_helper(thr, idx, duk_js_tointeger); - return (duk_uint_t) duk__api_coerce_d2ui(thr, idx, 0 /*def_value*/, 0 /*require*/); -} - -DUK_EXTERNAL duk_int32_t duk_to_int32(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - duk_int32_t ret; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_require_tval(thr, idx); - DUK_ASSERT(tv != NULL); - ret = duk_js_toint32(thr, tv); - - /* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */ - tv = duk_require_tval(thr, idx); - DUK_TVAL_SET_I32_UPDREF(thr, tv, ret); /* side effects */ - return ret; -} - -DUK_EXTERNAL duk_uint32_t duk_to_uint32(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - duk_uint32_t ret; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_require_tval(thr, idx); - DUK_ASSERT(tv != NULL); - ret = duk_js_touint32(thr, tv); - - /* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */ - tv = duk_require_tval(thr, idx); - DUK_TVAL_SET_U32_UPDREF(thr, tv, ret); /* side effects */ - return ret; -} - -DUK_EXTERNAL duk_uint16_t duk_to_uint16(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - duk_uint16_t ret; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_require_tval(thr, idx); - DUK_ASSERT(tv != NULL); - ret = duk_js_touint16(thr, tv); - - /* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */ - tv = duk_require_tval(thr, idx); - DUK_TVAL_SET_U32_UPDREF(thr, tv, ret); /* side effects */ - return ret; -} - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -/* Special coercion for Uint8ClampedArray. */ -DUK_INTERNAL duk_uint8_t duk_to_uint8clamped(duk_hthread *thr, duk_idx_t idx) { - duk_double_t d; - duk_double_t t; - duk_uint8_t ret; - - DUK_ASSERT_API_ENTRY(thr); - - /* XXX: Simplify this algorithm, should be possible to come up with - * a shorter and faster algorithm by inspecting IEEE representation - * directly. - */ - - d = duk_to_number(thr, idx); - if (d <= 0.0) { - return 0; - } else if (d >= 255) { - return 255; - } else if (DUK_ISNAN(d)) { - /* Avoid NaN-to-integer coercion as it is compiler specific. */ - return 0; - } - - t = d - DUK_FLOOR(d); - if (duk_double_equals(t, 0.5)) { - /* Exact halfway, round to even. */ - ret = (duk_uint8_t) d; - ret = (ret + 1) & 0xfe; /* Example: d=3.5, t=0.5 -> ret = (3 + 1) & 0xfe = 4 & 0xfe = 4 - * Example: d=4.5, t=0.5 -> ret = (4 + 1) & 0xfe = 5 & 0xfe = 4 - */ - } else { - /* Not halfway, round to nearest. */ - ret = (duk_uint8_t) (d + 0.5); - } - return ret; -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -DUK_EXTERNAL const char *duk_to_lstring(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len) { - DUK_ASSERT_API_ENTRY(thr); - - (void) duk_to_string(thr, idx); - DUK_ASSERT(duk_is_string(thr, idx)); - return duk_require_lstring(thr, idx, out_len); -} - -DUK_LOCAL duk_ret_t duk__safe_to_string_raw(duk_hthread *thr, void *udata) { - DUK_CTX_ASSERT_VALID(thr); - DUK_UNREF(udata); - - (void) duk_to_string(thr, -1); - return 1; -} - -DUK_EXTERNAL const char *duk_safe_to_lstring(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len) { - DUK_ASSERT_API_ENTRY(thr); - - idx = duk_require_normalize_index(thr, idx); - - /* We intentionally ignore the duk_safe_call() return value and only - * check the output type. This way we don't also need to check that - * the returned value is indeed a string in the success case. - */ - - duk_dup(thr, idx); - (void) duk_safe_call(thr, duk__safe_to_string_raw, NULL /*udata*/, 1 /*nargs*/, 1 /*nrets*/); - if (!duk_is_string(thr, -1)) { - /* Error: try coercing error to string once. */ - (void) duk_safe_call(thr, duk__safe_to_string_raw, NULL /*udata*/, 1 /*nargs*/, 1 /*nrets*/); - if (!duk_is_string(thr, -1)) { - /* Double error */ - duk_pop_unsafe(thr); - duk_push_hstring_stridx(thr, DUK_STRIDX_UC_ERROR); - } else { - ; - } - } else { - /* String; may be a symbol, accepted. */ - ; - } - DUK_ASSERT(duk_is_string(thr, -1)); - - duk_replace(thr, idx); - DUK_ASSERT(duk_get_string(thr, idx) != NULL); - return duk_get_lstring(thr, idx, out_len); -} - -DUK_EXTERNAL const char *duk_to_stacktrace(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - idx = duk_require_normalize_index(thr, idx); - - /* The expected argument to the call is an Error object. The stack - * trace is extracted without an inheritance-based instanceof check - * so that one can also extract the stack trace of a foreign error - * created in another Realm. Accept only a string .stack property. - */ - if (duk_is_object(thr, idx)) { - (void) duk_get_prop_string(thr, idx, "stack"); - if (duk_is_string(thr, -1)) { - duk_replace(thr, idx); - } else { - duk_pop(thr); - } - } - - return duk_to_string(thr, idx); -} - -DUK_LOCAL duk_ret_t duk__safe_to_stacktrace_raw(duk_hthread *thr, void *udata) { - DUK_CTX_ASSERT_VALID(thr); - DUK_UNREF(udata); - - (void) duk_to_stacktrace(thr, -1); - - return 1; -} - -DUK_EXTERNAL const char *duk_safe_to_stacktrace(duk_hthread *thr, duk_idx_t idx) { - duk_int_t rc; - - DUK_ASSERT_API_ENTRY(thr); - idx = duk_require_normalize_index(thr, idx); - - duk_dup(thr, idx); - rc = duk_safe_call(thr, duk__safe_to_stacktrace_raw, NULL /*udata*/, 1 /*nargs*/, 1 /*nrets*/); - if (rc != 0) { - /* Coercion failed. Try to coerce the coercion itself error - * to a stack trace once. If that also fails, return a fixed, - * preallocated 'Error' string to avoid potential infinite loop. - */ - rc = duk_safe_call(thr, duk__safe_to_stacktrace_raw, NULL /*udata*/, 1 /*nargs*/, 1 /*nrets*/); - if (rc != 0) { - duk_pop_unsafe(thr); - duk_push_hstring_stridx(thr, DUK_STRIDX_UC_ERROR); - } - } - duk_replace(thr, idx); - - return duk_get_string(thr, idx); -} - -DUK_INTERNAL duk_hstring *duk_to_property_key_hstring(duk_hthread *thr, duk_idx_t idx) { - duk_hstring *h; - - DUK_ASSERT_API_ENTRY(thr); - - duk_to_primitive(thr, idx, DUK_HINT_STRING); /* needed for e.g. Symbol objects */ - h = duk_get_hstring(thr, idx); - if (h == NULL) { - /* The "is string?" check may seem unnecessary, but as things - * are duk_to_hstring() invokes ToString() which fails for - * symbols. But since symbols are already strings for Duktape - * C API, we check for that before doing the coercion. - */ - h = duk_to_hstring(thr, idx); - } - DUK_ASSERT(h != NULL); - return h; -} - -#if defined(DUK_USE_DEBUGGER_SUPPORT) /* only needed by debugger for now */ -DUK_INTERNAL duk_hstring *duk_safe_to_hstring(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - - (void) duk_safe_to_string(thr, idx); - DUK_ASSERT(duk_is_string(thr, idx)); - DUK_ASSERT(duk_get_hstring(thr, idx) != NULL); - return duk_known_hstring(thr, idx); -} -#endif - -/* Push Object.prototype.toString() output for 'tv'. */ -DUK_INTERNAL void duk_push_class_string_tval(duk_hthread *thr, duk_tval *tv, duk_bool_t avoid_side_effects) { - duk_hobject *h_obj; - duk_small_uint_t classnum; - duk_small_uint_t stridx; - duk_tval tv_tmp; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(tv != NULL); - - /* Stabilize 'tv', duk_push_literal() may trigger side effects. */ - DUK_TVAL_SET_TVAL(&tv_tmp, tv); - tv = &tv_tmp; - - /* Conceptually for any non-undefined/null value we should do a - * ToObject() coercion and look up @@toStringTag (from the object - * prototype) to see if a custom result should be used, with the - * exception of Arrays which are handled specially first. - * - * We'd like to avoid the actual conversion, but even for primitive - * types the prototype may have @@toStringTag. What's worse, the - * @@toStringTag property may be a getter that must get the object - * coerced value (not the prototype) as its 'this' binding. - * - * For now, do an actual object coercion. This could be avoided by - * doing a side effect free lookup to see if a getter would be invoked. - * If not, the value can be read directly and the object coercion could - * be avoided. This may not be worth it in practice, because - * Object.prototype.toString() is usually not performance critical. - */ - - duk_push_literal(thr, "[object "); /* -> [ ... "[object" ] */ - - switch (DUK_TVAL_GET_TAG(tv)) { - case DUK_TAG_UNUSED: /* Treat like 'undefined', shouldn't happen. */ - case DUK_TAG_UNDEFINED: { - duk_push_hstring_stridx(thr, DUK_STRIDX_UC_UNDEFINED); - goto finish; - } - case DUK_TAG_NULL: { - duk_push_hstring_stridx(thr, DUK_STRIDX_UC_NULL); - goto finish; - } - } - - duk_push_tval(thr, tv); - tv = NULL; /* Invalidated by ToObject(). */ - h_obj = duk_to_hobject(thr, -1); - DUK_ASSERT(h_obj != NULL); - if (duk_js_isarray_hobject(h_obj)) { - stridx = DUK_STRIDX_UC_ARRAY; - } else { - /* [ ... "[object" obj ] */ - -#if defined(DUK_USE_SYMBOL_BUILTIN) - /* XXX: better handling with avoid_side_effects == 1; lookup tval - * without Proxy or getter side effects, and use it in sanitized - * form if it's a string. - */ - if (!avoid_side_effects) { - (void) duk_get_prop_stridx(thr, -1, DUK_STRIDX_WELLKNOWN_SYMBOL_TO_STRING_TAG); - if (duk_is_string_notsymbol(thr, -1)) { - duk_remove_m2(thr); - goto finish; - } - duk_pop_unsafe(thr); - } -#else - DUK_UNREF(avoid_side_effects); -#endif - - classnum = DUK_HOBJECT_GET_CLASS_NUMBER(h_obj); - stridx = DUK_HOBJECT_CLASS_NUMBER_TO_STRIDX(classnum); - } - duk_pop_unsafe(thr); - duk_push_hstring_stridx(thr, stridx); - -finish: - /* [ ... "[object" tag ] */ - duk_push_literal(thr, "]"); - duk_concat(thr, 3); /* [ ... "[object" tag "]" ] -> [ ... res ] */ -} - -/* XXX: other variants like uint, u32 etc */ -DUK_INTERNAL duk_int_t -duk_to_int_clamped_raw(duk_hthread *thr, duk_idx_t idx, duk_int_t minval, duk_int_t maxval, duk_bool_t *out_clamped) { - duk_tval *tv; - duk_tval tv_tmp; - duk_double_t d, dmin, dmax; - duk_int_t res; - duk_bool_t clamped = 0; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_require_tval(thr, idx); - DUK_ASSERT(tv != NULL); - d = duk_js_tointeger(thr, tv); /* E5 Section 9.4, ToInteger() */ - - dmin = (duk_double_t) minval; - dmax = (duk_double_t) maxval; - - if (d < dmin) { - clamped = 1; - res = minval; - d = dmin; - } else if (d > dmax) { - clamped = 1; - res = maxval; - d = dmax; - } else { - res = (duk_int_t) d; - } - DUK_UNREF(d); /* SCANBUILD: with suitable dmin/dmax limits 'd' is unused */ - /* 'd' and 'res' agree here */ - - /* Relookup in case duk_js_tointeger() ends up e.g. coercing an object. */ - tv = duk_get_tval(thr, idx); - DUK_ASSERT(tv != NULL); /* not popped by side effect */ - DUK_TVAL_SET_TVAL(&tv_tmp, tv); -#if defined(DUK_USE_FASTINT) -#if (DUK_INT_MAX <= 0x7fffffffL) - DUK_TVAL_SET_I32(tv, res); -#else - /* Clamping needed if duk_int_t is 64 bits. */ - if (res >= DUK_FASTINT_MIN && res <= DUK_FASTINT_MAX) { - DUK_TVAL_SET_FASTINT(tv, res); - } else { - DUK_TVAL_SET_NUMBER(tv, d); - } -#endif -#else - DUK_TVAL_SET_NUMBER(tv, d); /* no need to incref */ -#endif - DUK_TVAL_DECREF(thr, &tv_tmp); /* side effects */ - - if (out_clamped) { - *out_clamped = clamped; - } else { - /* coerced value is updated to value stack even when RangeError thrown */ - if (clamped) { - DUK_ERROR_RANGE(thr, DUK_STR_NUMBER_OUTSIDE_RANGE); - DUK_WO_NORETURN(return 0;); - } - } - - return res; -} - -DUK_INTERNAL duk_int_t duk_to_int_clamped(duk_hthread *thr, duk_idx_t idx, duk_idx_t minval, duk_idx_t maxval) { - duk_bool_t dummy; - - DUK_ASSERT_API_ENTRY(thr); - - return duk_to_int_clamped_raw(thr, idx, minval, maxval, &dummy); -} - -DUK_INTERNAL duk_int_t duk_to_int_check_range(duk_hthread *thr, duk_idx_t idx, duk_int_t minval, duk_int_t maxval) { - DUK_ASSERT_API_ENTRY(thr); - return duk_to_int_clamped_raw(thr, idx, minval, maxval, NULL); /* out_clamped==NULL -> RangeError if outside range */ -} - -DUK_EXTERNAL const char *duk_to_string(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - - DUK_ASSERT_API_ENTRY(thr); - - idx = duk_require_normalize_index(thr, idx); - tv = DUK_GET_TVAL_POSIDX(thr, idx); - DUK_ASSERT(tv != NULL); - - switch (DUK_TVAL_GET_TAG(tv)) { - case DUK_TAG_UNDEFINED: { - duk_push_hstring_stridx(thr, DUK_STRIDX_LC_UNDEFINED); - break; - } - case DUK_TAG_NULL: { - duk_push_hstring_stridx(thr, DUK_STRIDX_LC_NULL); - break; - } - case DUK_TAG_BOOLEAN: { - if (DUK_TVAL_GET_BOOLEAN(tv)) { - duk_push_hstring_stridx(thr, DUK_STRIDX_TRUE); - } else { - duk_push_hstring_stridx(thr, DUK_STRIDX_FALSE); - } - break; - } - case DUK_TAG_STRING: { - /* Nop for actual strings, TypeError for Symbols. - * Because various internals rely on ToString() coercion of - * internal strings, -allow- (NOP) string coercion for hidden - * symbols. - */ -#if 1 - duk_hstring *h; - h = DUK_TVAL_GET_STRING(tv); - DUK_ASSERT(h != NULL); - if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) { - DUK_ERROR_TYPE(thr, DUK_STR_CANNOT_STRING_COERCE_SYMBOL); - DUK_WO_NORETURN(goto skip_replace;); - } else { - goto skip_replace; - } -#else - goto skip_replace; -#endif - break; - } - case DUK_TAG_BUFFER: /* Go through Uint8Array.prototype.toString() for coercion. */ - case DUK_TAG_OBJECT: { - /* Plain buffers: go through ArrayBuffer.prototype.toString() - * for coercion. - * - * Symbol objects: duk_to_primitive() results in a plain symbol - * value, and duk_to_string() then causes a TypeError. - */ - duk_to_primitive(thr, idx, DUK_HINT_STRING); - DUK_ASSERT(!duk_is_buffer(thr, idx)); /* ToPrimitive() must guarantee */ - DUK_ASSERT(!duk_is_object(thr, idx)); - return duk_to_string(thr, idx); /* Note: recursive call */ - } - case DUK_TAG_POINTER: { - void *ptr = DUK_TVAL_GET_POINTER(tv); - if (ptr != NULL) { - duk_push_sprintf(thr, DUK_STR_FMT_PTR, (void *) ptr); - } else { - /* Represent a null pointer as 'null' to be consistent with - * the JX format variant. Native '%p' format for a NULL - * pointer may be e.g. '(nil)'. - */ - duk_push_hstring_stridx(thr, DUK_STRIDX_LC_NULL); - } - break; - } - case DUK_TAG_LIGHTFUNC: { - /* Should match Function.prototype.toString() */ - duk_push_lightfunc_tostring(thr, tv); - break; - } -#if defined(DUK_USE_FASTINT) - case DUK_TAG_FASTINT: -#endif - default: { - /* number */ - DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv)); - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); - duk_push_tval(thr, tv); - duk_numconv_stringify(thr, 10 /*radix*/, 0 /*precision:shortest*/, 0 /*force_exponential*/); - break; - } - } - - duk_replace(thr, idx); - -skip_replace: - DUK_ASSERT(duk_is_string(thr, idx)); - return duk_require_string(thr, idx); -} - -DUK_INTERNAL duk_hstring *duk_to_hstring(duk_hthread *thr, duk_idx_t idx) { - duk_hstring *ret; - - DUK_ASSERT_API_ENTRY(thr); - - duk_to_string(thr, idx); - ret = duk_get_hstring(thr, idx); - DUK_ASSERT(ret != NULL); - return ret; -} - -DUK_INTERNAL duk_hstring *duk_to_hstring_m1(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - return duk_to_hstring(thr, -1); -} - -DUK_INTERNAL duk_hstring *duk_to_hstring_acceptsymbol(duk_hthread *thr, duk_idx_t idx) { - duk_hstring *ret; - - DUK_ASSERT_API_ENTRY(thr); - - ret = duk_get_hstring(thr, idx); - if (DUK_UNLIKELY(ret && DUK_HSTRING_HAS_SYMBOL(ret))) { - return ret; - } - return duk_to_hstring(thr, idx); -} - -/* Convert a plain buffer or any buffer object into a string, using the buffer - * bytes 1:1 in the internal string representation. For views the active byte - * slice (not element slice interpreted as an initializer) is used. This is - * necessary in Duktape 2.x because ToString(plainBuffer) no longer creates a - * string with the same bytes as in the buffer but rather (usually) - * '[object ArrayBuffer]'. - */ -DUK_EXTERNAL const char *duk_buffer_to_string(duk_hthread *thr, duk_idx_t idx) { - void *ptr_src; - duk_size_t len; - const char *res; - - DUK_ASSERT_API_ENTRY(thr); - - idx = duk_require_normalize_index(thr, idx); - - ptr_src = duk_require_buffer_data(thr, idx, &len); - DUK_ASSERT(ptr_src != NULL || len == 0); - - res = duk_push_lstring(thr, (const char *) ptr_src, len); - duk_replace(thr, idx); - return res; -} - -DUK_EXTERNAL void *duk_to_buffer_raw(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size, duk_uint_t mode) { - duk_hbuffer *h_buf; - const duk_uint8_t *src_data; - duk_size_t src_size; - duk_uint8_t *dst_data; - - DUK_ASSERT_API_ENTRY(thr); - - idx = duk_require_normalize_index(thr, idx); - - h_buf = duk_get_hbuffer(thr, idx); - if (h_buf != NULL) { - /* Buffer is kept as is, with the fixed/dynamic nature of the - * buffer only changed if requested. An external buffer - * is converted into a non-external dynamic buffer in a - * duk_to_dynamic_buffer() call. - */ - duk_uint_t tmp; - duk_uint8_t *tmp_ptr; - - tmp_ptr = (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_buf); - src_data = (const duk_uint8_t *) tmp_ptr; - src_size = DUK_HBUFFER_GET_SIZE(h_buf); - - tmp = (DUK_HBUFFER_HAS_DYNAMIC(h_buf) ? DUK_BUF_MODE_DYNAMIC : DUK_BUF_MODE_FIXED); - if ((tmp == mode && !DUK_HBUFFER_HAS_EXTERNAL(h_buf)) || mode == DUK_BUF_MODE_DONTCARE) { - /* Note: src_data may be NULL if input is a zero-size - * dynamic buffer. - */ - dst_data = tmp_ptr; - goto skip_copy; - } - } else { - /* Non-buffer value is first ToString() coerced, then converted - * to a buffer (fixed buffer is used unless a dynamic buffer is - * explicitly requested). Symbols are rejected with a TypeError. - * XXX: C API could maybe allow symbol-to-buffer coercion? - */ - src_data = (const duk_uint8_t *) duk_to_lstring(thr, idx, &src_size); - } - - dst_data = (duk_uint8_t *) duk_push_buffer(thr, src_size, (mode == DUK_BUF_MODE_DYNAMIC) /*dynamic*/); - /* dst_data may be NULL if size is zero. */ - duk_memcpy_unsafe((void *) dst_data, (const void *) src_data, (size_t) src_size); - - duk_replace(thr, idx); -skip_copy: - - if (out_size) { - *out_size = src_size; - } - return dst_data; -} - -DUK_EXTERNAL void *duk_to_pointer(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - void *res; - - DUK_ASSERT_API_ENTRY(thr); - - idx = duk_require_normalize_index(thr, idx); - tv = DUK_GET_TVAL_POSIDX(thr, idx); - DUK_ASSERT(tv != NULL); - - switch (DUK_TVAL_GET_TAG(tv)) { - case DUK_TAG_UNDEFINED: - case DUK_TAG_NULL: - case DUK_TAG_BOOLEAN: - res = NULL; - break; - case DUK_TAG_POINTER: - res = DUK_TVAL_GET_POINTER(tv); - break; - case DUK_TAG_STRING: - case DUK_TAG_OBJECT: - case DUK_TAG_BUFFER: - /* Heap allocated: return heap pointer which is NOT useful - * for the caller, except for debugging. - */ - res = (void *) DUK_TVAL_GET_HEAPHDR(tv); - break; - case DUK_TAG_LIGHTFUNC: - /* Function pointers do not always cast correctly to void * - * (depends on memory and segmentation model for instance), - * so they coerce to NULL. - */ - res = NULL; - break; -#if defined(DUK_USE_FASTINT) - case DUK_TAG_FASTINT: -#endif - default: - /* number */ - DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv)); - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); - res = NULL; - break; - } - - duk_push_pointer(thr, res); - duk_replace(thr, idx); - return res; -} - -DUK_LOCAL void duk__push_func_from_lightfunc(duk_hthread *thr, duk_c_function func, duk_small_uint_t lf_flags) { - duk_idx_t nargs; - duk_uint_t flags = 0; /* shared flags for a subset of types */ - duk_small_uint_t lf_len; - duk_hnatfunc *nf; - - nargs = (duk_idx_t) DUK_LFUNC_FLAGS_GET_NARGS(lf_flags); - if (nargs == DUK_LFUNC_NARGS_VARARGS) { - nargs = (duk_idx_t) DUK_VARARGS; - } - - flags = DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_CONSTRUCTABLE | DUK_HOBJECT_FLAG_CALLABLE | - DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_FLAG_NATFUNC | DUK_HOBJECT_FLAG_NEWENV | DUK_HOBJECT_FLAG_STRICT | - DUK_HOBJECT_FLAG_NOTAIL | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION); - (void) duk__push_c_function_raw(thr, func, nargs, flags, DUK_BIDX_NATIVE_FUNCTION_PROTOTYPE); - - lf_len = DUK_LFUNC_FLAGS_GET_LENGTH(lf_flags); - if ((duk_idx_t) lf_len != nargs) { - /* Explicit length is only needed if it differs from 'nargs'. */ - duk_push_int(thr, (duk_int_t) lf_len); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE); - } - -#if defined(DUK_USE_FUNC_NAME_PROPERTY) - duk_push_lightfunc_name_raw(thr, func, lf_flags); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C); -#endif - - nf = duk_known_hnatfunc(thr, -1); - nf->magic = (duk_int16_t) DUK_LFUNC_FLAGS_GET_MAGIC(lf_flags); -} - -DUK_EXTERNAL void duk_to_object(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - duk_uint_t flags = 0; /* shared flags for a subset of types */ - duk_small_int_t proto = 0; - - DUK_ASSERT_API_ENTRY(thr); - - idx = duk_require_normalize_index(thr, idx); - tv = DUK_GET_TVAL_POSIDX(thr, idx); - DUK_ASSERT(tv != NULL); - - switch (DUK_TVAL_GET_TAG(tv)) { -#if !defined(DUK_USE_BUFFEROBJECT_SUPPORT) - case DUK_TAG_BUFFER: /* With no bufferobject support, don't object coerce. */ -#endif - case DUK_TAG_UNDEFINED: - case DUK_TAG_NULL: { - DUK_ERROR_TYPE(thr, DUK_STR_NOT_OBJECT_COERCIBLE); - DUK_WO_NORETURN(return;); - break; - } - case DUK_TAG_BOOLEAN: { - flags = - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BOOLEAN); - proto = DUK_BIDX_BOOLEAN_PROTOTYPE; - goto create_object; - } - case DUK_TAG_STRING: { - duk_hstring *h; - h = DUK_TVAL_GET_STRING(tv); - DUK_ASSERT(h != NULL); - if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) { - flags = DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_FASTREFS | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_SYMBOL); - proto = DUK_BIDX_SYMBOL_PROTOTYPE; - } else { - flags = DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_STRING); - proto = DUK_BIDX_STRING_PROTOTYPE; - } - goto create_object; - } - case DUK_TAG_OBJECT: { - /* nop */ - break; - } -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) - case DUK_TAG_BUFFER: { - /* A plain buffer object coerces to a full ArrayBuffer which - * is not fully transparent behavior (ToObject() should be a - * nop for an object). This behavior matches lightfuncs which - * also coerce to an equivalent Function object. There are - * also downsides to defining ToObject(plainBuffer) as a no-op; - * for example duk_to_hobject() could result in a NULL pointer. - */ - duk_hbuffer *h_buf; - - h_buf = DUK_TVAL_GET_BUFFER(tv); - DUK_ASSERT(h_buf != NULL); - duk_hbufobj_push_uint8array_from_plain(thr, h_buf); - goto replace_value; - } -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - case DUK_TAG_POINTER: { - flags = - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_POINTER); - proto = DUK_BIDX_POINTER_PROTOTYPE; - goto create_object; - } - case DUK_TAG_LIGHTFUNC: { - /* Lightfunc coerces to a Function instance with concrete - * properties. Since 'length' is virtual for Duktape/C - * functions, don't need to define that. The result is made - * extensible to mimic what happens to strings in object - * coercion: - * - * > Object.isExtensible(Object('foo')) - * true - */ - duk_small_uint_t lf_flags; - duk_c_function func; - - DUK_TVAL_GET_LIGHTFUNC(tv, func, lf_flags); - duk__push_func_from_lightfunc(thr, func, lf_flags); - goto replace_value; - } -#if defined(DUK_USE_FASTINT) - case DUK_TAG_FASTINT: -#endif - default: { - DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv)); - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); - flags = - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_NUMBER); - proto = DUK_BIDX_NUMBER_PROTOTYPE; - goto create_object; - } - } - DUK_ASSERT(duk_is_object(thr, idx)); - return; - -create_object: - (void) duk_push_object_helper(thr, flags, proto); - - /* Note: Boolean prototype's internal value property is not writable, - * but duk_xdef_prop_stridx() disregards the write protection. Boolean - * instances are immutable. - * - * String and buffer special behaviors are already enabled which is not - * ideal, but a write to the internal value is not affected by them. - */ - duk_dup(thr, idx); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE); - -replace_value: - duk_replace(thr, idx); - DUK_ASSERT(duk_is_object(thr, idx)); -} - -DUK_INTERNAL duk_hobject *duk_to_hobject(duk_hthread *thr, duk_idx_t idx) { - duk_hobject *ret; - - DUK_ASSERT_API_ENTRY(thr); - - duk_to_object(thr, idx); - ret = duk_known_hobject(thr, idx); - return ret; -} - -/* - * Type checking - */ - -DUK_LOCAL duk_bool_t duk__tag_check(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t tag) { - duk_tval *tv; - - tv = duk_get_tval_or_unused(thr, idx); - DUK_ASSERT(tv != NULL); - return (DUK_TVAL_GET_TAG(tv) == tag); -} - -DUK_LOCAL duk_bool_t duk__obj_flag_any_default_false(duk_hthread *thr, duk_idx_t idx, duk_uint_t flag_mask) { - duk_hobject *obj; - - DUK_ASSERT_API_ENTRY(thr); - - obj = duk_get_hobject(thr, idx); - if (obj) { - return (DUK_HEAPHDR_CHECK_FLAG_BITS((duk_heaphdr *) obj, flag_mask) ? 1 : 0); - } - return 0; -} - -DUK_INTERNAL duk_int_t duk_get_type_tval(duk_tval *tv) { - DUK_ASSERT(tv != NULL); - -#if defined(DUK_USE_PACKED_TVAL) - switch (DUK_TVAL_GET_TAG(tv)) { - case DUK_TAG_UNUSED: - return DUK_TYPE_NONE; - case DUK_TAG_UNDEFINED: - return DUK_TYPE_UNDEFINED; - case DUK_TAG_NULL: - return DUK_TYPE_NULL; - case DUK_TAG_BOOLEAN: - return DUK_TYPE_BOOLEAN; - case DUK_TAG_STRING: - return DUK_TYPE_STRING; - case DUK_TAG_OBJECT: - return DUK_TYPE_OBJECT; - case DUK_TAG_BUFFER: - return DUK_TYPE_BUFFER; - case DUK_TAG_POINTER: - return DUK_TYPE_POINTER; - case DUK_TAG_LIGHTFUNC: - return DUK_TYPE_LIGHTFUNC; -#if defined(DUK_USE_FASTINT) - case DUK_TAG_FASTINT: -#endif - default: - /* Note: number has no explicit tag (in 8-byte representation) */ - DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv)); - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); - return DUK_TYPE_NUMBER; - } -#else /* DUK_USE_PACKED_TVAL */ - DUK_ASSERT(DUK_TVAL_IS_VALID_TAG(tv)); - DUK_ASSERT(sizeof(duk__type_from_tag) / sizeof(duk_uint_t) == DUK_TAG_MAX - DUK_TAG_MIN + 1); - return (duk_int_t) duk__type_from_tag[DUK_TVAL_GET_TAG(tv) - DUK_TAG_MIN]; -#endif /* DUK_USE_PACKED_TVAL */ -} - -DUK_EXTERNAL duk_int_t duk_get_type(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_get_tval_or_unused(thr, idx); - DUK_ASSERT(tv != NULL); - - return duk_get_type_tval(tv); -} - -#if defined(DUK_USE_VERBOSE_ERRORS) && defined(DUK_USE_PARANOID_ERRORS) -DUK_LOCAL const char * const duk__type_names[] = { "none", "undefined", "null", "boolean", "number", - "string", "object", "buffer", "pointer", "lightfunc" }; - -DUK_INTERNAL const char *duk_get_type_name(duk_hthread *thr, duk_idx_t idx) { - duk_int_t type_tag; - - DUK_ASSERT_API_ENTRY(thr); - - type_tag = duk_get_type(thr, idx); - DUK_ASSERT(type_tag >= DUK_TYPE_MIN && type_tag <= DUK_TYPE_MAX); - DUK_ASSERT(DUK_TYPE_MIN == 0 && sizeof(duk__type_names) / sizeof(const char *) == DUK_TYPE_MAX + 1); - - return duk__type_names[type_tag]; -} -#endif /* DUK_USE_VERBOSE_ERRORS && DUK_USE_PARANOID_ERRORS */ - -DUK_INTERNAL duk_small_uint_t duk_get_class_number(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - duk_hobject *obj; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_get_tval_or_unused(thr, idx); - DUK_ASSERT(tv != NULL); - - switch (DUK_TVAL_GET_TAG(tv)) { - case DUK_TAG_OBJECT: - obj = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(obj != NULL); - return DUK_HOBJECT_GET_CLASS_NUMBER(obj); - case DUK_TAG_BUFFER: - /* Buffers behave like Uint8Array objects. */ - return DUK_HOBJECT_CLASS_UINT8ARRAY; - case DUK_TAG_LIGHTFUNC: - /* Lightfuncs behave like Function objects. */ - return DUK_HOBJECT_CLASS_FUNCTION; - default: - /* Primitive or UNUSED, no class number. */ - return DUK_HOBJECT_CLASS_NONE; - } -} - -DUK_EXTERNAL duk_bool_t duk_check_type(duk_hthread *thr, duk_idx_t idx, duk_int_t type) { - DUK_ASSERT_API_ENTRY(thr); - - return (duk_get_type(thr, idx) == type) ? 1 : 0; -} - -DUK_INTERNAL duk_uint_t duk_get_type_mask_tval(duk_tval *tv) { - DUK_ASSERT(tv != NULL); - -#if defined(DUK_USE_PACKED_TVAL) - switch (DUK_TVAL_GET_TAG(tv)) { - case DUK_TAG_UNUSED: - return DUK_TYPE_MASK_NONE; - case DUK_TAG_UNDEFINED: - return DUK_TYPE_MASK_UNDEFINED; - case DUK_TAG_NULL: - return DUK_TYPE_MASK_NULL; - case DUK_TAG_BOOLEAN: - return DUK_TYPE_MASK_BOOLEAN; - case DUK_TAG_STRING: - return DUK_TYPE_MASK_STRING; - case DUK_TAG_OBJECT: - return DUK_TYPE_MASK_OBJECT; - case DUK_TAG_BUFFER: - return DUK_TYPE_MASK_BUFFER; - case DUK_TAG_POINTER: - return DUK_TYPE_MASK_POINTER; - case DUK_TAG_LIGHTFUNC: - return DUK_TYPE_MASK_LIGHTFUNC; -#if defined(DUK_USE_FASTINT) - case DUK_TAG_FASTINT: -#endif - default: - /* Note: number has no explicit tag (in 8-byte representation) */ - DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv)); - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); - return DUK_TYPE_MASK_NUMBER; - } -#else /* DUK_USE_PACKED_TVAL */ - DUK_ASSERT(DUK_TVAL_IS_VALID_TAG(tv)); - DUK_ASSERT(sizeof(duk__type_mask_from_tag) / sizeof(duk_uint_t) == DUK_TAG_MAX - DUK_TAG_MIN + 1); - return duk__type_mask_from_tag[DUK_TVAL_GET_TAG(tv) - DUK_TAG_MIN]; -#endif /* DUK_USE_PACKED_TVAL */ -} - -DUK_EXTERNAL duk_uint_t duk_get_type_mask(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_get_tval_or_unused(thr, idx); - DUK_ASSERT(tv != NULL); - - return duk_get_type_mask_tval(tv); -} - -DUK_EXTERNAL duk_bool_t duk_check_type_mask(duk_hthread *thr, duk_idx_t idx, duk_uint_t mask) { - DUK_ASSERT_API_ENTRY(thr); - - if (DUK_LIKELY((duk_get_type_mask(thr, idx) & mask) != 0U)) { - return 1; - } - if (mask & DUK_TYPE_MASK_THROW) { - DUK_ERROR_TYPE(thr, DUK_STR_UNEXPECTED_TYPE); - DUK_WO_NORETURN(return 0;); - } - return 0; -} - -DUK_EXTERNAL duk_bool_t duk_is_undefined(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - return duk__tag_check(thr, idx, DUK_TAG_UNDEFINED); -} - -DUK_EXTERNAL duk_bool_t duk_is_null(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - return duk__tag_check(thr, idx, DUK_TAG_NULL); -} - -DUK_EXTERNAL duk_bool_t duk_is_boolean(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - return duk__tag_check(thr, idx, DUK_TAG_BOOLEAN); -} - -DUK_EXTERNAL duk_bool_t duk_is_number(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - - DUK_ASSERT_API_ENTRY(thr); - - /* - * Number is special because it doesn't have a specific - * tag in the 8-byte representation. - */ - - /* XXX: shorter version for unpacked representation? */ - - tv = duk_get_tval_or_unused(thr, idx); - DUK_ASSERT(tv != NULL); - return DUK_TVAL_IS_NUMBER(tv); -} - -DUK_EXTERNAL duk_bool_t duk_is_nan(duk_hthread *thr, duk_idx_t idx) { - /* XXX: This will now return false for non-numbers, even though they would - * coerce to NaN (as a general rule). In particular, duk_get_number() - * returns a NaN for non-numbers, so should this function also return - * true for non-numbers? - */ - - duk_tval *tv; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_get_tval_or_unused(thr, idx); - DUK_ASSERT(tv != NULL); - - /* XXX: for packed duk_tval an explicit "is number" check is unnecessary */ - if (!DUK_TVAL_IS_NUMBER(tv)) { - return 0; - } - return (duk_bool_t) DUK_ISNAN(DUK_TVAL_GET_NUMBER(tv)); -} - -DUK_EXTERNAL duk_bool_t duk_is_string(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - return duk__tag_check(thr, idx, DUK_TAG_STRING); -} - -DUK_INTERNAL duk_bool_t duk_is_string_notsymbol(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - return duk_get_hstring_notsymbol(thr, idx) != NULL; -} - -DUK_EXTERNAL duk_bool_t duk_is_object(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - return duk__tag_check(thr, idx, DUK_TAG_OBJECT); -} - -DUK_EXTERNAL duk_bool_t duk_is_buffer(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - return duk__tag_check(thr, idx, DUK_TAG_BUFFER); -} - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_EXTERNAL duk_bool_t duk_is_buffer_data(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_get_tval_or_unused(thr, idx); - DUK_ASSERT(tv != NULL); - if (DUK_TVAL_IS_BUFFER(tv)) { - return 1; - } else if (DUK_TVAL_IS_OBJECT(tv)) { - duk_hobject *h = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - if (DUK_HOBJECT_IS_BUFOBJ(h)) { - return 1; - } - } - return 0; -} -#else /* DUK_USE_BUFFEROBJECT_SUPPORT */ -DUK_EXTERNAL duk_bool_t duk_is_buffer_data(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - - return duk_is_buffer(thr, idx); -} - -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -DUK_EXTERNAL duk_bool_t duk_is_pointer(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - return duk__tag_check(thr, idx, DUK_TAG_POINTER); -} - -DUK_EXTERNAL duk_bool_t duk_is_lightfunc(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - return duk__tag_check(thr, idx, DUK_TAG_LIGHTFUNC); -} - -DUK_EXTERNAL duk_bool_t duk_is_symbol(duk_hthread *thr, duk_idx_t idx) { - duk_hstring *h; - - DUK_ASSERT_API_ENTRY(thr); - h = duk_get_hstring(thr, idx); - /* Use DUK_LIKELY() here because caller may be more likely to type - * check an expected symbol than not. - */ - if (DUK_LIKELY(h != NULL && DUK_HSTRING_HAS_SYMBOL(h))) { - return 1; - } - return 0; -} - -/* IsArray(), returns true for Array instance or Proxy of Array instance. */ -DUK_EXTERNAL duk_bool_t duk_is_array(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_get_tval(thr, idx); - if (tv) { - return duk_js_isarray(tv); - } - return 0; -} - -DUK_EXTERNAL duk_bool_t duk_is_function(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_get_tval_or_unused(thr, idx); - if (DUK_TVAL_IS_OBJECT(tv)) { - duk_hobject *h; - h = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - return DUK_HOBJECT_HAS_CALLABLE(h) ? 1 : 0; - } - if (DUK_TVAL_IS_LIGHTFUNC(tv)) { - return 1; - } - return 0; -} - -DUK_INTERNAL duk_bool_t duk_is_callable_tval(duk_hthread *thr, duk_tval *tv) { - DUK_ASSERT_API_ENTRY(thr); - - DUK_UNREF(thr); - - if (DUK_TVAL_IS_OBJECT(tv)) { - duk_hobject *h; - h = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - return DUK_HOBJECT_HAS_CALLABLE(h) ? 1 : 0; - } - if (DUK_TVAL_IS_LIGHTFUNC(tv)) { - return 1; - } - return 0; -} - -DUK_EXTERNAL duk_bool_t duk_is_constructable(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_get_tval_or_unused(thr, idx); - if (DUK_TVAL_IS_OBJECT(tv)) { - duk_hobject *h; - h = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - return DUK_HOBJECT_HAS_CONSTRUCTABLE(h) ? 1 : 0; - } - if (DUK_TVAL_IS_LIGHTFUNC(tv)) { - return 1; - } - return 0; -} - -DUK_EXTERNAL duk_bool_t duk_is_c_function(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - return duk__obj_flag_any_default_false(thr, idx, DUK_HOBJECT_FLAG_NATFUNC); -} - -DUK_EXTERNAL duk_bool_t duk_is_ecmascript_function(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - return duk__obj_flag_any_default_false(thr, idx, DUK_HOBJECT_FLAG_COMPFUNC); -} - -DUK_EXTERNAL duk_bool_t duk_is_bound_function(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - return duk__obj_flag_any_default_false(thr, idx, DUK_HOBJECT_FLAG_BOUNDFUNC); -} - -DUK_EXTERNAL duk_bool_t duk_is_thread(duk_hthread *thr, duk_idx_t idx) { - duk_hobject *obj; - - DUK_ASSERT_API_ENTRY(thr); - - obj = duk_get_hobject(thr, idx); - if (obj) { - return (DUK_HOBJECT_GET_CLASS_NUMBER(obj) == DUK_HOBJECT_CLASS_THREAD ? 1 : 0); - } - return 0; -} - -DUK_EXTERNAL duk_bool_t duk_is_fixed_buffer(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_get_tval_or_unused(thr, idx); - DUK_ASSERT(tv != NULL); - if (DUK_TVAL_IS_BUFFER(tv)) { - duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv); - DUK_ASSERT(h != NULL); - return (DUK_HBUFFER_HAS_DYNAMIC(h) ? 0 : 1); - } - return 0; -} - -DUK_EXTERNAL duk_bool_t duk_is_dynamic_buffer(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_get_tval_or_unused(thr, idx); - DUK_ASSERT(tv != NULL); - if (DUK_TVAL_IS_BUFFER(tv)) { - duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv); - DUK_ASSERT(h != NULL); - return (DUK_HBUFFER_HAS_DYNAMIC(h) && !DUK_HBUFFER_HAS_EXTERNAL(h) ? 1 : 0); - } - return 0; -} - -DUK_EXTERNAL duk_bool_t duk_is_external_buffer(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_get_tval_or_unused(thr, idx); - DUK_ASSERT(tv != NULL); - if (DUK_TVAL_IS_BUFFER(tv)) { - duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv); - DUK_ASSERT(h != NULL); - return (DUK_HBUFFER_HAS_DYNAMIC(h) && DUK_HBUFFER_HAS_EXTERNAL(h) ? 1 : 0); - } - return 0; -} - -DUK_EXTERNAL duk_errcode_t duk_get_error_code(duk_hthread *thr, duk_idx_t idx) { - duk_hobject *h; - duk_uint_t sanity; - - DUK_ASSERT_API_ENTRY(thr); - - h = duk_get_hobject(thr, idx); - - sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY; - do { - if (!h) { - return DUK_ERR_NONE; - } - - /* XXX: something more convenient? */ - - if (h == thr->builtins[DUK_BIDX_EVAL_ERROR_PROTOTYPE]) { - return DUK_ERR_EVAL_ERROR; - } - if (h == thr->builtins[DUK_BIDX_RANGE_ERROR_PROTOTYPE]) { - return DUK_ERR_RANGE_ERROR; - } - if (h == thr->builtins[DUK_BIDX_REFERENCE_ERROR_PROTOTYPE]) { - return DUK_ERR_REFERENCE_ERROR; - } - if (h == thr->builtins[DUK_BIDX_SYNTAX_ERROR_PROTOTYPE]) { - return DUK_ERR_SYNTAX_ERROR; - } - if (h == thr->builtins[DUK_BIDX_TYPE_ERROR_PROTOTYPE]) { - return DUK_ERR_TYPE_ERROR; - } - if (h == thr->builtins[DUK_BIDX_URI_ERROR_PROTOTYPE]) { - return DUK_ERR_URI_ERROR; - } - if (h == thr->builtins[DUK_BIDX_ERROR_PROTOTYPE]) { - return DUK_ERR_ERROR; - } - - h = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h); - } while (--sanity > 0); - - return DUK_ERR_NONE; -} - -/* - * Pushers - */ - -DUK_INTERNAL void duk_push_tval(duk_hthread *thr, duk_tval *tv) { - duk_tval *tv_slot; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(tv != NULL); - - DUK__CHECK_SPACE(); - tv_slot = thr->valstack_top++; - DUK_TVAL_SET_TVAL(tv_slot, tv); - DUK_TVAL_INCREF(thr, tv); /* no side effects */ -} - -DUK_EXTERNAL void duk_push_undefined(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - - DUK__CHECK_SPACE(); - - /* Because value stack init policy is 'undefined above top', - * we don't need to write, just assert. - */ - thr->valstack_top++; - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top - 1)); -} - -DUK_EXTERNAL void duk_push_null(duk_hthread *thr) { - duk_tval *tv_slot; - - DUK_ASSERT_API_ENTRY(thr); - DUK__CHECK_SPACE(); - tv_slot = thr->valstack_top++; - DUK_TVAL_SET_NULL(tv_slot); -} - -DUK_EXTERNAL void duk_push_boolean(duk_hthread *thr, duk_bool_t val) { - duk_tval *tv_slot; - duk_small_int_t b; - - DUK_ASSERT_API_ENTRY(thr); - DUK__CHECK_SPACE(); - b = (val ? 1 : 0); /* ensure value is 1 or 0 (not other non-zero) */ - tv_slot = thr->valstack_top++; - DUK_TVAL_SET_BOOLEAN(tv_slot, b); -} - -DUK_EXTERNAL void duk_push_true(duk_hthread *thr) { - duk_tval *tv_slot; - - DUK_ASSERT_API_ENTRY(thr); - DUK__CHECK_SPACE(); - tv_slot = thr->valstack_top++; - DUK_TVAL_SET_BOOLEAN_TRUE(tv_slot); -} - -DUK_EXTERNAL void duk_push_false(duk_hthread *thr) { - duk_tval *tv_slot; - - DUK_ASSERT_API_ENTRY(thr); - DUK__CHECK_SPACE(); - tv_slot = thr->valstack_top++; - DUK_TVAL_SET_BOOLEAN_FALSE(tv_slot); -} - -/* normalize NaN which may not match our canonical internal NaN */ -DUK_EXTERNAL void duk_push_number(duk_hthread *thr, duk_double_t val) { - duk_tval *tv_slot; - duk_double_union du; - - DUK_ASSERT_API_ENTRY(thr); - DUK__CHECK_SPACE(); - du.d = val; - DUK_DBLUNION_NORMALIZE_NAN_CHECK(&du); - tv_slot = thr->valstack_top++; - DUK_TVAL_SET_NUMBER(tv_slot, du.d); -} - -DUK_EXTERNAL void duk_push_int(duk_hthread *thr, duk_int_t val) { -#if defined(DUK_USE_FASTINT) - duk_tval *tv_slot; - - DUK_ASSERT_API_ENTRY(thr); - DUK__CHECK_SPACE(); - tv_slot = thr->valstack_top++; -#if DUK_INT_MAX <= 0x7fffffffL - DUK_TVAL_SET_I32(tv_slot, (duk_int32_t) val); -#else - if (val >= DUK_FASTINT_MIN && val <= DUK_FASTINT_MAX) { - DUK_TVAL_SET_FASTINT(tv_slot, (duk_int64_t) val); - } else { - duk_double_t = (duk_double_t) val; - DUK_TVAL_SET_NUMBER(tv_slot, d); - } -#endif -#else /* DUK_USE_FASTINT */ - duk_tval *tv_slot; - duk_double_t d; - - DUK_ASSERT_API_ENTRY(thr); - DUK__CHECK_SPACE(); - d = (duk_double_t) val; - tv_slot = thr->valstack_top++; - DUK_TVAL_SET_NUMBER(tv_slot, d); -#endif /* DUK_USE_FASTINT */ -} - -DUK_EXTERNAL void duk_push_uint(duk_hthread *thr, duk_uint_t val) { -#if defined(DUK_USE_FASTINT) - duk_tval *tv_slot; - - DUK_ASSERT_API_ENTRY(thr); - DUK__CHECK_SPACE(); - tv_slot = thr->valstack_top++; -#if DUK_UINT_MAX <= 0xffffffffUL - DUK_TVAL_SET_U32(tv_slot, (duk_uint32_t) val); -#else - if (val <= DUK_FASTINT_MAX) { /* val is unsigned so >= 0 */ - /* XXX: take advantage of val being unsigned, no need to mask */ - DUK_TVAL_SET_FASTINT(tv_slot, (duk_int64_t) val); - } else { - duk_double_t = (duk_double_t) val; - DUK_TVAL_SET_NUMBER(tv_slot, d); - } -#endif -#else /* DUK_USE_FASTINT */ - duk_tval *tv_slot; - duk_double_t d; - - DUK_ASSERT_API_ENTRY(thr); - DUK__CHECK_SPACE(); - d = (duk_double_t) val; - tv_slot = thr->valstack_top++; - DUK_TVAL_SET_NUMBER(tv_slot, d); -#endif /* DUK_USE_FASTINT */ -} - -DUK_EXTERNAL void duk_push_nan(duk_hthread *thr) { - duk_tval *tv_slot; - duk_double_union du; - - DUK_ASSERT_API_ENTRY(thr); - DUK__CHECK_SPACE(); - DUK_DBLUNION_SET_NAN(&du); - DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du)); - tv_slot = thr->valstack_top++; - DUK_TVAL_SET_NUMBER(tv_slot, du.d); -} - -DUK_EXTERNAL const char *duk_push_lstring(duk_hthread *thr, const char *str, duk_size_t len) { - duk_hstring *h; - duk_tval *tv_slot; - - DUK_ASSERT_API_ENTRY(thr); - - /* Check stack before interning (avoid hanging temp). */ - DUK__CHECK_SPACE(); - - /* NULL with zero length represents an empty string; NULL with higher - * length is also now treated like an empty string although it is - * a bit dubious. This is unlike duk_push_string() which pushes a - * 'null' if the input string is a NULL. - */ - if (DUK_UNLIKELY(str == NULL)) { - len = 0U; - } - - /* Check for maximum string length. */ - if (DUK_UNLIKELY(len > DUK_HSTRING_MAX_BYTELEN)) { - DUK_ERROR_RANGE(thr, DUK_STR_STRING_TOO_LONG); - DUK_WO_NORETURN(return NULL;); - } - - h = duk_heap_strtable_intern_checked(thr, (const duk_uint8_t *) str, (duk_uint32_t) len); - DUK_ASSERT(h != NULL); - - tv_slot = thr->valstack_top++; - DUK_TVAL_SET_STRING(tv_slot, h); - DUK_HSTRING_INCREF(thr, h); /* no side effects */ - - return (const char *) DUK_HSTRING_GET_DATA(h); -} - -DUK_EXTERNAL const char *duk_push_string(duk_hthread *thr, const char *str) { - DUK_ASSERT_API_ENTRY(thr); - - if (str) { - return duk_push_lstring(thr, str, DUK_STRLEN(str)); - } else { - duk_push_null(thr); - return NULL; - } -} - -#if !defined(DUK_USE_PREFER_SIZE) -#if defined(DUK_USE_LITCACHE_SIZE) -DUK_EXTERNAL const char *duk_push_literal_raw(duk_hthread *thr, const char *str, duk_size_t len) { - duk_hstring *h; - duk_tval *tv_slot; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(str != NULL); - DUK_ASSERT(str[len] == (char) 0); - - /* Check for maximum string length. */ - if (DUK_UNLIKELY(len > DUK_HSTRING_MAX_BYTELEN)) { - DUK_ERROR_RANGE(thr, DUK_STR_STRING_TOO_LONG); - DUK_WO_NORETURN(return NULL;); - } - - h = duk_heap_strtable_intern_literal_checked(thr, (const duk_uint8_t *) str, (duk_uint32_t) len); - DUK_ASSERT(h != NULL); - - tv_slot = thr->valstack_top++; - DUK_TVAL_SET_STRING(tv_slot, h); - DUK_HSTRING_INCREF(thr, h); /* no side effects */ - - return (const char *) DUK_HSTRING_GET_DATA(h); -} -#else /* DUK_USE_LITCACHE_SIZE */ -DUK_EXTERNAL const char *duk_push_literal_raw(duk_hthread *thr, const char *str, duk_size_t len) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(str != NULL); - DUK_ASSERT(str[len] == (char) 0); - - return duk_push_lstring(thr, str, len); -} -#endif /* DUK_USE_LITCACHE_SIZE */ -#endif /* !DUK_USE_PREFER_SIZE */ - -DUK_EXTERNAL void duk_push_pointer(duk_hthread *thr, void *val) { - duk_tval *tv_slot; - - DUK_ASSERT_API_ENTRY(thr); - DUK__CHECK_SPACE(); - tv_slot = thr->valstack_top++; - DUK_TVAL_SET_POINTER(tv_slot, val); -} - -DUK_INTERNAL duk_hstring *duk_push_uint_to_hstring(duk_hthread *thr, duk_uint_t i) { - duk_hstring *h_tmp; - - DUK_ASSERT_API_ENTRY(thr); - - /* XXX: this could be a direct DUK_SPRINTF to a buffer followed by duk_push_string() */ - duk_push_uint(thr, (duk_uint_t) i); - h_tmp = duk_to_hstring_m1(thr); - DUK_ASSERT(h_tmp != NULL); - return h_tmp; -} - -DUK_LOCAL void duk__push_this_helper(duk_hthread *thr, duk_small_uint_t check_object_coercible) { - duk_tval *tv_slot; - - DUK__CHECK_SPACE(); - - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top)); /* because of valstack init policy */ - tv_slot = thr->valstack_top++; - - if (DUK_UNLIKELY(thr->callstack_curr == NULL)) { - if (check_object_coercible) { - goto type_error; - } - /* 'undefined' already on stack top */ - } else { - duk_tval *tv; - - /* 'this' binding is just before current activation's bottom */ - DUK_ASSERT(thr->valstack_bottom > thr->valstack); - tv = thr->valstack_bottom - 1; - if (check_object_coercible && (DUK_TVAL_IS_UNDEFINED(tv) || DUK_TVAL_IS_NULL(tv))) { - /* XXX: better macro for DUK_TVAL_IS_UNDEFINED_OR_NULL(tv) */ - goto type_error; - } - - DUK_TVAL_SET_TVAL(tv_slot, tv); - DUK_TVAL_INCREF(thr, tv); - } - return; - -type_error: - DUK_ERROR_TYPE(thr, DUK_STR_NOT_OBJECT_COERCIBLE); - DUK_WO_NORETURN(return;); -} - -DUK_EXTERNAL void duk_push_this(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - - duk__push_this_helper(thr, 0 /*check_object_coercible*/); -} - -DUK_INTERNAL void duk_push_this_check_object_coercible(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - - duk__push_this_helper(thr, 1 /*check_object_coercible*/); -} - -DUK_INTERNAL duk_hobject *duk_push_this_coercible_to_object(duk_hthread *thr) { - duk_hobject *h; - - DUK_ASSERT_API_ENTRY(thr); - - duk__push_this_helper(thr, 1 /*check_object_coercible*/); - h = duk_to_hobject(thr, -1); - DUK_ASSERT(h != NULL); - return h; -} - -DUK_INTERNAL duk_hstring *duk_push_this_coercible_to_string(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - - duk__push_this_helper(thr, 1 /*check_object_coercible*/); - return duk_to_hstring_m1(thr); /* This will reject all Symbol values; accepts Symbol objects. */ -} - -DUK_INTERNAL duk_tval *duk_get_borrowed_this_tval(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - - DUK_ASSERT(thr->callstack_top > 0); /* caller required to know */ - DUK_ASSERT(thr->callstack_curr != NULL); /* caller required to know */ - DUK_ASSERT(thr->valstack_bottom > thr->valstack); /* consequence of above */ - DUK_ASSERT(thr->valstack_bottom - 1 >= thr->valstack); /* 'this' binding exists */ - - return thr->valstack_bottom - 1; -} - -DUK_EXTERNAL void duk_push_new_target(duk_hthread *thr) { - duk_activation *act; - - DUK_ASSERT_API_ENTRY(thr); - - /* https://www.ecma-international.org/ecma-262/6.0/#sec-meta-properties-runtime-semantics-evaluation - * https://www.ecma-international.org/ecma-262/6.0/#sec-getnewtarget - * - * No newTarget support now, so as a first approximation - * use the resolved (non-bound) target function. - * - * Check CONSTRUCT flag from current function, or if running - * direct eval, from a non-direct-eval parent (with possibly - * more than one nested direct eval). An alternative to this - * would be to store [[NewTarget]] as a hidden symbol of the - * lexical scope, and then just look up that variable. - * - * Calls from the application will either be for an empty - * call stack, or a Duktape/C function as the top activation. - */ - - act = thr->callstack_curr; - for (;;) { - if (act == NULL) { - break; - } - - if (act->flags & DUK_ACT_FLAG_CONSTRUCT) { - duk_push_tval(thr, &act->tv_func); - return; - } else if (act->flags & DUK_ACT_FLAG_DIRECT_EVAL) { - act = act->parent; - } else { - break; - } - } - - duk_push_undefined(thr); -} - -DUK_EXTERNAL void duk_push_current_function(duk_hthread *thr) { - duk_activation *act; - - DUK_ASSERT_API_ENTRY(thr); - - act = thr->callstack_curr; - if (act != NULL) { - duk_push_tval(thr, &act->tv_func); - } else { - duk_push_undefined(thr); - } -} - -DUK_EXTERNAL void duk_push_current_thread(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - - if (thr->heap->curr_thread) { - duk_push_hobject(thr, (duk_hobject *) thr->heap->curr_thread); - } else { - duk_push_undefined(thr); - } -} - -DUK_EXTERNAL void duk_push_global_object(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - - duk_push_hobject_bidx(thr, DUK_BIDX_GLOBAL); -} - -/* XXX: size optimize */ -DUK_LOCAL void duk__push_stash(duk_hthread *thr) { - if (!duk_xget_owndataprop_stridx_short(thr, -1, DUK_STRIDX_INT_VALUE)) { - DUK_DDD(DUK_DDDPRINT("creating heap/global/thread stash on first use")); - duk_pop_unsafe(thr); - duk_push_bare_object(thr); - duk_dup_top(thr); - duk_xdef_prop_stridx_short(thr, - -3, - DUK_STRIDX_INT_VALUE, - DUK_PROPDESC_FLAGS_C); /* [ ... parent stash stash ] -> [ ... parent stash ] */ - } - duk_remove_m2(thr); -} - -DUK_EXTERNAL void duk_push_heap_stash(duk_hthread *thr) { - duk_heap *heap; - DUK_ASSERT_API_ENTRY(thr); - heap = thr->heap; - DUK_ASSERT(heap->heap_object != NULL); - duk_push_hobject(thr, heap->heap_object); - duk__push_stash(thr); -} - -DUK_EXTERNAL void duk_push_global_stash(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - duk_push_global_object(thr); - duk__push_stash(thr); -} - -DUK_EXTERNAL void duk_push_thread_stash(duk_hthread *thr, duk_hthread *target_thr) { - DUK_ASSERT_API_ENTRY(thr); - if (DUK_UNLIKELY(target_thr == NULL)) { - DUK_ERROR_TYPE_INVALID_ARGS(thr); - DUK_WO_NORETURN(return;); - } - duk_push_hobject(thr, (duk_hobject *) target_thr); - duk__push_stash(thr); -} - -/* XXX: duk_ssize_t would be useful here */ -DUK_LOCAL duk_int_t duk__try_push_vsprintf(duk_hthread *thr, void *buf, duk_size_t sz, const char *fmt, va_list ap) { - duk_int_t len; - - DUK_CTX_ASSERT_VALID(thr); - DUK_UNREF(thr); - - /* NUL terminator handling doesn't matter here */ - len = DUK_VSNPRINTF((char *) buf, sz, fmt, ap); - if (len < (duk_int_t) sz) { - /* Return value of 'sz' or more indicates output was (potentially) - * truncated. - */ - return (duk_int_t) len; - } - return -1; -} - -DUK_EXTERNAL const char *duk_push_vsprintf(duk_hthread *thr, const char *fmt, va_list ap) { - duk_uint8_t stack_buf[DUK_PUSH_SPRINTF_INITIAL_SIZE]; - duk_size_t sz = DUK_PUSH_SPRINTF_INITIAL_SIZE; - duk_bool_t pushed_buf = 0; - void *buf; - duk_int_t len; /* XXX: duk_ssize_t */ - const char *res; - - DUK_ASSERT_API_ENTRY(thr); - - /* special handling of fmt==NULL */ - if (!fmt) { - duk_hstring *h_str; - duk_push_hstring_empty(thr); - h_str = duk_known_hstring(thr, -1); - return (const char *) DUK_HSTRING_GET_DATA(h_str); - } - - /* initial estimate based on format string */ - sz = DUK_STRLEN(fmt) + 16; /* format plus something to avoid just missing */ - if (sz < DUK_PUSH_SPRINTF_INITIAL_SIZE) { - sz = DUK_PUSH_SPRINTF_INITIAL_SIZE; - } - DUK_ASSERT(sz > 0); - - /* Try to make do with a stack buffer to avoid allocating a temporary buffer. - * This works 99% of the time which is quite nice. - */ - for (;;) { - va_list ap_copy; /* copied so that 'ap' can be reused */ - - if (sz <= sizeof(stack_buf)) { - buf = stack_buf; - } else if (!pushed_buf) { - pushed_buf = 1; - buf = duk_push_dynamic_buffer(thr, sz); - } else { - buf = duk_resize_buffer(thr, -1, sz); - } - DUK_ASSERT(buf != NULL); - - DUK_VA_COPY(ap_copy, ap); - len = duk__try_push_vsprintf(thr, buf, sz, fmt, ap_copy); - va_end(ap_copy); - if (len >= 0) { - break; - } - - /* failed, resize and try again */ - sz = sz * 2; - if (DUK_UNLIKELY(sz >= DUK_PUSH_SPRINTF_SANITY_LIMIT)) { - DUK_ERROR_RANGE(thr, DUK_STR_RESULT_TOO_LONG); - DUK_WO_NORETURN(return NULL;); - } - } - - /* Cannot use duk_buffer_to_string() on the buffer because it is - * usually larger than 'len'; 'buf' is also usually a stack buffer. - */ - res = duk_push_lstring(thr, (const char *) buf, (duk_size_t) len); /* [ buf? res ] */ - if (pushed_buf) { - duk_remove_m2(thr); - } - return res; -} - -DUK_EXTERNAL const char *duk_push_sprintf(duk_hthread *thr, const char *fmt, ...) { - va_list ap; - const char *ret; - - DUK_ASSERT_API_ENTRY(thr); - - /* allow fmt==NULL */ - va_start(ap, fmt); - ret = duk_push_vsprintf(thr, fmt, ap); - va_end(ap); - - return ret; -} - -DUK_INTERNAL duk_hobject *duk_push_object_helper(duk_hthread *thr, - duk_uint_t hobject_flags_and_class, - duk_small_int_t prototype_bidx) { - duk_tval *tv_slot; - duk_hobject *h; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(prototype_bidx == -1 || (prototype_bidx >= 0 && prototype_bidx < DUK_NUM_BUILTINS)); - - DUK__CHECK_SPACE(); - - h = duk_hobject_alloc(thr, hobject_flags_and_class); - DUK_ASSERT(h != NULL); - - DUK_DDD(DUK_DDDPRINT("created object with flags: 0x%08lx", (unsigned long) h->hdr.h_flags)); - - tv_slot = thr->valstack_top; - DUK_TVAL_SET_OBJECT(tv_slot, h); - DUK_HOBJECT_INCREF(thr, h); /* no side effects */ - thr->valstack_top++; - - /* object is now reachable */ - - if (prototype_bidx >= 0) { - DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, h, thr->builtins[prototype_bidx]); - } else { - DUK_ASSERT(prototype_bidx == -1); - DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h) == NULL); - } - - return h; -} - -DUK_INTERNAL duk_hobject *duk_push_object_helper_proto(duk_hthread *thr, duk_uint_t hobject_flags_and_class, duk_hobject *proto) { - duk_hobject *h; - - DUK_ASSERT_API_ENTRY(thr); - - h = duk_push_object_helper(thr, hobject_flags_and_class, -1); - DUK_ASSERT(h != NULL); - DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, h, proto); - return h; -} - -DUK_EXTERNAL duk_idx_t duk_push_object(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - - (void) duk_push_object_helper(thr, - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_FASTREFS | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT), - DUK_BIDX_OBJECT_PROTOTYPE); - return duk_get_top_index_unsafe(thr); -} - -DUK_EXTERNAL duk_idx_t duk_push_array(duk_hthread *thr) { - duk_uint_t flags; - duk_harray *obj; - duk_idx_t ret; - duk_tval *tv_slot; - - DUK_ASSERT_API_ENTRY(thr); - - flags = DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_FLAG_ARRAY_PART | - DUK_HOBJECT_FLAG_EXOTIC_ARRAY | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAY); - - obj = duk_harray_alloc(thr, flags); - DUK_ASSERT(obj != NULL); - - DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) obj, thr->builtins[DUK_BIDX_ARRAY_PROTOTYPE]); - - tv_slot = thr->valstack_top; - DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj); - DUK_HOBJECT_INCREF(thr, obj); /* XXX: could preallocate with refcount = 1 */ - ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom); - thr->valstack_top++; - - DUK_ASSERT(obj->length == 0); /* Array .length starts at zero. */ - return ret; -} - -DUK_EXTERNAL duk_idx_t duk_push_bare_array(duk_hthread *thr) { - duk_uint_t flags; - duk_harray *obj; - duk_idx_t ret; - duk_tval *tv_slot; - - DUK_ASSERT_API_ENTRY(thr); - - flags = DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_FLAG_ARRAY_PART | - DUK_HOBJECT_FLAG_EXOTIC_ARRAY | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAY); - - obj = duk_harray_alloc(thr, flags); - DUK_ASSERT(obj != NULL); - - tv_slot = thr->valstack_top; - DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj); - DUK_HOBJECT_INCREF(thr, obj); /* XXX: could preallocate with refcount = 1 */ - ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom); - thr->valstack_top++; - - DUK_ASSERT(obj->length == 0); /* Array .length starts at zero. */ - return ret; -} - -DUK_INTERNAL duk_harray *duk_push_harray(duk_hthread *thr) { - /* XXX: API call could do this directly, cast to void in API macro. */ - duk_harray *a; - - DUK_ASSERT_API_ENTRY(thr); - - (void) duk_push_array(thr); - DUK_ASSERT(DUK_TVAL_IS_OBJECT(thr->valstack_top - 1)); - a = (duk_harray *) DUK_TVAL_GET_OBJECT(thr->valstack_top - 1); - DUK_ASSERT(a != NULL); - return a; -} - -/* Push a duk_harray with preallocated size (.length also set to match size). - * Caller may then populate array part of the duk_harray directly. - */ -DUK_INTERNAL duk_harray *duk_push_harray_with_size(duk_hthread *thr, duk_uint32_t size) { - duk_harray *a; - - DUK_ASSERT_API_ENTRY(thr); - - a = duk_push_harray(thr); - - duk_hobject_realloc_props(thr, (duk_hobject *) a, 0, size, 0, 0); - a->length = size; - return a; -} - -DUK_INTERNAL duk_tval *duk_push_harray_with_size_outptr(duk_hthread *thr, duk_uint32_t size) { - duk_harray *a; - - DUK_ASSERT_API_ENTRY(thr); - - a = duk_push_harray_with_size(thr, size); - DUK_ASSERT(a != NULL); - return DUK_HOBJECT_A_GET_BASE(thr->heap, (duk_hobject *) a); -} - -DUK_EXTERNAL duk_idx_t duk_push_thread_raw(duk_hthread *thr, duk_uint_t flags) { - duk_hthread *obj; - duk_idx_t ret; - duk_tval *tv_slot; - - DUK_ASSERT_API_ENTRY(thr); - - DUK__CHECK_SPACE(); - - obj = duk_hthread_alloc(thr, DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_THREAD)); - DUK_ASSERT(obj != NULL); - obj->state = DUK_HTHREAD_STATE_INACTIVE; -#if defined(DUK_USE_ROM_STRINGS) - /* Nothing to initialize, strs[] is in ROM. */ -#else -#if defined(DUK_USE_HEAPPTR16) - obj->strs16 = thr->strs16; -#else - obj->strs = thr->strs; -#endif -#endif - DUK_DDD(DUK_DDDPRINT("created thread object with flags: 0x%08lx", (unsigned long) obj->obj.hdr.h_flags)); - - /* make the new thread reachable */ - tv_slot = thr->valstack_top; - DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj); - DUK_HTHREAD_INCREF(thr, obj); - ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom); - thr->valstack_top++; - - /* important to do this *after* pushing, to make the thread reachable for gc */ - if (DUK_UNLIKELY(!duk_hthread_init_stacks(thr->heap, obj))) { - DUK_ERROR_ALLOC_FAILED(thr); - DUK_WO_NORETURN(return 0;); - } - - /* initialize built-ins - either by copying or creating new ones */ - if (flags & DUK_THREAD_NEW_GLOBAL_ENV) { - duk_hthread_create_builtin_objects(obj); - } else { - duk_hthread_copy_builtin_objects(thr, obj); - } - - /* default prototype */ - DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) obj, obj->builtins[DUK_BIDX_THREAD_PROTOTYPE]); - - /* Initial stack size satisfies the stack slack constraints so there - * is no need to require stack here. - */ - DUK_ASSERT(DUK_VALSTACK_INITIAL_SIZE >= DUK_VALSTACK_API_ENTRY_MINIMUM + DUK_VALSTACK_INTERNAL_EXTRA); - - return ret; -} - -DUK_INTERNAL duk_hcompfunc *duk_push_hcompfunc(duk_hthread *thr) { - duk_hcompfunc *obj; - duk_tval *tv_slot; - - DUK_ASSERT_API_ENTRY(thr); - - DUK__CHECK_SPACE(); - - /* Template functions are not strictly constructable (they don't - * have a "prototype" property for instance), so leave the - * DUK_HOBJECT_FLAG_CONSRUCTABLE flag cleared here. - */ - - obj = duk_hcompfunc_alloc(thr, - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_CALLABLE | DUK_HOBJECT_FLAG_COMPFUNC | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION)); - if (DUK_UNLIKELY(obj == NULL)) { - DUK_ERROR_ALLOC_FAILED(thr); - DUK_WO_NORETURN(return NULL;); - } - - DUK_DDD(DUK_DDDPRINT("created compiled function object with flags: 0x%08lx", (unsigned long) obj->obj.hdr.h_flags)); - - tv_slot = thr->valstack_top; - DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj); - DUK_HOBJECT_INCREF(thr, obj); - thr->valstack_top++; - - /* default prototype */ - DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) obj) == NULL); - DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) obj, thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]); - - return obj; -} - -DUK_INTERNAL duk_hboundfunc *duk_push_hboundfunc(duk_hthread *thr) { - duk_hboundfunc *obj; - duk_tval *tv_slot; - - DUK_ASSERT_API_ENTRY(thr); - - DUK__CHECK_SPACE(); - obj = duk_hboundfunc_alloc(thr->heap, - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_BOUNDFUNC | DUK_HOBJECT_FLAG_CONSTRUCTABLE | - DUK_HOBJECT_FLAG_CALLABLE | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION)); - if (!obj) { - DUK_ERROR_ALLOC_FAILED(thr); - DUK_WO_NORETURN(return NULL;); - } - - tv_slot = thr->valstack_top++; - DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj); - DUK_HOBJECT_INCREF(thr, obj); - - /* Prototype is left as NULL because the caller always sets it (and - * it depends on the target function). - */ - DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) obj) == NULL); - - return obj; -} - -DUK_LOCAL duk_idx_t -duk__push_c_function_raw(duk_hthread *thr, duk_c_function func, duk_idx_t nargs, duk_uint_t flags, duk_small_uint_t proto_bidx) { - duk_hnatfunc *obj; - duk_idx_t ret; - duk_tval *tv_slot; - duk_int16_t func_nargs; - - DUK_CTX_ASSERT_VALID(thr); - - DUK__CHECK_SPACE(); - - if (DUK_UNLIKELY(func == NULL)) { - goto api_error; - } - if (nargs >= 0 && nargs < DUK_HNATFUNC_NARGS_MAX) { - func_nargs = (duk_int16_t) nargs; - } else if (nargs == DUK_VARARGS) { - func_nargs = DUK_HNATFUNC_NARGS_VARARGS; - } else { - goto api_error; - } - - obj = duk_hnatfunc_alloc(thr, flags); - DUK_ASSERT(obj != NULL); - - obj->func = func; - obj->nargs = func_nargs; - - DUK_DDD(DUK_DDDPRINT("created native function object with flags: 0x%08lx, nargs=%ld", - (unsigned long) obj->obj.hdr.h_flags, - (long) obj->nargs)); - - tv_slot = thr->valstack_top; - DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj); - DUK_HOBJECT_INCREF(thr, obj); - ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom); - thr->valstack_top++; - - DUK_ASSERT_BIDX_VALID(proto_bidx); - DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) obj, thr->builtins[proto_bidx]); - return ret; - -api_error: - DUK_ERROR_TYPE_INVALID_ARGS(thr); - DUK_WO_NORETURN(return 0;); -} - -DUK_EXTERNAL duk_idx_t duk_push_c_function(duk_hthread *thr, duk_c_function func, duk_int_t nargs) { - duk_uint_t flags; - - DUK_ASSERT_API_ENTRY(thr); - - flags = DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_CONSTRUCTABLE | DUK_HOBJECT_FLAG_CALLABLE | - DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_FLAG_NATFUNC | DUK_HOBJECT_FLAG_NEWENV | DUK_HOBJECT_FLAG_STRICT | - DUK_HOBJECT_FLAG_NOTAIL | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION); - - /* Default prototype is a Duktape specific %NativeFunctionPrototype% - * which provides .length and .name getters. - */ - return duk__push_c_function_raw(thr, func, nargs, flags, DUK_BIDX_NATIVE_FUNCTION_PROTOTYPE); -} - -DUK_INTERNAL void duk_push_c_function_builtin(duk_hthread *thr, duk_c_function func, duk_int_t nargs) { - duk_uint_t flags; - - DUK_ASSERT_API_ENTRY(thr); - - flags = DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_CONSTRUCTABLE | DUK_HOBJECT_FLAG_CALLABLE | - DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_FLAG_NATFUNC | DUK_HOBJECT_FLAG_NEWENV | DUK_HOBJECT_FLAG_STRICT | - DUK_HOBJECT_FLAG_NOTAIL | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION); - - /* Must use Function.prototype for standard built-in functions. */ - (void) duk__push_c_function_raw(thr, func, nargs, flags, DUK_BIDX_FUNCTION_PROTOTYPE); -} - -DUK_INTERNAL void duk_push_c_function_builtin_noconstruct(duk_hthread *thr, duk_c_function func, duk_int_t nargs) { - duk_uint_t flags; - - DUK_ASSERT_API_ENTRY(thr); - - flags = DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_CALLABLE | DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_FLAG_NATFUNC | - DUK_HOBJECT_FLAG_NEWENV | DUK_HOBJECT_FLAG_STRICT | DUK_HOBJECT_FLAG_NOTAIL | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION); - - /* Must use Function.prototype for standard built-in functions. */ - (void) duk__push_c_function_raw(thr, func, nargs, flags, DUK_BIDX_FUNCTION_PROTOTYPE); -} - -DUK_EXTERNAL duk_idx_t -duk_push_c_lightfunc(duk_hthread *thr, duk_c_function func, duk_idx_t nargs, duk_idx_t length, duk_int_t magic) { - duk_small_uint_t lf_flags; - duk_tval *tv_slot; - - DUK_ASSERT_API_ENTRY(thr); - - DUK__CHECK_SPACE(); - - if (nargs >= DUK_LFUNC_NARGS_MIN && nargs <= DUK_LFUNC_NARGS_MAX) { - /* as is */ - } else if (nargs == DUK_VARARGS) { - nargs = DUK_LFUNC_NARGS_VARARGS; - } else { - goto api_error; - } - if (DUK_UNLIKELY(!(length >= DUK_LFUNC_LENGTH_MIN && length <= DUK_LFUNC_LENGTH_MAX))) { - goto api_error; - } - if (DUK_UNLIKELY(!(magic >= DUK_LFUNC_MAGIC_MIN && magic <= DUK_LFUNC_MAGIC_MAX))) { - goto api_error; - } - - lf_flags = DUK_LFUNC_FLAGS_PACK((duk_small_int_t) magic, (duk_small_uint_t) length, (duk_small_uint_t) nargs); - tv_slot = thr->valstack_top++; - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv_slot)); - DUK_TVAL_SET_LIGHTFUNC(tv_slot, func, lf_flags); - DUK_ASSERT(tv_slot >= thr->valstack_bottom); - return (duk_idx_t) (tv_slot - thr->valstack_bottom); - -api_error: - DUK_ERROR_TYPE_INVALID_ARGS(thr); - DUK_WO_NORETURN(return 0;); -} - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_hbufobj *duk_push_bufobj_raw(duk_hthread *thr, - duk_uint_t hobject_flags_and_class, - duk_small_int_t prototype_bidx) { - duk_hbufobj *obj; - duk_tval *tv_slot; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(prototype_bidx >= 0); - - DUK__CHECK_SPACE(); - - obj = duk_hbufobj_alloc(thr, hobject_flags_and_class); - DUK_ASSERT(obj != NULL); - - DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) obj, thr->builtins[prototype_bidx]); - DUK_HBUFOBJ_ASSERT_VALID(obj); - - tv_slot = thr->valstack_top; - DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj); - DUK_HOBJECT_INCREF(thr, obj); - thr->valstack_top++; - - return obj; -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -/* XXX: There's quite a bit of overlap with buffer creation handling in - * duk_bi_buffer.c. Look for overlap and refactor. - */ -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -#define DUK__PACK_ARGS(classnum, protobidx, elemtype, elemshift, istypedarray) \ - (((classnum) << 24) | ((protobidx) << 16) | ((elemtype) << 8) | ((elemshift) << 4) | (istypedarray)) - -static const duk_uint32_t duk__bufobj_flags_lookup[] = { - /* Node.js Buffers are Uint8Array instances which inherit from Buffer.prototype. */ - DUK__PACK_ARGS(DUK_HOBJECT_CLASS_ARRAYBUFFER, - DUK_BIDX_ARRAYBUFFER_PROTOTYPE, - DUK_HBUFOBJ_ELEM_UINT8, - 0, - 0), /* DUK_BUFOBJ_ARRAYBUFFER */ - DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT8ARRAY, - DUK_BIDX_NODEJS_BUFFER_PROTOTYPE, - DUK_HBUFOBJ_ELEM_UINT8, - 0, - 1), /* DUK_BUFOBJ_NODEJS_BUFFER */ - DUK__PACK_ARGS(DUK_HOBJECT_CLASS_DATAVIEW, - DUK_BIDX_DATAVIEW_PROTOTYPE, - DUK_HBUFOBJ_ELEM_UINT8, - 0, - 0), /* DUK_BUFOBJ_DATAVIEW */ - DUK__PACK_ARGS(DUK_HOBJECT_CLASS_INT8ARRAY, - DUK_BIDX_INT8ARRAY_PROTOTYPE, - DUK_HBUFOBJ_ELEM_INT8, - 0, - 1), /* DUK_BUFOBJ_INT8ARRAY */ - DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT8ARRAY, - DUK_BIDX_UINT8ARRAY_PROTOTYPE, - DUK_HBUFOBJ_ELEM_UINT8, - 0, - 1), /* DUK_BUFOBJ_UINT8ARRAY */ - DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT8CLAMPEDARRAY, - DUK_BIDX_UINT8CLAMPEDARRAY_PROTOTYPE, - DUK_HBUFOBJ_ELEM_UINT8CLAMPED, - 0, - 1), /* DUK_BUFOBJ_UINT8CLAMPEDARRAY */ - DUK__PACK_ARGS(DUK_HOBJECT_CLASS_INT16ARRAY, - DUK_BIDX_INT16ARRAY_PROTOTYPE, - DUK_HBUFOBJ_ELEM_INT16, - 1, - 1), /* DUK_BUFOBJ_INT16ARRAY */ - DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT16ARRAY, - DUK_BIDX_UINT16ARRAY_PROTOTYPE, - DUK_HBUFOBJ_ELEM_UINT16, - 1, - 1), /* DUK_BUFOBJ_UINT16ARRAY */ - DUK__PACK_ARGS(DUK_HOBJECT_CLASS_INT32ARRAY, - DUK_BIDX_INT32ARRAY_PROTOTYPE, - DUK_HBUFOBJ_ELEM_INT32, - 2, - 1), /* DUK_BUFOBJ_INT32ARRAY */ - DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT32ARRAY, - DUK_BIDX_UINT32ARRAY_PROTOTYPE, - DUK_HBUFOBJ_ELEM_UINT32, - 2, - 1), /* DUK_BUFOBJ_UINT32ARRAY */ - DUK__PACK_ARGS(DUK_HOBJECT_CLASS_FLOAT32ARRAY, - DUK_BIDX_FLOAT32ARRAY_PROTOTYPE, - DUK_HBUFOBJ_ELEM_FLOAT32, - 2, - 1), /* DUK_BUFOBJ_FLOAT32ARRAY */ - DUK__PACK_ARGS(DUK_HOBJECT_CLASS_FLOAT64ARRAY, - DUK_BIDX_FLOAT64ARRAY_PROTOTYPE, - DUK_HBUFOBJ_ELEM_FLOAT64, - 3, - 1) /* DUK_BUFOBJ_FLOAT64ARRAY */ -}; -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_EXTERNAL void duk_push_buffer_object(duk_hthread *thr, - duk_idx_t idx_buffer, - duk_size_t byte_offset, - duk_size_t byte_length, - duk_uint_t flags) { - duk_hbufobj *h_bufobj; - duk_hbuffer *h_val; - duk_hobject *h_arraybuf; - duk_uint32_t tmp; - duk_uint_t classnum; - duk_uint_t protobidx; - duk_uint_t lookupidx; - duk_uint_t uint_offset, uint_length, uint_added; - - DUK_ASSERT_API_ENTRY(thr); - - /* The underlying types for offset/length in duk_hbufobj is - * duk_uint_t; make sure argument values fit. - */ - uint_offset = (duk_uint_t) byte_offset; - uint_length = (duk_uint_t) byte_length; - if (sizeof(duk_size_t) != sizeof(duk_uint_t)) { - if (DUK_UNLIKELY((duk_size_t) uint_offset != byte_offset || (duk_size_t) uint_length != byte_length)) { - goto range_error; - } - } - - DUK_ASSERT_DISABLE(flags >= 0); /* flags is unsigned */ - lookupidx = flags; - if (DUK_UNLIKELY(lookupidx >= sizeof(duk__bufobj_flags_lookup) / sizeof(duk_uint32_t))) { - goto arg_error; - } - tmp = duk__bufobj_flags_lookup[lookupidx]; - classnum = tmp >> 24; - protobidx = (tmp >> 16) & 0xff; - - h_arraybuf = duk_get_hobject(thr, idx_buffer); - if (h_arraybuf != NULL && /* argument is an object */ - flags != DUK_BUFOBJ_ARRAYBUFFER && /* creating a view */ - DUK_HOBJECT_GET_CLASS_NUMBER(h_arraybuf) == DUK_HOBJECT_CLASS_ARRAYBUFFER /* argument is ArrayBuffer */) { - duk_uint_t tmp_offset; - - DUK_HBUFOBJ_ASSERT_VALID((duk_hbufobj *) h_arraybuf); - h_val = ((duk_hbufobj *) h_arraybuf)->buf; - if (DUK_UNLIKELY(h_val == NULL)) { - goto arg_error; - } - - tmp_offset = uint_offset + ((duk_hbufobj *) h_arraybuf)->offset; - if (DUK_UNLIKELY(tmp_offset < uint_offset)) { - goto range_error; - } - uint_offset = tmp_offset; - - /* Note intentional difference to new TypedArray(): we allow - * caller to create an uncovered typed array (which is memory - * safe); new TypedArray() rejects it. - */ - } else { - /* Handle unexpected object arguments here too, for nice error - * messages. - */ - h_arraybuf = NULL; - h_val = duk_require_hbuffer(thr, idx_buffer); - } - - /* Wrap check for offset+length. */ - uint_added = uint_offset + uint_length; - if (DUK_UNLIKELY(uint_added < uint_offset)) { - goto range_error; - } - DUK_ASSERT(uint_added >= uint_offset && uint_added >= uint_length); - - DUK_ASSERT(h_val != NULL); - - h_bufobj = duk_push_bufobj_raw(thr, - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_BUFOBJ | DUK_HOBJECT_CLASS_AS_FLAGS(classnum), - (duk_small_int_t) protobidx); - DUK_ASSERT(h_bufobj != NULL); - - h_bufobj->buf = h_val; - DUK_HBUFFER_INCREF(thr, h_val); - h_bufobj->buf_prop = h_arraybuf; - DUK_HOBJECT_INCREF_ALLOWNULL(thr, h_arraybuf); - h_bufobj->offset = uint_offset; - h_bufobj->length = uint_length; - h_bufobj->shift = (tmp >> 4) & 0x0f; - h_bufobj->elem_type = (tmp >> 8) & 0xff; - h_bufobj->is_typedarray = tmp & 0x0f; - DUK_HBUFOBJ_ASSERT_VALID(h_bufobj); - - /* TypedArray views need an automatic ArrayBuffer which must be - * provided as .buffer property of the view. The ArrayBuffer is - * referenced via duk_hbufobj->buf_prop and an inherited .buffer - * accessor returns it. The ArrayBuffer is created lazily on first - * access if necessary so we don't need to do anything more here. - */ - return; - -range_error: - DUK_ERROR_RANGE(thr, DUK_STR_INVALID_ARGS); - DUK_WO_NORETURN(return;); - -arg_error: - DUK_ERROR_TYPE(thr, DUK_STR_INVALID_ARGS); - DUK_WO_NORETURN(return;); -} -#else /* DUK_USE_BUFFEROBJECT_SUPPORT */ -DUK_EXTERNAL void duk_push_buffer_object(duk_hthread *thr, - duk_idx_t idx_buffer, - duk_size_t byte_offset, - duk_size_t byte_length, - duk_uint_t flags) { - DUK_ASSERT_API_ENTRY(thr); - DUK_UNREF(idx_buffer); - DUK_UNREF(byte_offset); - DUK_UNREF(byte_length); - DUK_UNREF(flags); - DUK_ERROR_UNSUPPORTED(thr); - DUK_WO_NORETURN(return;); -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -DUK_EXTERNAL duk_idx_t duk_push_error_object_va_raw(duk_hthread *thr, - duk_errcode_t err_code, - const char *filename, - duk_int_t line, - const char *fmt, - va_list ap) { - duk_hobject *proto; -#if defined(DUK_USE_AUGMENT_ERROR_CREATE) - duk_small_uint_t augment_flags; -#endif - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(thr != NULL); - DUK_UNREF(filename); - DUK_UNREF(line); - - /* Error code also packs a tracedata related flag. */ -#if defined(DUK_USE_AUGMENT_ERROR_CREATE) - augment_flags = 0; - if (err_code & DUK_ERRCODE_FLAG_NOBLAME_FILELINE) { - augment_flags = DUK_AUGMENT_FLAG_NOBLAME_FILELINE; - } -#endif - err_code = err_code & (~DUK_ERRCODE_FLAG_NOBLAME_FILELINE); - - /* error gets its 'name' from the prototype */ - proto = duk_error_prototype_from_code(thr, err_code); - (void) duk_push_object_helper_proto(thr, - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_FASTREFS | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ERROR), - proto); - - /* ... and its 'message' from an instance property */ - if (fmt) { - duk_push_vsprintf(thr, fmt, ap); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_MESSAGE, DUK_PROPDESC_FLAGS_WC); - } else { - /* If no explicit message given, put error code into message field - * (as a number). This is not fully in keeping with the ECMAScript - * error model because messages are supposed to be strings (Error - * constructors use ToString() on their argument). However, it's - * probably more useful than having a separate 'code' property. - */ - duk_push_int(thr, err_code); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_MESSAGE, DUK_PROPDESC_FLAGS_WC); - } - - /* XXX: .code = err_code disabled, not sure if useful */ - - /* Creation time error augmentation */ -#if defined(DUK_USE_AUGMENT_ERROR_CREATE) - /* filename may be NULL in which case file/line is not recorded */ - duk_err_augment_error_create(thr, thr, filename, line, augment_flags); /* may throw an error */ -#endif - - return duk_get_top_index_unsafe(thr); -} - -DUK_EXTERNAL duk_idx_t -duk_push_error_object_raw(duk_hthread *thr, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, ...) { - va_list ap; - duk_idx_t ret; - - DUK_ASSERT_API_ENTRY(thr); - - va_start(ap, fmt); - ret = duk_push_error_object_va_raw(thr, err_code, filename, line, fmt, ap); - va_end(ap); - return ret; -} - -#if !defined(DUK_USE_VARIADIC_MACROS) -DUK_EXTERNAL duk_idx_t duk_push_error_object_stash(duk_hthread *thr, duk_errcode_t err_code, const char *fmt, ...) { - const char *filename = duk_api_global_filename; - duk_int_t line = duk_api_global_line; - va_list ap; - duk_idx_t ret; - - DUK_ASSERT_API_ENTRY(thr); - - duk_api_global_filename = NULL; - duk_api_global_line = 0; - va_start(ap, fmt); - ret = duk_push_error_object_va_raw(thr, err_code, filename, line, fmt, ap); - va_end(ap); - return ret; -} -#endif /* DUK_USE_VARIADIC_MACROS */ - -DUK_EXTERNAL void *duk_push_buffer_raw(duk_hthread *thr, duk_size_t size, duk_small_uint_t flags) { - duk_tval *tv_slot; - duk_hbuffer *h; - void *buf_data; - - DUK_ASSERT_API_ENTRY(thr); - - DUK__CHECK_SPACE(); - - /* Check for maximum buffer length. */ - if (DUK_UNLIKELY(size > DUK_HBUFFER_MAX_BYTELEN)) { - DUK_ERROR_RANGE(thr, DUK_STR_BUFFER_TOO_LONG); - DUK_WO_NORETURN(return NULL;); - } - - h = duk_hbuffer_alloc(thr->heap, size, flags, &buf_data); - if (DUK_UNLIKELY(h == NULL)) { - DUK_ERROR_ALLOC_FAILED(thr); - DUK_WO_NORETURN(return NULL;); - } - - tv_slot = thr->valstack_top; - DUK_TVAL_SET_BUFFER(tv_slot, h); - DUK_HBUFFER_INCREF(thr, h); - thr->valstack_top++; - - return (void *) buf_data; -} - -DUK_INTERNAL void *duk_push_fixed_buffer_nozero(duk_hthread *thr, duk_size_t len) { - DUK_ASSERT_API_ENTRY(thr); - return duk_push_buffer_raw(thr, len, DUK_BUF_FLAG_NOZERO); -} - -DUK_INTERNAL void *duk_push_fixed_buffer_zero(duk_hthread *thr, duk_size_t len) { - void *ptr; - - DUK_ASSERT_API_ENTRY(thr); - - ptr = duk_push_buffer_raw(thr, len, 0); - DUK_ASSERT(ptr != NULL); -#if !defined(DUK_USE_ZERO_BUFFER_DATA) - /* ES2015 requires zeroing even when DUK_USE_ZERO_BUFFER_DATA - * is not set. - */ - duk_memzero((void *) ptr, (size_t) len); -#endif - return ptr; -} - -#if defined(DUK_USE_ES6_PROXY) -DUK_EXTERNAL duk_idx_t duk_push_proxy(duk_hthread *thr, duk_uint_t proxy_flags) { - duk_hobject *h_target; - duk_hobject *h_handler; - duk_hproxy *h_proxy; - duk_tval *tv_slot; - duk_uint_t flags; - - DUK_ASSERT_API_ENTRY(thr); - DUK_UNREF(proxy_flags); - - /* DUK__CHECK_SPACE() unnecessary because the Proxy is written to - * value stack in-place. - */ -#if 0 - DUK__CHECK_SPACE(); -#endif - - /* Reject a proxy object as the target because it would need - * special handling in property lookups. (ES2015 has no such - * restriction.) - */ - h_target = duk_require_hobject_promote_mask(thr, -2, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER); - DUK_ASSERT(h_target != NULL); - if (DUK_HOBJECT_IS_PROXY(h_target)) { - goto fail_args; - } - - /* Reject a proxy object as the handler because it would cause - * potentially unbounded recursion. (ES2015 has no such - * restriction.) - * - * There's little practical reason to use a lightfunc or a plain - * buffer as the handler table: one could only provide traps via - * their prototype objects (Function.prototype and ArrayBuffer.prototype). - * Even so, as lightfuncs and plain buffers mimic their object - * counterparts, they're promoted and accepted here. - */ - h_handler = duk_require_hobject_promote_mask(thr, -1, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER); - DUK_ASSERT(h_handler != NULL); - if (DUK_HOBJECT_IS_PROXY(h_handler)) { - goto fail_args; - } - - /* XXX: Proxy object currently has no prototype, so ToPrimitive() - * coercion fails which is a bit confusing. - */ - - /* CALLABLE and CONSTRUCTABLE flags are copied from the (initial) - * target, see ES2015 Sections 9.5.15 and 9.5.13. - */ - flags = DUK_HEAPHDR_GET_FLAGS((duk_heaphdr *) h_target) & (DUK_HOBJECT_FLAG_CALLABLE | DUK_HOBJECT_FLAG_CONSTRUCTABLE); - flags |= DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ; - if (flags & DUK_HOBJECT_FLAG_CALLABLE) { - flags |= DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION) | DUK_HOBJECT_FLAG_SPECIAL_CALL; - } else { - flags |= DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT); - } - - h_proxy = duk_hproxy_alloc(thr, flags); - DUK_ASSERT(h_proxy != NULL); - DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) h_proxy) == NULL); - - /* Initialize Proxy target and handler references; avoid INCREF - * by stealing the value stack refcounts via direct value stack - * manipulation. INCREF is needed for the Proxy itself however. - */ - DUK_ASSERT(h_target != NULL); - h_proxy->target = h_target; - DUK_ASSERT(h_handler != NULL); - h_proxy->handler = h_handler; - DUK_HPROXY_ASSERT_VALID(h_proxy); - - DUK_ASSERT(duk_get_hobject(thr, -2) == h_target); - DUK_ASSERT(duk_get_hobject(thr, -1) == h_handler); - tv_slot = thr->valstack_top - 2; - DUK_ASSERT(tv_slot >= thr->valstack_bottom); - DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) h_proxy); - DUK_HOBJECT_INCREF(thr, (duk_hobject *) h_proxy); - tv_slot++; - DUK_TVAL_SET_UNDEFINED(tv_slot); /* [ ... target handler ] -> [ ... proxy undefined ] */ - thr->valstack_top = tv_slot; /* -> [ ... proxy ] */ - - DUK_DD(DUK_DDPRINT("created Proxy: %!iT", duk_get_tval(thr, -1))); - - return (duk_idx_t) (thr->valstack_top - thr->valstack_bottom - 1); - -fail_args: - DUK_ERROR_TYPE_INVALID_ARGS(thr); - DUK_WO_NORETURN(return 0;); -} -#else /* DUK_USE_ES6_PROXY */ -DUK_EXTERNAL duk_idx_t duk_push_proxy(duk_hthread *thr, duk_uint_t proxy_flags) { - DUK_ASSERT_API_ENTRY(thr); - DUK_UNREF(proxy_flags); - DUK_ERROR_UNSUPPORTED(thr); - DUK_WO_NORETURN(return 0;); -} -#endif /* DUK_USE_ES6_PROXY */ - -#if defined(DUK_USE_ASSERTIONS) -DUK_LOCAL void duk__validate_push_heapptr(duk_hthread *thr, void *ptr) { - duk_heaphdr *h; - duk_heaphdr *curr; - duk_bool_t found = 0; - - h = (duk_heaphdr *) ptr; - if (h == NULL) { - /* Allowed. */ - return; - } - DUK_ASSERT(h != NULL); - DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h)); - - /* One particular problem case is where an object has been - * queued for finalization but the finalizer hasn't yet been - * executed. - * - * Corner case: we're running in a finalizer for object X, and - * user code calls duk_push_heapptr() for X itself. In this - * case X will be in finalize_list, and we can detect the case - * by seeing that X's FINALIZED flag is set (which is done before - * the finalizer starts executing). - */ -#if defined(DUK_USE_FINALIZER_SUPPORT) - for (curr = thr->heap->finalize_list; curr != NULL; curr = DUK_HEAPHDR_GET_NEXT(thr->heap, curr)) { - /* FINALIZABLE is set for all objects on finalize_list - * except for an object being finalized right now. So - * can't assert here. - */ -#if 0 - DUK_ASSERT(DUK_HEAPHDR_HAS_FINALIZABLE(curr)); -#endif - - if (curr == h) { - if (DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr *) h)) { - /* Object is currently being finalized. */ - DUK_ASSERT(found == 0); /* Would indicate corrupted lists. */ - found = 1; - } else { - /* Not being finalized but on finalize_list, - * allowed since Duktape 2.1. - */ - DUK_ASSERT(found == 0); /* Would indicate corrupted lists. */ - found = 1; - } - } - } -#endif /* DUK_USE_FINALIZER_SUPPORT */ - -#if defined(DUK_USE_REFERENCE_COUNTING) - /* Because refzero_list is now processed to completion inline with - * no side effects, it's always empty here. - */ - DUK_ASSERT(thr->heap->refzero_list == NULL); -#endif - - /* If not present in finalize_list (or refzero_list), it - * must be either in heap_allocated or the string table. - */ - if (DUK_HEAPHDR_IS_STRING(h)) { - duk_uint32_t i; - duk_hstring *str; - duk_heap *heap = thr->heap; - - DUK_ASSERT(found == 0); - for (i = 0; i < heap->st_size; i++) { -#if defined(DUK_USE_STRTAB_PTRCOMP) - str = DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, heap->strtable16[i]); -#else - str = heap->strtable[i]; -#endif - while (str != NULL) { - if (str == (duk_hstring *) h) { - DUK_ASSERT(found == 0); /* Would indicate corrupted lists. */ - found = 1; - break; - } - str = str->hdr.h_next; - } - } - DUK_ASSERT(found != 0); - } else { - for (curr = thr->heap->heap_allocated; curr != NULL; curr = DUK_HEAPHDR_GET_NEXT(thr->heap, curr)) { - if (curr == h) { - DUK_ASSERT(found == 0); /* Would indicate corrupted lists. */ - found = 1; - } - } - DUK_ASSERT(found != 0); - } -} -#endif /* DUK_USE_ASSERTIONS */ - -DUK_EXTERNAL duk_idx_t duk_push_heapptr(duk_hthread *thr, void *ptr) { - duk_idx_t ret; - duk_tval *tv; - - DUK_ASSERT_API_ENTRY(thr); - - /* Reviving an object using a heap pointer is a dangerous API - * operation: if the application doesn't guarantee that the - * pointer target is always reachable, difficult-to-diagnose - * problems may ensue. Try to validate the 'ptr' argument to - * the extent possible. - */ - -#if defined(DUK_USE_ASSERTIONS) - duk__validate_push_heapptr(thr, ptr); -#endif - - DUK__CHECK_SPACE(); - - ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom); - tv = thr->valstack_top++; - - if (ptr == NULL) { - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv)); - return ret; - } - - DUK_HEAPHDR_ASSERT_VALID((duk_heaphdr *) ptr); - - /* If the argument is on finalize_list it has technically been - * unreachable before duk_push_heapptr() but it's still safe to - * push it. Starting from Duktape 2.1 allow application code to - * do so. There are two main cases: - * - * (1) The object is on the finalize_list and we're called by - * the finalizer for the object being finalized. In this - * case do nothing: finalize_list handling will deal with - * the object queueing. This is detected by the object not - * having a FINALIZABLE flag despite being on the finalize_list; - * the flag is cleared for the object being finalized only. - * - * (2) The object is on the finalize_list but is not currently - * being processed. In this case the object can be queued - * back to heap_allocated with a few flags cleared, in effect - * cancelling the finalizer. - */ - if (DUK_UNLIKELY(DUK_HEAPHDR_HAS_FINALIZABLE((duk_heaphdr *) ptr))) { - duk_heaphdr *curr; - - DUK_D(DUK_DPRINT("duk_push_heapptr() with a pointer on finalize_list, autorescue")); - - curr = (duk_heaphdr *) ptr; - DUK_HEAPHDR_CLEAR_FINALIZABLE(curr); - - /* Because FINALIZED is set prior to finalizer call, it will - * be set for the object being currently finalized, but not - * for other objects on finalize_list. - */ - DUK_HEAPHDR_CLEAR_FINALIZED(curr); - - /* Dequeue object from finalize_list and queue it back to - * heap_allocated. - */ -#if defined(DUK_USE_REFERENCE_COUNTING) - DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(curr) >= 1); /* Preincremented on finalize_list insert. */ - DUK_HEAPHDR_PREDEC_REFCOUNT(curr); -#endif - DUK_HEAP_REMOVE_FROM_FINALIZE_LIST(thr->heap, curr); - DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(thr->heap, curr); - - /* Continue with the rest. */ - } - - switch (DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) ptr)) { - case DUK_HTYPE_STRING: - DUK_TVAL_SET_STRING(tv, (duk_hstring *) ptr); - break; - case DUK_HTYPE_OBJECT: - DUK_TVAL_SET_OBJECT(tv, (duk_hobject *) ptr); - break; - default: - DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) ptr) == DUK_HTYPE_BUFFER); - DUK_TVAL_SET_BUFFER(tv, (duk_hbuffer *) ptr); - break; - } - - DUK_HEAPHDR_INCREF(thr, (duk_heaphdr *) ptr); - - return ret; -} - -/* Push object with no prototype, i.e. a "bare" object. */ -DUK_EXTERNAL duk_idx_t duk_push_bare_object(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - - (void) duk_push_object_helper(thr, - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_FASTREFS | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT), - -1); /* no prototype */ - return duk_get_top_index_unsafe(thr); -} - -DUK_INTERNAL void duk_push_hstring(duk_hthread *thr, duk_hstring *h) { - duk_tval tv; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(h != NULL); - - DUK_TVAL_SET_STRING(&tv, h); - duk_push_tval(thr, &tv); -} - -DUK_INTERNAL void duk_push_hstring_stridx(duk_hthread *thr, duk_small_uint_t stridx) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT_STRIDX_VALID(stridx); - duk_push_hstring(thr, DUK_HTHREAD_GET_STRING(thr, stridx)); -} - -DUK_INTERNAL void duk_push_hstring_empty(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - duk_push_hstring(thr, DUK_HTHREAD_GET_STRING(thr, DUK_STRIDX_EMPTY_STRING)); -} - -DUK_INTERNAL void duk_push_hobject(duk_hthread *thr, duk_hobject *h) { - duk_tval tv; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(h != NULL); - - DUK_TVAL_SET_OBJECT(&tv, h); - duk_push_tval(thr, &tv); -} - -DUK_INTERNAL void duk_push_hbuffer(duk_hthread *thr, duk_hbuffer *h) { - duk_tval tv; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(h != NULL); - - DUK_TVAL_SET_BUFFER(&tv, h); - duk_push_tval(thr, &tv); -} - -DUK_INTERNAL void duk_push_hobject_bidx(duk_hthread *thr, duk_small_int_t builtin_idx) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(builtin_idx >= 0 && builtin_idx < DUK_NUM_BUILTINS); - DUK_ASSERT(thr->builtins[builtin_idx] != NULL); - - duk_push_hobject(thr, thr->builtins[builtin_idx]); -} - -/* - * Poppers - */ - -DUK_LOCAL DUK_ALWAYS_INLINE void duk__pop_n_unsafe_raw(duk_hthread *thr, duk_idx_t count) { - duk_tval *tv; -#if defined(DUK_USE_REFERENCE_COUNTING) - duk_tval *tv_end; -#endif - - DUK_CTX_ASSERT_VALID(thr); - DUK_ASSERT(count >= 0); - DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) count); - -#if defined(DUK_USE_REFERENCE_COUNTING) - tv = thr->valstack_top; - tv_end = tv - count; - while (tv != tv_end) { - tv--; - DUK_ASSERT(tv >= thr->valstack_bottom); - DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ(thr, tv); - } - thr->valstack_top = tv; - DUK_REFZERO_CHECK_FAST(thr); -#else - tv = thr->valstack_top; - while (count > 0) { - count--; - tv--; - DUK_ASSERT(tv >= thr->valstack_bottom); - DUK_TVAL_SET_UNDEFINED(tv); - } - thr->valstack_top = tv; -#endif - - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); -} - -DUK_EXTERNAL void duk_pop_n(duk_hthread *thr, duk_idx_t count) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - - if (DUK_UNLIKELY((duk_uidx_t) (thr->valstack_top - thr->valstack_bottom) < (duk_uidx_t) count)) { - DUK_ERROR_RANGE_INVALID_COUNT(thr); - DUK_WO_NORETURN(return;); - } - DUK_ASSERT(count >= 0); - - duk__pop_n_unsafe_raw(thr, count); -} - -#if defined(DUK_USE_PREFER_SIZE) -DUK_INTERNAL void duk_pop_n_unsafe(duk_hthread *thr, duk_idx_t count) { - DUK_ASSERT_API_ENTRY(thr); - duk_pop_n(thr, count); -} -#else /* DUK_USE_PREFER_SIZE */ -DUK_INTERNAL void duk_pop_n_unsafe(duk_hthread *thr, duk_idx_t count) { - DUK_ASSERT_API_ENTRY(thr); - duk__pop_n_unsafe_raw(thr, count); -} -#endif /* DUK_USE_PREFER_SIZE */ - -/* Pop N elements without DECREF (in effect "stealing" any actual refcounts). */ -#if defined(DUK_USE_REFERENCE_COUNTING) -DUK_INTERNAL void duk_pop_n_nodecref_unsafe(duk_hthread *thr, duk_idx_t count) { - duk_tval *tv; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(count >= 0); - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) count); - - tv = thr->valstack_top; - while (count > 0) { - count--; - tv--; - DUK_ASSERT(tv >= thr->valstack_bottom); - DUK_TVAL_SET_UNDEFINED(tv); - } - thr->valstack_top = tv; - - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); -} -#else /* DUK_USE_REFERENCE_COUNTING */ -DUK_INTERNAL void duk_pop_n_nodecref_unsafe(duk_hthread *thr, duk_idx_t count) { - DUK_ASSERT_API_ENTRY(thr); - duk_pop_n_unsafe(thr, count); -} -#endif /* DUK_USE_REFERENCE_COUNTING */ - -/* Popping one element is called so often that when footprint is not an issue, - * compile a specialized function for it. - */ -#if defined(DUK_USE_PREFER_SIZE) -DUK_EXTERNAL void duk_pop(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - duk_pop_n(thr, 1); -} -DUK_INTERNAL void duk_pop_unsafe(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - duk_pop_n_unsafe(thr, 1); -} -DUK_INTERNAL void duk_pop_nodecref_unsafe(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - duk_pop_n_nodecref_unsafe(thr, 1); -} -#else /* DUK_USE_PREFER_SIZE */ -DUK_LOCAL DUK_ALWAYS_INLINE void duk__pop_unsafe_raw(duk_hthread *thr) { - duk_tval *tv; - - DUK_CTX_ASSERT_VALID(thr); - DUK_ASSERT(thr->valstack_top != thr->valstack_bottom); - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) 1); - - tv = --thr->valstack_top; - DUK_ASSERT(tv >= thr->valstack_bottom); -#if defined(DUK_USE_REFERENCE_COUNTING) - DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv); /* side effects */ -#else - DUK_TVAL_SET_UNDEFINED(tv); -#endif - - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); -} -DUK_EXTERNAL void duk_pop(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - if (DUK_UNLIKELY(thr->valstack_top == thr->valstack_bottom)) { - DUK_ERROR_RANGE_INVALID_COUNT(thr); - DUK_WO_NORETURN(return;); - } - - duk__pop_unsafe_raw(thr); -} -DUK_INTERNAL void duk_pop_unsafe(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - duk__pop_unsafe_raw(thr); -} -DUK_INTERNAL void duk_pop_nodecref_unsafe(duk_hthread *thr) { - duk_tval *tv; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(thr->valstack_top != thr->valstack_bottom); - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) 1); - - tv = --thr->valstack_top; - DUK_ASSERT(tv >= thr->valstack_bottom); - DUK_TVAL_SET_UNDEFINED(tv); - - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); -} -#endif /* !DUK_USE_PREFER_SIZE */ - -#if defined(DUK_USE_PREFER_SIZE) -DUK_INTERNAL void duk_pop_undefined(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - duk_pop_nodecref_unsafe(thr); -} -#else /* DUK_USE_PREFER_SIZE */ -DUK_INTERNAL void duk_pop_undefined(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(thr->valstack_top != thr->valstack_bottom); - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) 1); - - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top - 1)); - thr->valstack_top--; - - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); -} -#endif /* !DUK_USE_PREFER_SIZE */ - -#if defined(DUK_USE_PREFER_SIZE) -DUK_EXTERNAL void duk_pop_2(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - duk_pop_n(thr, 2); -} -DUK_INTERNAL void duk_pop_2_unsafe(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - duk_pop_n_unsafe(thr, 2); -} -DUK_INTERNAL void duk_pop_2_nodecref_unsafe(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - duk_pop_n_nodecref_unsafe(thr, 2); -} -#else -DUK_LOCAL DUK_ALWAYS_INLINE void duk__pop_2_unsafe_raw(duk_hthread *thr) { - duk_tval *tv; - - DUK_CTX_ASSERT_VALID(thr); - DUK_ASSERT(thr->valstack_top != thr->valstack_bottom); - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) 2); - - tv = --thr->valstack_top; - DUK_ASSERT(tv >= thr->valstack_bottom); -#if defined(DUK_USE_REFERENCE_COUNTING) - DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv); /* side effects */ -#else - DUK_TVAL_SET_UNDEFINED(tv); -#endif - tv = --thr->valstack_top; - DUK_ASSERT(tv >= thr->valstack_bottom); -#if defined(DUK_USE_REFERENCE_COUNTING) - DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv); /* side effects */ -#else - DUK_TVAL_SET_UNDEFINED(tv); -#endif - - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); -} -DUK_EXTERNAL void duk_pop_2(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - if (DUK_UNLIKELY(thr->valstack_top - 2 < thr->valstack_bottom)) { - DUK_ERROR_RANGE_INVALID_COUNT(thr); - DUK_WO_NORETURN(return;); - } - - duk__pop_2_unsafe_raw(thr); -} -DUK_INTERNAL void duk_pop_2_unsafe(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - duk__pop_2_unsafe_raw(thr); -} -DUK_INTERNAL void duk_pop_2_nodecref_unsafe(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(thr->valstack_top != thr->valstack_bottom); - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) 2); - - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top - 1)); - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top - 2)); - thr->valstack_top -= 2; - - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); -} -#endif /* !DUK_USE_PREFER_SIZE */ - -DUK_EXTERNAL void duk_pop_3(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - duk_pop_n(thr, 3); -} - -DUK_INTERNAL void duk_pop_3_unsafe(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - duk_pop_n_unsafe(thr, 3); -} - -DUK_INTERNAL void duk_pop_3_nodecref_unsafe(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - duk_pop_n_nodecref_unsafe(thr, 3); -} - -/* - * Pack and unpack (pack value stack entries into an array and vice versa) - */ - -/* XXX: pack index range? array index offset? */ -/* XXX: need ability to pack into a bare array? */ -DUK_INTERNAL void duk_pack(duk_hthread *thr, duk_idx_t count) { - duk_tval *tv_src; - duk_tval *tv_dst; - duk_tval *tv_curr; - duk_tval *tv_limit; - duk_idx_t top; - - DUK_ASSERT_API_ENTRY(thr); - - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - top = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom); - DUK_ASSERT(top >= 0); - if (DUK_UNLIKELY((duk_uidx_t) count > (duk_uidx_t) top)) { - /* Also handles negative count. */ - DUK_ERROR_RANGE_INVALID_COUNT(thr); - DUK_WO_NORETURN(return;); - } - DUK_ASSERT(count >= 0); - - /* Wrapping is controlled by the check above: value stack top can be - * at most DUK_USE_VALSTACK_LIMIT which is low enough so that - * multiplying with sizeof(duk_tval) won't wrap. - */ - DUK_ASSERT(count >= 0 && count <= (duk_idx_t) DUK_USE_VALSTACK_LIMIT); - DUK_ASSERT((duk_size_t) count <= DUK_SIZE_MAX / sizeof(duk_tval)); /* no wrapping */ - - tv_dst = duk_push_harray_with_size_outptr(thr, (duk_uint32_t) count); /* XXX: uninitialized would be OK */ - DUK_ASSERT(count == 0 || tv_dst != NULL); - DUK_ASSERT(!duk_is_bare_object(thr, -1)); - - /* Copy value stack values directly to the array part without - * any refcount updates: net refcount changes are zero. - */ - tv_src = thr->valstack_top - count - 1; - duk_memcpy_unsafe((void *) tv_dst, (const void *) tv_src, (size_t) count * sizeof(duk_tval)); - - /* Overwrite result array to final value stack location and wipe - * the rest; no refcount operations needed. - */ - - tv_dst = tv_src; /* when count == 0, same as tv_src (OK) */ - tv_src = thr->valstack_top - 1; - DUK_TVAL_SET_TVAL(tv_dst, tv_src); - - /* XXX: internal helper to wipe a value stack segment? */ - tv_curr = tv_dst + 1; - tv_limit = thr->valstack_top; - while (tv_curr != tv_limit) { - /* Wipe policy: keep as 'undefined'. */ - DUK_TVAL_SET_UNDEFINED(tv_curr); - tv_curr++; - } - thr->valstack_top = tv_dst + 1; -} - -DUK_INTERNAL duk_idx_t duk_unpack_array_like(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - - DUK_ASSERT_API_ENTRY(thr); - - tv = duk_require_tval(thr, idx); - if (DUK_LIKELY(DUK_TVAL_IS_OBJECT(tv))) { - duk_hobject *h; - duk_uint32_t len; - duk_uint32_t i; - - h = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - DUK_UNREF(h); - -#if defined(DUK_USE_ARRAY_FASTPATH) /* close enough */ - if (DUK_LIKELY(DUK_HOBJECT_IS_ARRAY(h) && ((duk_harray *) h)->length <= DUK_HOBJECT_GET_ASIZE(h))) { - duk_harray *h_arr; - duk_tval *tv_src; - duk_tval *tv_dst; - - h_arr = (duk_harray *) h; - len = h_arr->length; - if (DUK_UNLIKELY(len >= 0x80000000UL)) { - goto fail_over_2g; - } - duk_require_stack(thr, (duk_idx_t) len); - - /* The potential allocation in duk_require_stack() may - * run a finalizer which modifies the argArray so that - * e.g. becomes sparse. So, we need to recheck that the - * array didn't change size and that there's still a - * valid backing array part. - * - * XXX: alternatively, could prevent finalizers for the - * duration. - */ - if (DUK_UNLIKELY(len != h_arr->length || h_arr->length > DUK_HOBJECT_GET_ASIZE((duk_hobject *) h_arr))) { - goto skip_fast; - } - - /* Main fast path: arguments array is almost always - * an actual array (though it might also be an arguments - * object). - */ - - DUK_DDD(DUK_DDDPRINT("fast path for %ld elements", (long) h_arr->length)); - tv_src = DUK_HOBJECT_A_GET_BASE(thr->heap, h); - tv_dst = thr->valstack_top; - while (len-- > 0) { - DUK_ASSERT(tv_dst < thr->valstack_end); - if (DUK_UNLIKELY(DUK_TVAL_IS_UNUSED(tv_src))) { - /* Gaps are very unlikely. Skip over them, - * without an ancestor lookup (technically - * not compliant). - */ - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv_dst)); /* valstack policy */ - } else { - DUK_TVAL_SET_TVAL(tv_dst, tv_src); - DUK_TVAL_INCREF(thr, tv_dst); - } - tv_src++; - tv_dst++; - } - DUK_ASSERT(tv_dst <= thr->valstack_end); - thr->valstack_top = tv_dst; - return (duk_idx_t) h_arr->length; - } - skip_fast: -#endif /* DUK_USE_ARRAY_FASTPATH */ - - /* Slow path: actual lookups. The initial 'length' lookup - * decides the output length, regardless of side effects that - * may resize or change the argArray while we read the - * indices. - */ - idx = duk_normalize_index(thr, idx); - duk_get_prop_stridx(thr, idx, DUK_STRIDX_LENGTH); - len = duk_to_uint32(thr, -1); /* ToUint32() coercion required */ - if (DUK_UNLIKELY(len >= 0x80000000UL)) { - goto fail_over_2g; - } - duk_pop_unsafe(thr); - DUK_DDD(DUK_DDDPRINT("slow path for %ld elements", (long) len)); - - duk_require_stack(thr, (duk_idx_t) len); - for (i = 0; i < len; i++) { - duk_get_prop_index(thr, idx, (duk_uarridx_t) i); - } - return (duk_idx_t) len; - } else if (DUK_TVAL_IS_UNDEFINED(tv) || DUK_TVAL_IS_NULL(tv)) { - return 0; - } - - DUK_ERROR_TYPE_INVALID_ARGS(thr); - DUK_WO_NORETURN(return 0;); - -fail_over_2g: - DUK_ERROR_RANGE_INVALID_LENGTH(thr); - DUK_WO_NORETURN(return 0;); -} - -/* - * Error throwing - */ - -#if defined(DUK_USE_GCC_PRAGMAS) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wsuggest-attribute=noreturn" -#elif defined(DUK_USE_CLANG_PRAGMAS) -#pragma clang diagnostic push -#endif - -DUK_EXTERNAL void duk_throw_raw(duk_hthread *thr) { - duk_tval *tv_val; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(thr->valstack_bottom >= thr->valstack); - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - DUK_ASSERT(thr->valstack_end >= thr->valstack_top); - - if (DUK_UNLIKELY(thr->valstack_top == thr->valstack_bottom)) { - DUK_ERROR_TYPE_INVALID_ARGS(thr); - DUK_WO_NORETURN(return;); - } - - /* Errors are augmented when they are created, not when they are - * thrown or re-thrown. The current error handler, however, runs - * just before an error is thrown. - */ - - /* Sync so that augmentation sees up-to-date activations, NULL - * thr->ptr_curr_pc so that it's not used if side effects occur - * in augmentation or longjmp handling. - */ - duk_hthread_sync_and_null_currpc(thr); - -#if defined(DUK_USE_AUGMENT_ERROR_THROW) - DUK_DDD(DUK_DDDPRINT("THROW ERROR (API): %!dT (before throw augment)", (duk_tval *) duk_get_tval(thr, -1))); - duk_err_augment_error_throw(thr); -#endif - DUK_DDD(DUK_DDDPRINT("THROW ERROR (API): %!dT (after throw augment)", (duk_tval *) duk_get_tval(thr, -1))); - - tv_val = DUK_GET_TVAL_NEGIDX(thr, -1); - duk_err_setup_ljstate1(thr, DUK_LJ_TYPE_THROW, tv_val); -#if defined(DUK_USE_DEBUGGER_SUPPORT) - duk_err_check_debugger_integration(thr); -#endif - - /* thr->heap->lj.jmpbuf_ptr is checked by duk_err_longjmp() so we don't - * need to check that here. If the value is NULL, a fatal error occurs - * because we can't return. - */ - - duk_err_longjmp(thr); - DUK_UNREACHABLE(); -} - -DUK_EXTERNAL void duk_fatal_raw(duk_hthread *thr, const char *err_msg) { - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - DUK_ASSERT(thr->heap->fatal_func != NULL); - - DUK_D(DUK_DPRINT("fatal error occurred: %s", err_msg ? err_msg : "NULL")); - - /* fatal_func should be noreturn, but noreturn declarations on function - * pointers has a very spotty support apparently so it's not currently - * done. - */ - thr->heap->fatal_func(thr->heap->heap_udata, err_msg); - - /* If the fatal handler returns, all bets are off. It'd be nice to - * print something here but since we don't want to depend on stdio, - * there's no way to do so portably. - */ - DUK_D(DUK_DPRINT("fatal error handler returned, all bets are off!")); - for (;;) { - /* loop forever, don't return (function marked noreturn) */ - } -} - -DUK_EXTERNAL void duk_error_va_raw(duk_hthread *thr, - duk_errcode_t err_code, - const char *filename, - duk_int_t line, - const char *fmt, - va_list ap) { - DUK_ASSERT_API_ENTRY(thr); - - duk_push_error_object_va_raw(thr, err_code, filename, line, fmt, ap); - (void) duk_throw(thr); - DUK_WO_NORETURN(return;); -} - -DUK_EXTERNAL void duk_error_raw(duk_hthread *thr, - duk_errcode_t err_code, - const char *filename, - duk_int_t line, - const char *fmt, - ...) { - va_list ap; - - DUK_ASSERT_API_ENTRY(thr); - - va_start(ap, fmt); - duk_push_error_object_va_raw(thr, err_code, filename, line, fmt, ap); - va_end(ap); - (void) duk_throw(thr); - DUK_WO_NORETURN(return;); -} - -#if defined(DUK_USE_GCC_PRAGMAS) -#pragma GCC diagnostic pop -#elif defined(DUK_USE_CLANG_PRAGMAS) -#pragma clang diagnostic pop -#endif - -#if !defined(DUK_USE_VARIADIC_MACROS) -DUK_NORETURN( - DUK_LOCAL_DECL void duk__throw_error_from_stash(duk_hthread *thr, duk_errcode_t err_code, const char *fmt, va_list ap)); - -DUK_LOCAL void duk__throw_error_from_stash(duk_hthread *thr, duk_errcode_t err_code, const char *fmt, va_list ap) { - const char *filename; - duk_int_t line; - - DUK_CTX_ASSERT_VALID(thr); - - filename = duk_api_global_filename; - line = duk_api_global_line; - duk_api_global_filename = NULL; - duk_api_global_line = 0; - - duk_push_error_object_va_raw(thr, err_code, filename, line, fmt, ap); - (void) duk_throw(thr); - DUK_WO_NORETURN(return;); -} - -#define DUK__ERROR_STASH_SHARED(code) \ - do { \ - va_list ap; \ - va_start(ap, fmt); \ - duk__throw_error_from_stash(thr, (code), fmt, ap); \ - va_end(ap); \ - DUK_WO_NORETURN(return 0;); \ - } while (0) - -DUK_EXTERNAL duk_ret_t duk_error_stash(duk_hthread *thr, duk_errcode_t err_code, const char *fmt, ...) { - DUK_ASSERT_API_ENTRY(thr); - DUK__ERROR_STASH_SHARED(err_code); -} -DUK_EXTERNAL duk_ret_t duk_generic_error_stash(duk_hthread *thr, const char *fmt, ...) { - DUK_ASSERT_API_ENTRY(thr); - DUK__ERROR_STASH_SHARED(DUK_ERR_ERROR); -} -DUK_EXTERNAL duk_ret_t duk_eval_error_stash(duk_hthread *thr, const char *fmt, ...) { - DUK_ASSERT_API_ENTRY(thr); - DUK__ERROR_STASH_SHARED(DUK_ERR_EVAL_ERROR); -} -DUK_EXTERNAL duk_ret_t duk_range_error_stash(duk_hthread *thr, const char *fmt, ...) { - DUK_ASSERT_API_ENTRY(thr); - DUK__ERROR_STASH_SHARED(DUK_ERR_RANGE_ERROR); -} -DUK_EXTERNAL duk_ret_t duk_reference_error_stash(duk_hthread *thr, const char *fmt, ...) { - DUK_ASSERT_API_ENTRY(thr); - DUK__ERROR_STASH_SHARED(DUK_ERR_REFERENCE_ERROR); -} -DUK_EXTERNAL duk_ret_t duk_syntax_error_stash(duk_hthread *thr, const char *fmt, ...) { - DUK_ASSERT_API_ENTRY(thr); - DUK__ERROR_STASH_SHARED(DUK_ERR_SYNTAX_ERROR); -} -DUK_EXTERNAL duk_ret_t duk_type_error_stash(duk_hthread *thr, const char *fmt, ...) { - DUK_ASSERT_API_ENTRY(thr); - DUK__ERROR_STASH_SHARED(DUK_ERR_TYPE_ERROR); -} -DUK_EXTERNAL duk_ret_t duk_uri_error_stash(duk_hthread *thr, const char *fmt, ...) { - DUK_ASSERT_API_ENTRY(thr); - DUK__ERROR_STASH_SHARED(DUK_ERR_URI_ERROR); -} -#endif /* DUK_USE_VARIADIC_MACROS */ - -/* - * Comparison - */ - -DUK_EXTERNAL duk_bool_t duk_equals(duk_hthread *thr, duk_idx_t idx1, duk_idx_t idx2) { - duk_tval *tv1, *tv2; - - DUK_ASSERT_API_ENTRY(thr); - - tv1 = duk_get_tval(thr, idx1); - tv2 = duk_get_tval(thr, idx2); - if ((tv1 == NULL) || (tv2 == NULL)) { - return 0; - } - - /* Coercion may be needed, the helper handles that by pushing the - * tagged values to the stack. - */ - return duk_js_equals(thr, tv1, tv2); -} - -DUK_EXTERNAL duk_bool_t duk_strict_equals(duk_hthread *thr, duk_idx_t idx1, duk_idx_t idx2) { - duk_tval *tv1, *tv2; - - DUK_ASSERT_API_ENTRY(thr); - - tv1 = duk_get_tval(thr, idx1); - tv2 = duk_get_tval(thr, idx2); - if ((tv1 == NULL) || (tv2 == NULL)) { - return 0; - } - - /* No coercions or other side effects, so safe */ - return duk_js_strict_equals(tv1, tv2); -} - -DUK_EXTERNAL duk_bool_t duk_samevalue(duk_hthread *thr, duk_idx_t idx1, duk_idx_t idx2) { - duk_tval *tv1, *tv2; - - DUK_ASSERT_API_ENTRY(thr); - - tv1 = duk_get_tval(thr, idx1); - tv2 = duk_get_tval(thr, idx2); - if ((tv1 == NULL) || (tv2 == NULL)) { - return 0; - } - - /* No coercions or other side effects, so safe */ - return duk_js_samevalue(tv1, tv2); -} - -/* - * instanceof - */ - -DUK_EXTERNAL duk_bool_t duk_instanceof(duk_hthread *thr, duk_idx_t idx1, duk_idx_t idx2) { - duk_tval *tv1, *tv2; - - DUK_ASSERT_API_ENTRY(thr); - - /* Index validation is strict, which differs from duk_equals(). - * The strict behavior mimics how instanceof itself works, e.g. - * it is a TypeError if rval is not a -callable- object. It would - * be somewhat inconsistent if rval would be allowed to be - * non-existent without a TypeError. - */ - tv1 = duk_require_tval(thr, idx1); - DUK_ASSERT(tv1 != NULL); - tv2 = duk_require_tval(thr, idx2); - DUK_ASSERT(tv2 != NULL); - - return duk_js_instanceof(thr, tv1, tv2); -} - -/* - * Lightfunc - */ - -DUK_INTERNAL void duk_push_lightfunc_name_raw(duk_hthread *thr, duk_c_function func, duk_small_uint_t lf_flags) { - /* Lightfunc name, includes Duktape/C native function pointer, which - * can often be used to locate the function from a symbol table. - * The name also includes the 16-bit duk_tval flags field because it - * includes the magic value. Because a single native function often - * provides different functionality depending on the magic value, it - * seems reasonably to include it in the name. - * - * On the other hand, a complicated name increases string table - * pressure in low memory environments (but only when function name - * is accessed). - */ - - DUK_ASSERT_API_ENTRY(thr); - - duk_push_literal(thr, "light_"); - duk_push_string_funcptr(thr, (duk_uint8_t *) &func, sizeof(func)); - duk_push_sprintf(thr, "_%04x", (unsigned int) lf_flags); - duk_concat(thr, 3); -} - -DUK_INTERNAL void duk_push_lightfunc_name(duk_hthread *thr, duk_tval *tv) { - duk_c_function func; - duk_small_uint_t lf_flags; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv)); - - DUK_TVAL_GET_LIGHTFUNC(tv, func, lf_flags); - duk_push_lightfunc_name_raw(thr, func, lf_flags); -} - -DUK_INTERNAL void duk_push_lightfunc_tostring(duk_hthread *thr, duk_tval *tv) { - duk_c_function func; - duk_small_uint_t lf_flags; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv)); - - DUK_TVAL_GET_LIGHTFUNC(tv, func, lf_flags); /* read before 'tv' potentially invalidated */ - duk_push_literal(thr, "function "); - duk_push_lightfunc_name_raw(thr, func, lf_flags); - duk_push_literal(thr, "() { [lightfunc code] }"); - duk_concat(thr, 3); -} - -/* - * Function pointers - * - * Printing function pointers is non-portable, so we do that by hex printing - * bytes from memory. - */ - -DUK_INTERNAL void duk_push_string_funcptr(duk_hthread *thr, duk_uint8_t *ptr, duk_size_t sz) { - duk_uint8_t buf[32 * 2]; - duk_uint8_t *p, *q; - duk_small_uint_t i; - duk_small_uint_t t; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(sz <= 32); /* sanity limit for function pointer size */ - - p = buf; -#if defined(DUK_USE_INTEGER_LE) - q = ptr + sz; -#else - q = ptr; -#endif - for (i = 0; i < sz; i++) { -#if defined(DUK_USE_INTEGER_LE) - t = *(--q); -#else - t = *(q++); -#endif - *p++ = duk_lc_digits[t >> 4]; - *p++ = duk_lc_digits[t & 0x0f]; - } - - duk_push_lstring(thr, (const char *) buf, sz * 2); -} - -/* - * Push readable string summarizing duk_tval. The operation is side effect - * free and will only throw from internal errors (e.g. out of memory). - * This is used by e.g. property access code to summarize a key/base safely, - * and is not intended to be fast (but small and safe). - */ - -/* String limits for summary strings. */ -#define DUK__READABLE_SUMMARY_MAXCHARS 96 /* maximum supported by helper */ -#define DUK__READABLE_STRING_MAXCHARS 32 /* for strings/symbols */ -#define DUK__READABLE_ERRMSG_MAXCHARS 96 /* for error messages */ - -/* String sanitizer which escapes ASCII control characters and a few other - * ASCII characters, passes Unicode as is, and replaces invalid UTF-8 with - * question marks. No errors are thrown for any input string, except in out - * of memory situations. - */ -DUK_LOCAL void duk__push_hstring_readable_unicode(duk_hthread *thr, duk_hstring *h_input, duk_small_uint_t maxchars) { - const duk_uint8_t *p, *p_start, *p_end; - duk_uint8_t buf[DUK_UNICODE_MAX_XUTF8_LENGTH * DUK__READABLE_SUMMARY_MAXCHARS + 2 /*quotes*/ + 3 /*periods*/]; - duk_uint8_t *q; - duk_ucodepoint_t cp; - duk_small_uint_t nchars; - - DUK_CTX_ASSERT_VALID(thr); - DUK_ASSERT(h_input != NULL); - DUK_ASSERT(maxchars <= DUK__READABLE_SUMMARY_MAXCHARS); - - p_start = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_input); - p_end = p_start + DUK_HSTRING_GET_BYTELEN(h_input); - p = p_start; - q = buf; - - nchars = 0; - *q++ = (duk_uint8_t) DUK_ASC_SINGLEQUOTE; - for (;;) { - if (p >= p_end) { - break; - } - if (nchars == maxchars) { - *q++ = (duk_uint8_t) DUK_ASC_PERIOD; - *q++ = (duk_uint8_t) DUK_ASC_PERIOD; - *q++ = (duk_uint8_t) DUK_ASC_PERIOD; - break; - } - if (duk_unicode_decode_xutf8(thr, &p, p_start, p_end, &cp)) { - if (cp < 0x20 || cp == 0x7f || cp == DUK_ASC_SINGLEQUOTE || cp == DUK_ASC_BACKSLASH) { - DUK_ASSERT(DUK_UNICODE_MAX_XUTF8_LENGTH >= 4); /* estimate is valid */ - DUK_ASSERT((cp >> 4) <= 0x0f); - *q++ = (duk_uint8_t) DUK_ASC_BACKSLASH; - *q++ = (duk_uint8_t) DUK_ASC_LC_X; - *q++ = (duk_uint8_t) duk_lc_digits[cp >> 4]; - *q++ = (duk_uint8_t) duk_lc_digits[cp & 0x0f]; - } else { - q += duk_unicode_encode_xutf8(cp, q); - } - } else { - p++; /* advance manually */ - *q++ = (duk_uint8_t) DUK_ASC_QUESTION; - } - nchars++; - } - *q++ = (duk_uint8_t) DUK_ASC_SINGLEQUOTE; - - duk_push_lstring(thr, (const char *) buf, (duk_size_t) (q - buf)); -} - -DUK_LOCAL const char *duk__push_string_tval_readable(duk_hthread *thr, duk_tval *tv, duk_bool_t error_aware) { - DUK_CTX_ASSERT_VALID(thr); - /* 'tv' may be NULL */ - - if (tv == NULL) { - duk_push_literal(thr, "none"); - } else { - switch (DUK_TVAL_GET_TAG(tv)) { - case DUK_TAG_STRING: { - duk_hstring *h = DUK_TVAL_GET_STRING(tv); - if (DUK_HSTRING_HAS_SYMBOL(h)) { - /* XXX: string summary produces question marks - * so this is not very ideal. - */ - duk_push_literal(thr, "[Symbol "); - duk_push_string(thr, duk__get_symbol_type_string(h)); - duk_push_literal(thr, " "); - duk__push_hstring_readable_unicode(thr, h, DUK__READABLE_STRING_MAXCHARS); - duk_push_literal(thr, "]"); - duk_concat(thr, 5); - break; - } - duk__push_hstring_readable_unicode(thr, h, DUK__READABLE_STRING_MAXCHARS); - break; - } - case DUK_TAG_OBJECT: { - duk_hobject *h = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - - if (error_aware && duk_hobject_prototype_chain_contains(thr, - h, - thr->builtins[DUK_BIDX_ERROR_PROTOTYPE], - 1 /*ignore_loop*/)) { - /* Get error message in a side effect free way if - * possible; if not, summarize as a generic object. - * Error message currently gets quoted. - */ - /* XXX: better internal getprop call; get without side effects - * but traverse inheritance chain. - */ - duk_tval *tv_msg; - tv_msg = duk_hobject_find_entry_tval_ptr_stridx(thr->heap, h, DUK_STRIDX_MESSAGE); - if (tv_msg != NULL && DUK_TVAL_IS_STRING(tv_msg)) { - /* It's critical to avoid recursion so - * only summarize a string .message. - */ - duk__push_hstring_readable_unicode(thr, - DUK_TVAL_GET_STRING(tv_msg), - DUK__READABLE_ERRMSG_MAXCHARS); - break; - } - } - duk_push_class_string_tval(thr, tv, 1 /*avoid_side_effects*/); - break; - } - case DUK_TAG_BUFFER: { - /* While plain buffers mimic Uint8Arrays, they summarize differently. - * This is useful so that the summarized string accurately reflects the - * internal type which may matter for figuring out bugs etc. - */ - /* XXX: Hex encoded, length limited buffer summary here? */ - duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv); - DUK_ASSERT(h != NULL); - duk_push_sprintf(thr, "[buffer:%ld]", (long) DUK_HBUFFER_GET_SIZE(h)); - break; - } - case DUK_TAG_POINTER: { - /* Surround with parentheses like in JX, ensures NULL pointer - * is distinguishable from null value ("(null)" vs "null"). - */ - duk_push_tval(thr, tv); - duk_push_sprintf(thr, "(%s)", duk_to_string(thr, -1)); - duk_remove_m2(thr); - break; - } - default: { - duk_push_tval(thr, tv); - break; - } - } - } - - return duk_to_string(thr, -1); -} -DUK_INTERNAL const char *duk_push_string_tval_readable(duk_hthread *thr, duk_tval *tv) { - DUK_ASSERT_API_ENTRY(thr); - return duk__push_string_tval_readable(thr, tv, 0 /*error_aware*/); -} - -DUK_INTERNAL const char *duk_push_string_readable(duk_hthread *thr, duk_idx_t idx) { - DUK_ASSERT_API_ENTRY(thr); - return duk_push_string_tval_readable(thr, duk_get_tval(thr, idx)); -} - -DUK_INTERNAL const char *duk_push_string_tval_readable_error(duk_hthread *thr, duk_tval *tv) { - DUK_ASSERT_API_ENTRY(thr); - return duk__push_string_tval_readable(thr, tv, 1 /*error_aware*/); -} - -DUK_INTERNAL void duk_push_symbol_descriptive_string(duk_hthread *thr, duk_hstring *h) { - const duk_uint8_t *p; - const duk_uint8_t *p_end; - const duk_uint8_t *q; - - DUK_ASSERT_API_ENTRY(thr); - - /* .toString() */ - duk_push_literal(thr, "Symbol("); - p = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h); - p_end = p + DUK_HSTRING_GET_BYTELEN(h); - DUK_ASSERT(p[0] == 0xff || (p[0] & 0xc0) == 0x80); - p++; - for (q = p; q < p_end; q++) { - if (*q == 0xffU) { - /* Terminate either at end-of-string (but NUL MUST - * be accepted without terminating description) or - * 0xFF, which is used to mark start of unique trailer - * (and cannot occur in CESU-8 / extended UTF-8). - */ - break; - } - } - duk_push_lstring(thr, (const char *) p, (duk_size_t) (q - p)); - duk_push_literal(thr, ")"); - duk_concat(thr, 3); -} - -/* - * Functions - */ - -#if 0 /* not used yet */ -DUK_INTERNAL void duk_push_hnatfunc_name(duk_hthread *thr, duk_hnatfunc *h) { - duk_c_function func; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(h != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_NATFUNC((duk_hobject *) h)); - - duk_push_sprintf(thr, "native_"); - func = h->func; - duk_push_string_funcptr(thr, (duk_uint8_t *) &func, sizeof(func)); - duk_push_sprintf(thr, "_%04x_%04x", - (unsigned int) (duk_uint16_t) h->nargs, - (unsigned int) (duk_uint16_t) h->magic); - duk_concat(thr, 3); -} -#endif - -/* - * duk_tval slice copy - */ - -DUK_INTERNAL void duk_copy_tvals_incref(duk_hthread *thr, duk_tval *tv_dst, duk_tval *tv_src, duk_size_t count) { - duk_tval *tv; - - DUK_ASSERT_API_ENTRY(thr); - DUK_UNREF(thr); - DUK_ASSERT(count * sizeof(duk_tval) >= count); /* no wrap */ - - duk_memcpy_unsafe((void *) tv_dst, (const void *) tv_src, count * sizeof(duk_tval)); - - tv = tv_dst; - while (count-- > 0) { - DUK_TVAL_INCREF(thr, tv); - tv++; - } -} - -/* automatic undefs */ -#undef DUK__ASSERT_SPACE -#undef DUK__CHECK_SPACE -#undef DUK__ERROR_STASH_SHARED -#undef DUK__PACK_ARGS -#undef DUK__READABLE_ERRMSG_MAXCHARS -#undef DUK__READABLE_STRING_MAXCHARS -#undef DUK__READABLE_SUMMARY_MAXCHARS -/* - * String manipulation - */ - -/* #include duk_internal.h -> already included */ - -DUK_LOCAL void duk__concat_and_join_helper(duk_hthread *thr, duk_idx_t count_in, duk_bool_t is_join) { - duk_uint_t count; - duk_uint_t i; - duk_size_t idx; - duk_size_t len; - duk_hstring *h; - duk_uint8_t *buf; - - DUK_CTX_ASSERT_VALID(thr); - - if (DUK_UNLIKELY(count_in <= 0)) { - if (count_in < 0) { - DUK_ERROR_RANGE_INVALID_COUNT(thr); - DUK_WO_NORETURN(return;); - } - DUK_ASSERT(count_in == 0); - duk_push_hstring_empty(thr); - return; - } - count = (duk_uint_t) count_in; - - if (is_join) { - duk_size_t t1, t2, limit; - h = duk_to_hstring(thr, -((duk_idx_t) count) - 1); - DUK_ASSERT(h != NULL); - - /* A bit tricky overflow test, see doc/code-issues.rst. */ - t1 = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h); - t2 = (duk_size_t) (count - 1); - limit = (duk_size_t) DUK_HSTRING_MAX_BYTELEN; - if (DUK_UNLIKELY(t2 != 0 && t1 > limit / t2)) { - /* Combined size of separators already overflows. */ - goto error_overflow; - } - len = (duk_size_t) (t1 * t2); - } else { - len = (duk_size_t) 0; - } - - for (i = count; i >= 1; i--) { - duk_size_t new_len; - h = duk_to_hstring(thr, -((duk_idx_t) i)); - new_len = len + (duk_size_t) DUK_HSTRING_GET_BYTELEN(h); - - /* Impose a string maximum length, need to handle overflow - * correctly. - */ - if (new_len < len || /* wrapped */ - new_len > (duk_size_t) DUK_HSTRING_MAX_BYTELEN) { - goto error_overflow; - } - len = new_len; - } - - DUK_DDD(DUK_DDDPRINT("join/concat %lu strings, total length %lu bytes", (unsigned long) count, (unsigned long) len)); - - /* Use stack allocated buffer to ensure reachability in errors - * (e.g. intern error). - */ - buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, len); - DUK_ASSERT(buf != NULL); - - /* [ ... (sep) str1 str2 ... strN buf ] */ - - idx = 0; - for (i = count; i >= 1; i--) { - if (is_join && i != count) { - h = duk_require_hstring(thr, -((duk_idx_t) count) - 2); /* extra -1 for buffer */ - duk_memcpy(buf + idx, DUK_HSTRING_GET_DATA(h), DUK_HSTRING_GET_BYTELEN(h)); - idx += DUK_HSTRING_GET_BYTELEN(h); - } - h = duk_require_hstring(thr, -((duk_idx_t) i) - 1); /* extra -1 for buffer */ - duk_memcpy(buf + idx, DUK_HSTRING_GET_DATA(h), DUK_HSTRING_GET_BYTELEN(h)); - idx += DUK_HSTRING_GET_BYTELEN(h); - } - - DUK_ASSERT(idx == len); - - /* [ ... (sep) str1 str2 ... strN buf ] */ - - /* Get rid of the strings early to minimize memory use before intern. */ - - if (is_join) { - duk_replace(thr, -((duk_idx_t) count) - 2); /* overwrite sep */ - duk_pop_n(thr, (duk_idx_t) count); - } else { - duk_replace(thr, -((duk_idx_t) count) - 1); /* overwrite str1 */ - duk_pop_n(thr, (duk_idx_t) (count - 1)); - } - - /* [ ... buf ] */ - - (void) duk_buffer_to_string(thr, -1); /* Safe if inputs are safe. */ - - /* [ ... res ] */ - return; - -error_overflow: - DUK_ERROR_RANGE(thr, DUK_STR_RESULT_TOO_LONG); - DUK_WO_NORETURN(return;); -} - -DUK_EXTERNAL void duk_concat(duk_hthread *thr, duk_idx_t count) { - DUK_ASSERT_API_ENTRY(thr); - - duk__concat_and_join_helper(thr, count, 0 /*is_join*/); -} - -#if defined(DUK_USE_PREFER_SIZE) -DUK_INTERNAL void duk_concat_2(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - duk_concat(thr, 2); -} -#else /* DUK_USE_PREFER_SIZE */ -DUK_INTERNAL void duk_concat_2(duk_hthread *thr) { - duk_hstring *h1; - duk_hstring *h2; - duk_uint8_t *buf; - duk_size_t len1; - duk_size_t len2; - duk_size_t len; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(duk_get_top(thr) >= 2); /* Trusted caller. */ - - h1 = duk_to_hstring(thr, -2); - h2 = duk_to_hstring(thr, -1); - len1 = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h1); - len2 = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h2); - len = len1 + len2; - if (DUK_UNLIKELY(len < len1 || /* wrapped */ - len > (duk_size_t) DUK_HSTRING_MAX_BYTELEN)) { - goto error_overflow; - } - buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, len); - DUK_ASSERT(buf != NULL); - - duk_memcpy((void *) buf, (const void *) DUK_HSTRING_GET_DATA(h1), (size_t) len1); - duk_memcpy((void *) (buf + len1), (const void *) DUK_HSTRING_GET_DATA(h2), (size_t) len2); - (void) duk_buffer_to_string(thr, -1); /* Safe if inputs are safe. */ - - /* [ ... str1 str2 buf ] */ - - duk_replace(thr, -3); - duk_pop_unsafe(thr); - return; - -error_overflow: - DUK_ERROR_RANGE(thr, DUK_STR_RESULT_TOO_LONG); - DUK_WO_NORETURN(return;); -} -#endif /* DUK_USE_PREFER_SIZE */ - -DUK_EXTERNAL void duk_join(duk_hthread *thr, duk_idx_t count) { - DUK_ASSERT_API_ENTRY(thr); - - duk__concat_and_join_helper(thr, count, 1 /*is_join*/); -} - -/* XXX: could map/decode be unified with duk_unicode_support.c code? - * Case conversion needs also the character surroundings though. - */ - -DUK_EXTERNAL void duk_decode_string(duk_hthread *thr, duk_idx_t idx, duk_decode_char_function callback, void *udata) { - duk_hstring *h_input; - const duk_uint8_t *p, *p_start, *p_end; - duk_codepoint_t cp; - - DUK_ASSERT_API_ENTRY(thr); - - h_input = duk_require_hstring(thr, idx); /* Accept symbols. */ - DUK_ASSERT(h_input != NULL); - - p_start = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_input); - p_end = p_start + DUK_HSTRING_GET_BYTELEN(h_input); - p = p_start; - - for (;;) { - if (p >= p_end) { - break; - } - cp = (duk_codepoint_t) duk_unicode_decode_xutf8_checked(thr, &p, p_start, p_end); - callback(udata, cp); - } -} - -DUK_EXTERNAL void duk_map_string(duk_hthread *thr, duk_idx_t idx, duk_map_char_function callback, void *udata) { - duk_hstring *h_input; - duk_bufwriter_ctx bw_alloc; - duk_bufwriter_ctx *bw; - const duk_uint8_t *p, *p_start, *p_end; - duk_codepoint_t cp; - - DUK_ASSERT_API_ENTRY(thr); - - idx = duk_normalize_index(thr, idx); - - h_input = duk_require_hstring(thr, idx); /* Accept symbols. */ - DUK_ASSERT(h_input != NULL); - - bw = &bw_alloc; - DUK_BW_INIT_PUSHBUF(thr, bw, DUK_HSTRING_GET_BYTELEN(h_input)); /* Reasonable output estimate. */ - - p_start = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_input); - p_end = p_start + DUK_HSTRING_GET_BYTELEN(h_input); - p = p_start; - - for (;;) { - /* XXX: could write output in chunks with fewer ensure calls, - * but relative benefit would be small here. - */ - - if (p >= p_end) { - break; - } - cp = (duk_codepoint_t) duk_unicode_decode_xutf8_checked(thr, &p, p_start, p_end); - cp = callback(udata, cp); - - DUK_BW_WRITE_ENSURE_XUTF8(thr, bw, cp); - } - - DUK_BW_COMPACT(thr, bw); - (void) duk_buffer_to_string(thr, -1); /* Safe, extended UTF-8 encoded. */ - duk_replace(thr, idx); -} - -DUK_EXTERNAL void duk_substring(duk_hthread *thr, duk_idx_t idx, duk_size_t start_offset, duk_size_t end_offset) { - duk_hstring *h; - duk_hstring *res; - duk_size_t start_byte_offset; - duk_size_t end_byte_offset; - duk_size_t charlen; - - DUK_ASSERT_API_ENTRY(thr); - - idx = duk_require_normalize_index(thr, idx); /* Accept symbols. */ - h = duk_require_hstring(thr, idx); - DUK_ASSERT(h != NULL); - - charlen = DUK_HSTRING_GET_CHARLEN(h); - if (end_offset >= charlen) { - end_offset = charlen; - } - if (start_offset > end_offset) { - start_offset = end_offset; - } - - DUK_ASSERT_DISABLE(start_offset >= 0); - DUK_ASSERT(start_offset <= end_offset && start_offset <= DUK_HSTRING_GET_CHARLEN(h)); - DUK_ASSERT_DISABLE(end_offset >= 0); - DUK_ASSERT(end_offset >= start_offset && end_offset <= DUK_HSTRING_GET_CHARLEN(h)); - - /* Guaranteed by string limits. */ - DUK_ASSERT(start_offset <= DUK_UINT32_MAX); - DUK_ASSERT(end_offset <= DUK_UINT32_MAX); - - start_byte_offset = (duk_size_t) duk_heap_strcache_offset_char2byte(thr, h, (duk_uint_fast32_t) start_offset); - end_byte_offset = (duk_size_t) duk_heap_strcache_offset_char2byte(thr, h, (duk_uint_fast32_t) end_offset); - - DUK_ASSERT(end_byte_offset >= start_byte_offset); - DUK_ASSERT(end_byte_offset - start_byte_offset <= DUK_UINT32_MAX); /* Guaranteed by string limits. */ - - /* No size check is necessary. */ - res = duk_heap_strtable_intern_checked(thr, - DUK_HSTRING_GET_DATA(h) + start_byte_offset, - (duk_uint32_t) (end_byte_offset - start_byte_offset)); - - duk_push_hstring(thr, res); - duk_replace(thr, idx); -} - -/* XXX: this is quite clunky. Add Unicode helpers to scan backwards and - * forwards with a callback to process codepoints? - */ -DUK_EXTERNAL void duk_trim(duk_hthread *thr, duk_idx_t idx) { - duk_hstring *h; - const duk_uint8_t *p, *p_start, *p_end, *p_tmp1, *p_tmp2; /* pointers for scanning */ - const duk_uint8_t *q_start, *q_end; /* start (incl) and end (excl) of trimmed part */ - duk_codepoint_t cp; - - DUK_ASSERT_API_ENTRY(thr); - - idx = duk_require_normalize_index(thr, idx); /* Accept symbols. */ - h = duk_require_hstring(thr, idx); - DUK_ASSERT(h != NULL); - - p_start = DUK_HSTRING_GET_DATA(h); - p_end = p_start + DUK_HSTRING_GET_BYTELEN(h); - - p = p_start; - while (p < p_end) { - p_tmp1 = p; - cp = (duk_codepoint_t) duk_unicode_decode_xutf8_checked(thr, &p_tmp1, p_start, p_end); - if (!(duk_unicode_is_whitespace(cp) || duk_unicode_is_line_terminator(cp))) { - break; - } - p = p_tmp1; - } - q_start = p; - if (p == p_end) { - /* Entire string is whitespace. */ - q_end = p; - goto scan_done; - } - - p = p_end; - while (p > p_start) { - p_tmp1 = p; - while (p > p_start) { - p--; - if (((*p) & 0xc0) != 0x80) { - break; - } - } - p_tmp2 = p; - - cp = (duk_codepoint_t) duk_unicode_decode_xutf8_checked(thr, &p_tmp2, p_start, p_end); - if (!(duk_unicode_is_whitespace(cp) || duk_unicode_is_line_terminator(cp))) { - p = p_tmp1; - break; - } - } - q_end = p; - -scan_done: - /* This may happen when forward and backward scanning disagree - * (possible for non-extended-UTF-8 strings). - */ - if (q_end < q_start) { - q_end = q_start; - } - - DUK_ASSERT(q_start >= p_start && q_start <= p_end); - DUK_ASSERT(q_end >= p_start && q_end <= p_end); - DUK_ASSERT(q_end >= q_start); - - DUK_DDD(DUK_DDDPRINT("trim: p_start=%p, p_end=%p, q_start=%p, q_end=%p", - (const void *) p_start, - (const void *) p_end, - (const void *) q_start, - (const void *) q_end)); - - if (q_start == p_start && q_end == p_end) { - DUK_DDD(DUK_DDDPRINT("nothing was trimmed: avoid interning (hashing etc)")); - return; - } - - duk_push_lstring(thr, (const char *) q_start, (duk_size_t) (q_end - q_start)); - duk_replace(thr, idx); -} - -DUK_EXTERNAL duk_codepoint_t duk_char_code_at(duk_hthread *thr, duk_idx_t idx, duk_size_t char_offset) { - duk_hstring *h; - duk_ucodepoint_t cp; - - DUK_ASSERT_API_ENTRY(thr); - - /* XXX: Share code with String.prototype.charCodeAt? Main difference - * is handling of clamped offsets. - */ - - h = duk_require_hstring(thr, idx); /* Accept symbols. */ - DUK_ASSERT(h != NULL); - - DUK_ASSERT_DISABLE(char_offset >= 0); /* Always true, arg is unsigned. */ - if (char_offset >= DUK_HSTRING_GET_CHARLEN(h)) { - return 0; - } - - DUK_ASSERT(char_offset <= DUK_UINT_MAX); /* Guaranteed by string limits. */ - cp = duk_hstring_char_code_at_raw(thr, h, (duk_uint_t) char_offset, 0 /*surrogate_aware*/); - return (duk_codepoint_t) cp; -} -/* - * Date/time. - */ - -/* #include duk_internal.h -> already included */ - -DUK_INTERNAL duk_double_t duk_time_get_ecmascript_time(duk_hthread *thr) { - /* ECMAScript time, with millisecond fractions. Exposed via - * duk_get_now() for example. - */ - DUK_UNREF(thr); - return (duk_double_t) DUK_USE_DATE_GET_NOW(thr); -} - -DUK_INTERNAL duk_double_t duk_time_get_ecmascript_time_nofrac(duk_hthread *thr) { - /* ECMAScript time without millisecond fractions. Exposed via - * the Date built-in which doesn't allow fractions. - */ - DUK_UNREF(thr); - return (duk_double_t) DUK_FLOOR(DUK_USE_DATE_GET_NOW(thr)); -} - -DUK_INTERNAL duk_double_t duk_time_get_monotonic_time(duk_hthread *thr) { - DUK_UNREF(thr); -#if defined(DUK_USE_GET_MONOTONIC_TIME) - return (duk_double_t) DUK_USE_GET_MONOTONIC_TIME(thr); -#else - return (duk_double_t) DUK_USE_DATE_GET_NOW(thr); -#endif -} - -DUK_EXTERNAL duk_double_t duk_get_now(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - DUK_UNREF(thr); - - /* This API intentionally allows millisecond fractions. */ - return duk_time_get_ecmascript_time(thr); -} - -#if 0 /* XXX: worth exposing? */ -DUK_EXTERNAL duk_double_t duk_get_monotonic_time(duk_hthread *thr) { - DUK_ASSERT_API_ENTRY(thr); - DUK_UNREF(thr); - - return duk_time_get_monotonic_time(thr); -} -#endif - -DUK_EXTERNAL void duk_time_to_components(duk_hthread *thr, duk_double_t timeval, duk_time_components *comp) { - duk_int_t parts[DUK_DATE_IDX_NUM_PARTS]; - duk_double_t dparts[DUK_DATE_IDX_NUM_PARTS]; - duk_uint_t flags; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(comp != NULL); /* XXX: or check? */ - DUK_UNREF(thr); - - /* Convert as one-based, but change month to zero-based to match the - * ECMAScript Date built-in behavior 1:1. - */ - flags = DUK_DATE_FLAG_ONEBASED | DUK_DATE_FLAG_NAN_TO_ZERO; - - duk_bi_date_timeval_to_parts(timeval, parts, dparts, flags); - - /* XXX: sub-millisecond accuracy for the API */ - - DUK_ASSERT(dparts[DUK_DATE_IDX_MONTH] >= 1.0 && dparts[DUK_DATE_IDX_MONTH] <= 12.0); - comp->year = dparts[DUK_DATE_IDX_YEAR]; - comp->month = dparts[DUK_DATE_IDX_MONTH] - 1.0; - comp->day = dparts[DUK_DATE_IDX_DAY]; - comp->hours = dparts[DUK_DATE_IDX_HOUR]; - comp->minutes = dparts[DUK_DATE_IDX_MINUTE]; - comp->seconds = dparts[DUK_DATE_IDX_SECOND]; - comp->milliseconds = dparts[DUK_DATE_IDX_MILLISECOND]; - comp->weekday = dparts[DUK_DATE_IDX_WEEKDAY]; -} - -DUK_EXTERNAL duk_double_t duk_components_to_time(duk_hthread *thr, duk_time_components *comp) { - duk_double_t d; - duk_double_t dparts[DUK_DATE_IDX_NUM_PARTS]; - duk_uint_t flags; - - DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT(comp != NULL); /* XXX: or check? */ - DUK_UNREF(thr); - - /* Match Date constructor behavior (with UTC time). Month is given - * as zero-based. Day-of-month is given as one-based so normalize - * it to zero-based as the internal conversion helpers expects all - * components to be zero-based. - */ - flags = 0; - - /* XXX: expensive conversion; use array format in API instead, or unify - * time provider and time API to use same struct? - */ - - dparts[DUK_DATE_IDX_YEAR] = comp->year; - dparts[DUK_DATE_IDX_MONTH] = comp->month; - dparts[DUK_DATE_IDX_DAY] = comp->day - 1.0; - dparts[DUK_DATE_IDX_HOUR] = comp->hours; - dparts[DUK_DATE_IDX_MINUTE] = comp->minutes; - dparts[DUK_DATE_IDX_SECOND] = comp->seconds; - dparts[DUK_DATE_IDX_MILLISECOND] = comp->milliseconds; - dparts[DUK_DATE_IDX_WEEKDAY] = 0; /* ignored */ - - d = duk_bi_date_get_timeval_from_dparts(dparts, flags); - - return d; -} -/* - * Array built-ins - * - * Most Array built-ins are intentionally generic in ECMAScript, and are - * intended to work even when the 'this' binding is not an Array instance. - * This ECMAScript feature is also used by much real world code. For this - * reason the implementations here don't assume exotic Array behavior or - * e.g. presence of a .length property. However, some algorithms have a - * fast path for duk_harray backed actual Array instances, enabled when - * footprint is not a concern. - * - * XXX: the "Throw" flag should be set for (almost?) all [[Put]] and - * [[Delete]] operations, but it's currently false throughout. Go through - * all put/delete cases and check throw flag use. Need a new API primitive - * which allows throws flag to be specified. - * - * XXX: array lengths above 2G won't work reliably. There are many places - * where one needs a full signed 32-bit range ([-0xffffffff, 0xffffffff], - * i.e. -33- bits). Although array 'length' cannot be written to be outside - * the unsigned 32-bit range (E5.1 Section 15.4.5.1 throws a RangeError if so) - * some intermediate values may be above 0xffffffff and this may not be always - * correctly handled now (duk_uint32_t is not enough for all algorithms). - * For instance, push() can legitimately write entries beyond length 0xffffffff - * and cause a RangeError only at the end. To do this properly, the current - * push() implementation tracks the array index using a 'double' instead of a - * duk_uint32_t (which is somewhat awkward). See test-bi-array-push-maxlen.js. - * - * On using "put" vs. "def" prop - * ============================= - * - * Code below must be careful to use the appropriate primitive as it matters - * for compliance. When using "put" there may be inherited properties in - * Array.prototype which cause side effects when values are written. When - * using "define" there are no such side effects, and many test262 test cases - * check for this (for real world code, such side effects are very rare). - * Both "put" and "define" are used in the E5.1 specification; as a rule, - * "put" is used when modifying an existing array (or a non-array 'this' - * binding) and "define" for setting values into a fresh result array. - */ - -/* #include duk_internal.h -> already included */ - -/* Perform an intermediate join when this many elements have been pushed - * on the value stack. - */ -#define DUK__ARRAY_MID_JOIN_LIMIT 4096 - -#if defined(DUK_USE_ARRAY_BUILTIN) - -/* - * Shared helpers. - */ - -/* Shared entry code for many Array built-ins: the 'this' binding is pushed - * on the value stack and object coerced, and the current .length is returned. - * Note that length is left on stack (it could be popped, but that's not - * usually necessary because call handling will clean it up automatically). - */ -DUK_LOCAL duk_uint32_t duk__push_this_obj_len_u32(duk_hthread *thr) { - duk_uint32_t len; - - /* XXX: push more directly? */ - (void) duk_push_this_coercible_to_object(thr); - DUK_HOBJECT_ASSERT_VALID(duk_get_hobject(thr, -1)); - duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_LENGTH); - len = duk_to_uint32(thr, -1); - - /* -> [ ... ToObject(this) ToUint32(length) ] */ - return len; -} - -DUK_LOCAL duk_uint32_t duk__push_this_obj_len_u32_limited(duk_hthread *thr) { - /* Range limited to [0, 0x7fffffff] range, i.e. range that can be - * represented with duk_int32_t. Use this when the method doesn't - * handle the full 32-bit unsigned range correctly. - */ - duk_uint32_t ret = duk__push_this_obj_len_u32(thr); - if (DUK_UNLIKELY(ret >= 0x80000000UL)) { - DUK_ERROR_RANGE_INVALID_LENGTH(thr); - DUK_WO_NORETURN(return 0U;); - } - return ret; -} - -#if defined(DUK_USE_ARRAY_FASTPATH) -/* Check if 'this' binding is an Array instance (duk_harray) which satisfies - * a few other guarantees for fast path operation. The fast path doesn't - * need to handle all operations, even for duk_harrays, but must handle a - * significant fraction to improve performance. Return a non-NULL duk_harray - * pointer when all fast path criteria are met, NULL otherwise. - */ -DUK_LOCAL duk_harray *duk__arraypart_fastpath_this(duk_hthread *thr) { - duk_tval *tv; - duk_hobject *h; - duk_uint_t flags_mask, flags_bits, flags_value; - - DUK_ASSERT(thr->valstack_bottom > thr->valstack); /* because call in progress */ - tv = DUK_GET_THIS_TVAL_PTR(thr); - - /* Fast path requires that 'this' is a duk_harray. Read only arrays - * (ROM backed) are also rejected for simplicity. - */ - if (!DUK_TVAL_IS_OBJECT(tv)) { - DUK_DD(DUK_DDPRINT("reject array fast path: not an object")); - return NULL; - } - h = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - flags_mask = DUK_HOBJECT_FLAG_ARRAY_PART | DUK_HOBJECT_FLAG_EXOTIC_ARRAY | DUK_HEAPHDR_FLAG_READONLY; - flags_bits = DUK_HOBJECT_FLAG_ARRAY_PART | DUK_HOBJECT_FLAG_EXOTIC_ARRAY; - flags_value = DUK_HEAPHDR_GET_FLAGS_RAW((duk_heaphdr *) h); - if ((flags_value & flags_mask) != flags_bits) { - DUK_DD(DUK_DDPRINT("reject array fast path: object flag check failed")); - return NULL; - } - - /* In some cases a duk_harray's 'length' may be larger than the - * current array part allocation. Avoid the fast path in these - * cases, so that all fast path code can safely assume that all - * items in the range [0,length[ are backed by the current array - * part allocation. - */ - if (((duk_harray *) h)->length > DUK_HOBJECT_GET_ASIZE(h)) { - DUK_DD(DUK_DDPRINT("reject array fast path: length > array part size")); - return NULL; - } - - /* Guarantees for fast path. */ - DUK_ASSERT(h != NULL); - DUK_ASSERT(DUK_HOBJECT_GET_ASIZE(h) == 0 || DUK_HOBJECT_A_GET_BASE(thr->heap, h) != NULL); - DUK_ASSERT(((duk_harray *) h)->length <= DUK_HOBJECT_GET_ASIZE(h)); - - DUK_DD(DUK_DDPRINT("array fast path allowed for: %!O", (duk_heaphdr *) h)); - return (duk_harray *) h; -} -#endif /* DUK_USE_ARRAY_FASTPATH */ - -/* - * Constructor - */ - -DUK_INTERNAL duk_ret_t duk_bi_array_constructor(duk_hthread *thr) { - duk_idx_t nargs; - duk_harray *a; - duk_double_t d; - duk_uint32_t len; - duk_uint32_t len_prealloc; - - nargs = duk_get_top(thr); - - if (nargs == 1 && duk_is_number(thr, 0)) { - /* XXX: expensive check (also shared elsewhere - so add a shared internal API call?) */ - d = duk_get_number(thr, 0); - len = duk_to_uint32(thr, 0); - if (!duk_double_equals((duk_double_t) len, d)) { - DUK_DCERROR_RANGE_INVALID_LENGTH(thr); - } - - /* For small lengths create a dense preallocated array. - * For large arrays preallocate an initial part. - */ - len_prealloc = len < 64 ? len : 64; - a = duk_push_harray_with_size(thr, len_prealloc); - DUK_ASSERT(a != NULL); - DUK_ASSERT(!duk_is_bare_object(thr, -1)); - a->length = len; - return 1; - } - - duk_pack(thr, nargs); - return 1; -} - -/* - * isArray() - */ - -DUK_INTERNAL duk_ret_t duk_bi_array_constructor_is_array(duk_hthread *thr) { - DUK_ASSERT_TOP(thr, 1); - duk_push_boolean(thr, duk_js_isarray(DUK_GET_TVAL_POSIDX(thr, 0))); - return 1; -} - -/* - * toString() - */ - -DUK_INTERNAL duk_ret_t duk_bi_array_prototype_to_string(duk_hthread *thr) { - (void) duk_push_this_coercible_to_object(thr); - duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_JOIN); - - /* [ ... this func ] */ - if (!duk_is_callable(thr, -1)) { - /* Fall back to the initial (original) Object.toString(). We don't - * currently have pointers to the built-in functions, only the top - * level global objects (like "Array") so this is now done in a bit - * of a hacky manner. It would be cleaner to push the (original) - * function and use duk_call_method(). - */ - - /* XXX: 'this' will be ToObject() coerced twice, which is incorrect - * but should have no visible side effects. - */ - DUK_DDD(DUK_DDDPRINT("this.join is not callable, fall back to (original) Object.toString")); - duk_set_top(thr, 0); - return duk_bi_object_prototype_to_string(thr); /* has access to 'this' binding */ - } - - /* [ ... this func ] */ - - duk_insert(thr, -2); - - /* [ ... func this ] */ - - DUK_DDD( - DUK_DDDPRINT("calling: func=%!iT, this=%!iT", (duk_tval *) duk_get_tval(thr, -2), (duk_tval *) duk_get_tval(thr, -1))); - duk_call_method(thr, 0); - - return 1; -} - -/* - * concat() - */ - -DUK_INTERNAL duk_ret_t duk_bi_array_prototype_concat(duk_hthread *thr) { - duk_idx_t i, n; - duk_uint32_t j, idx, len; - duk_hobject *h; - duk_size_t tmp_len; - - /* XXX: In ES2015 Array .length can be up to 2^53-1. The current - * implementation is limited to 2^32-1. - */ - - /* XXX: Fast path for array 'this' and array element. */ - - /* XXX: The insert here is a bit expensive if there are a lot of items. - * It could also be special cased in the outermost for loop quite easily - * (as the element is dup()'d anyway). - */ - - (void) duk_push_this_coercible_to_object(thr); - duk_insert(thr, 0); - n = duk_get_top(thr); - duk_push_array(thr); /* -> [ ToObject(this) item1 ... itemN arr ] */ - - /* NOTE: The Array special behaviors are NOT invoked by duk_xdef_prop_index() - * (which differs from the official algorithm). If no error is thrown, this - * doesn't matter as the length is updated at the end. However, if an error - * is thrown, the length will be unset. That shouldn't matter because the - * caller won't get a reference to the intermediate value. - */ - - idx = 0; - for (i = 0; i < n; i++) { - duk_bool_t spreadable; - duk_bool_t need_has_check; - - DUK_ASSERT_TOP(thr, n + 1); - - /* [ ToObject(this) item1 ... itemN arr ] */ - - h = duk_get_hobject(thr, i); - - if (h == NULL) { - spreadable = 0; - } else { -#if defined(DUK_USE_SYMBOL_BUILTIN) - duk_get_prop_stridx(thr, i, DUK_STRIDX_WELLKNOWN_SYMBOL_IS_CONCAT_SPREADABLE); - if (duk_is_undefined(thr, -1)) { - spreadable = duk_js_isarray_hobject(h); - } else { - spreadable = duk_to_boolean(thr, -1); - } - duk_pop_nodecref_unsafe(thr); -#else - spreadable = duk_js_isarray_hobject(h); -#endif - } - - if (!spreadable) { - duk_dup(thr, i); - duk_xdef_prop_index_wec(thr, -2, idx); - idx++; - if (DUK_UNLIKELY(idx == 0U)) { - /* Index after update is 0, and index written - * was 0xffffffffUL which is no longer a valid - * array index. - */ - goto fail_wrap; - } - continue; - } - - DUK_ASSERT(duk_is_object(thr, i)); - need_has_check = (DUK_HOBJECT_IS_PROXY(h) != 0); /* Always 0 w/o Proxy support. */ - - /* [ ToObject(this) item1 ... itemN arr ] */ - - tmp_len = duk_get_length(thr, i); - len = (duk_uint32_t) tmp_len; - if (DUK_UNLIKELY(tmp_len != (duk_size_t) len)) { - goto fail_wrap; - } - if (DUK_UNLIKELY(idx + len < idx)) { - /* Result length must be at most 0xffffffffUL to be - * a valid 32-bit array index. - */ - goto fail_wrap; - } - for (j = 0; j < len; j++) { - /* For a Proxy element, an explicit 'has' check is - * needed to allow the Proxy to present gaps. - */ - if (need_has_check) { - if (duk_has_prop_index(thr, i, j)) { - duk_get_prop_index(thr, i, j); - duk_xdef_prop_index_wec(thr, -2, idx); - } - } else { - if (duk_get_prop_index(thr, i, j)) { - duk_xdef_prop_index_wec(thr, -2, idx); - } else { - duk_pop_undefined(thr); - } - } - idx++; - DUK_ASSERT(idx != 0U); /* Wrap check above. */ - } - } - - /* ES5.1 has a specification "bug" in that nonexistent trailing - * elements don't affect the result .length. Test262 and other - * engines disagree, and the specification bug was fixed in ES2015 - * (see NOTE 1 in https://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.concat). - */ - duk_push_uarridx(thr, idx); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); - - DUK_ASSERT_TOP(thr, n + 1); - return 1; - -fail_wrap: - DUK_ERROR_RANGE_INVALID_LENGTH(thr); - DUK_WO_NORETURN(return 0;); -} - -/* - * join(), toLocaleString() - * - * Note: checking valstack is necessary, but only in the per-element loop. - * - * Note: the trivial approach of pushing all the elements on the value stack - * and then calling duk_join() fails when the array contains a large number - * of elements. This problem can't be offloaded to duk_join() because the - * elements to join must be handled here and have special handling. Current - * approach is to do intermediate joins with very large number of elements. - * There is no fancy handling; the prefix gets re-joined multiple times. - */ - -DUK_INTERNAL duk_ret_t duk_bi_array_prototype_join_shared(duk_hthread *thr) { - duk_uint32_t len, count; - duk_uint32_t idx; - duk_small_int_t to_locale_string = duk_get_current_magic(thr); - duk_idx_t valstack_required; - - /* For join(), nargs is 1. For toLocaleString(), nargs is 0 and - * setting the top essentially pushes an undefined to the stack, - * thus defaulting to a comma separator. - */ - duk_set_top(thr, 1); - if (duk_is_undefined(thr, 0)) { - duk_pop_undefined(thr); - duk_push_hstring_stridx(thr, DUK_STRIDX_COMMA); - } else { - duk_to_string(thr, 0); - } - - len = duk__push_this_obj_len_u32(thr); - - /* [ sep ToObject(this) len ] */ - - DUK_DDD(DUK_DDDPRINT("sep=%!T, this=%!T, len=%lu", - (duk_tval *) duk_get_tval(thr, 0), - (duk_tval *) duk_get_tval(thr, 1), - (unsigned long) len)); - - /* The extra (+4) is tight. */ - valstack_required = (duk_idx_t) ((len >= DUK__ARRAY_MID_JOIN_LIMIT ? DUK__ARRAY_MID_JOIN_LIMIT : len) + 4); - duk_require_stack(thr, valstack_required); - - duk_dup_0(thr); - - /* [ sep ToObject(this) len sep ] */ - - count = 0; - idx = 0; - for (;;) { - DUK_DDD(DUK_DDDPRINT("join idx=%ld", (long) idx)); - if (count >= DUK__ARRAY_MID_JOIN_LIMIT || /* intermediate join to avoid valstack overflow */ - idx >= len) { /* end of loop (careful with len==0) */ - /* [ sep ToObject(this) len sep str0 ... str(count-1) ] */ - DUK_DDD(DUK_DDDPRINT("mid/final join, count=%ld, idx=%ld, len=%ld", (long) count, (long) idx, (long) len)); - duk_join(thr, (duk_idx_t) count); /* -> [ sep ToObject(this) len str ] */ - duk_dup_0(thr); /* -> [ sep ToObject(this) len str sep ] */ - duk_insert(thr, -2); /* -> [ sep ToObject(this) len sep str ] */ - count = 1; - } - if (idx >= len) { - /* if true, the stack already contains the final result */ - break; - } - - duk_get_prop_index(thr, 1, (duk_uarridx_t) idx); - if (duk_is_null_or_undefined(thr, -1)) { - duk_pop_nodecref_unsafe(thr); - duk_push_hstring_empty(thr); - } else { - if (to_locale_string) { - duk_to_object(thr, -1); - duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_TO_LOCALE_STRING); - duk_insert(thr, -2); /* -> [ ... toLocaleString ToObject(val) ] */ - duk_call_method(thr, 0); - } - duk_to_string(thr, -1); - } - - count++; - idx++; - } - - /* [ sep ToObject(this) len sep result ] */ - - return 1; -} - -/* - * pop(), push() - */ - -#if defined(DUK_USE_ARRAY_FASTPATH) -DUK_LOCAL duk_ret_t duk__array_pop_fastpath(duk_hthread *thr, duk_harray *h_arr) { - duk_tval *tv_arraypart; - duk_tval *tv_val; - duk_uint32_t len; - - tv_arraypart = DUK_HOBJECT_A_GET_BASE(thr->heap, (duk_hobject *) h_arr); - len = h_arr->length; - if (len <= 0) { - /* nop, return undefined */ - return 0; - } - - len--; - h_arr->length = len; - - /* Fast path doesn't check for an index property inherited from - * Array.prototype. This is quite often acceptable; if not, - * disable fast path. - */ - DUK_ASSERT_VS_SPACE(thr); - tv_val = tv_arraypart + len; - if (DUK_TVAL_IS_UNUSED(tv_val)) { - /* No net refcount change. Value stack already has - * 'undefined' based on value stack init policy. - */ - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top)); - DUK_ASSERT(DUK_TVAL_IS_UNUSED(tv_val)); - } else { - /* No net refcount change. */ - DUK_TVAL_SET_TVAL(thr->valstack_top, tv_val); - DUK_TVAL_SET_UNUSED(tv_val); - } - thr->valstack_top++; - - /* XXX: there's no shrink check in the fast path now */ - - return 1; -} -#endif /* DUK_USE_ARRAY_FASTPATH */ - -DUK_INTERNAL duk_ret_t duk_bi_array_prototype_pop(duk_hthread *thr) { - duk_uint32_t len; - duk_uint32_t idx; -#if defined(DUK_USE_ARRAY_FASTPATH) - duk_harray *h_arr; -#endif - - DUK_ASSERT_TOP(thr, 0); - -#if defined(DUK_USE_ARRAY_FASTPATH) - h_arr = duk__arraypart_fastpath_this(thr); - if (h_arr) { - return duk__array_pop_fastpath(thr, h_arr); - } -#endif - - /* XXX: Merge fastpath check into a related call (push this, coerce length, etc)? */ - - len = duk__push_this_obj_len_u32(thr); - if (len == 0) { - duk_push_int(thr, 0); - duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LENGTH); - return 0; - } - idx = len - 1; - - duk_get_prop_index(thr, 0, (duk_uarridx_t) idx); - duk_del_prop_index(thr, 0, (duk_uarridx_t) idx); - duk_push_u32(thr, idx); - duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LENGTH); - return 1; -} - -#if defined(DUK_USE_ARRAY_FASTPATH) -DUK_LOCAL duk_ret_t duk__array_push_fastpath(duk_hthread *thr, duk_harray *h_arr) { - duk_tval *tv_arraypart; - duk_tval *tv_src; - duk_tval *tv_dst; - duk_uint32_t len; - duk_idx_t i, n; - - len = h_arr->length; - tv_arraypart = DUK_HOBJECT_A_GET_BASE(thr->heap, (duk_hobject *) h_arr); - - n = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom); - DUK_ASSERT(n >= 0); - DUK_ASSERT((duk_uint32_t) n <= DUK_UINT32_MAX); - if (DUK_UNLIKELY(len + (duk_uint32_t) n < len)) { - DUK_D(DUK_DPRINT("Array.prototype.push() would go beyond 32-bit length, throw")); - DUK_DCERROR_RANGE_INVALID_LENGTH(thr); /* != 0 return value returned as is by caller */ - } - if (len + (duk_uint32_t) n > DUK_HOBJECT_GET_ASIZE((duk_hobject *) h_arr)) { - /* Array part would need to be extended. Rely on slow path - * for now. - * - * XXX: Rework hobject code a bit and add extend support. - */ - return 0; - } - - tv_src = thr->valstack_bottom; - tv_dst = tv_arraypart + len; - for (i = 0; i < n; i++) { - /* No net refcount change; reset value stack values to - * undefined to satisfy value stack init policy. - */ - DUK_TVAL_SET_TVAL(tv_dst, tv_src); - DUK_TVAL_SET_UNDEFINED(tv_src); - tv_src++; - tv_dst++; - } - thr->valstack_top = thr->valstack_bottom; - len += (duk_uint32_t) n; - h_arr->length = len; - - DUK_ASSERT((duk_uint_t) len == len); - duk_push_uint(thr, (duk_uint_t) len); - return 1; -} -#endif /* DUK_USE_ARRAY_FASTPATH */ - -DUK_INTERNAL duk_ret_t duk_bi_array_prototype_push(duk_hthread *thr) { - /* Note: 'this' is not necessarily an Array object. The push() - * algorithm is supposed to work for other kinds of objects too, - * so the algorithm has e.g. an explicit update for the 'length' - * property which is normally "magical" in arrays. - */ - - duk_uint32_t len; - duk_idx_t i, n; -#if defined(DUK_USE_ARRAY_FASTPATH) - duk_harray *h_arr; -#endif - -#if defined(DUK_USE_ARRAY_FASTPATH) - h_arr = duk__arraypart_fastpath_this(thr); - if (h_arr) { - duk_ret_t rc; - rc = duk__array_push_fastpath(thr, h_arr); - if (rc != 0) { - return rc; - } - DUK_DD(DUK_DDPRINT("array push() fast path exited, resize case")); - } -#endif - - n = duk_get_top(thr); - len = duk__push_this_obj_len_u32(thr); - - /* [ arg1 ... argN obj length ] */ - - /* Technically Array.prototype.push() can create an Array with length - * longer than 2^32-1, i.e. outside the 32-bit range. The final length - * is *not* wrapped to 32 bits in the specification. - * - * This implementation tracks length with a uint32 because it's much - * more practical. - * - * See: test-bi-array-push-maxlen.js. - */ - - if (len + (duk_uint32_t) n < len) { - DUK_D(DUK_DPRINT("Array.prototype.push() would go beyond 32-bit length, throw")); - DUK_DCERROR_RANGE_INVALID_LENGTH(thr); - } - - for (i = 0; i < n; i++) { - duk_dup(thr, i); - duk_put_prop_index(thr, -3, (duk_uarridx_t) (len + (duk_uint32_t) i)); - } - len += (duk_uint32_t) n; - - duk_push_u32(thr, len); - duk_dup_top(thr); - duk_put_prop_stridx_short(thr, -4, DUK_STRIDX_LENGTH); - - /* [ arg1 ... argN obj length new_length ] */ - return 1; -} - -/* - * sort() - * - * Currently qsort with random pivot. This is now really, really slow, - * because there is no fast path for array parts. - * - * Signed indices are used because qsort() leaves and degenerate cases - * may use a negative offset. - */ - -DUK_LOCAL duk_small_int_t duk__array_sort_compare(duk_hthread *thr, duk_int_t idx1, duk_int_t idx2) { - duk_bool_t have1, have2; - duk_bool_t undef1, undef2; - duk_small_int_t ret; - duk_idx_t idx_obj = 1; /* fixed offsets in valstack */ - duk_idx_t idx_fn = 0; - duk_hstring *h1, *h2; - - /* Fast exit if indices are identical. This is valid for a non-existent property, - * for an undefined value, and almost always for ToString() coerced comparison of - * arbitrary values (corner cases where this is not the case include e.g. a an - * object with varying ToString() coercion). - * - * The specification does not prohibit "caching" of values read from the array, so - * assuming equality for comparing an index with itself falls into the category of - * "caching". - * - * Also, compareFn may be inconsistent, so skipping a call to compareFn here may - * have an effect on the final result. The specification does not require any - * specific behavior for inconsistent compare functions, so again, this fast path - * is OK. - */ - - if (idx1 == idx2) { - DUK_DDD(DUK_DDDPRINT("duk__array_sort_compare: idx1=%ld, idx2=%ld -> indices identical, quick exit", - (long) idx1, - (long) idx2)); - return 0; - } - - have1 = duk_get_prop_index(thr, idx_obj, (duk_uarridx_t) idx1); - have2 = duk_get_prop_index(thr, idx_obj, (duk_uarridx_t) idx2); - - DUK_DDD(DUK_DDDPRINT("duk__array_sort_compare: idx1=%ld, idx2=%ld, have1=%ld, have2=%ld, val1=%!T, val2=%!T", - (long) idx1, - (long) idx2, - (long) have1, - (long) have2, - (duk_tval *) duk_get_tval(thr, -2), - (duk_tval *) duk_get_tval(thr, -1))); - - if (have1) { - if (have2) { - ; - } else { - ret = -1; - goto pop_ret; - } - } else { - if (have2) { - ret = 1; - goto pop_ret; - } else { - ret = 0; - goto pop_ret; - } - } - - undef1 = duk_is_undefined(thr, -2); - undef2 = duk_is_undefined(thr, -1); - if (undef1) { - if (undef2) { - ret = 0; - goto pop_ret; - } else { - ret = 1; - goto pop_ret; - } - } else { - if (undef2) { - ret = -1; - goto pop_ret; - } else { - ; - } - } - - if (!duk_is_undefined(thr, idx_fn)) { - duk_double_t d; - - /* No need to check callable; duk_call() will do that. */ - duk_dup(thr, idx_fn); /* -> [ ... x y fn ] */ - duk_insert(thr, -3); /* -> [ ... fn x y ] */ - duk_call(thr, 2); /* -> [ ... res ] */ - - /* ES5 is a bit vague about what to do if the return value is - * not a number. ES2015 provides a concrete description: - * http://www.ecma-international.org/ecma-262/6.0/#sec-sortcompare. - */ - - d = duk_to_number_m1(thr); - if (d < 0.0) { - ret = -1; - } else if (d > 0.0) { - ret = 1; - } else { - /* Because NaN compares to false, NaN is handled here - * without an explicit check above. - */ - ret = 0; - } - - duk_pop_nodecref_unsafe(thr); - DUK_DDD(DUK_DDDPRINT("-> result %ld (from comparefn, after coercion)", (long) ret)); - return ret; - } - - /* string compare is the default (a bit oddly) */ - - /* XXX: any special handling for plain array; causes repeated coercion now? */ - h1 = duk_to_hstring(thr, -2); - h2 = duk_to_hstring_m1(thr); - DUK_ASSERT(h1 != NULL); - DUK_ASSERT(h2 != NULL); - - ret = duk_js_string_compare(h1, h2); /* retval is directly usable */ - goto pop_ret; - -pop_ret: - duk_pop_2_unsafe(thr); - DUK_DDD(DUK_DDDPRINT("-> result %ld", (long) ret)); - return ret; -} - -DUK_LOCAL void duk__array_sort_swap(duk_hthread *thr, duk_int_t l, duk_int_t r) { - duk_bool_t have_l, have_r; - duk_idx_t idx_obj = 1; /* fixed offset in valstack */ - - if (l == r) { - return; - } - - /* swap elements; deal with non-existent elements correctly */ - have_l = duk_get_prop_index(thr, idx_obj, (duk_uarridx_t) l); - have_r = duk_get_prop_index(thr, idx_obj, (duk_uarridx_t) r); - - if (have_r) { - /* right exists, [[Put]] regardless whether or not left exists */ - duk_put_prop_index(thr, idx_obj, (duk_uarridx_t) l); - } else { - duk_del_prop_index(thr, idx_obj, (duk_uarridx_t) l); - duk_pop_undefined(thr); - } - - if (have_l) { - duk_put_prop_index(thr, idx_obj, (duk_uarridx_t) r); - } else { - duk_del_prop_index(thr, idx_obj, (duk_uarridx_t) r); - duk_pop_undefined(thr); - } -} - -#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2) -/* Debug print which visualizes the qsort partitioning process. */ -DUK_LOCAL void duk__debuglog_qsort_state(duk_hthread *thr, duk_int_t lo, duk_int_t hi, duk_int_t pivot) { - char buf[4096]; - char *ptr = buf; - duk_int_t i, n; - n = (duk_int_t) duk_get_length(thr, 1); - if (n > 4000) { - n = 4000; - } - *ptr++ = '['; - for (i = 0; i < n; i++) { - if (i == pivot) { - *ptr++ = '|'; - } else if (i == lo) { - *ptr++ = '<'; - } else if (i == hi) { - *ptr++ = '>'; - } else if (i >= lo && i <= hi) { - *ptr++ = '-'; - } else { - *ptr++ = ' '; - } - } - *ptr++ = ']'; - *ptr++ = '\0'; - - DUK_DDD(DUK_DDDPRINT("%s (lo=%ld, hi=%ld, pivot=%ld)", (const char *) buf, (long) lo, (long) hi, (long) pivot)); -} -#endif - -DUK_LOCAL void duk__array_qsort(duk_hthread *thr, duk_int_t lo, duk_int_t hi) { - duk_int_t p, l, r; - - /* The lo/hi indices may be crossed and hi < 0 is possible at entry. */ - - DUK_DDD(DUK_DDDPRINT("duk__array_qsort: lo=%ld, hi=%ld, obj=%!T", (long) lo, (long) hi, (duk_tval *) duk_get_tval(thr, 1))); - - DUK_ASSERT_TOP(thr, 3); - - /* In some cases it may be that lo > hi, or hi < 0; these - * degenerate cases happen e.g. for empty arrays, and in - * recursion leaves. - */ - - /* trivial cases */ - if (hi - lo < 1) { - DUK_DDD(DUK_DDDPRINT("degenerate case, return immediately")); - return; - } - DUK_ASSERT(hi > lo); - DUK_ASSERT(hi - lo + 1 >= 2); - - /* randomized pivot selection */ - p = lo + (duk_int_t) (duk_util_get_random_double(thr) * (duk_double_t) (hi - lo + 1)); - DUK_ASSERT(p >= lo && p <= hi); - DUK_DDD(DUK_DDDPRINT("lo=%ld, hi=%ld, chose pivot p=%ld", (long) lo, (long) hi, (long) p)); - - /* move pivot out of the way */ - duk__array_sort_swap(thr, p, lo); - p = lo; - DUK_DDD(DUK_DDDPRINT("pivot moved out of the way: %!T", (duk_tval *) duk_get_tval(thr, 1))); - - l = lo + 1; - r = hi; - for (;;) { - /* find elements to swap */ - for (;;) { - DUK_DDD(DUK_DDDPRINT("left scan: l=%ld, r=%ld, p=%ld", (long) l, (long) r, (long) p)); - if (l >= hi) { - break; - } - if (duk__array_sort_compare(thr, l, p) >= 0) { /* !(l < p) */ - break; - } - l++; - } - for (;;) { - DUK_DDD(DUK_DDDPRINT("right scan: l=%ld, r=%ld, p=%ld", (long) l, (long) r, (long) p)); - if (r <= lo) { - break; - } - if (duk__array_sort_compare(thr, p, r) >= 0) { /* !(p < r) */ - break; - } - r--; - } - if (l >= r) { - goto done; - } - DUK_ASSERT(l < r); - - DUK_DDD(DUK_DDDPRINT("swap %ld and %ld", (long) l, (long) r)); - - duk__array_sort_swap(thr, l, r); - - DUK_DDD(DUK_DDDPRINT("after swap: %!T", (duk_tval *) duk_get_tval(thr, 1))); - l++; - r--; - } -done: - /* Note that 'l' and 'r' may cross, i.e. r < l */ - DUK_ASSERT(l >= lo && l <= hi); - DUK_ASSERT(r >= lo && r <= hi); - - /* XXX: there's no explicit recursion bound here now. For the average - * qsort recursion depth O(log n) that's not really necessary: e.g. for - * 2**32 recursion depth would be about 32 which is OK. However, qsort - * worst case recursion depth is O(n) which may be a problem. - */ - - /* move pivot to its final place */ - DUK_DDD(DUK_DDDPRINT("before final pivot swap: %!T", (duk_tval *) duk_get_tval(thr, 1))); - duk__array_sort_swap(thr, lo, r); - -#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2) - duk__debuglog_qsort_state(thr, lo, hi, r); -#endif - - DUK_DDD(DUK_DDDPRINT("recurse: pivot=%ld, obj=%!T", (long) r, (duk_tval *) duk_get_tval(thr, 1))); - duk__array_qsort(thr, lo, r - 1); - duk__array_qsort(thr, r + 1, hi); -} - -DUK_INTERNAL duk_ret_t duk_bi_array_prototype_sort(duk_hthread *thr) { - duk_uint32_t len; - - /* XXX: len >= 0x80000000 won't work below because a signed type - * is needed by qsort. - */ - len = duk__push_this_obj_len_u32_limited(thr); - - /* stack[0] = compareFn - * stack[1] = ToObject(this) - * stack[2] = ToUint32(length) - */ - - if (len > 0) { - /* avoid degenerate cases, so that (len - 1) won't underflow */ - duk__array_qsort(thr, (duk_int_t) 0, (duk_int_t) (len - 1)); - } - - DUK_ASSERT_TOP(thr, 3); - duk_pop_nodecref_unsafe(thr); - return 1; /* return ToObject(this) */ -} - -/* - * splice() - */ - -/* XXX: this compiles to over 500 bytes now, even without special handling - * for an array part. Uses signed ints so does not handle full array range correctly. - */ - -/* XXX: can shift() / unshift() use the same helper? - * shift() is (close to?) <--> splice(0, 1) - * unshift is (close to?) <--> splice(0, 0, [items])? - */ - -DUK_INTERNAL duk_ret_t duk_bi_array_prototype_splice(duk_hthread *thr) { - duk_idx_t nargs; - duk_uint32_t len_u32; - duk_int_t len; - duk_bool_t have_delcount; - duk_int_t item_count; - duk_int_t act_start; - duk_int_t del_count; - duk_int_t i, n; - - DUK_UNREF(have_delcount); - - nargs = duk_get_top(thr); - if (nargs < 2) { - duk_set_top(thr, 2); - nargs = 2; - have_delcount = 0; - } else { - have_delcount = 1; - } - - /* XXX: len >= 0x80000000 won't work below because we need to be - * able to represent -len. - */ - len_u32 = duk__push_this_obj_len_u32_limited(thr); - len = (duk_int_t) len_u32; - DUK_ASSERT(len >= 0); - - act_start = duk_to_int_clamped(thr, 0, -len, len); - if (act_start < 0) { - act_start = len + act_start; - } - DUK_ASSERT(act_start >= 0 && act_start <= len); - -#if defined(DUK_USE_NONSTD_ARRAY_SPLICE_DELCOUNT) - if (have_delcount) { -#endif - del_count = duk_to_int_clamped(thr, 1, 0, len - act_start); -#if defined(DUK_USE_NONSTD_ARRAY_SPLICE_DELCOUNT) - } else { - /* E5.1 standard behavior when deleteCount is not given would be - * to treat it just like if 'undefined' was given, which coerces - * ultimately to 0. Real world behavior is to splice to the end - * of array, see test-bi-array-proto-splice-no-delcount.js. - */ - del_count = len - act_start; - } -#endif - - DUK_ASSERT(nargs >= 2); - item_count = (duk_int_t) (nargs - 2); - - DUK_ASSERT(del_count >= 0 && del_count <= len - act_start); - DUK_ASSERT(del_count + act_start <= len); - - /* For now, restrict result array into 32-bit length range. */ - if (((duk_double_t) len) - ((duk_double_t) del_count) + ((duk_double_t) item_count) > (duk_double_t) DUK_UINT32_MAX) { - DUK_D(DUK_DPRINT("Array.prototype.splice() would go beyond 32-bit length, throw")); - DUK_DCERROR_RANGE_INVALID_LENGTH(thr); - } - - duk_push_array(thr); - - /* stack[0] = start - * stack[1] = deleteCount - * stack[2...nargs-1] = items - * stack[nargs] = ToObject(this) -3 - * stack[nargs+1] = ToUint32(length) -2 - * stack[nargs+2] = result array -1 - */ - - DUK_ASSERT_TOP(thr, nargs + 3); - - /* Step 9: copy elements-to-be-deleted into the result array */ - - for (i = 0; i < del_count; i++) { - if (duk_get_prop_index(thr, -3, (duk_uarridx_t) (act_start + i))) { - duk_xdef_prop_index_wec(thr, -2, (duk_uarridx_t) i); /* throw flag irrelevant (false in std alg) */ - } else { - duk_pop_undefined(thr); - } - } - duk_push_u32(thr, (duk_uint32_t) del_count); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); - - /* Steps 12 and 13: reorganize elements to make room for itemCount elements */ - - if (item_count < del_count) { - /* [ A B C D E F G H ] rel_index = 2, del_count 3, item count 1 - * -> [ A B F G H ] (conceptual intermediate step) - * -> [ A B . F G H ] (placeholder marked) - * [ A B C F G H ] (actual result at this point, C will be replaced) - */ - - DUK_ASSERT_TOP(thr, nargs + 3); - - n = len - del_count; - for (i = act_start; i < n; i++) { - if (duk_get_prop_index(thr, -3, (duk_uarridx_t) (i + del_count))) { - duk_put_prop_index(thr, -4, (duk_uarridx_t) (i + item_count)); - } else { - duk_pop_undefined(thr); - duk_del_prop_index(thr, -3, (duk_uarridx_t) (i + item_count)); - } - } - - DUK_ASSERT_TOP(thr, nargs + 3); - - /* loop iterator init and limit changed from standard algorithm */ - n = len - del_count + item_count; - for (i = len - 1; i >= n; i--) { - duk_del_prop_index(thr, -3, (duk_uarridx_t) i); - } - - DUK_ASSERT_TOP(thr, nargs + 3); - } else if (item_count > del_count) { - /* [ A B C D E F G H ] rel_index = 2, del_count 3, item count 4 - * -> [ A B F G H ] (conceptual intermediate step) - * -> [ A B . . . . F G H ] (placeholder marked) - * [ A B C D E F F G H ] (actual result at this point) - */ - - DUK_ASSERT_TOP(thr, nargs + 3); - - /* loop iterator init and limit changed from standard algorithm */ - for (i = len - del_count - 1; i >= act_start; i--) { - if (duk_get_prop_index(thr, -3, (duk_uarridx_t) (i + del_count))) { - duk_put_prop_index(thr, -4, (duk_uarridx_t) (i + item_count)); - } else { - duk_pop_undefined(thr); - duk_del_prop_index(thr, -3, (duk_uarridx_t) (i + item_count)); - } - } - - DUK_ASSERT_TOP(thr, nargs + 3); - } else { - /* [ A B C D E F G H ] rel_index = 2, del_count 3, item count 3 - * -> [ A B F G H ] (conceptual intermediate step) - * -> [ A B . . . F G H ] (placeholder marked) - * [ A B C D E F G H ] (actual result at this point) - */ - } - DUK_ASSERT_TOP(thr, nargs + 3); - - /* Step 15: insert itemCount elements into the hole made above */ - - for (i = 0; i < item_count; i++) { - duk_dup(thr, i + 2); /* args start at index 2 */ - duk_put_prop_index(thr, -4, (duk_uarridx_t) (act_start + i)); - } - - /* Step 16: update length; note that the final length may be above 32 bit range - * (but we checked above that this isn't the case here) - */ - - duk_push_u32(thr, (duk_uint32_t) (len - del_count + item_count)); - duk_put_prop_stridx_short(thr, -4, DUK_STRIDX_LENGTH); - - /* result array is already at the top of stack */ - DUK_ASSERT_TOP(thr, nargs + 3); - return 1; -} - -/* - * reverse() - */ - -DUK_INTERNAL duk_ret_t duk_bi_array_prototype_reverse(duk_hthread *thr) { - duk_uint32_t len; - duk_uint32_t middle; - duk_uint32_t lower, upper; - duk_bool_t have_lower, have_upper; - - len = duk__push_this_obj_len_u32(thr); - middle = len / 2; - - /* If len <= 1, middle will be 0 and for-loop bails out - * immediately (0 < 0 -> false). - */ - - for (lower = 0; lower < middle; lower++) { - DUK_ASSERT(len >= 2); - DUK_ASSERT_TOP(thr, 2); - - DUK_ASSERT(len >= lower + 1); - upper = len - lower - 1; - - have_lower = duk_get_prop_index(thr, -2, (duk_uarridx_t) lower); - have_upper = duk_get_prop_index(thr, -3, (duk_uarridx_t) upper); - - /* [ ToObject(this) ToUint32(length) lowerValue upperValue ] */ - - if (have_upper) { - duk_put_prop_index(thr, -4, (duk_uarridx_t) lower); - } else { - duk_del_prop_index(thr, -4, (duk_uarridx_t) lower); - duk_pop_undefined(thr); - } - - if (have_lower) { - duk_put_prop_index(thr, -3, (duk_uarridx_t) upper); - } else { - duk_del_prop_index(thr, -3, (duk_uarridx_t) upper); - duk_pop_undefined(thr); - } - - DUK_ASSERT_TOP(thr, 2); - } - - DUK_ASSERT_TOP(thr, 2); - duk_pop_unsafe(thr); /* -> [ ToObject(this) ] */ - return 1; -} - -/* - * slice() - */ - -DUK_INTERNAL duk_ret_t duk_bi_array_prototype_slice(duk_hthread *thr) { - duk_uint32_t len_u32; - duk_int_t len; - duk_int_t start, end; - duk_int_t i; - duk_uarridx_t idx; - duk_uint32_t res_length = 0; - - /* XXX: len >= 0x80000000 won't work below because we need to be - * able to represent -len. - */ - len_u32 = duk__push_this_obj_len_u32_limited(thr); - len = (duk_int_t) len_u32; - DUK_ASSERT(len >= 0); - - duk_push_array(thr); - - /* stack[0] = start - * stack[1] = end - * stack[2] = ToObject(this) - * stack[3] = ToUint32(length) - * stack[4] = result array - */ - - start = duk_to_int_clamped(thr, 0, -len, len); - if (start < 0) { - start = len + start; - } - /* XXX: could duk_is_undefined() provide defaulting undefined to 'len' - * (the upper limit)? - */ - if (duk_is_undefined(thr, 1)) { - end = len; - } else { - end = duk_to_int_clamped(thr, 1, -len, len); - if (end < 0) { - end = len + end; - } - } - DUK_ASSERT(start >= 0 && start <= len); - DUK_ASSERT(end >= 0 && end <= len); - - idx = 0; - for (i = start; i < end; i++) { - DUK_ASSERT_TOP(thr, 5); - if (duk_get_prop_index(thr, 2, (duk_uarridx_t) i)) { - duk_xdef_prop_index_wec(thr, 4, idx); - res_length = idx + 1; - } else { - duk_pop_undefined(thr); - } - idx++; - DUK_ASSERT_TOP(thr, 5); - } - - duk_push_u32(thr, res_length); - duk_xdef_prop_stridx_short(thr, 4, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); - - DUK_ASSERT_TOP(thr, 5); - return 1; -} - -/* - * shift() - */ - -DUK_INTERNAL duk_ret_t duk_bi_array_prototype_shift(duk_hthread *thr) { - duk_uint32_t len; - duk_uint32_t i; - - len = duk__push_this_obj_len_u32(thr); - if (len == 0) { - duk_push_int(thr, 0); - duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LENGTH); - return 0; - } - - duk_get_prop_index(thr, 0, 0); - - /* stack[0] = object (this) - * stack[1] = ToUint32(length) - * stack[2] = elem at index 0 (retval) - */ - - for (i = 1; i < len; i++) { - DUK_ASSERT_TOP(thr, 3); - if (duk_get_prop_index(thr, 0, (duk_uarridx_t) i)) { - /* fromPresent = true */ - duk_put_prop_index(thr, 0, (duk_uarridx_t) (i - 1)); - } else { - /* fromPresent = false */ - duk_del_prop_index(thr, 0, (duk_uarridx_t) (i - 1)); - duk_pop_undefined(thr); - } - } - duk_del_prop_index(thr, 0, (duk_uarridx_t) (len - 1)); - - duk_push_u32(thr, (duk_uint32_t) (len - 1)); - duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LENGTH); - - DUK_ASSERT_TOP(thr, 3); - return 1; -} - -/* - * unshift() - */ - -DUK_INTERNAL duk_ret_t duk_bi_array_prototype_unshift(duk_hthread *thr) { - duk_idx_t nargs; - duk_uint32_t len; - duk_uint32_t i; - - nargs = duk_get_top(thr); - len = duk__push_this_obj_len_u32(thr); - - /* stack[0...nargs-1] = unshift args (vararg) - * stack[nargs] = ToObject(this) - * stack[nargs+1] = ToUint32(length) - */ - - DUK_ASSERT_TOP(thr, nargs + 2); - - /* Note: unshift() may operate on indices above unsigned 32-bit range - * and the final length may be >= 2**32. However, we restrict the - * final result to 32-bit range for practicality. - */ - - if (len + (duk_uint32_t) nargs < len) { - DUK_D(DUK_DPRINT("Array.prototype.unshift() would go beyond 32-bit length, throw")); - DUK_DCERROR_RANGE_INVALID_LENGTH(thr); - } - - i = len; - while (i > 0) { - DUK_ASSERT_TOP(thr, nargs + 2); - i--; - /* k+argCount-1; note that may be above 32-bit range */ - - if (duk_get_prop_index(thr, -2, (duk_uarridx_t) i)) { - /* fromPresent = true */ - /* [ ... ToObject(this) ToUint32(length) val ] */ - duk_put_prop_index( - thr, - -3, - (duk_uarridx_t) (i + (duk_uint32_t) nargs)); /* -> [ ... ToObject(this) ToUint32(length) ] */ - } else { - /* fromPresent = false */ - /* [ ... ToObject(this) ToUint32(length) val ] */ - duk_pop_undefined(thr); - duk_del_prop_index( - thr, - -2, - (duk_uarridx_t) (i + (duk_uint32_t) nargs)); /* -> [ ... ToObject(this) ToUint32(length) ] */ - } - DUK_ASSERT_TOP(thr, nargs + 2); - } - - for (i = 0; i < (duk_uint32_t) nargs; i++) { - DUK_ASSERT_TOP(thr, nargs + 2); - duk_dup(thr, (duk_idx_t) i); /* -> [ ... ToObject(this) ToUint32(length) arg[i] ] */ - duk_put_prop_index(thr, -3, (duk_uarridx_t) i); - DUK_ASSERT_TOP(thr, nargs + 2); - } - - DUK_ASSERT_TOP(thr, nargs + 2); - duk_push_u32(thr, len + (duk_uint32_t) nargs); - duk_dup_top(thr); /* -> [ ... ToObject(this) ToUint32(length) final_len final_len ] */ - duk_put_prop_stridx_short(thr, -4, DUK_STRIDX_LENGTH); - return 1; -} - -/* - * indexOf(), lastIndexOf() - */ - -DUK_INTERNAL duk_ret_t duk_bi_array_prototype_indexof_shared(duk_hthread *thr) { - duk_idx_t nargs; - duk_int_t i, len; - duk_int_t from_idx; - duk_small_int_t idx_step = duk_get_current_magic(thr); /* idx_step is +1 for indexOf, -1 for lastIndexOf */ - - /* lastIndexOf() needs to be a vararg function because we must distinguish - * between an undefined fromIndex and a "not given" fromIndex; indexOf() is - * made vararg for symmetry although it doesn't strictly need to be. - */ - - nargs = duk_get_top(thr); - duk_set_top(thr, 2); - - /* XXX: must be able to represent -len */ - len = (duk_int_t) duk__push_this_obj_len_u32_limited(thr); - if (len == 0) { - goto not_found; - } - - /* Index clamping is a bit tricky, we must ensure that we'll only iterate - * through elements that exist and that the specific requirements from E5.1 - * Sections 15.4.4.14 and 15.4.4.15 are fulfilled; especially: - * - * - indexOf: clamp to [-len,len], negative handling -> [0,len], - * if clamped result is len, for-loop bails out immediately - * - * - lastIndexOf: clamp to [-len-1, len-1], negative handling -> [-1, len-1], - * if clamped result is -1, for-loop bails out immediately - * - * If fromIndex is not given, ToInteger(undefined) = 0, which is correct - * for indexOf() but incorrect for lastIndexOf(). Hence special handling, - * and why lastIndexOf() needs to be a vararg function. - */ - - if (nargs >= 2) { - /* indexOf: clamp fromIndex to [-len, len] - * (if fromIndex == len, for-loop terminates directly) - * - * lastIndexOf: clamp fromIndex to [-len - 1, len - 1] - * (if clamped to -len-1 -> fromIndex becomes -1, terminates for-loop directly) - */ - from_idx = duk_to_int_clamped(thr, 1, (idx_step > 0 ? -len : -len - 1), (idx_step > 0 ? len : len - 1)); - if (from_idx < 0) { - /* for lastIndexOf, result may be -1 (mark immediate termination) */ - from_idx = len + from_idx; - } - } else { - /* for indexOf, ToInteger(undefined) would be 0, i.e. correct, but - * handle both indexOf and lastIndexOf specially here. - */ - if (idx_step > 0) { - from_idx = 0; - } else { - from_idx = len - 1; - } - } - - /* stack[0] = searchElement - * stack[1] = fromIndex - * stack[2] = object - * stack[3] = length (not needed, but not popped above) - */ - - for (i = from_idx; i >= 0 && i < len; i += idx_step) { - DUK_ASSERT_TOP(thr, 4); - - if (duk_get_prop_index(thr, 2, (duk_uarridx_t) i)) { - DUK_ASSERT_TOP(thr, 5); - if (duk_strict_equals(thr, 0, 4)) { - duk_push_int(thr, i); - return 1; - } - } - - duk_pop_unsafe(thr); - } - -not_found: - duk_push_int(thr, -1); - return 1; -} - -/* - * every(), some(), forEach(), map(), filter() - */ - -#define DUK__ITER_EVERY 0 -#define DUK__ITER_SOME 1 -#define DUK__ITER_FOREACH 2 -#define DUK__ITER_MAP 3 -#define DUK__ITER_FILTER 4 - -/* XXX: This helper is a bit awkward because the handling for the different iteration - * callers is quite different. This now compiles to a bit less than 500 bytes, so with - * 5 callers the net result is about 100 bytes / caller. - */ - -DUK_INTERNAL duk_ret_t duk_bi_array_prototype_iter_shared(duk_hthread *thr) { - duk_uint32_t len; - duk_uint32_t i; - duk_uarridx_t k; - duk_bool_t bval; - duk_small_int_t iter_type = duk_get_current_magic(thr); - duk_uint32_t res_length = 0; - - /* each call this helper serves has nargs==2 */ - DUK_ASSERT_TOP(thr, 2); - - len = duk__push_this_obj_len_u32(thr); - duk_require_callable(thr, 0); - /* if thisArg not supplied, behave as if undefined was supplied */ - - if (iter_type == DUK__ITER_MAP || iter_type == DUK__ITER_FILTER) { - duk_push_array(thr); - } else { - duk_push_undefined(thr); - } - - /* stack[0] = callback - * stack[1] = thisArg - * stack[2] = object - * stack[3] = ToUint32(length) (unused, but avoid unnecessary pop) - * stack[4] = result array (or undefined) - */ - - k = 0; /* result index for filter() */ - for (i = 0; i < len; i++) { - DUK_ASSERT_TOP(thr, 5); - - if (!duk_get_prop_index(thr, 2, (duk_uarridx_t) i)) { - /* For 'map' trailing missing elements don't invoke the - * callback but count towards the result length. - */ - if (iter_type == DUK__ITER_MAP) { - res_length = i + 1; - } - duk_pop_undefined(thr); - continue; - } - - /* The original value needs to be preserved for filter(), hence - * this funny order. We can't re-get the value because of side - * effects. - */ - - duk_dup_0(thr); - duk_dup_1(thr); - duk_dup_m3(thr); - duk_push_u32(thr, i); - duk_dup_2(thr); /* [ ... val callback thisArg val i obj ] */ - duk_call_method(thr, 3); /* -> [ ... val retval ] */ - - switch (iter_type) { - case DUK__ITER_EVERY: - bval = duk_to_boolean(thr, -1); - if (!bval) { - /* stack top contains 'false' */ - return 1; - } - break; - case DUK__ITER_SOME: - bval = duk_to_boolean(thr, -1); - if (bval) { - /* stack top contains 'true' */ - return 1; - } - break; - case DUK__ITER_FOREACH: - /* nop */ - break; - case DUK__ITER_MAP: - duk_dup_top(thr); - duk_xdef_prop_index_wec(thr, 4, (duk_uarridx_t) i); /* retval to result[i] */ - res_length = i + 1; - break; - case DUK__ITER_FILTER: - bval = duk_to_boolean(thr, -1); - if (bval) { - duk_dup_m2(thr); /* orig value */ - duk_xdef_prop_index_wec(thr, 4, (duk_uarridx_t) k); - k++; - res_length = k; - } - break; - default: - DUK_UNREACHABLE(); - break; - } - duk_pop_2_unsafe(thr); - - DUK_ASSERT_TOP(thr, 5); - } - - switch (iter_type) { - case DUK__ITER_EVERY: - duk_push_true(thr); - break; - case DUK__ITER_SOME: - duk_push_false(thr); - break; - case DUK__ITER_FOREACH: - duk_push_undefined(thr); - break; - case DUK__ITER_MAP: - case DUK__ITER_FILTER: - DUK_ASSERT_TOP(thr, 5); - DUK_ASSERT(duk_is_array(thr, -1)); /* topmost element is the result array already */ - duk_push_u32(thr, res_length); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); - break; - default: - DUK_UNREACHABLE(); - break; - } - - return 1; -} - -/* - * reduce(), reduceRight() - */ - -DUK_INTERNAL duk_ret_t duk_bi_array_prototype_reduce_shared(duk_hthread *thr) { - duk_idx_t nargs; - duk_bool_t have_acc; - duk_uint32_t i, len; - duk_small_int_t idx_step = duk_get_current_magic(thr); /* idx_step is +1 for reduce, -1 for reduceRight */ - - /* We're a varargs function because we need to detect whether - * initialValue was given or not. - */ - nargs = duk_get_top(thr); - DUK_DDD(DUK_DDDPRINT("nargs=%ld", (long) nargs)); - - duk_set_top(thr, 2); - len = duk__push_this_obj_len_u32(thr); - duk_require_callable(thr, 0); - - /* stack[0] = callback fn - * stack[1] = initialValue - * stack[2] = object (coerced this) - * stack[3] = length (not needed, but not popped above) - * stack[4] = accumulator - */ - - have_acc = 0; - if (nargs >= 2) { - duk_dup_1(thr); - have_acc = 1; - } - DUK_DDD(DUK_DDDPRINT("have_acc=%ld, acc=%!T", (long) have_acc, (duk_tval *) duk_get_tval(thr, 3))); - - /* For len == 0, i is initialized to len - 1 which underflows. - * The condition (i < len) will then exit the for-loop on the - * first round which is correct. Similarly, loop termination - * happens by i underflowing. - */ - - for (i = (idx_step >= 0 ? 0 : len - 1); i < len; /* i >= 0 would always be true */ - i += (duk_uint32_t) idx_step) { - DUK_DDD(DUK_DDDPRINT("i=%ld, len=%ld, have_acc=%ld, top=%ld, acc=%!T", - (long) i, - (long) len, - (long) have_acc, - (long) duk_get_top(thr), - (duk_tval *) duk_get_tval(thr, 4))); - - DUK_ASSERT((have_acc && duk_get_top(thr) == 5) || (!have_acc && duk_get_top(thr) == 4)); - - if (!duk_has_prop_index(thr, 2, (duk_uarridx_t) i)) { - continue; - } - - if (!have_acc) { - DUK_ASSERT_TOP(thr, 4); - duk_get_prop_index(thr, 2, (duk_uarridx_t) i); - have_acc = 1; - DUK_ASSERT_TOP(thr, 5); - } else { - DUK_ASSERT_TOP(thr, 5); - duk_dup_0(thr); - duk_dup(thr, 4); - duk_get_prop_index(thr, 2, (duk_uarridx_t) i); - duk_push_u32(thr, i); - duk_dup_2(thr); - DUK_DDD(DUK_DDDPRINT("calling reduce function: func=%!T, prev=%!T, curr=%!T, idx=%!T, obj=%!T", - (duk_tval *) duk_get_tval(thr, -5), - (duk_tval *) duk_get_tval(thr, -4), - (duk_tval *) duk_get_tval(thr, -3), - (duk_tval *) duk_get_tval(thr, -2), - (duk_tval *) duk_get_tval(thr, -1))); - duk_call(thr, 4); - DUK_DDD(DUK_DDDPRINT("-> result: %!T", (duk_tval *) duk_get_tval(thr, -1))); - duk_replace(thr, 4); - DUK_ASSERT_TOP(thr, 5); - } - } - - if (!have_acc) { - DUK_DCERROR_TYPE_INVALID_ARGS(thr); - } - - DUK_ASSERT_TOP(thr, 5); - return 1; -} - -#endif /* DUK_USE_ARRAY_BUILTIN */ - -/* automatic undefs */ -#undef DUK__ARRAY_MID_JOIN_LIMIT -#undef DUK__ITER_EVERY -#undef DUK__ITER_FILTER -#undef DUK__ITER_FOREACH -#undef DUK__ITER_MAP -#undef DUK__ITER_SOME -/* - * Boolean built-ins - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_BOOLEAN_BUILTIN) - -/* Shared helper to provide toString() and valueOf(). Checks 'this', gets - * the primitive value to stack top, and optionally coerces with ToString(). - */ -DUK_INTERNAL duk_ret_t duk_bi_boolean_prototype_tostring_shared(duk_hthread *thr) { - duk_tval *tv; - duk_hobject *h; - duk_small_int_t coerce_tostring = duk_get_current_magic(thr); - - /* XXX: there is room to use a shared helper here, many built-ins - * check the 'this' type, and if it's an object, check its class, - * then get its internal value, etc. - */ - - duk_push_this(thr); - tv = duk_get_tval(thr, -1); - DUK_ASSERT(tv != NULL); - - if (DUK_TVAL_IS_BOOLEAN(tv)) { - goto type_ok; - } else if (DUK_TVAL_IS_OBJECT(tv)) { - h = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - - if (DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_BOOLEAN) { - duk_xget_owndataprop_stridx_short(thr, -1, DUK_STRIDX_INT_VALUE); - DUK_ASSERT(duk_is_boolean(thr, -1)); - goto type_ok; - } - } - - DUK_DCERROR_TYPE_INVALID_ARGS(thr); - /* never here */ - -type_ok: - if (coerce_tostring) { - duk_to_string(thr, -1); - } - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_boolean_constructor(duk_hthread *thr) { - duk_hobject *h_this; - - duk_to_boolean(thr, 0); - - if (duk_is_constructor_call(thr)) { - /* XXX: helper; rely on Boolean.prototype as being non-writable, non-configurable */ - duk_push_this(thr); - h_this = duk_known_hobject(thr, -1); - DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_this) == thr->builtins[DUK_BIDX_BOOLEAN_PROTOTYPE]); - - DUK_HOBJECT_SET_CLASS_NUMBER(h_this, DUK_HOBJECT_CLASS_BOOLEAN); - - duk_dup_0(thr); /* -> [ val obj val ] */ - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE); /* XXX: proper flags? */ - } /* unbalanced stack */ - - return 1; -} - -#endif /* DUK_USE_BOOLEAN_BUILTIN */ -/* - * ES2015 TypedArray and Node.js Buffer built-ins - */ - -/* #include duk_internal.h -> already included */ - -/* - * Helpers for buffer handling, enabled with DUK_USE_BUFFEROBJECT_SUPPORT. - */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -/* Map class number (minus DUK_HOBJECT_CLASS_BUFOBJ_MIN) to a bidx for the - * default internal prototype. - */ -static const duk_uint8_t duk__buffer_proto_from_classnum[] = { - DUK_BIDX_ARRAYBUFFER_PROTOTYPE, DUK_BIDX_DATAVIEW_PROTOTYPE, DUK_BIDX_INT8ARRAY_PROTOTYPE, - DUK_BIDX_UINT8ARRAY_PROTOTYPE, DUK_BIDX_UINT8CLAMPEDARRAY_PROTOTYPE, DUK_BIDX_INT16ARRAY_PROTOTYPE, - DUK_BIDX_UINT16ARRAY_PROTOTYPE, DUK_BIDX_INT32ARRAY_PROTOTYPE, DUK_BIDX_UINT32ARRAY_PROTOTYPE, - DUK_BIDX_FLOAT32ARRAY_PROTOTYPE, DUK_BIDX_FLOAT64ARRAY_PROTOTYPE -}; - -/* Map DUK_HBUFOBJ_ELEM_xxx to duk_hobject class number. - * Sync with duk_hbufobj.h and duk_hobject.h. - */ -static const duk_uint8_t duk__buffer_class_from_elemtype[9] = { DUK_HOBJECT_CLASS_UINT8ARRAY, DUK_HOBJECT_CLASS_UINT8CLAMPEDARRAY, - DUK_HOBJECT_CLASS_INT8ARRAY, DUK_HOBJECT_CLASS_UINT16ARRAY, - DUK_HOBJECT_CLASS_INT16ARRAY, DUK_HOBJECT_CLASS_UINT32ARRAY, - DUK_HOBJECT_CLASS_INT32ARRAY, DUK_HOBJECT_CLASS_FLOAT32ARRAY, - DUK_HOBJECT_CLASS_FLOAT64ARRAY }; - -/* Map DUK_HBUFOBJ_ELEM_xxx to prototype object built-in index. - * Sync with duk_hbufobj.h. - */ -static const duk_uint8_t duk__buffer_proto_from_elemtype[9] = { - DUK_BIDX_UINT8ARRAY_PROTOTYPE, DUK_BIDX_UINT8CLAMPEDARRAY_PROTOTYPE, DUK_BIDX_INT8ARRAY_PROTOTYPE, - DUK_BIDX_UINT16ARRAY_PROTOTYPE, DUK_BIDX_INT16ARRAY_PROTOTYPE, DUK_BIDX_UINT32ARRAY_PROTOTYPE, - DUK_BIDX_INT32ARRAY_PROTOTYPE, DUK_BIDX_FLOAT32ARRAY_PROTOTYPE, DUK_BIDX_FLOAT64ARRAY_PROTOTYPE -}; - -/* Map DUK__FLD_xxx to byte size. */ -static const duk_uint8_t duk__buffer_nbytes_from_fldtype[6] = { - 1, /* DUK__FLD_8BIT */ - 2, /* DUK__FLD_16BIT */ - 4, /* DUK__FLD_32BIT */ - 4, /* DUK__FLD_FLOAT */ - 8, /* DUK__FLD_DOUBLE */ - 0 /* DUK__FLD_VARINT; not relevant here */ -}; - -/* Bitfield for each DUK_HBUFOBJ_ELEM_xxx indicating which element types - * are compatible with a blind byte copy for the TypedArray set() method (also - * used for TypedArray constructor). Array index is target buffer elem type, - * bitfield indicates compatible source types. The types must have same byte - * size and they must be coercion compatible. - */ -#if !defined(DUK_USE_PREFER_SIZE) -static duk_uint16_t duk__buffer_elemtype_copy_compatible[9] = { - /* xxx -> DUK_HBUFOBJ_ELEM_UINT8 */ - (1U << DUK_HBUFOBJ_ELEM_UINT8) | (1U << DUK_HBUFOBJ_ELEM_UINT8CLAMPED) | (1U << DUK_HBUFOBJ_ELEM_INT8), - - /* xxx -> DUK_HBUFOBJ_ELEM_UINT8CLAMPED - * Note: INT8 is -not- copy compatible, e.g. -1 would coerce to 0x00. - */ - (1U << DUK_HBUFOBJ_ELEM_UINT8) | (1U << DUK_HBUFOBJ_ELEM_UINT8CLAMPED), - - /* xxx -> DUK_HBUFOBJ_ELEM_INT8 */ - (1U << DUK_HBUFOBJ_ELEM_UINT8) | (1U << DUK_HBUFOBJ_ELEM_UINT8CLAMPED) | (1U << DUK_HBUFOBJ_ELEM_INT8), - - /* xxx -> DUK_HBUFOBJ_ELEM_UINT16 */ - (1U << DUK_HBUFOBJ_ELEM_UINT16) | (1U << DUK_HBUFOBJ_ELEM_INT16), - - /* xxx -> DUK_HBUFOBJ_ELEM_INT16 */ - (1U << DUK_HBUFOBJ_ELEM_UINT16) | (1U << DUK_HBUFOBJ_ELEM_INT16), - - /* xxx -> DUK_HBUFOBJ_ELEM_UINT32 */ - (1U << DUK_HBUFOBJ_ELEM_UINT32) | (1U << DUK_HBUFOBJ_ELEM_INT32), - - /* xxx -> DUK_HBUFOBJ_ELEM_INT32 */ - (1U << DUK_HBUFOBJ_ELEM_UINT32) | (1U << DUK_HBUFOBJ_ELEM_INT32), - - /* xxx -> DUK_HBUFOBJ_ELEM_FLOAT32 */ - (1U << DUK_HBUFOBJ_ELEM_FLOAT32), - - /* xxx -> DUK_HBUFOBJ_ELEM_FLOAT64 */ - (1U << DUK_HBUFOBJ_ELEM_FLOAT64) -}; -#endif /* !DUK_USE_PREFER_SIZE */ - -DUK_LOCAL duk_hbufobj *duk__hbufobj_promote_this(duk_hthread *thr) { - duk_tval *tv_dst; - duk_hbufobj *res; - - duk_push_this(thr); - DUK_ASSERT(duk_is_buffer(thr, -1)); - res = (duk_hbufobj *) duk_to_hobject(thr, -1); - DUK_HBUFOBJ_ASSERT_VALID(res); - DUK_DD(DUK_DDPRINT("promoted 'this' automatically to an ArrayBuffer: %!iT", duk_get_tval(thr, -1))); - - tv_dst = duk_get_borrowed_this_tval(thr); - DUK_TVAL_SET_OBJECT_UPDREF(thr, tv_dst, (duk_hobject *) res); - duk_pop(thr); - - return res; -} - -#define DUK__BUFOBJ_FLAG_THROW (1 << 0) -#define DUK__BUFOBJ_FLAG_PROMOTE (1 << 1) - -/* Shared helper. When DUK__BUFOBJ_FLAG_PROMOTE is given, the return value is - * always a duk_hbufobj *. Without the flag the return value can also be a - * plain buffer, and the caller must check for it using DUK_HEAPHDR_IS_BUFFER(). - */ -DUK_LOCAL duk_heaphdr *duk__getrequire_bufobj_this(duk_hthread *thr, duk_small_uint_t flags) { - duk_tval *tv; - duk_hbufobj *h_this; - - DUK_ASSERT(thr != NULL); - - tv = duk_get_borrowed_this_tval(thr); - DUK_ASSERT(tv != NULL); - - if (DUK_TVAL_IS_OBJECT(tv)) { - h_this = (duk_hbufobj *) DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h_this != NULL); - if (DUK_HOBJECT_IS_BUFOBJ((duk_hobject *) h_this)) { - DUK_HBUFOBJ_ASSERT_VALID(h_this); - return (duk_heaphdr *) h_this; - } - } else if (DUK_TVAL_IS_BUFFER(tv)) { - if (flags & DUK__BUFOBJ_FLAG_PROMOTE) { - /* Promote a plain buffer to a Uint8Array. This is very - * inefficient but allows plain buffer to be used wherever an - * Uint8Array is used with very small cost; hot path functions - * like index read/write calls should provide direct buffer - * support to avoid promotion. - */ - /* XXX: make this conditional to a flag if call sites need it? */ - h_this = duk__hbufobj_promote_this(thr); - DUK_ASSERT(h_this != NULL); - DUK_HBUFOBJ_ASSERT_VALID(h_this); - return (duk_heaphdr *) h_this; - } else { - /* XXX: ugly, share return pointer for duk_hbuffer. */ - return (duk_heaphdr *) DUK_TVAL_GET_BUFFER(tv); - } - } - - if (flags & DUK__BUFOBJ_FLAG_THROW) { - DUK_ERROR_TYPE(thr, DUK_STR_NOT_BUFFER); - DUK_WO_NORETURN(return NULL;); - } - return NULL; -} - -/* Check that 'this' is a duk_hbufobj and return a pointer to it. */ -DUK_LOCAL duk_hbufobj *duk__get_bufobj_this(duk_hthread *thr) { - return (duk_hbufobj *) duk__getrequire_bufobj_this(thr, DUK__BUFOBJ_FLAG_PROMOTE); -} - -/* Check that 'this' is a duk_hbufobj and return a pointer to it - * (NULL if not). - */ -DUK_LOCAL duk_hbufobj *duk__require_bufobj_this(duk_hthread *thr) { - return (duk_hbufobj *) duk__getrequire_bufobj_this(thr, DUK__BUFOBJ_FLAG_THROW | DUK__BUFOBJ_FLAG_PROMOTE); -} - -/* Check that value is a duk_hbufobj and return a pointer to it. */ -DUK_LOCAL duk_hbufobj *duk__require_bufobj_value(duk_hthread *thr, duk_idx_t idx) { - duk_tval *tv; - duk_hbufobj *h_obj; - - /* Don't accept relative indices now. */ - DUK_ASSERT(idx >= 0); - - tv = duk_require_tval(thr, idx); - DUK_ASSERT(tv != NULL); - if (DUK_TVAL_IS_OBJECT(tv)) { - h_obj = (duk_hbufobj *) DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h_obj != NULL); - if (DUK_HOBJECT_IS_BUFOBJ((duk_hobject *) h_obj)) { - DUK_HBUFOBJ_ASSERT_VALID(h_obj); - return h_obj; - } - } else if (DUK_TVAL_IS_BUFFER(tv)) { - h_obj = (duk_hbufobj *) duk_to_hobject(thr, idx); - DUK_ASSERT(h_obj != NULL); - DUK_HBUFOBJ_ASSERT_VALID(h_obj); - return h_obj; - } - - DUK_ERROR_TYPE(thr, DUK_STR_NOT_BUFFER); - DUK_WO_NORETURN(return NULL;); -} - -DUK_LOCAL void duk__set_bufobj_buffer(duk_hthread *thr, duk_hbufobj *h_bufobj, duk_hbuffer *h_val) { - DUK_ASSERT(thr != NULL); - DUK_ASSERT(h_bufobj != NULL); - DUK_ASSERT(h_bufobj->buf == NULL); /* no need to decref */ - DUK_ASSERT(h_val != NULL); - DUK_HBUFOBJ_ASSERT_VALID(h_bufobj); - DUK_UNREF(thr); - - h_bufobj->buf = h_val; - DUK_HBUFFER_INCREF(thr, h_val); - h_bufobj->length = (duk_uint_t) DUK_HBUFFER_GET_SIZE(h_val); - DUK_ASSERT(h_bufobj->shift == 0); - DUK_ASSERT(h_bufobj->elem_type == DUK_HBUFOBJ_ELEM_UINT8); - DUK_ASSERT(h_bufobj->is_typedarray == 0); - - DUK_HBUFOBJ_ASSERT_VALID(h_bufobj); -} - -/* Shared offset/length coercion helper. */ -DUK_LOCAL void duk__resolve_offset_opt_length(duk_hthread *thr, - duk_hbufobj *h_bufarg, - duk_idx_t idx_offset, - duk_idx_t idx_length, - duk_uint_t *out_offset, - duk_uint_t *out_length, - duk_bool_t throw_flag) { - duk_int_t offset_signed; - duk_int_t length_signed; - duk_uint_t offset; - duk_uint_t length; - - offset_signed = duk_to_int(thr, idx_offset); - if (offset_signed < 0) { - goto fail_range; - } - offset = (duk_uint_t) offset_signed; - if (offset > h_bufarg->length) { - goto fail_range; - } - DUK_ASSERT_DISABLE(offset >= 0); /* unsigned */ - DUK_ASSERT(offset <= h_bufarg->length); - - if (duk_is_undefined(thr, idx_length)) { - DUK_ASSERT(h_bufarg->length >= offset); - length = h_bufarg->length - offset; /* >= 0 */ - } else { - length_signed = duk_to_int(thr, idx_length); - if (length_signed < 0) { - goto fail_range; - } - length = (duk_uint_t) length_signed; - DUK_ASSERT(h_bufarg->length >= offset); - if (length > h_bufarg->length - offset) { - /* Unlike for negative arguments, some call sites - * want length to be clamped if it's positive. - */ - if (throw_flag) { - goto fail_range; - } else { - length = h_bufarg->length - offset; - } - } - } - DUK_ASSERT_DISABLE(length >= 0); /* unsigned */ - DUK_ASSERT(offset + length <= h_bufarg->length); - - *out_offset = offset; - *out_length = length; - return; - -fail_range: - DUK_ERROR_RANGE(thr, DUK_STR_INVALID_ARGS); - DUK_WO_NORETURN(return;); -} - -/* Shared lenient buffer length clamping helper. No negative indices, no - * element/byte shifting. - */ -DUK_LOCAL void duk__clamp_startend_nonegidx_noshift(duk_hthread *thr, - duk_int_t buffer_length, - duk_idx_t idx_start, - duk_idx_t idx_end, - duk_int_t *out_start_offset, - duk_int_t *out_end_offset) { - duk_int_t start_offset; - duk_int_t end_offset; - - DUK_ASSERT(out_start_offset != NULL); - DUK_ASSERT(out_end_offset != NULL); - - /* undefined coerces to zero which is correct */ - start_offset = duk_to_int_clamped(thr, idx_start, 0, buffer_length); - if (duk_is_undefined(thr, idx_end)) { - end_offset = buffer_length; - } else { - end_offset = duk_to_int_clamped(thr, idx_end, start_offset, buffer_length); - } - - DUK_ASSERT(start_offset >= 0); - DUK_ASSERT(start_offset <= buffer_length); - DUK_ASSERT(end_offset >= 0); - DUK_ASSERT(end_offset <= buffer_length); - DUK_ASSERT(start_offset <= end_offset); - - *out_start_offset = start_offset; - *out_end_offset = end_offset; -} - -/* Shared lenient buffer length clamping helper. Indices are treated as - * element indices (though output values are byte offsets) which only - * really matters for TypedArray views as other buffer object have a zero - * shift. Negative indices are counted from end of input slice; crossed - * indices are clamped to zero length; and final indices are clamped - * against input slice. Used for e.g. ArrayBuffer slice(). - */ -DUK_LOCAL void duk__clamp_startend_negidx_shifted(duk_hthread *thr, - duk_int_t buffer_length, - duk_uint8_t buffer_shift, - duk_idx_t idx_start, - duk_idx_t idx_end, - duk_int_t *out_start_offset, - duk_int_t *out_end_offset) { - duk_int_t start_offset; - duk_int_t end_offset; - - DUK_ASSERT(out_start_offset != NULL); - DUK_ASSERT(out_end_offset != NULL); - - buffer_length >>= buffer_shift; /* as (full) elements */ - - /* Resolve start/end offset as element indices first; arguments - * at idx_start/idx_end are element offsets. Working with element - * indices first also avoids potential for wrapping. - */ - - start_offset = duk_to_int(thr, idx_start); - if (start_offset < 0) { - start_offset = buffer_length + start_offset; - } - if (duk_is_undefined(thr, idx_end)) { - end_offset = buffer_length; - } else { - end_offset = duk_to_int(thr, idx_end); - if (end_offset < 0) { - end_offset = buffer_length + end_offset; - } - } - /* Note: start_offset/end_offset can still be < 0 here. */ - - if (start_offset < 0) { - start_offset = 0; - } else if (start_offset > buffer_length) { - start_offset = buffer_length; - } - if (end_offset < start_offset) { - end_offset = start_offset; - } else if (end_offset > buffer_length) { - end_offset = buffer_length; - } - DUK_ASSERT(start_offset >= 0); - DUK_ASSERT(start_offset <= buffer_length); - DUK_ASSERT(end_offset >= 0); - DUK_ASSERT(end_offset <= buffer_length); - DUK_ASSERT(start_offset <= end_offset); - - /* Convert indices to byte offsets. */ - start_offset <<= buffer_shift; - end_offset <<= buffer_shift; - - *out_start_offset = start_offset; - *out_end_offset = end_offset; -} - -DUK_INTERNAL void duk_hbufobj_promote_plain(duk_hthread *thr, duk_idx_t idx) { - if (duk_is_buffer(thr, idx)) { - duk_to_object(thr, idx); - } -} - -DUK_INTERNAL void duk_hbufobj_push_uint8array_from_plain(duk_hthread *thr, duk_hbuffer *h_buf) { - /* Push Uint8Array which will share the same underlying buffer as - * the plain buffer argument. Also create an ArrayBuffer with the - * same backing for the result .buffer property. - */ - - duk_push_hbuffer(thr, h_buf); - duk_push_buffer_object(thr, -1, 0, (duk_size_t) DUK_HBUFFER_GET_SIZE(h_buf), DUK_BUFOBJ_UINT8ARRAY); - duk_remove_m2(thr); - -#if 0 - /* More verbose equivalent; maybe useful if e.g. .buffer is omitted. */ - h_bufobj = duk_push_bufobj_raw(thr, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_FLAG_BUFOBJ | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_UINT8ARRAY), - DUK_BIDX_UINT8ARRAY_PROTOTYPE); - DUK_ASSERT(h_bufobj != NULL); - duk__set_bufobj_buffer(thr, h_bufobj, h_buf); - h_bufobj->is_typedarray = 1; - DUK_HBUFOBJ_ASSERT_VALID(h_bufobj); - - h_arrbuf = duk_push_bufobj_raw(thr, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_FLAG_BUFOBJ | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER), - DUK_BIDX_ARRAYBUFFER_PROTOTYPE); - DUK_ASSERT(h_arrbuf != NULL); - duk__set_bufobj_buffer(thr, h_arrbuf, h_buf); - DUK_ASSERT(h_arrbuf->is_typedarray == 0); - DUK_HBUFOBJ_ASSERT_VALID(h_arrbuf); - - DUK_ASSERT(h_bufobj->buf_prop == NULL); - h_bufobj->buf_prop = (duk_hobject *) h_arrbuf; - DUK_ASSERT(h_arrbuf != NULL); - DUK_HBUFOBJ_INCREF(thr, h_arrbuf); - duk_pop(thr); -#endif -} - -/* Indexed read helper for buffer objects, also called from outside this file. */ -DUK_INTERNAL void duk_hbufobj_push_validated_read(duk_hthread *thr, - duk_hbufobj *h_bufobj, - duk_uint8_t *p, - duk_small_uint_t elem_size) { - duk_double_union du; - - DUK_ASSERT(elem_size > 0); - duk_memcpy((void *) du.uc, (const void *) p, (size_t) elem_size); - - switch (h_bufobj->elem_type) { - case DUK_HBUFOBJ_ELEM_UINT8: - case DUK_HBUFOBJ_ELEM_UINT8CLAMPED: - duk_push_uint(thr, (duk_uint_t) du.uc[0]); - break; - case DUK_HBUFOBJ_ELEM_INT8: - duk_push_int(thr, (duk_int_t) (duk_int8_t) du.uc[0]); - break; - case DUK_HBUFOBJ_ELEM_UINT16: - duk_push_uint(thr, (duk_uint_t) du.us[0]); - break; - case DUK_HBUFOBJ_ELEM_INT16: - duk_push_int(thr, (duk_int_t) (duk_int16_t) du.us[0]); - break; - case DUK_HBUFOBJ_ELEM_UINT32: - duk_push_uint(thr, (duk_uint_t) du.ui[0]); - break; - case DUK_HBUFOBJ_ELEM_INT32: - duk_push_int(thr, (duk_int_t) (duk_int32_t) du.ui[0]); - break; - case DUK_HBUFOBJ_ELEM_FLOAT32: - duk_push_number(thr, (duk_double_t) du.f[0]); - break; - case DUK_HBUFOBJ_ELEM_FLOAT64: - duk_push_number(thr, (duk_double_t) du.d); - break; - default: - DUK_UNREACHABLE(); - } -} - -/* Indexed write helper for buffer objects, also called from outside this file. */ -DUK_INTERNAL void duk_hbufobj_validated_write(duk_hthread *thr, duk_hbufobj *h_bufobj, duk_uint8_t *p, duk_small_uint_t elem_size) { - duk_double_union du; - - /* NOTE! Caller must ensure that any side effects from the - * coercions below are safe. If that cannot be guaranteed - * (which is normally the case), caller must coerce the - * argument using duk_to_number() before any pointer - * validations; the result of duk_to_number() always coerces - * without side effects here. - */ - - switch (h_bufobj->elem_type) { - case DUK_HBUFOBJ_ELEM_UINT8: - du.uc[0] = (duk_uint8_t) duk_to_uint32(thr, -1); - break; - case DUK_HBUFOBJ_ELEM_UINT8CLAMPED: - du.uc[0] = (duk_uint8_t) duk_to_uint8clamped(thr, -1); - break; - case DUK_HBUFOBJ_ELEM_INT8: - du.uc[0] = (duk_uint8_t) duk_to_int32(thr, -1); - break; - case DUK_HBUFOBJ_ELEM_UINT16: - du.us[0] = (duk_uint16_t) duk_to_uint32(thr, -1); - break; - case DUK_HBUFOBJ_ELEM_INT16: - du.us[0] = (duk_uint16_t) duk_to_int32(thr, -1); - break; - case DUK_HBUFOBJ_ELEM_UINT32: - du.ui[0] = (duk_uint32_t) duk_to_uint32(thr, -1); - break; - case DUK_HBUFOBJ_ELEM_INT32: - du.ui[0] = (duk_uint32_t) duk_to_int32(thr, -1); - break; - case DUK_HBUFOBJ_ELEM_FLOAT32: - /* A double-to-float cast is undefined behavior in C99 if - * the cast is out-of-range, so use a helper. Example: - * runtime error: value -1e+100 is outside the range of representable values of type 'float' - */ - du.f[0] = duk_double_to_float_t(duk_to_number_m1(thr)); - break; - case DUK_HBUFOBJ_ELEM_FLOAT64: - du.d = (duk_double_t) duk_to_number_m1(thr); - break; - default: - DUK_UNREACHABLE(); - } - - DUK_ASSERT(elem_size > 0); - duk_memcpy((void *) p, (const void *) du.uc, (size_t) elem_size); -} - -/* Helper to create a fixed buffer from argument value at index 0. - * Node.js and allocPlain() compatible. - */ -DUK_LOCAL duk_hbuffer *duk__hbufobj_fixed_from_argvalue(duk_hthread *thr) { - duk_int_t len; - duk_int_t i; - duk_size_t buf_size; - duk_uint8_t *buf; - - switch (duk_get_type(thr, 0)) { - case DUK_TYPE_NUMBER: { - len = duk_to_int_clamped(thr, 0, 0, DUK_INT_MAX); - (void) duk_push_fixed_buffer_zero(thr, (duk_size_t) len); - break; - } - case DUK_TYPE_BUFFER: { /* Treat like Uint8Array. */ - goto slow_copy; - } - case DUK_TYPE_OBJECT: { - duk_hobject *h; - duk_hbufobj *h_bufobj; - - /* For Node.js Buffers "Passing an ArrayBuffer returns a Buffer - * that shares allocated memory with the given ArrayBuffer." - * https://nodejs.org/api/buffer.html#buffer_buffer_from_buffer_alloc_and_buffer_allocunsafe - */ - - h = duk_known_hobject(thr, 0); - if (DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_ARRAYBUFFER) { - DUK_ASSERT(DUK_HOBJECT_IS_BUFOBJ(h)); - h_bufobj = (duk_hbufobj *) h; - if (DUK_UNLIKELY(h_bufobj->buf == NULL)) { - DUK_ERROR_TYPE_INVALID_ARGS(thr); - DUK_WO_NORETURN(return NULL;); - } - if (DUK_UNLIKELY(h_bufobj->offset != 0 || h_bufobj->length != DUK_HBUFFER_GET_SIZE(h_bufobj->buf))) { - /* No support for ArrayBuffers with slice - * offset/length. - */ - DUK_ERROR_TYPE_INVALID_ARGS(thr); - DUK_WO_NORETURN(return NULL;); - } - duk_push_hbuffer(thr, h_bufobj->buf); - return h_bufobj->buf; - } - goto slow_copy; - } - case DUK_TYPE_STRING: { - /* ignore encoding for now */ - duk_require_hstring_notsymbol(thr, 0); - duk_dup_0(thr); - (void) duk_to_buffer(thr, -1, &buf_size); - break; - } - default: - DUK_ERROR_TYPE_INVALID_ARGS(thr); - DUK_WO_NORETURN(return NULL;); - } - -done: - DUK_ASSERT(duk_is_buffer(thr, -1)); - return duk_known_hbuffer(thr, -1); - -slow_copy: - /* XXX: fast path for typed arrays and other buffer objects? */ - - (void) duk_get_prop_stridx_short(thr, 0, DUK_STRIDX_LENGTH); - len = duk_to_int_clamped(thr, -1, 0, DUK_INT_MAX); - duk_pop(thr); - buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, (duk_size_t) len); /* no zeroing, all indices get initialized */ - for (i = 0; i < len; i++) { - /* XXX: fast path for array or buffer arguments? */ - duk_get_prop_index(thr, 0, (duk_uarridx_t) i); - buf[i] = (duk_uint8_t) (duk_to_uint32(thr, -1) & 0xffU); - duk_pop(thr); - } - goto done; -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -/* - * Node.js Buffer constructor - * - * Node.js Buffers are just Uint8Arrays with internal prototype set to - * Buffer.prototype so they're handled otherwise the same as Uint8Array. - * However, the constructor arguments are very different so a separate - * constructor entry point is used. - */ -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_constructor(duk_hthread *thr) { - duk_hbuffer *h_buf; - - h_buf = duk__hbufobj_fixed_from_argvalue(thr); - DUK_ASSERT(h_buf != NULL); - - duk_push_buffer_object(thr, -1, 0, DUK_HBUFFER_FIXED_GET_SIZE((duk_hbuffer_fixed *) (void *) h_buf), DUK_BUFOBJ_UINT8ARRAY); - duk_push_hobject_bidx(thr, DUK_BIDX_NODEJS_BUFFER_PROTOTYPE); - duk_set_prototype(thr, -2); - - /* XXX: a more direct implementation */ - - return 1; -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -/* - * ArrayBuffer, DataView, and TypedArray constructors - */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_arraybuffer_constructor(duk_hthread *thr) { - duk_hbufobj *h_bufobj; - duk_hbuffer *h_val; - duk_int_t len; - - DUK_CTX_ASSERT_VALID(thr); - - duk_require_constructor_call(thr); - - len = duk_to_int(thr, 0); - if (len < 0) { - goto fail_length; - } - (void) duk_push_fixed_buffer_zero(thr, (duk_size_t) len); - h_val = (duk_hbuffer *) duk_known_hbuffer(thr, -1); - - h_bufobj = duk_push_bufobj_raw(thr, - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_BUFOBJ | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER), - DUK_BIDX_ARRAYBUFFER_PROTOTYPE); - DUK_ASSERT(h_bufobj != NULL); - - duk__set_bufobj_buffer(thr, h_bufobj, h_val); - DUK_HBUFOBJ_ASSERT_VALID(h_bufobj); - - return 1; - -fail_length: - DUK_DCERROR_RANGE_INVALID_LENGTH(thr); -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -/* Format of magic, bits: - * 0...1: elem size shift (0-3) - * 2...5: elem type (DUK_HBUFOBJ_ELEM_xxx) - * - * XXX: add prototype bidx explicitly to magic instead of using a mapping? - */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_hthread *thr) { - duk_tval *tv; - duk_hobject *h_obj; - duk_hbufobj *h_bufobj = NULL; - duk_hbufobj *h_bufarg = NULL; - duk_hbuffer *h_val; - duk_small_uint_t magic; - duk_small_uint_t shift; - duk_small_uint_t elem_type; - duk_small_uint_t elem_size; - duk_small_uint_t class_num; - duk_small_uint_t proto_bidx; - duk_uint_t align_mask; - duk_uint_t elem_length; - duk_int_t elem_length_signed; - duk_uint_t byte_length; - duk_small_uint_t copy_mode; - - /* XXX: The same copy helpers could be shared with at least some - * buffer functions. - */ - - duk_require_constructor_call(thr); - - /* We could fit built-in index into magic but that'd make the magic - * number dependent on built-in numbering (genbuiltins.py doesn't - * handle that yet). So map both class and prototype from the - * element type. - */ - magic = (duk_small_uint_t) duk_get_current_magic(thr); - shift = magic & 0x03U; /* bits 0...1: shift */ - elem_type = (magic >> 2) & 0x0fU; /* bits 2...5: type */ - elem_size = 1U << shift; - align_mask = elem_size - 1; - DUK_ASSERT(elem_type < sizeof(duk__buffer_proto_from_elemtype) / sizeof(duk_uint8_t)); - proto_bidx = duk__buffer_proto_from_elemtype[elem_type]; - DUK_ASSERT(proto_bidx < DUK_NUM_BUILTINS); - DUK_ASSERT(elem_type < sizeof(duk__buffer_class_from_elemtype) / sizeof(duk_uint8_t)); - class_num = duk__buffer_class_from_elemtype[elem_type]; - - DUK_DD(DUK_DDPRINT("typedarray constructor, magic=%d, shift=%d, elem_type=%d, " - "elem_size=%d, proto_bidx=%d, class_num=%d", - (int) magic, - (int) shift, - (int) elem_type, - (int) elem_size, - (int) proto_bidx, - (int) class_num)); - - /* Argument variants. When the argument is an ArrayBuffer a view to - * the same buffer is created; otherwise a new ArrayBuffer is always - * created. - */ - - /* XXX: initial iteration to treat a plain buffer like an ArrayBuffer: - * coerce to an ArrayBuffer object and use that as .buffer. The underlying - * buffer will be the same but result .buffer !== inputPlainBuffer. - */ - duk_hbufobj_promote_plain(thr, 0); - - tv = duk_get_tval(thr, 0); - DUK_ASSERT(tv != NULL); /* arg count */ - if (DUK_TVAL_IS_OBJECT(tv)) { - h_obj = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h_obj != NULL); - - if (DUK_HOBJECT_GET_CLASS_NUMBER(h_obj) == DUK_HOBJECT_CLASS_ARRAYBUFFER) { - /* ArrayBuffer: unlike any other argument variant, create - * a view into the existing buffer. - */ - - duk_int_t byte_offset_signed; - duk_uint_t byte_offset; - - h_bufarg = (duk_hbufobj *) h_obj; - - byte_offset_signed = duk_to_int(thr, 1); - if (byte_offset_signed < 0) { - goto fail_arguments; - } - byte_offset = (duk_uint_t) byte_offset_signed; - if (byte_offset > h_bufarg->length || (byte_offset & align_mask) != 0) { - /* Must be >= 0 and multiple of element size. */ - goto fail_arguments; - } - if (duk_is_undefined(thr, 2)) { - DUK_ASSERT(h_bufarg->length >= byte_offset); - byte_length = h_bufarg->length - byte_offset; - if ((byte_length & align_mask) != 0) { - /* Must be element size multiple from - * start offset to end of buffer. - */ - goto fail_arguments; - } - elem_length = (byte_length >> shift); - } else { - elem_length_signed = duk_to_int(thr, 2); - if (elem_length_signed < 0) { - goto fail_arguments; - } - elem_length = (duk_uint_t) elem_length_signed; - byte_length = elem_length << shift; - if ((byte_length >> shift) != elem_length) { - /* Byte length would overflow. */ - /* XXX: easier check with less code? */ - goto fail_arguments; - } - DUK_ASSERT(h_bufarg->length >= byte_offset); - if (byte_length > h_bufarg->length - byte_offset) { - /* Not enough data. */ - goto fail_arguments; - } - } - DUK_UNREF(elem_length); - DUK_ASSERT_DISABLE(byte_offset >= 0); - DUK_ASSERT(byte_offset <= h_bufarg->length); - DUK_ASSERT_DISABLE(byte_length >= 0); - DUK_ASSERT(byte_offset + byte_length <= h_bufarg->length); - DUK_ASSERT((elem_length << shift) == byte_length); - - h_bufobj = duk_push_bufobj_raw(thr, - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_BUFOBJ | - DUK_HOBJECT_CLASS_AS_FLAGS(class_num), - (duk_small_int_t) proto_bidx); - h_val = h_bufarg->buf; - if (h_val == NULL) { - DUK_DCERROR_TYPE_INVALID_ARGS(thr); - } - h_bufobj->buf = h_val; - DUK_HBUFFER_INCREF(thr, h_val); - h_bufobj->offset = h_bufarg->offset + byte_offset; - h_bufobj->length = byte_length; - h_bufobj->shift = (duk_uint8_t) shift; - h_bufobj->elem_type = (duk_uint8_t) elem_type; - h_bufobj->is_typedarray = 1; - DUK_HBUFOBJ_ASSERT_VALID(h_bufobj); - - /* Set .buffer to the argument ArrayBuffer. */ - DUK_ASSERT(h_bufobj->buf_prop == NULL); - h_bufobj->buf_prop = (duk_hobject *) h_bufarg; - DUK_ASSERT(h_bufarg != NULL); - DUK_HBUFOBJ_INCREF(thr, h_bufarg); - return 1; - } else if (DUK_HOBJECT_IS_BUFOBJ(h_obj)) { - /* TypedArray (or other non-ArrayBuffer duk_hbufobj). - * Conceptually same behavior as for an Array-like argument, - * with a few fast paths. - */ - - h_bufarg = (duk_hbufobj *) h_obj; - DUK_HBUFOBJ_ASSERT_VALID(h_bufarg); - elem_length_signed = (duk_int_t) (h_bufarg->length >> h_bufarg->shift); - if (h_bufarg->buf == NULL) { - DUK_DCERROR_TYPE_INVALID_ARGS(thr); - } - - /* Select copy mode. Must take into account element - * compatibility and validity of the underlying source - * buffer. - */ - - DUK_DDD(DUK_DDDPRINT("selecting copy mode for bufobj arg, " - "src byte_length=%ld, src shift=%d, " - "src/dst elem_length=%ld; " - "dst shift=%d -> dst byte_length=%ld", - (long) h_bufarg->length, - (int) h_bufarg->shift, - (long) elem_length_signed, - (int) shift, - (long) (elem_length_signed << shift))); - - copy_mode = 2; /* default is explicit index read/write copy */ -#if !defined(DUK_USE_PREFER_SIZE) - /* With a size optimized build copy_mode 2 is enough. - * Modes 0 and 1 are faster but conceptually the same. - */ - DUK_ASSERT(elem_type < sizeof(duk__buffer_elemtype_copy_compatible) / sizeof(duk_uint16_t)); - if (DUK_HBUFOBJ_VALID_SLICE(h_bufarg)) { - if ((duk__buffer_elemtype_copy_compatible[elem_type] & (1 << h_bufarg->elem_type)) != 0) { - DUK_DDD(DUK_DDDPRINT("source/target are copy compatible, memcpy")); - DUK_ASSERT(shift == h_bufarg->shift); /* byte sizes will match */ - copy_mode = 0; - } else { - DUK_DDD(DUK_DDDPRINT("source/target not copy compatible but valid, fast copy")); - copy_mode = 1; - } - } -#endif /* !DUK_USE_PREFER_SIZE */ - } else { - /* Array or Array-like */ - elem_length_signed = (duk_int_t) duk_get_length(thr, 0); - copy_mode = 2; - } - } else { - /* Non-object argument is simply int coerced, matches - * V8 behavior (except for "null", which we coerce to - * 0 but V8 TypeErrors). - */ - elem_length_signed = duk_to_int(thr, 0); - copy_mode = 3; - } - if (elem_length_signed < 0) { - goto fail_arguments; - } - elem_length = (duk_uint_t) elem_length_signed; - byte_length = (duk_uint_t) (elem_length << shift); - if ((byte_length >> shift) != elem_length) { - /* Byte length would overflow. */ - /* XXX: easier check with less code? */ - goto fail_arguments; - } - - DUK_DDD(DUK_DDDPRINT("elem_length=%ld, byte_length=%ld", (long) elem_length, (long) byte_length)); - - /* ArrayBuffer argument is handled specially above; the rest of the - * argument variants are handled by shared code below. - * - * ArrayBuffer in h_bufobj->buf_prop is intentionally left unset. - * It will be automatically created by the .buffer accessor on - * first access. - */ - - /* Push the resulting view object on top of a plain fixed buffer. */ - (void) duk_push_fixed_buffer(thr, byte_length); - h_val = duk_known_hbuffer(thr, -1); - DUK_ASSERT(h_val != NULL); - - h_bufobj = - duk_push_bufobj_raw(thr, - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_BUFOBJ | DUK_HOBJECT_CLASS_AS_FLAGS(class_num), - (duk_small_int_t) proto_bidx); - - h_bufobj->buf = h_val; - DUK_HBUFFER_INCREF(thr, h_val); - DUK_ASSERT(h_bufobj->offset == 0); - h_bufobj->length = byte_length; - h_bufobj->shift = (duk_uint8_t) shift; - h_bufobj->elem_type = (duk_uint8_t) elem_type; - h_bufobj->is_typedarray = 1; - DUK_HBUFOBJ_ASSERT_VALID(h_bufobj); - - /* Copy values, the copy method depends on the arguments. - * - * Copy mode decision may depend on the validity of the underlying - * buffer of the source argument; there must be no harmful side effects - * from there to here for copy_mode to still be valid. - */ - DUK_DDD(DUK_DDDPRINT("copy mode: %d", (int) copy_mode)); - switch (copy_mode) { - /* Copy modes 0 and 1 can be omitted in size optimized build, - * copy mode 2 handles them (but more slowly). - */ -#if !defined(DUK_USE_PREFER_SIZE) - case 0: { - /* Use byte copy. */ - - duk_uint8_t *p_src; - duk_uint8_t *p_dst; - - DUK_ASSERT(h_bufobj != NULL); - DUK_ASSERT(h_bufobj->buf != NULL); - DUK_ASSERT(DUK_HBUFOBJ_VALID_SLICE(h_bufobj)); - DUK_ASSERT(h_bufarg != NULL); - DUK_ASSERT(h_bufarg->buf != NULL); - DUK_ASSERT(DUK_HBUFOBJ_VALID_SLICE(h_bufarg)); - - p_dst = DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_bufobj); - p_src = DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_bufarg); - - DUK_DDD(DUK_DDDPRINT("using memcpy: p_src=%p, p_dst=%p, byte_length=%ld", - (void *) p_src, - (void *) p_dst, - (long) byte_length)); - - duk_memcpy_unsafe((void *) p_dst, (const void *) p_src, (size_t) byte_length); - break; - } - case 1: { - /* Copy values through direct validated reads and writes. */ - - duk_small_uint_t src_elem_size; - duk_small_uint_t dst_elem_size; - duk_uint8_t *p_src; - duk_uint8_t *p_src_end; - duk_uint8_t *p_dst; - - DUK_ASSERT(h_bufobj != NULL); - DUK_ASSERT(h_bufobj->buf != NULL); - DUK_ASSERT(DUK_HBUFOBJ_VALID_SLICE(h_bufobj)); - DUK_ASSERT(h_bufarg != NULL); - DUK_ASSERT(h_bufarg->buf != NULL); - DUK_ASSERT(DUK_HBUFOBJ_VALID_SLICE(h_bufarg)); - - src_elem_size = (duk_small_uint_t) (1U << h_bufarg->shift); - dst_elem_size = elem_size; - - p_src = DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_bufarg); - p_dst = DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_bufobj); - p_src_end = p_src + h_bufarg->length; - - DUK_DDD(DUK_DDDPRINT("using fast copy: p_src=%p, p_src_end=%p, p_dst=%p, " - "src_elem_size=%d, dst_elem_size=%d", - (void *) p_src, - (void *) p_src_end, - (void *) p_dst, - (int) src_elem_size, - (int) dst_elem_size)); - - while (p_src != p_src_end) { - DUK_DDD(DUK_DDDPRINT("fast path per element copy loop: " - "p_src=%p, p_src_end=%p, p_dst=%p", - (void *) p_src, - (void *) p_src_end, - (void *) p_dst)); - /* A validated read() is always a number, so it's write coercion - * is always side effect free an won't invalidate pointers etc. - */ - duk_hbufobj_push_validated_read(thr, h_bufarg, p_src, src_elem_size); - duk_hbufobj_validated_write(thr, h_bufobj, p_dst, dst_elem_size); - duk_pop(thr); - p_src += src_elem_size; - p_dst += dst_elem_size; - } - break; - } -#endif /* !DUK_USE_PREFER_SIZE */ - case 2: { - /* Copy values by index reads and writes. Let virtual - * property handling take care of coercion. - */ - duk_uint_t i; - - DUK_DDD(DUK_DDDPRINT("using slow copy")); - - for (i = 0; i < elem_length; i++) { - duk_get_prop_index(thr, 0, (duk_uarridx_t) i); - duk_put_prop_index(thr, -2, (duk_uarridx_t) i); - } - break; - } - default: - case 3: { - /* No copy, leave zero bytes in the buffer. There's no - * ambiguity with Float32/Float64 because zero bytes also - * represent 0.0. - */ - - DUK_DDD(DUK_DDDPRINT("using no copy")); - break; - } - } - - return 1; - -fail_arguments: - DUK_DCERROR_RANGE_INVALID_ARGS(thr); -} -#else /* DUK_USE_BUFFEROBJECT_SUPPORT */ -/* When bufferobject support is disabled, new Uint8Array() could still be - * supported to create a plain fixed buffer. Disabled for now. - */ -#if 0 -DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_hthread *thr) { - duk_int_t elem_length_signed; - duk_uint_t byte_length; - - /* XXX: The same copy helpers could be shared with at least some - * buffer functions. - */ - - duk_require_constructor_call(thr); - - elem_length_signed = duk_require_int(thr, 0); - if (elem_length_signed < 0) { - goto fail_arguments; - } - byte_length = (duk_uint_t) elem_length_signed; - - (void) duk_push_fixed_buffer_zero(thr, (duk_size_t) byte_length); - return 1; - - fail_arguments: - DUK_DCERROR_RANGE_INVALID_ARGS(thr); -} -#endif /* 0 */ -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_dataview_constructor(duk_hthread *thr) { - duk_hbufobj *h_bufarg; - duk_hbufobj *h_bufobj; - duk_hbuffer *h_val; - duk_uint_t offset; - duk_uint_t length; - - duk_require_constructor_call(thr); - - h_bufarg = duk__require_bufobj_value(thr, 0); - DUK_ASSERT(h_bufarg != NULL); - if (DUK_HOBJECT_GET_CLASS_NUMBER((duk_hobject *) h_bufarg) != DUK_HOBJECT_CLASS_ARRAYBUFFER) { - DUK_DCERROR_TYPE_INVALID_ARGS(thr); - } - - duk__resolve_offset_opt_length(thr, h_bufarg, 1, 2, &offset, &length, 1 /*throw_flag*/); - DUK_ASSERT(offset <= h_bufarg->length); - DUK_ASSERT(offset + length <= h_bufarg->length); - - h_bufobj = duk_push_bufobj_raw(thr, - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_BUFOBJ | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DATAVIEW), - DUK_BIDX_DATAVIEW_PROTOTYPE); - - h_val = h_bufarg->buf; - if (h_val == NULL) { - DUK_DCERROR_TYPE_INVALID_ARGS(thr); - } - h_bufobj->buf = h_val; - DUK_HBUFFER_INCREF(thr, h_val); - h_bufobj->offset = h_bufarg->offset + offset; - h_bufobj->length = length; - DUK_ASSERT(h_bufobj->shift == 0); - DUK_ASSERT(h_bufobj->elem_type == DUK_HBUFOBJ_ELEM_UINT8); - DUK_ASSERT(h_bufobj->is_typedarray == 0); - - DUK_ASSERT(h_bufobj->buf_prop == NULL); - h_bufobj->buf_prop = (duk_hobject *) h_bufarg; - DUK_ASSERT(h_bufarg != NULL); - DUK_HBUFOBJ_INCREF(thr, h_bufarg); - - DUK_HBUFOBJ_ASSERT_VALID(h_bufobj); - return 1; -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -/* - * ArrayBuffer.isView() - */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_arraybuffer_isview(duk_hthread *thr) { - duk_hobject *h_obj; - duk_bool_t ret = 0; - - if (duk_is_buffer(thr, 0)) { - ret = 1; - } else { - h_obj = duk_get_hobject(thr, 0); - if (h_obj != NULL && DUK_HOBJECT_IS_BUFOBJ(h_obj)) { - /* DataView needs special casing: ArrayBuffer.isView() is - * true, but ->is_typedarray is 0. - */ - ret = ((duk_hbufobj *) h_obj)->is_typedarray || - (DUK_HOBJECT_GET_CLASS_NUMBER(h_obj) == DUK_HOBJECT_CLASS_DATAVIEW); - } - } - duk_push_boolean(thr, ret); - return 1; -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -/* - * Uint8Array.allocPlain() - */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_uint8array_allocplain(duk_hthread *thr) { - duk__hbufobj_fixed_from_argvalue(thr); - return 1; -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -/* - * Uint8Array.plainOf() - */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_uint8array_plainof(duk_hthread *thr) { - duk_hbufobj *h_bufobj; - -#if !defined(DUK_USE_PREFER_SIZE) - /* Avoid churn if argument is already a plain buffer. */ - if (duk_is_buffer(thr, 0)) { - return 1; - } -#endif - - /* Promotes plain buffers to ArrayBuffers, so for a plain buffer - * argument we'll create a pointless temporary (but still work - * correctly). - */ - h_bufobj = duk__require_bufobj_value(thr, 0); - if (h_bufobj->buf == NULL) { - duk_push_undefined(thr); - } else { - duk_push_hbuffer(thr, h_bufobj->buf); - } - return 1; -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -/* - * Node.js Buffer: toString([encoding], [start], [end]) - */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_tostring(duk_hthread *thr) { - duk_hbufobj *h_this; - duk_int_t start_offset, end_offset; - duk_uint8_t *buf_slice; - duk_size_t slice_length; - - h_this = duk__get_bufobj_this(thr); - if (h_this == NULL) { - /* XXX: happens e.g. when evaluating: String(Buffer.prototype). */ - duk_push_literal(thr, "[object Object]"); - return 1; - } - DUK_HBUFOBJ_ASSERT_VALID(h_this); - - /* Ignore encoding for now. */ - - duk__clamp_startend_nonegidx_noshift(thr, - (duk_int_t) h_this->length, - 1 /*idx_start*/, - 2 /*idx_end*/, - &start_offset, - &end_offset); - - slice_length = (duk_size_t) (end_offset - start_offset); - buf_slice = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, slice_length); /* all bytes initialized below */ - DUK_ASSERT(buf_slice != NULL); - - /* Neutered or uncovered, TypeError. */ - if (h_this->buf == NULL || !DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h_this, (duk_size_t) start_offset + slice_length)) { - DUK_DCERROR_TYPE_INVALID_ARGS(thr); - } - - /* XXX: ideally we wouldn't make a copy but a view into the buffer for the - * decoding process. Or the decoding helper could be changed to accept - * the slice info (a buffer pointer is NOT a good approach because guaranteeing - * its stability is difficult). - */ - - DUK_ASSERT(DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h_this, (duk_size_t) start_offset + slice_length)); - duk_memcpy_unsafe((void *) buf_slice, - (const void *) (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this) + start_offset), - (size_t) slice_length); - - /* Use the equivalent of: new TextEncoder().encode(this) to convert the - * string. Result will be valid UTF-8; non-CESU-8 inputs are currently - * interpreted loosely. Value stack convention is a bit odd for now. - */ - duk_replace(thr, 0); - duk_set_top(thr, 1); - return duk_textdecoder_decode_utf8_nodejs(thr); -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -/* - * Node.js Buffer.prototype: toJSON() - */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_tojson(duk_hthread *thr) { - duk_hbufobj *h_this; - duk_uint8_t *buf; - duk_uint_t i, n; - duk_tval *tv; - - h_this = duk__require_bufobj_this(thr); - DUK_ASSERT(h_this != NULL); - - if (h_this->buf == NULL || !DUK_HBUFOBJ_VALID_SLICE(h_this)) { - /* Serialize uncovered backing buffer as a null; doesn't - * really matter as long we're memory safe. - */ - duk_push_null(thr); - return 1; - } - - duk_push_object(thr); - duk_push_hstring_stridx(thr, DUK_STRIDX_UC_BUFFER); - duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_TYPE); - - /* XXX: uninitialized would be OK */ - DUK_ASSERT_DISABLE((duk_size_t) h_this->length <= (duk_size_t) DUK_UINT32_MAX); - tv = duk_push_harray_with_size_outptr(thr, (duk_uint32_t) h_this->length); /* XXX: needs revision with >4G buffers */ - DUK_ASSERT(!duk_is_bare_object(thr, -1)); - - DUK_ASSERT(h_this->buf != NULL); - buf = DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this); - for (i = 0, n = h_this->length; i < n; i++) { - DUK_TVAL_SET_U32(tv + i, (duk_uint32_t) buf[i]); /* no need for decref or incref */ - } - duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_DATA); - - return 1; -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -/* - * Node.js Buffer.prototype.equals() - * Node.js Buffer.prototype.compare() - * Node.js Buffer.compare() - */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_buffer_compare_shared(duk_hthread *thr) { - duk_small_uint_t magic; - duk_hbufobj *h_bufarg1; - duk_hbufobj *h_bufarg2; - duk_small_int_t comp_res; - - /* XXX: keep support for plain buffers and non-Node.js buffers? */ - - magic = (duk_small_uint_t) duk_get_current_magic(thr); - if (magic & 0x02U) { - /* Static call style. */ - h_bufarg1 = duk__require_bufobj_value(thr, 0); - h_bufarg2 = duk__require_bufobj_value(thr, 1); - } else { - h_bufarg1 = duk__require_bufobj_this(thr); - h_bufarg2 = duk__require_bufobj_value(thr, 0); - } - DUK_ASSERT(h_bufarg1 != NULL); - DUK_ASSERT(h_bufarg2 != NULL); - - /* We want to compare the slice/view areas of the arguments. - * If either slice/view is invalid (underlying buffer is shorter) - * ensure equals() is false, but otherwise the only thing that - * matters is to be memory safe. - */ - - if (DUK_HBUFOBJ_VALID_SLICE(h_bufarg1) && DUK_HBUFOBJ_VALID_SLICE(h_bufarg2)) { - comp_res = duk_js_data_compare( - (const duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_bufarg1->buf) + h_bufarg1->offset, - (const duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_bufarg2->buf) + h_bufarg2->offset, - (duk_size_t) h_bufarg1->length, - (duk_size_t) h_bufarg2->length); - } else { - comp_res = -1; /* either nonzero value is ok */ - } - - if (magic & 0x01U) { - /* compare: similar to string comparison but for buffer data. */ - duk_push_int(thr, comp_res); - } else { - /* equals */ - duk_push_boolean(thr, (comp_res == 0)); - } - - return 1; -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -/* - * Node.js Buffer.prototype.fill() - */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_fill(duk_hthread *thr) { - duk_hbufobj *h_this; - const duk_uint8_t *fill_str_ptr; - duk_size_t fill_str_len; - duk_uint8_t fill_value; - duk_int_t fill_offset; - duk_int_t fill_end; - duk_size_t fill_length; - duk_uint8_t *p; - - h_this = duk__require_bufobj_this(thr); - DUK_ASSERT(h_this != NULL); - if (h_this->buf == NULL) { - DUK_DCERROR_TYPE_INVALID_ARGS(thr); - } - - /* [ value offset end ] */ - - if (duk_is_string_notsymbol(thr, 0)) { - fill_str_ptr = (const duk_uint8_t *) duk_get_lstring(thr, 0, &fill_str_len); - DUK_ASSERT(fill_str_ptr != NULL); - } else { - /* Symbols get ToNumber() coerced and cause TypeError. */ - fill_value = (duk_uint8_t) duk_to_uint32(thr, 0); - fill_str_ptr = (const duk_uint8_t *) &fill_value; - fill_str_len = 1; - } - - /* Fill offset handling is more lenient than in Node.js. */ - - duk__clamp_startend_nonegidx_noshift(thr, - (duk_int_t) h_this->length, - 1 /*idx_start*/, - 2 /*idx_end*/, - &fill_offset, - &fill_end); - - DUK_DDD(DUK_DDDPRINT("fill: fill_value=%02x, fill_offset=%ld, fill_end=%ld, view length=%ld", - (unsigned int) fill_value, - (long) fill_offset, - (long) fill_end, - (long) h_this->length)); - - DUK_ASSERT(fill_end - fill_offset >= 0); - DUK_ASSERT(h_this->buf != NULL); - - p = (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this) + fill_offset); - fill_length = (duk_size_t) (fill_end - fill_offset); - if (fill_str_len == 1) { - /* Handle single character fills as memset() even when - * the fill data comes from a one-char argument. - */ - duk_memset_unsafe((void *) p, (int) fill_str_ptr[0], (size_t) fill_length); - } else if (fill_str_len > 1) { - duk_size_t i, n, t; - - for (i = 0, n = (duk_size_t) (fill_end - fill_offset), t = 0; i < n; i++) { - p[i] = fill_str_ptr[t++]; - if (t >= fill_str_len) { - t = 0; - } - } - } else { - DUK_DDD(DUK_DDDPRINT("zero size fill pattern, ignore silently")); - } - - /* Return the Buffer to allow chaining: b.fill(0x11).fill(0x22, 3, 5).toString() */ - duk_push_this(thr); - return 1; -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -/* - * Node.js Buffer.prototype.write(string, [offset], [length], [encoding]) - */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_write(duk_hthread *thr) { - duk_hbufobj *h_this; - duk_uint_t offset; - duk_uint_t length; - const duk_uint8_t *str_data; - duk_size_t str_len; - - /* XXX: very inefficient support for plain buffers */ - h_this = duk__require_bufobj_this(thr); - DUK_ASSERT(h_this != NULL); - - /* Argument must be a string, e.g. a buffer is not allowed. */ - str_data = (const duk_uint8_t *) duk_require_lstring_notsymbol(thr, 0, &str_len); - - duk__resolve_offset_opt_length(thr, h_this, 1, 2, &offset, &length, 0 /*throw_flag*/); - DUK_ASSERT(offset <= h_this->length); - DUK_ASSERT(offset + length <= h_this->length); - - /* XXX: encoding is ignored now. */ - - if (length > str_len) { - length = (duk_uint_t) str_len; - } - - if (DUK_HBUFOBJ_VALID_SLICE(h_this)) { - /* Cannot overlap. */ - duk_memcpy_unsafe((void *) (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this) + offset), - (const void *) str_data, - (size_t) length); - } else { - DUK_DDD(DUK_DDDPRINT("write() target buffer is not covered, silent ignore")); - } - - duk_push_uint(thr, length); - return 1; -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -/* - * Node.js Buffer.prototype.copy() - */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_copy(duk_hthread *thr) { - duk_hbufobj *h_this; - duk_hbufobj *h_bufarg; - duk_int_t source_length; - duk_int_t target_length; - duk_int_t target_start, source_start, source_end; - duk_uint_t target_ustart, source_ustart, source_uend; - duk_uint_t copy_size = 0; - - /* [ targetBuffer targetStart sourceStart sourceEnd ] */ - - h_this = duk__require_bufobj_this(thr); - h_bufarg = duk__require_bufobj_value(thr, 0); - DUK_ASSERT(h_this != NULL); - DUK_ASSERT(h_bufarg != NULL); - source_length = (duk_int_t) h_this->length; - target_length = (duk_int_t) h_bufarg->length; - - target_start = duk_to_int(thr, 1); - source_start = duk_to_int(thr, 2); - if (duk_is_undefined(thr, 3)) { - source_end = source_length; - } else { - source_end = duk_to_int(thr, 3); - } - - DUK_DDD(DUK_DDDPRINT("checking copy args: target_start=%ld, target_length=%ld, " - "source_start=%ld, source_end=%ld, source_length=%ld", - (long) target_start, - (long) h_bufarg->length, - (long) source_start, - (long) source_end, - (long) source_length)); - - /* This behavior mostly mimics Node.js now. */ - - if (source_start < 0 || source_end < 0 || target_start < 0) { - /* Negative offsets cause a RangeError. */ - goto fail_bounds; - } - source_ustart = (duk_uint_t) source_start; - source_uend = (duk_uint_t) source_end; - target_ustart = (duk_uint_t) target_start; - if (source_ustart >= source_uend || /* crossed offsets or zero size */ - source_ustart >= (duk_uint_t) source_length || /* source out-of-bounds (but positive) */ - target_ustart >= (duk_uint_t) target_length) { /* target out-of-bounds (but positive) */ - goto silent_ignore; - } - if (source_uend >= (duk_uint_t) source_length) { - /* Source end clamped silently to available length. */ - source_uend = (duk_uint_t) source_length; - } - copy_size = source_uend - source_ustart; - if (target_ustart + copy_size > (duk_uint_t) target_length) { - /* Clamp to target's end if too long. - * - * NOTE: there's no overflow possibility in the comparison; - * both target_ustart and copy_size are >= 0 and based on - * values in duk_int_t range. Adding them as duk_uint_t - * values is then guaranteed not to overflow. - */ - DUK_ASSERT(target_ustart + copy_size >= target_ustart); /* no overflow */ - DUK_ASSERT(target_ustart + copy_size >= copy_size); /* no overflow */ - copy_size = (duk_uint_t) target_length - target_ustart; - } - - DUK_DDD(DUK_DDDPRINT("making copy: target_ustart=%lu source_ustart=%lu copy_size=%lu", - (unsigned long) target_ustart, - (unsigned long) source_ustart, - (unsigned long) copy_size)); - - DUK_ASSERT(copy_size >= 1); - DUK_ASSERT(source_ustart <= (duk_uint_t) source_length); - DUK_ASSERT(source_ustart + copy_size <= (duk_uint_t) source_length); - DUK_ASSERT(target_ustart <= (duk_uint_t) target_length); - DUK_ASSERT(target_ustart + copy_size <= (duk_uint_t) target_length); - - /* Ensure copy is covered by underlying buffers. */ - DUK_ASSERT(h_bufarg->buf != NULL); /* length check */ - DUK_ASSERT(h_this->buf != NULL); /* length check */ - if (DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h_bufarg, target_ustart + copy_size) && - DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h_this, source_ustart + copy_size)) { - /* Must use memmove() because copy area may overlap (source and target - * buffer may be the same, or from different slices. - */ - duk_memmove_unsafe((void *) (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_bufarg) + target_ustart), - (const void *) (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this) + source_ustart), - (size_t) copy_size); - } else { - DUK_DDD(DUK_DDDPRINT("buffer copy not covered by underlying buffer(s), ignoring")); - } - -silent_ignore: - /* Return value is like write(), number of bytes written. - * The return value matters because of code like: - * "off += buf.copy(...)". - */ - duk_push_uint(thr, copy_size); - return 1; - -fail_bounds: - DUK_DCERROR_RANGE_INVALID_ARGS(thr); -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -/* - * TypedArray.prototype.set() - * - * TypedArray set() is pretty interesting to implement because: - * - * - The source argument may be a plain array or a typedarray. If the - * source is a TypedArray, values are decoded and re-encoded into the - * target (not as a plain byte copy). This may happen even when the - * element byte size is the same, e.g. integer values may be re-encoded - * into floats. - * - * - Source and target may refer to the same underlying buffer, so that - * the set() operation may overlap. The specification requires that this - * must work as if a copy was made before the operation. Note that this - * is NOT a simple memmove() situation because the source and target - * byte sizes may be different -- e.g. a 4-byte source (Int8Array) may - * expand to a 16-byte target (Uint32Array) so that the target overlaps - * the source both from beginning and the end (unlike in typical memmove). - * - * - Even if 'buf' pointers of the source and target differ, there's no - * guarantee that their memory areas don't overlap. This may be the - * case with external buffers. - * - * Even so, it is nice to optimize for the common case: - * - * - Source and target separate buffers or non-overlapping. - * - * - Source and target have a compatible type so that a plain byte copy - * is possible. Note that while e.g. uint8 and int8 are compatible - * (coercion one way or another doesn't change the byte representation), - * e.g. int8 and uint8clamped are NOT compatible when writing int8 - * values into uint8clamped typedarray (-1 would clamp to 0 for instance). - * - * See test-bi-typedarray-proto-set.js. - */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_hthread *thr) { - duk_hbufobj *h_this; - duk_hobject *h_obj; - duk_uarridx_t i, n; - duk_int_t offset_signed; - duk_uint_t offset_elems; - duk_uint_t offset_bytes; - - h_this = duk__require_bufobj_this(thr); - DUK_ASSERT(h_this != NULL); - DUK_HBUFOBJ_ASSERT_VALID(h_this); - - if (h_this->buf == NULL) { - DUK_DDD(DUK_DDDPRINT("source neutered, skip copy")); - return 0; - } - - duk_hbufobj_promote_plain(thr, 0); - h_obj = duk_require_hobject(thr, 0); - - /* XXX: V8 throws a TypeError for negative values. Would it - * be more useful to interpret negative offsets here from the - * end of the buffer too? - */ - offset_signed = duk_to_int(thr, 1); - if (offset_signed < 0) { - /* For some reason this is a TypeError (at least in V8). */ - DUK_DCERROR_TYPE_INVALID_ARGS(thr); - } - offset_elems = (duk_uint_t) offset_signed; - offset_bytes = offset_elems << h_this->shift; - if ((offset_bytes >> h_this->shift) != offset_elems) { - /* Byte length would overflow. */ - /* XXX: easier check with less code? */ - goto fail_args; - } - if (offset_bytes > h_this->length) { - /* Equality may be OK but >length not. Checking - * this explicitly avoids some overflow cases - * below. - */ - goto fail_args; - } - DUK_ASSERT(offset_bytes <= h_this->length); - - /* Fast path: source is a TypedArray (or any bufobj). */ - - if (DUK_HOBJECT_IS_BUFOBJ(h_obj)) { - duk_hbufobj *h_bufarg; -#if !defined(DUK_USE_PREFER_SIZE) - duk_uint16_t comp_mask; -#endif - duk_small_int_t no_overlap = 0; - duk_uint_t src_length; - duk_uint_t dst_length; - duk_uint_t dst_length_elems; - duk_uint8_t *p_src_base; - duk_uint8_t *p_src_end; - duk_uint8_t *p_src; - duk_uint8_t *p_dst_base; - duk_uint8_t *p_dst; - duk_small_uint_t src_elem_size; - duk_small_uint_t dst_elem_size; - - h_bufarg = (duk_hbufobj *) h_obj; - DUK_HBUFOBJ_ASSERT_VALID(h_bufarg); - - if (h_bufarg->buf == NULL) { - DUK_DDD(DUK_DDDPRINT("target neutered, skip copy")); - return 0; - } - - /* Nominal size check. */ - src_length = h_bufarg->length; /* bytes in source */ - dst_length_elems = (src_length >> h_bufarg->shift); /* elems in source and dest */ - dst_length = dst_length_elems << h_this->shift; /* bytes in dest */ - if ((dst_length >> h_this->shift) != dst_length_elems) { - /* Byte length would overflow. */ - /* XXX: easier check with less code? */ - goto fail_args; - } - DUK_DDD(DUK_DDDPRINT("nominal size check: src_length=%ld, dst_length=%ld", (long) src_length, (long) dst_length)); - DUK_ASSERT(offset_bytes <= h_this->length); - if (dst_length > h_this->length - offset_bytes) { - /* Overflow not an issue because subtraction is used on the right - * side and guaranteed to be >= 0. - */ - DUK_DDD(DUK_DDDPRINT("copy exceeds target buffer nominal length")); - goto fail_args; - } - if (!DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h_this, offset_bytes + dst_length)) { - DUK_DDD(DUK_DDDPRINT("copy not covered by underlying target buffer, ignore")); - return 0; - } - - p_src_base = DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_bufarg); - p_dst_base = DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this) + offset_bytes; - - /* Check actual underlying buffers for validity and that they - * cover the copy. No side effects are allowed after the check - * so that the validity status doesn't change. - */ - if (!DUK_HBUFOBJ_VALID_SLICE(h_this) || !DUK_HBUFOBJ_VALID_SLICE(h_bufarg)) { - /* The condition could be more narrow and check for the - * copy area only, but there's no need for fine grained - * behavior when the underlying buffer is misconfigured. - */ - DUK_DDD(DUK_DDDPRINT("source and/or target not covered by underlying buffer, skip copy")); - return 0; - } - - /* We want to do a straight memory copy if possible: this is - * an important operation because .set() is the TypedArray - * way to copy chunks of memory. However, because set() - * conceptually works in terms of elements, not all views are - * compatible with direct byte copying. - * - * If we do manage a direct copy, the "overlap issue" handled - * below can just be solved using memmove() because the source - * and destination element sizes are necessarily equal. - */ - -#if !defined(DUK_USE_PREFER_SIZE) - DUK_ASSERT(h_this->elem_type < sizeof(duk__buffer_elemtype_copy_compatible) / sizeof(duk_uint16_t)); - comp_mask = duk__buffer_elemtype_copy_compatible[h_this->elem_type]; - if (comp_mask & (1 << h_bufarg->elem_type)) { - DUK_ASSERT(src_length == dst_length); - - DUK_DDD(DUK_DDDPRINT("fast path: able to use memmove() because views are compatible")); - duk_memmove_unsafe((void *) p_dst_base, (const void *) p_src_base, (size_t) dst_length); - return 0; - } - DUK_DDD(DUK_DDDPRINT("fast path: views are not compatible with a byte copy, copy by item")); -#endif /* !DUK_USE_PREFER_SIZE */ - - /* We want to avoid making a copy to process set() but that's - * not always possible: the source and the target may overlap - * and because element sizes are different, the overlap cannot - * always be handled with a memmove() or choosing the copy - * direction in a certain way. For example, if source type is - * uint8 and target type is uint32, the target area may exceed - * the source area from both ends! - * - * Note that because external buffers may point to the same - * memory areas, we must ultimately make this check using - * pointers. - * - * NOTE: careful with side effects: any side effect may cause - * a buffer resize (or external buffer pointer/length update)! - */ - - DUK_DDD(DUK_DDDPRINT("overlap check: p_src_base=%p, src_length=%ld, " - "p_dst_base=%p, dst_length=%ld", - (void *) p_src_base, - (long) src_length, - (void *) p_dst_base, - (long) dst_length)); - - if (p_src_base >= p_dst_base + dst_length || /* source starts after dest ends */ - p_src_base + src_length <= p_dst_base) { /* source ends before dest starts */ - no_overlap = 1; - } - - if (!no_overlap) { - /* There's overlap: the desired end result is that - * conceptually a copy is made to avoid "trampling" - * of source data by destination writes. We make - * an actual temporary copy to handle this case. - */ - duk_uint8_t *p_src_copy; - - DUK_DDD(DUK_DDDPRINT("there is overlap, make a copy of the source")); - p_src_copy = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, src_length); - DUK_ASSERT(p_src_copy != NULL); - duk_memcpy_unsafe((void *) p_src_copy, (const void *) p_src_base, (size_t) src_length); - - p_src_base = p_src_copy; /* use p_src_base from now on */ - } - /* Value stack intentionally mixed size here. */ - - DUK_DDD(DUK_DDDPRINT("after overlap check: p_src_base=%p, src_length=%ld, " - "p_dst_base=%p, dst_length=%ld, valstack top=%ld", - (void *) p_src_base, - (long) src_length, - (void *) p_dst_base, - (long) dst_length, - (long) duk_get_top(thr))); - - /* Ready to make the copy. We must proceed element by element - * and must avoid any side effects that might cause the buffer - * validity check above to become invalid. - * - * Although we work through the value stack here, only plain - * numbers are handled which should be side effect safe. - */ - - src_elem_size = (duk_small_uint_t) (1U << h_bufarg->shift); - dst_elem_size = (duk_small_uint_t) (1U << h_this->shift); - p_src = p_src_base; - p_dst = p_dst_base; - p_src_end = p_src_base + src_length; - - while (p_src != p_src_end) { - DUK_DDD(DUK_DDDPRINT("fast path per element copy loop: " - "p_src=%p, p_src_end=%p, p_dst=%p", - (void *) p_src, - (void *) p_src_end, - (void *) p_dst)); - /* A validated read() is always a number, so it's write coercion - * is always side effect free an won't invalidate pointers etc. - */ - duk_hbufobj_push_validated_read(thr, h_bufarg, p_src, src_elem_size); - duk_hbufobj_validated_write(thr, h_this, p_dst, dst_elem_size); - duk_pop(thr); - p_src += src_elem_size; - p_dst += dst_elem_size; - } - - return 0; - } else { - /* Slow path: quite slow, but we save space by using the property code - * to write coerce target values. We don't need to worry about overlap - * here because the source is not a TypedArray. - * - * We could use the bufobj write coercion helper but since the - * property read may have arbitrary side effects, full validity checks - * would be needed for every element anyway. - */ - - n = (duk_uarridx_t) duk_get_length(thr, 0); - DUK_ASSERT(offset_bytes <= h_this->length); - if ((n << h_this->shift) > h_this->length - offset_bytes) { - /* Overflow not an issue because subtraction is used on the right - * side and guaranteed to be >= 0. - */ - DUK_DDD(DUK_DDDPRINT("copy exceeds target buffer nominal length")); - goto fail_args; - } - - /* There's no need to check for buffer validity status for the - * target here: the property access code will do that for each - * element. Moreover, if we did check the validity here, side - * effects from reading the source argument might invalidate - * the results anyway. - */ - - DUK_ASSERT_TOP(thr, 2); - duk_push_this(thr); - - for (i = 0; i < n; i++) { - duk_get_prop_index(thr, 0, i); - duk_put_prop_index(thr, 2, offset_elems + i); - } - } - - return 0; - -fail_args: - DUK_DCERROR_RANGE_INVALID_ARGS(thr); -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -/* - * Node.js Buffer.prototype.slice([start], [end]) - * ArrayBuffer.prototype.slice(begin, [end]) - * TypedArray.prototype.subarray(begin, [end]) - * - * The API calls are almost identical; negative indices are counted from end - * of buffer, and final indices are clamped (allowing crossed indices). Main - * differences: - * - * - Copy/view behavior; Node.js .slice() and TypedArray .subarray() create - * views, ArrayBuffer .slice() creates a copy - * - * - Resulting object has a different class and prototype depending on the - * call (or 'this' argument) - * - * - TypedArray .subarray() arguments are element indices, not byte offsets - * - * - Plain buffer argument creates a plain buffer slice - */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_LOCAL void duk__arraybuffer_plain_slice(duk_hthread *thr, duk_hbuffer *h_val) { - duk_int_t start_offset, end_offset; - duk_uint_t slice_length; - duk_uint8_t *p_copy; - duk_size_t copy_length; - - duk__clamp_startend_negidx_shifted(thr, - (duk_int_t) DUK_HBUFFER_GET_SIZE(h_val), - 0 /*buffer_shift*/, - 0 /*idx_start*/, - 1 /*idx_end*/, - &start_offset, - &end_offset); - DUK_ASSERT(end_offset <= (duk_int_t) DUK_HBUFFER_GET_SIZE(h_val)); - DUK_ASSERT(start_offset >= 0); - DUK_ASSERT(end_offset >= start_offset); - slice_length = (duk_uint_t) (end_offset - start_offset); - - p_copy = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, (duk_size_t) slice_length); - DUK_ASSERT(p_copy != NULL); - copy_length = slice_length; - - duk_memcpy_unsafe((void *) p_copy, - (const void *) ((duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_val) + start_offset), - copy_length); -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -/* Shared helper for slice/subarray operation. - * Magic: 0x01=isView, 0x02=copy, 0x04=Node.js Buffer special handling. - */ -DUK_INTERNAL duk_ret_t duk_bi_buffer_slice_shared(duk_hthread *thr) { - duk_small_int_t magic; - duk_small_uint_t res_class_num; - duk_small_int_t res_proto_bidx; - duk_hbufobj *h_this; - duk_hbufobj *h_bufobj; - duk_hbuffer *h_val; - duk_int_t start_offset, end_offset; - duk_uint_t slice_length; - duk_tval *tv; - - /* [ start end ] */ - - magic = duk_get_current_magic(thr); - - tv = duk_get_borrowed_this_tval(thr); - DUK_ASSERT(tv != NULL); - - if (DUK_TVAL_IS_BUFFER(tv)) { - /* For plain buffers return a plain buffer slice. */ - h_val = DUK_TVAL_GET_BUFFER(tv); - DUK_ASSERT(h_val != NULL); - - if (magic & 0x02) { - /* Make copy: ArrayBuffer.prototype.slice() uses this. */ - duk__arraybuffer_plain_slice(thr, h_val); - return 1; - } else { - /* View into existing buffer: cannot be done if the - * result is a plain buffer because there's no slice - * info. So return an ArrayBuffer instance; coerce - * the 'this' binding into an object and behave as if - * the original call was for an Object-coerced plain - * buffer (handled automatically by duk__require_bufobj_this()). - */ - - DUK_DDD(DUK_DDDPRINT("slice() doesn't handle view into plain buffer, coerce 'this' to ArrayBuffer object")); - /* fall through */ - } - } - tv = NULL; /* No longer valid nor needed. */ - - h_this = duk__require_bufobj_this(thr); - - /* Slice offsets are element (not byte) offsets, which only matters - * for TypedArray views, Node.js Buffer and ArrayBuffer have shift - * zero so byte and element offsets are the same. Negative indices - * are counted from end of slice, crossed indices are allowed (and - * result in zero length result), and final values are clamped - * against the current slice. There's intentionally no check - * against the underlying buffer here. - */ - - duk__clamp_startend_negidx_shifted(thr, - (duk_int_t) h_this->length, - (duk_uint8_t) h_this->shift, - 0 /*idx_start*/, - 1 /*idx_end*/, - &start_offset, - &end_offset); - DUK_ASSERT(end_offset >= start_offset); - DUK_ASSERT(start_offset >= 0); - DUK_ASSERT(end_offset >= 0); - slice_length = (duk_uint_t) (end_offset - start_offset); - - /* The resulting buffer object gets the same class and prototype as - * the buffer in 'this', e.g. if the input is a Uint8Array the - * result is a Uint8Array; if the input is a Float32Array, the - * result is a Float32Array. The result internal prototype should - * be the default prototype for the class (e.g. initial value of - * Uint8Array.prototype), not copied from the argument (Duktape 1.x - * did that). - * - * Node.js Buffers have special handling: they're Uint8Arrays as far - * as the internal class is concerned, so the new Buffer should also - * be an Uint8Array but inherit from Buffer.prototype. - */ - res_class_num = DUK_HOBJECT_GET_CLASS_NUMBER((duk_hobject *) h_this); - DUK_ASSERT(res_class_num >= DUK_HOBJECT_CLASS_BUFOBJ_MIN); /* type check guarantees */ - DUK_ASSERT(res_class_num <= DUK_HOBJECT_CLASS_BUFOBJ_MAX); - res_proto_bidx = duk__buffer_proto_from_classnum[res_class_num - DUK_HOBJECT_CLASS_BUFOBJ_MIN]; - if (magic & 0x04) { - res_proto_bidx = DUK_BIDX_NODEJS_BUFFER_PROTOTYPE; - } - h_bufobj = - duk_push_bufobj_raw(thr, - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_BUFOBJ | DUK_HOBJECT_CLASS_AS_FLAGS(res_class_num), - res_proto_bidx); - DUK_ASSERT(h_bufobj != NULL); - - DUK_ASSERT(h_bufobj->length == 0); - h_bufobj->shift = h_this->shift; /* inherit */ - h_bufobj->elem_type = h_this->elem_type; /* inherit */ - h_bufobj->is_typedarray = magic & 0x01; - DUK_ASSERT(h_bufobj->is_typedarray == 0 || h_bufobj->is_typedarray == 1); - - h_val = h_this->buf; - if (h_val == NULL) { - DUK_DCERROR_TYPE_INVALID_ARGS(thr); - } - - if (magic & 0x02) { - /* non-zero: make copy */ - duk_uint8_t *p_copy; - duk_size_t copy_length; - - p_copy = (duk_uint8_t *) duk_push_fixed_buffer_zero( - thr, - (duk_size_t) slice_length); /* must be zeroed, not all bytes always copied */ - DUK_ASSERT(p_copy != NULL); - - /* Copy slice, respecting underlying buffer limits; remainder - * is left as zero. - */ - copy_length = DUK_HBUFOBJ_CLAMP_BYTELENGTH(h_this, slice_length); - duk_memcpy_unsafe((void *) p_copy, - (const void *) (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this) + start_offset), - copy_length); - - h_val = duk_known_hbuffer(thr, -1); - - h_bufobj->buf = h_val; - DUK_HBUFFER_INCREF(thr, h_val); - h_bufobj->length = slice_length; - DUK_ASSERT(h_bufobj->offset == 0); - - duk_pop(thr); /* reachable so pop OK */ - } else { - h_bufobj->buf = h_val; - DUK_HBUFFER_INCREF(thr, h_val); - h_bufobj->length = slice_length; - h_bufobj->offset = h_this->offset + (duk_uint_t) start_offset; - - /* Copy the .buffer property, needed for TypedArray.prototype.subarray(). - * - * XXX: limit copy only for TypedArray classes specifically? - */ - - DUK_ASSERT(h_bufobj->buf_prop == NULL); - h_bufobj->buf_prop = h_this->buf_prop; /* may be NULL */ - DUK_HOBJECT_INCREF_ALLOWNULL(thr, (duk_hobject *) h_bufobj->buf_prop); - } - /* unbalanced stack on purpose */ - - DUK_HBUFOBJ_ASSERT_VALID(h_bufobj); - return 1; -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -/* - * Node.js Buffer.isEncoding() - */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_is_encoding(duk_hthread *thr) { - const char *encoding; - - /* only accept lowercase 'utf8' now. */ - - encoding = duk_to_string(thr, 0); - DUK_ASSERT(duk_is_string(thr, 0)); /* guaranteed by duk_to_string() */ - duk_push_boolean(thr, DUK_STRCMP(encoding, "utf8") == 0); - return 1; -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -/* - * Node.js Buffer.isBuffer() - */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_is_buffer(duk_hthread *thr) { - duk_hobject *h; - duk_hobject *h_proto; - duk_bool_t ret = 0; - - DUK_ASSERT(duk_get_top(thr) >= 1); /* nargs */ - h = duk_get_hobject(thr, 0); - if (h != NULL) { - h_proto = thr->builtins[DUK_BIDX_NODEJS_BUFFER_PROTOTYPE]; - DUK_ASSERT(h_proto != NULL); - - h = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h); - if (h != NULL) { - ret = duk_hobject_prototype_chain_contains(thr, h, h_proto, 0 /*ignore_loop*/); - } - } - - duk_push_boolean(thr, ret); - return 1; -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -/* - * Node.js Buffer.byteLength() - */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_byte_length(duk_hthread *thr) { - const char *str; - duk_size_t len; - - /* At the moment Buffer() will just use the string bytes as - * is (ignoring encoding), so we return the string length here - * unconditionally. - */ - - /* XXX: to be revised; Old Node.js behavior just coerces any buffer - * values to string: - * $ node - * > Buffer.byteLength(new Uint32Array(10)) - * 20 - * > Buffer.byteLength(new Uint32Array(100)) - * 20 - * (The 20 comes from '[object Uint32Array]'.length - */ - - str = duk_to_lstring(thr, 0, &len); - DUK_UNREF(str); - duk_push_size_t(thr, len); - return 1; -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -/* - * Node.js Buffer.concat() - */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_concat(duk_hthread *thr) { - duk_hobject *h_arg; - duk_uint_t total_length; - duk_hbufobj *h_bufobj; - duk_hbufobj *h_bufres; - duk_hbuffer *h_val; - duk_uint_t i, n; - duk_uint8_t *p; - duk_size_t space_left; - duk_size_t copy_size; - - /* Node.js accepts only actual Arrays. */ - h_arg = duk_require_hobject(thr, 0); - if (DUK_HOBJECT_GET_CLASS_NUMBER(h_arg) != DUK_HOBJECT_CLASS_ARRAY) { - DUK_DCERROR_TYPE_INVALID_ARGS(thr); - } - - /* Compute result length and validate argument buffers. */ - n = (duk_uint_t) duk_get_length(thr, 0); - total_length = 0; - for (i = 0; i < n; i++) { - /* Neutered checks not necessary here: neutered buffers have - * zero 'length' so we'll effectively skip them. - */ - DUK_ASSERT_TOP(thr, 2); /* [ array totalLength ] */ - duk_get_prop_index(thr, 0, (duk_uarridx_t) i); /* -> [ array totalLength buf ] */ - h_bufobj = duk__require_bufobj_value(thr, 2); - DUK_ASSERT(h_bufobj != NULL); - total_length += h_bufobj->length; - if (DUK_UNLIKELY(total_length < h_bufobj->length)) { - DUK_DCERROR_RANGE_INVALID_ARGS(thr); /* Wrapped. */ - } - duk_pop(thr); - } - /* In Node.js v0.12.1 a 1-element array is special and won't create a - * copy, this was fixed later so an explicit check no longer needed. - */ - - /* User totalLength overrides a computed length, but we'll check - * every copy in the copy loop. Note that duk_to_int() can - * technically have arbitrary side effects so we need to recheck - * the buffers in the copy loop. - */ - if (!duk_is_undefined(thr, 1) && n > 0) { - /* For n == 0, Node.js ignores totalLength argument and - * returns a zero length buffer. - */ - duk_int_t total_length_signed; - total_length_signed = duk_to_int(thr, 1); - if (total_length_signed < 0) { - DUK_DCERROR_RANGE_INVALID_ARGS(thr); - } - total_length = (duk_uint_t) total_length_signed; - } - - h_bufres = duk_push_bufobj_raw(thr, - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_BUFOBJ | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_UINT8ARRAY), - DUK_BIDX_NODEJS_BUFFER_PROTOTYPE); - DUK_ASSERT(h_bufres != NULL); - - p = (duk_uint8_t *) duk_push_fixed_buffer_zero(thr, - total_length); /* must be zeroed, all bytes not necessarily written over */ - DUK_ASSERT(p != NULL); - space_left = (duk_size_t) total_length; - - for (i = 0; i < n; i++) { - DUK_ASSERT_TOP(thr, 4); /* [ array totalLength bufres buf ] */ - - duk_get_prop_index(thr, 0, (duk_uarridx_t) i); - h_bufobj = duk__require_bufobj_value(thr, 4); - DUK_ASSERT(h_bufobj != NULL); - - copy_size = h_bufobj->length; - if (copy_size > space_left) { - copy_size = space_left; - } - - if (h_bufobj->buf != NULL && DUK_HBUFOBJ_VALID_SLICE(h_bufobj)) { - duk_memcpy_unsafe((void *) p, (const void *) DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_bufobj), copy_size); - } else { - /* Just skip, leaving zeroes in the result. */ - ; - } - p += copy_size; - space_left -= copy_size; - - duk_pop(thr); - } - - h_val = duk_known_hbuffer(thr, -1); - - duk__set_bufobj_buffer(thr, h_bufres, h_val); - h_bufres->is_typedarray = 1; - DUK_HBUFOBJ_ASSERT_VALID(h_bufres); - - duk_pop(thr); /* pop plain buffer, now reachable through h_bufres */ - - return 1; /* return h_bufres */ -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -/* - * Shared readfield and writefield methods - * - * The readfield/writefield methods need support for endianness and field - * types. All offsets are byte based so no offset shifting is needed. - */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -/* Format of magic, bits: - * 0...1: field type; 0=uint8, 1=uint16, 2=uint32, 3=float, 4=double, 5=unused, 6=unused, 7=unused - * 3: endianness: 0=little, 1=big - * 4: signed: 1=yes, 0=no - * 5: typedarray: 1=yes, 0=no - */ -#define DUK__FLD_8BIT 0 -#define DUK__FLD_16BIT 1 -#define DUK__FLD_32BIT 2 -#define DUK__FLD_FLOAT 3 -#define DUK__FLD_DOUBLE 4 -#define DUK__FLD_VARINT 5 -#define DUK__FLD_BIGENDIAN (1 << 3) -#define DUK__FLD_SIGNED (1 << 4) -#define DUK__FLD_TYPEDARRAY (1 << 5) - -/* XXX: split into separate functions for each field type? */ -DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_hthread *thr) { - duk_small_uint_t magic = (duk_small_uint_t) duk_get_current_magic(thr); - duk_small_uint_t magic_ftype; - duk_small_uint_t magic_bigendian; - duk_small_uint_t magic_signed; - duk_small_uint_t magic_typedarray; - duk_small_uint_t endswap; - duk_hbufobj *h_this; - duk_bool_t no_assert; - duk_int_t offset_signed; - duk_uint_t offset; - duk_uint_t buffer_length; - duk_uint_t check_length; - duk_uint8_t *buf; - duk_double_union du; - - magic_ftype = magic & 0x0007U; - magic_bigendian = magic & 0x0008U; - magic_signed = magic & 0x0010U; - magic_typedarray = magic & 0x0020U; - - h_this = duk__require_bufobj_this(thr); /* XXX: very inefficient for plain buffers */ - DUK_ASSERT(h_this != NULL); - buffer_length = h_this->length; - - /* [ offset noAssert ], when ftype != DUK__FLD_VARINT */ - /* [ offset fieldByteLength noAssert ], when ftype == DUK__FLD_VARINT */ - /* [ offset littleEndian ], when DUK__FLD_TYPEDARRAY (regardless of ftype) */ - - /* Handle TypedArray vs. Node.js Buffer arg differences */ - if (magic_typedarray) { - no_assert = 0; -#if defined(DUK_USE_INTEGER_LE) - endswap = !duk_to_boolean(thr, 1); /* 1=little endian */ -#else - endswap = duk_to_boolean(thr, 1); /* 1=little endian */ -#endif - } else { - no_assert = duk_to_boolean(thr, (magic_ftype == DUK__FLD_VARINT) ? 2 : 1); -#if defined(DUK_USE_INTEGER_LE) - endswap = magic_bigendian; -#else - endswap = !magic_bigendian; -#endif - } - - /* Offset is coerced first to signed integer range and then to unsigned. - * This ensures we can add a small byte length (1-8) to the offset in - * bound checks and not wrap. - */ - offset_signed = duk_to_int(thr, 0); - offset = (duk_uint_t) offset_signed; - if (offset_signed < 0) { - goto fail_bounds; - } - - DUK_DDD(DUK_DDDPRINT("readfield, buffer_length=%ld, offset=%ld, no_assert=%d, " - "magic=%04x, magic_fieldtype=%d, magic_bigendian=%d, magic_signed=%d, " - "endswap=%u", - (long) buffer_length, - (long) offset, - (int) no_assert, - (unsigned int) magic, - (int) magic_ftype, - (int) (magic_bigendian >> 3), - (int) (magic_signed >> 4), - (int) endswap)); - - /* Update 'buffer_length' to be the effective, safe limit which - * takes into account the underlying buffer. This value will be - * potentially invalidated by any side effect. - */ - check_length = DUK_HBUFOBJ_CLAMP_BYTELENGTH(h_this, buffer_length); - DUK_DDD(DUK_DDDPRINT("buffer_length=%ld, check_length=%ld", (long) buffer_length, (long) check_length)); - - if (h_this->buf) { - buf = DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this); - } else { - /* Neutered. We could go into the switch-case safely with - * buf == NULL because check_length == 0. To avoid scanbuild - * warnings, fail directly instead. - */ - DUK_ASSERT(check_length == 0); - goto fail_neutered; - } - DUK_ASSERT(buf != NULL); - - switch (magic_ftype) { - case DUK__FLD_8BIT: { - duk_uint8_t tmp; - if (offset + 1U > check_length) { - goto fail_bounds; - } - tmp = buf[offset]; - if (magic_signed) { - duk_push_int(thr, (duk_int_t) ((duk_int8_t) tmp)); - } else { - duk_push_uint(thr, (duk_uint_t) tmp); - } - break; - } - case DUK__FLD_16BIT: { - duk_uint16_t tmp; - if (offset + 2U > check_length) { - goto fail_bounds; - } - duk_memcpy((void *) du.uc, (const void *) (buf + offset), 2); - tmp = du.us[0]; - if (endswap) { - tmp = DUK_BSWAP16(tmp); - } - if (magic_signed) { - duk_push_int(thr, (duk_int_t) ((duk_int16_t) tmp)); - } else { - duk_push_uint(thr, (duk_uint_t) tmp); - } - break; - } - case DUK__FLD_32BIT: { - duk_uint32_t tmp; - if (offset + 4U > check_length) { - goto fail_bounds; - } - duk_memcpy((void *) du.uc, (const void *) (buf + offset), 4); - tmp = du.ui[0]; - if (endswap) { - tmp = DUK_BSWAP32(tmp); - } - if (magic_signed) { - duk_push_int(thr, (duk_int_t) ((duk_int32_t) tmp)); - } else { - duk_push_uint(thr, (duk_uint_t) tmp); - } - break; - } - case DUK__FLD_FLOAT: { - duk_uint32_t tmp; - if (offset + 4U > check_length) { - goto fail_bounds; - } - duk_memcpy((void *) du.uc, (const void *) (buf + offset), 4); - if (endswap) { - tmp = du.ui[0]; - tmp = DUK_BSWAP32(tmp); - du.ui[0] = tmp; - } - duk_push_number(thr, (duk_double_t) du.f[0]); - break; - } - case DUK__FLD_DOUBLE: { - if (offset + 8U > check_length) { - goto fail_bounds; - } - duk_memcpy((void *) du.uc, (const void *) (buf + offset), 8); - if (endswap) { - DUK_DBLUNION_BSWAP64(&du); - } - duk_push_number(thr, (duk_double_t) du.d); - break; - } - case DUK__FLD_VARINT: { - /* Node.js Buffer variable width integer field. We don't really - * care about speed here, so aim for shortest algorithm. - */ - duk_int_t field_bytelen; - duk_int_t i, i_step, i_end; -#if defined(DUK_USE_64BIT_OPS) - duk_int64_t tmp; - duk_small_uint_t shift_tmp; -#else - duk_double_t tmp; - duk_small_int_t highbyte; -#endif - const duk_uint8_t *p; - - field_bytelen = duk_get_int(thr, 1); /* avoid side effects! */ - if (field_bytelen < 1 || field_bytelen > 6) { - goto fail_field_length; - } - if (offset + (duk_uint_t) field_bytelen > check_length) { - goto fail_bounds; - } - p = (const duk_uint8_t *) (buf + offset); - - /* Slow gathering of value using either 64-bit arithmetic - * or IEEE doubles if 64-bit types not available. Handling - * of negative numbers is a bit non-obvious in both cases. - */ - - if (magic_bigendian) { - /* Gather in big endian */ - i = 0; - i_step = 1; - i_end = field_bytelen; /* one i_step over */ - } else { - /* Gather in little endian */ - i = field_bytelen - 1; - i_step = -1; - i_end = -1; /* one i_step over */ - } - -#if defined(DUK_USE_64BIT_OPS) - tmp = 0; - do { - DUK_ASSERT(i >= 0 && i < field_bytelen); - tmp = (tmp << 8) + (duk_int64_t) p[i]; - i += i_step; - } while (i != i_end); - - if (magic_signed) { - /* Shift to sign extend. Left shift must be unsigned - * to avoid undefined behavior; right shift must be - * signed to sign extend properly. - */ - shift_tmp = (duk_small_uint_t) (64U - (duk_small_uint_t) field_bytelen * 8U); - tmp = (duk_int64_t) ((duk_uint64_t) tmp << shift_tmp) >> shift_tmp; - } - - duk_push_i64(thr, tmp); -#else - highbyte = p[i]; - if (magic_signed && (highbyte & 0x80) != 0) { - /* 0xff => 255 - 256 = -1; 0x80 => 128 - 256 = -128 */ - tmp = (duk_double_t) (highbyte - 256); - } else { - tmp = (duk_double_t) highbyte; - } - for (;;) { - i += i_step; - if (i == i_end) { - break; - } - DUK_ASSERT(i >= 0 && i < field_bytelen); - tmp = (tmp * 256.0) + (duk_double_t) p[i]; - } - - duk_push_number(thr, tmp); -#endif - break; - } - default: { /* should never happen but default here */ - goto fail_bounds; - } - } - - return 1; - -fail_neutered: -fail_field_length: -fail_bounds: - if (no_assert) { - /* Node.js return value for noAssert out-of-bounds reads is - * usually (but not always) NaN. Return NaN consistently. - */ - duk_push_nan(thr); - return 1; - } - DUK_DCERROR_RANGE_INVALID_ARGS(thr); -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -/* XXX: split into separate functions for each field type? */ -DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_hthread *thr) { - duk_small_uint_t magic = (duk_small_uint_t) duk_get_current_magic(thr); - duk_small_uint_t magic_ftype; - duk_small_uint_t magic_bigendian; - duk_small_uint_t magic_signed; - duk_small_uint_t magic_typedarray; - duk_small_uint_t endswap; - duk_hbufobj *h_this; - duk_bool_t no_assert; - duk_int_t offset_signed; - duk_uint_t offset; - duk_uint_t buffer_length; - duk_uint_t check_length; - duk_uint8_t *buf; - duk_double_union du; - duk_int_t nbytes = 0; - - magic_ftype = magic & 0x0007U; - magic_bigendian = magic & 0x0008U; - magic_signed = magic & 0x0010U; - magic_typedarray = magic & 0x0020U; - DUK_UNREF(magic_signed); - - h_this = duk__require_bufobj_this(thr); /* XXX: very inefficient for plain buffers */ - DUK_ASSERT(h_this != NULL); - buffer_length = h_this->length; - - /* [ value offset noAssert ], when ftype != DUK__FLD_VARINT */ - /* [ value offset fieldByteLength noAssert ], when ftype == DUK__FLD_VARINT */ - /* [ offset value littleEndian ], when DUK__FLD_TYPEDARRAY (regardless of ftype) */ - - /* Handle TypedArray vs. Node.js Buffer arg differences */ - if (magic_typedarray) { - no_assert = 0; -#if defined(DUK_USE_INTEGER_LE) - endswap = !duk_to_boolean(thr, 2); /* 1=little endian */ -#else - endswap = duk_to_boolean(thr, 2); /* 1=little endian */ -#endif - duk_swap(thr, 0, 1); /* offset/value order different from Node.js */ - } else { - no_assert = duk_to_boolean(thr, (magic_ftype == DUK__FLD_VARINT) ? 3 : 2); -#if defined(DUK_USE_INTEGER_LE) - endswap = magic_bigendian; -#else - endswap = !magic_bigendian; -#endif - } - - /* Offset is coerced first to signed integer range and then to unsigned. - * This ensures we can add a small byte length (1-8) to the offset in - * bound checks and not wrap. - */ - offset_signed = duk_to_int(thr, 1); - offset = (duk_uint_t) offset_signed; - - /* We need 'nbytes' even for a failed offset; return value must be - * (offset + nbytes) even when write fails due to invalid offset. - */ - if (magic_ftype != DUK__FLD_VARINT) { - DUK_ASSERT(magic_ftype < (duk_small_uint_t) (sizeof(duk__buffer_nbytes_from_fldtype) / sizeof(duk_uint8_t))); - nbytes = duk__buffer_nbytes_from_fldtype[magic_ftype]; - } else { - nbytes = duk_get_int(thr, 2); - if (nbytes < 1 || nbytes > 6) { - goto fail_field_length; - } - } - DUK_ASSERT(nbytes >= 1 && nbytes <= 8); - - /* Now we can check offset validity. */ - if (offset_signed < 0) { - goto fail_bounds; - } - - DUK_DDD(DUK_DDDPRINT("writefield, value=%!T, buffer_length=%ld, offset=%ld, no_assert=%d, " - "magic=%04x, magic_fieldtype=%d, magic_bigendian=%d, magic_signed=%d, " - "endswap=%u", - duk_get_tval(thr, 0), - (long) buffer_length, - (long) offset, - (int) no_assert, - (unsigned int) magic, - (int) magic_ftype, - (int) (magic_bigendian >> 3), - (int) (magic_signed >> 4), - (int) endswap)); - - /* Coerce value to a number before computing check_length, so that - * the field type specific coercion below can't have side effects - * that would invalidate check_length. - */ - duk_to_number(thr, 0); - - /* Update 'buffer_length' to be the effective, safe limit which - * takes into account the underlying buffer. This value will be - * potentially invalidated by any side effect. - */ - check_length = DUK_HBUFOBJ_CLAMP_BYTELENGTH(h_this, buffer_length); - DUK_DDD(DUK_DDDPRINT("buffer_length=%ld, check_length=%ld", (long) buffer_length, (long) check_length)); - - if (h_this->buf) { - buf = DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this); - } else { - /* Neutered. We could go into the switch-case safely with - * buf == NULL because check_length == 0. To avoid scanbuild - * warnings, fail directly instead. - */ - DUK_ASSERT(check_length == 0); - goto fail_neutered; - } - DUK_ASSERT(buf != NULL); - - switch (magic_ftype) { - case DUK__FLD_8BIT: { - if (offset + 1U > check_length) { - goto fail_bounds; - } - /* sign doesn't matter when writing */ - buf[offset] = (duk_uint8_t) duk_to_uint32(thr, 0); - break; - } - case DUK__FLD_16BIT: { - duk_uint16_t tmp; - if (offset + 2U > check_length) { - goto fail_bounds; - } - tmp = (duk_uint16_t) duk_to_uint32(thr, 0); - if (endswap) { - tmp = DUK_BSWAP16(tmp); - } - du.us[0] = tmp; - /* sign doesn't matter when writing */ - duk_memcpy((void *) (buf + offset), (const void *) du.uc, 2); - break; - } - case DUK__FLD_32BIT: { - duk_uint32_t tmp; - if (offset + 4U > check_length) { - goto fail_bounds; - } - tmp = (duk_uint32_t) duk_to_uint32(thr, 0); - if (endswap) { - tmp = DUK_BSWAP32(tmp); - } - du.ui[0] = tmp; - /* sign doesn't matter when writing */ - duk_memcpy((void *) (buf + offset), (const void *) du.uc, 4); - break; - } - case DUK__FLD_FLOAT: { - duk_uint32_t tmp; - if (offset + 4U > check_length) { - goto fail_bounds; - } - du.f[0] = (duk_float_t) duk_to_number(thr, 0); - if (endswap) { - tmp = du.ui[0]; - tmp = DUK_BSWAP32(tmp); - du.ui[0] = tmp; - } - /* sign doesn't matter when writing */ - duk_memcpy((void *) (buf + offset), (const void *) du.uc, 4); - break; - } - case DUK__FLD_DOUBLE: { - if (offset + 8U > check_length) { - goto fail_bounds; - } - du.d = (duk_double_t) duk_to_number(thr, 0); - if (endswap) { - DUK_DBLUNION_BSWAP64(&du); - } - /* sign doesn't matter when writing */ - duk_memcpy((void *) (buf + offset), (const void *) du.uc, 8); - break; - } - case DUK__FLD_VARINT: { - /* Node.js Buffer variable width integer field. We don't really - * care about speed here, so aim for shortest algorithm. - */ - duk_int_t field_bytelen; - duk_int_t i, i_step, i_end; -#if defined(DUK_USE_64BIT_OPS) - duk_int64_t tmp; -#else - duk_double_t tmp; -#endif - duk_uint8_t *p; - - field_bytelen = (duk_int_t) nbytes; - if (offset + (duk_uint_t) field_bytelen > check_length) { - goto fail_bounds; - } - - /* Slow writing of value using either 64-bit arithmetic - * or IEEE doubles if 64-bit types not available. There's - * no special sign handling when writing varints. - */ - - if (magic_bigendian) { - /* Write in big endian */ - i = field_bytelen; /* one i_step added at top of loop */ - i_step = -1; - i_end = 0; - } else { - /* Write in little endian */ - i = -1; /* one i_step added at top of loop */ - i_step = 1; - i_end = field_bytelen - 1; - } - - /* XXX: The duk_to_number() cast followed by integer coercion - * is platform specific so NaN, +/- Infinity, and out-of-bounds - * values result in platform specific output now. - * See: test-bi-nodejs-buffer-proto-varint-special.js - */ - -#if defined(DUK_USE_64BIT_OPS) - tmp = (duk_int64_t) duk_to_number(thr, 0); - p = (duk_uint8_t *) (buf + offset); - do { - i += i_step; - DUK_ASSERT(i >= 0 && i < field_bytelen); - p[i] = (duk_uint8_t) (tmp & 0xff); - tmp = tmp >> 8; /* unnecessary shift for last byte */ - } while (i != i_end); -#else - tmp = duk_to_number(thr, 0); - p = (duk_uint8_t *) (buf + offset); - do { - i += i_step; - tmp = DUK_FLOOR(tmp); - DUK_ASSERT(i >= 0 && i < field_bytelen); - p[i] = (duk_uint8_t) (DUK_FMOD(tmp, 256.0)); - tmp = tmp / 256.0; /* unnecessary div for last byte */ - } while (i != i_end); -#endif - break; - } - default: { /* should never happen but default here */ - goto fail_bounds; - } - } - - /* Node.js Buffer: return offset + #bytes written (i.e. next - * write offset). - */ - if (magic_typedarray) { - /* For TypedArrays 'undefined' return value is specified - * by ES2015 (matches V8). - */ - return 0; - } - duk_push_uint(thr, offset + (duk_uint_t) nbytes); - return 1; - -fail_neutered: -fail_field_length: -fail_bounds: - if (no_assert) { - /* Node.js return value for failed writes is offset + #bytes - * that would have been written. - */ - /* XXX: for negative input offsets, 'offset' will be a large - * positive value so the result here is confusing. - */ - if (magic_typedarray) { - return 0; - } - duk_push_uint(thr, offset + (duk_uint_t) nbytes); - return 1; - } - DUK_DCERROR_RANGE_INVALID_ARGS(thr); -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -/* - * Accessors for .buffer, .byteLength, .byteOffset - */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_LOCAL duk_hbufobj *duk__autospawn_arraybuffer(duk_hthread *thr, duk_hbuffer *h_buf) { - duk_hbufobj *h_res; - - h_res = duk_push_bufobj_raw(thr, - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_BUFOBJ | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER), - DUK_BIDX_ARRAYBUFFER_PROTOTYPE); - DUK_ASSERT(h_res != NULL); - DUK_UNREF(h_res); - - duk__set_bufobj_buffer(thr, h_res, h_buf); - DUK_HBUFOBJ_ASSERT_VALID(h_res); - DUK_ASSERT(h_res->buf_prop == NULL); - return h_res; -} - -DUK_INTERNAL duk_ret_t duk_bi_typedarray_buffer_getter(duk_hthread *thr) { - duk_hbufobj *h_bufobj; - - h_bufobj = (duk_hbufobj *) duk__getrequire_bufobj_this(thr, DUK__BUFOBJ_FLAG_THROW /*flags*/); - DUK_ASSERT(h_bufobj != NULL); - if (DUK_HEAPHDR_IS_BUFFER((duk_heaphdr *) h_bufobj)) { - DUK_DD(DUK_DDPRINT("autospawn ArrayBuffer for plain buffer")); - (void) duk__autospawn_arraybuffer(thr, (duk_hbuffer *) h_bufobj); - return 1; - } else { - if (h_bufobj->buf_prop == NULL && - DUK_HOBJECT_GET_CLASS_NUMBER((duk_hobject *) h_bufobj) != DUK_HOBJECT_CLASS_ARRAYBUFFER && - h_bufobj->buf != NULL) { - duk_hbufobj *h_arrbuf; - - DUK_DD(DUK_DDPRINT("autospawn ArrayBuffer for typed array or DataView")); - h_arrbuf = duk__autospawn_arraybuffer(thr, h_bufobj->buf); - - if (h_bufobj->buf_prop == NULL) { - /* Must recheck buf_prop, in case ArrayBuffer - * alloc had a side effect which already filled - * it! - */ - - /* Set ArrayBuffer's .byteOffset and .byteLength based - * on the view so that Arraybuffer[view.byteOffset] - * matches view[0]. - */ - h_arrbuf->offset = 0; - DUK_ASSERT(h_bufobj->offset + h_bufobj->length >= h_bufobj->offset); /* Wrap check on creation. */ - h_arrbuf->length = h_bufobj->offset + h_bufobj->length; - DUK_ASSERT(h_arrbuf->buf_prop == NULL); - - DUK_ASSERT(h_bufobj->buf_prop == NULL); - h_bufobj->buf_prop = (duk_hobject *) h_arrbuf; - DUK_HBUFOBJ_INCREF(thr, h_arrbuf); /* Now reachable and accounted for. */ - } - - /* Left on stack; pushed for the second time below (OK). */ - } - if (h_bufobj->buf_prop) { - duk_push_hobject(thr, h_bufobj->buf_prop); - return 1; - } - } - return 0; -} - -DUK_INTERNAL duk_ret_t duk_bi_typedarray_byteoffset_getter(duk_hthread *thr) { - duk_hbufobj *h_bufobj; - - h_bufobj = (duk_hbufobj *) duk__getrequire_bufobj_this(thr, DUK__BUFOBJ_FLAG_THROW /*flags*/); - DUK_ASSERT(h_bufobj != NULL); - if (DUK_HEAPHDR_IS_BUFFER((duk_heaphdr *) h_bufobj)) { - duk_push_uint(thr, 0); - } else { - /* If neutered must return 0; offset is zeroed during - * neutering. - */ - duk_push_uint(thr, h_bufobj->offset); - } - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_typedarray_bytelength_getter(duk_hthread *thr) { - duk_hbufobj *h_bufobj; - - h_bufobj = (duk_hbufobj *) duk__getrequire_bufobj_this(thr, DUK__BUFOBJ_FLAG_THROW /*flags*/); - DUK_ASSERT(h_bufobj != NULL); - if (DUK_HEAPHDR_IS_BUFFER((duk_heaphdr *) h_bufobj)) { - duk_hbuffer *h_buf; - - h_buf = (duk_hbuffer *) h_bufobj; - DUK_ASSERT(DUK_HBUFFER_GET_SIZE(h_buf) <= DUK_UINT_MAX); /* Buffer limits. */ - duk_push_uint(thr, (duk_uint_t) DUK_HBUFFER_GET_SIZE(h_buf)); - } else { - /* If neutered must return 0; length is zeroed during - * neutering. - */ - duk_push_uint(thr, h_bufobj->length); - } - return 1; -} -#else /* DUK_USE_BUFFEROBJECT_SUPPORT */ -/* No .buffer getter without ArrayBuffer support. */ -#if 0 -DUK_INTERNAL duk_ret_t duk_bi_typedarray_buffer_getter(duk_hthread *thr) { - return 0; -} -#endif - -DUK_INTERNAL duk_ret_t duk_bi_typedarray_byteoffset_getter(duk_hthread *thr) { - duk_push_uint(thr, 0); - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_typedarray_bytelength_getter(duk_hthread *thr) { - duk_hbuffer *h_buf; - - /* XXX: helper? */ - duk_push_this(thr); - h_buf = duk_require_hbuffer(thr, -1); - duk_push_uint(thr, DUK_HBUFFER_GET_SIZE(h_buf)); - return 1; -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -/* automatic undefs */ -#undef DUK__BUFOBJ_FLAG_PROMOTE -#undef DUK__BUFOBJ_FLAG_THROW -#undef DUK__FLD_16BIT -#undef DUK__FLD_32BIT -#undef DUK__FLD_8BIT -#undef DUK__FLD_BIGENDIAN -#undef DUK__FLD_DOUBLE -#undef DUK__FLD_FLOAT -#undef DUK__FLD_SIGNED -#undef DUK__FLD_TYPEDARRAY -#undef DUK__FLD_VARINT -/* - * CBOR bindings. - * - * http://cbor.io/ - * https://tools.ietf.org/html/rfc7049 - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_CBOR_SUPPORT) - -/* #define DUK_CBOR_STRESS */ - -/* Default behavior for encoding strings: use CBOR text string if string - * is UTF-8 compatible, otherwise use CBOR byte string. These defines - * can be used to force either type for all strings. Using text strings - * for non-UTF-8 data is technically invalid CBOR. - */ -/* #define DUK_CBOR_TEXT_STRINGS */ -/* #define DUK_CBOR_BYTE_STRINGS */ - -/* Misc. defines. */ -/* #define DUK_CBOR_PREFER_SIZE */ -/* #define DUK_CBOR_DOUBLE_AS_IS */ -/* #define DUK_CBOR_DECODE_FASTPATH */ - -typedef struct { - duk_hthread *thr; - duk_uint8_t *ptr; - duk_uint8_t *buf; - duk_uint8_t *buf_end; - duk_size_t len; - duk_idx_t idx_buf; - duk_uint_t recursion_depth; - duk_uint_t recursion_limit; -} duk_cbor_encode_context; - -typedef struct { - duk_hthread *thr; - const duk_uint8_t *buf; - duk_size_t off; - duk_size_t len; - duk_uint_t recursion_depth; - duk_uint_t recursion_limit; -} duk_cbor_decode_context; - -DUK_LOCAL void duk__cbor_encode_value(duk_cbor_encode_context *enc_ctx); -DUK_LOCAL void duk__cbor_decode_value(duk_cbor_decode_context *dec_ctx); - -/* - * Misc - */ - -DUK_LOCAL duk_uint32_t duk__cbor_double_to_uint32(double d) { - /* Out of range casts are undefined behavior, so caller must avoid. */ - DUK_ASSERT(d >= 0.0 && d <= 4294967295.0); - return (duk_uint32_t) d; -} - -/* - * Encoding - */ - -DUK_LOCAL void duk__cbor_encode_error(duk_cbor_encode_context *enc_ctx) { - (void) duk_type_error(enc_ctx->thr, "cbor encode error"); -} - -DUK_LOCAL void duk__cbor_encode_req_stack(duk_cbor_encode_context *enc_ctx) { - duk_require_stack(enc_ctx->thr, 4); -} - -DUK_LOCAL void duk__cbor_encode_objarr_entry(duk_cbor_encode_context *enc_ctx) { - duk_hthread *thr = enc_ctx->thr; - - /* Native stack check in object/array recursion. */ - duk_native_stack_check(thr); - - /* When working with deeply recursive structures, this is important - * to ensure there's no effective depth limit. - */ - duk__cbor_encode_req_stack(enc_ctx); - - DUK_ASSERT(enc_ctx->recursion_depth <= enc_ctx->recursion_limit); - if (enc_ctx->recursion_depth >= enc_ctx->recursion_limit) { - DUK_ERROR_RANGE(thr, DUK_STR_ENC_RECLIMIT); - DUK_WO_NORETURN(return;); - } - enc_ctx->recursion_depth++; -} - -DUK_LOCAL void duk__cbor_encode_objarr_exit(duk_cbor_encode_context *enc_ctx) { - DUK_ASSERT(enc_ctx->recursion_depth > 0); - enc_ctx->recursion_depth--; -} - -/* Check that a size_t is in uint32 range to avoid out-of-range casts. */ -DUK_LOCAL void duk__cbor_encode_sizet_uint32_check(duk_cbor_encode_context *enc_ctx, duk_size_t len) { - if (DUK_UNLIKELY(sizeof(duk_size_t) > sizeof(duk_uint32_t) && len > (duk_size_t) DUK_UINT32_MAX)) { - duk__cbor_encode_error(enc_ctx); - } -} - -DUK_LOCAL DUK_NOINLINE void duk__cbor_encode_ensure_slowpath(duk_cbor_encode_context *enc_ctx, duk_size_t len) { - duk_size_t oldlen; - duk_size_t minlen; - duk_size_t newlen; - duk_uint8_t *p_new; - duk_size_t old_data_len; - - DUK_ASSERT(enc_ctx->ptr >= enc_ctx->buf); - DUK_ASSERT(enc_ctx->buf_end >= enc_ctx->ptr); - DUK_ASSERT(enc_ctx->buf_end >= enc_ctx->buf); - - /* Overflow check. - * - * Limit example: 0xffffffffUL / 2U = 0x7fffffffUL, we reject >= 0x80000000UL. - */ - oldlen = enc_ctx->len; - minlen = oldlen + len; - if (DUK_UNLIKELY(oldlen > DUK_SIZE_MAX / 2U || minlen < oldlen)) { - duk__cbor_encode_error(enc_ctx); - } - -#if defined(DUK_CBOR_STRESS) - newlen = oldlen + 1U; -#else - newlen = oldlen * 2U; -#endif - DUK_ASSERT(newlen >= oldlen); - - if (minlen > newlen) { - newlen = minlen; - } - DUK_ASSERT(newlen >= oldlen); - DUK_ASSERT(newlen >= minlen); - DUK_ASSERT(newlen > 0U); - - DUK_DD(DUK_DDPRINT("cbor encode buffer resized to %ld", (long) newlen)); - - p_new = (duk_uint8_t *) duk_resize_buffer(enc_ctx->thr, enc_ctx->idx_buf, newlen); - DUK_ASSERT(p_new != NULL); - old_data_len = (duk_size_t) (enc_ctx->ptr - enc_ctx->buf); - enc_ctx->buf = p_new; - enc_ctx->buf_end = p_new + newlen; - enc_ctx->ptr = p_new + old_data_len; - enc_ctx->len = newlen; -} - -DUK_LOCAL DUK_INLINE void duk__cbor_encode_ensure(duk_cbor_encode_context *enc_ctx, duk_size_t len) { - if (DUK_LIKELY((duk_size_t) (enc_ctx->buf_end - enc_ctx->ptr) >= len)) { - return; - } - duk__cbor_encode_ensure_slowpath(enc_ctx, len); -} - -DUK_LOCAL duk_size_t duk__cbor_get_reserve(duk_cbor_encode_context *enc_ctx) { - DUK_ASSERT(enc_ctx->ptr >= enc_ctx->buf); - DUK_ASSERT(enc_ctx->ptr <= enc_ctx->buf_end); - return (duk_size_t) (enc_ctx->buf_end - enc_ctx->ptr); -} - -DUK_LOCAL void duk__cbor_encode_uint32(duk_cbor_encode_context *enc_ctx, duk_uint32_t u, duk_uint8_t base) { - duk_uint8_t *p; - - /* Caller must ensure space. */ - DUK_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 4); - - p = enc_ctx->ptr; - if (DUK_LIKELY(u <= 23U)) { - *p++ = (duk_uint8_t) (base + (duk_uint8_t) u); - } else if (u <= 0xffUL) { - *p++ = base + 0x18U; - *p++ = (duk_uint8_t) u; - } else if (u <= 0xffffUL) { - *p++ = base + 0x19U; - DUK_RAW_WRITEINC_U16_BE(p, (duk_uint16_t) u); - } else { - *p++ = base + 0x1aU; - DUK_RAW_WRITEINC_U32_BE(p, u); - } - enc_ctx->ptr = p; -} - -#if defined(DUK_CBOR_DOUBLE_AS_IS) -DUK_LOCAL void duk__cbor_encode_double(duk_cbor_encode_context *enc_ctx, double d) { - duk_uint8_t *p; - - /* Caller must ensure space. */ - DUK_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 8); - - p = enc_ctx->ptr; - *p++ = 0xfbU; - DUK_RAW_WRITEINC_DOUBLE_BE(p, d); - p += 8; - enc_ctx->ptr = p; -} -#else /* DUK_CBOR_DOUBLE_AS_IS */ -DUK_LOCAL void duk__cbor_encode_double_fp(duk_cbor_encode_context *enc_ctx, double d) { - duk_double_union u; - duk_uint16_t u16; - duk_int16_t expt; - duk_uint8_t *p; - - DUK_ASSERT(DUK_FPCLASSIFY(d) != DUK_FP_ZERO); - - /* Caller must ensure space. */ - DUK_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 8); - - /* Organize into little endian (no-op if platform is little endian). */ - u.d = d; - duk_dblunion_host_to_little(&u); - - /* Check if 'd' can represented as a normal half-float. - * Denormal half-floats could also be used, but that check - * isn't done now (denormal half-floats are decoded of course). - * So just check exponent range and that at most 10 significant - * bits (excluding implicit leading 1) are used in 'd'. - */ - u16 = (((duk_uint16_t) u.uc[7]) << 8) | ((duk_uint16_t) u.uc[6]); - expt = (duk_int16_t) ((u16 & 0x7ff0U) >> 4) - 1023; - - if (expt >= -14 && expt <= 15) { - /* Half-float normal exponents (excl. denormals). - * - * 7 6 5 4 3 2 1 0 (LE index) - * double: seeeeeee eeeemmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm - * half: seeeee mmmm mmmmmm00 00000000 00000000 00000000 00000000 00000000 - */ - duk_bool_t use_half_float; - - use_half_float = - (u.uc[0] == 0 && u.uc[1] == 0 && u.uc[2] == 0 && u.uc[3] == 0 && u.uc[4] == 0 && (u.uc[5] & 0x03U) == 0); - - if (use_half_float) { - duk_uint32_t t; - - expt += 15; - t = (duk_uint32_t) (u.uc[7] & 0x80U) << 8; - t += (duk_uint32_t) expt << 10; - t += ((duk_uint32_t) u.uc[6] & 0x0fU) << 6; - t += ((duk_uint32_t) u.uc[5]) >> 2; - - /* seeeeemm mmmmmmmm */ - p = enc_ctx->ptr; - *p++ = 0xf9U; - DUK_RAW_WRITEINC_U16_BE(p, (duk_uint16_t) t); - enc_ctx->ptr = p; - return; - } - } - - /* Same check for plain float. Also no denormal support here. */ - if (expt >= -126 && expt <= 127) { - /* Float normal exponents (excl. denormals). - * - * double: seeeeeee eeeemmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm - * float: seeee eeeemmmm mmmmmmmm mmmmmmmm mmm00000 00000000 00000000 00000000 - */ - duk_bool_t use_float; - duk_float_t d_float; - - /* We could do this explicit mantissa check, but doing - * a double-float-double cast is fine because we've - * already verified that the exponent is in range so - * that the narrower cast is not undefined behavior. - */ -#if 0 - use_float = - (u.uc[0] == 0 && u.uc[1] == 0 && u.uc[2] == 0 && (u.uc[3] & 0xe0U) == 0); -#endif - d_float = (duk_float_t) d; - use_float = duk_double_equals((duk_double_t) d_float, d); - if (use_float) { - p = enc_ctx->ptr; - *p++ = 0xfaU; - DUK_RAW_WRITEINC_FLOAT_BE(p, d_float); - enc_ctx->ptr = p; - return; - } - } - - /* Special handling for NaN and Inf which we want to encode as - * half-floats. They share the same (maximum) exponent. - */ - if (expt == 1024) { - DUK_ASSERT(DUK_ISNAN(d) || DUK_ISINF(d)); - p = enc_ctx->ptr; - *p++ = 0xf9U; - if (DUK_ISNAN(d)) { - /* Shortest NaN encoding is using a half-float. Lose the - * exact NaN bits in the process. IEEE double would be - * 7ff8 0000 0000 0000, i.e. a quiet NaN in most architectures - * (https://en.wikipedia.org/wiki/NaN#Encoding). The - * equivalent half float is 7e00. - */ - *p++ = 0x7eU; - } else { - /* Shortest +/- Infinity encoding is using a half-float. */ - if (DUK_SIGNBIT(d)) { - *p++ = 0xfcU; - } else { - *p++ = 0x7cU; - } - } - *p++ = 0x00U; - enc_ctx->ptr = p; - return; - } - - /* Cannot use half-float or float, encode as full IEEE double. */ - p = enc_ctx->ptr; - *p++ = 0xfbU; - DUK_RAW_WRITEINC_DOUBLE_BE(p, d); - enc_ctx->ptr = p; -} - -DUK_LOCAL void duk__cbor_encode_double(duk_cbor_encode_context *enc_ctx, double d) { - duk_uint8_t *p; - double d_floor; - - /* Integers and floating point values of all types are conceptually - * equivalent in CBOR. Try to always choose the shortest encoding - * which is not always immediately obvious. For example, NaN and Inf - * can be most compactly represented as a half-float (assuming NaN - * bits are not preserved), and 0x1'0000'0000 as a single precision - * float. Shortest forms in preference order (prefer integer over - * float when equal length): - * - * uint 1 byte [0,23] (not -0) - * sint 1 byte [-24,-1] - * uint+1 2 bytes [24,255] - * sint+1 2 bytes [-256,-25] - * uint+2 3 bytes [256,65535] - * sint+2 3 bytes [-65536,-257] - * half-float 3 bytes -0, NaN, +/- Infinity, range [-65504,65504] - * uint+4 5 bytes [65536,4294967295] - * sint+4 5 bytes [-4294967296,-258] - * float 5 bytes range [-(1 - 2^(-24)) * 2^128, (1 - 2^(-24)) * 2^128] - * uint+8 9 bytes [4294967296,18446744073709551615] - * sint+8 9 bytes [-18446744073709551616,-4294967297] - * double 9 bytes - * - * For whole numbers (compatible with integers): - * - 1-byte or 2-byte uint/sint representation is preferred for - * [-256,255]. - * - 3-byte uint/sint is preferred for [-65536,65535]. Half floats - * are never preferred because they have the same length. - * - 5-byte uint/sint is preferred for [-4294967296,4294967295]. - * Single precision floats are never preferred, and half-floats - * don't reach above the 3-byte uint/sint range so they're never - * preferred. - * - So, for all integers up to signed/unsigned 32-bit range the - * preferred encoding is always an integer uint/sint. - * - For integers above 32 bits the situation is more complicated. - * Half-floats are never useful for them because of their limited - * range, but IEEE single precision floats (5 bytes encoded) can - * represent some integers between the 32-bit and 64-bit ranges - * which require 9 bytes as a uint/sint. - * - * For floating point values not compatible with integers, the - * preferred encoding is quite clear: - * - For +Inf/-Inf use half-float. - * - For NaN use a half-float, assuming NaN bits ("payload") is - * not worth preserving. Duktape doesn't in general guarantee - * preservation of the NaN payload so using a half-float seems - * consistent with that. - * - For remaining values, prefer the shortest form which doesn't - * lose any precision. For normal half-floats and single precision - * floats this is simple: just check exponent and mantissa bits - * using a fixed mask. For denormal half-floats and single - * precision floats the check is a bit more complicated: a normal - * IEEE double can sometimes be represented as a denormal - * half-float or single precision float. - * - * https://en.wikipedia.org/wiki/Half-precision_floating-point_format#IEEE_754_half-precision_binary_floating-point_format:_binary16 - */ - - /* Caller must ensure space. */ - DUK_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 8); - - /* Most important path is integers. The floor() test will be true - * for Inf too (but not NaN). - */ - d_floor = DUK_FLOOR(d); /* identity if d is +/- 0.0, NaN, or +/- Infinity */ - if (DUK_LIKELY(duk_double_equals(d_floor, d) != 0)) { - DUK_ASSERT(!DUK_ISNAN(d)); /* NaN == NaN compares false. */ - if (DUK_SIGNBIT(d)) { - if (d >= -4294967296.0) { - d = -1.0 - d; - if (d >= 0.0) { - DUK_ASSERT(d >= 0.0); - duk__cbor_encode_uint32(enc_ctx, duk__cbor_double_to_uint32(d), 0x20U); - return; - } - - /* Input was negative zero, d == -1.0 < 0.0. - * Shortest -0 is using half-float. - */ - p = enc_ctx->ptr; - *p++ = 0xf9U; - *p++ = 0x80U; - *p++ = 0x00U; - enc_ctx->ptr = p; - return; - } - } else { - if (d <= 4294967295.0) { - /* Positive zero needs no special handling. */ - DUK_ASSERT(d >= 0.0); - duk__cbor_encode_uint32(enc_ctx, duk__cbor_double_to_uint32(d), 0x00U); - return; - } - } - } - - /* 64-bit integers are not supported at present. So - * we also don't need to deal with choosing between a - * 64-bit uint/sint representation vs. IEEE double or - * float. - */ - - DUK_ASSERT(DUK_FPCLASSIFY(d) != DUK_FP_ZERO); - duk__cbor_encode_double_fp(enc_ctx, d); -} -#endif /* DUK_CBOR_DOUBLE_AS_IS */ - -DUK_LOCAL void duk__cbor_encode_string_top(duk_cbor_encode_context *enc_ctx) { - const duk_uint8_t *str; - duk_size_t len; - duk_uint8_t *p; - - /* CBOR differentiates between UTF-8 text strings and byte strings. - * Text strings MUST be valid UTF-8, so not all Duktape strings can - * be encoded as valid CBOR text strings. Possible behaviors: - * - * 1. Use text string when input is valid UTF-8, otherwise use - * byte string (maybe tagged to indicate it was an extended - * UTF-8 string). - * 2. Always use text strings, but sanitize input string so that - * invalid UTF-8 is replaced with U+FFFD for example. Combine - * surrogates whenever possible. - * 3. Always use byte strings. This is simple and produces valid - * CBOR, but isn't ideal for interoperability. - * 4. Always use text strings, even for invalid UTF-8 such as - * codepoints in the surrogate pair range. This is simple but - * produces technically invalid CBOR for non-UTF-8 strings which - * may affect interoperability. - * - * Current default is 1; can be changed with defines. - */ - - /* Caller must ensure space. */ - DUK_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 8); - - str = (const duk_uint8_t *) duk_require_lstring(enc_ctx->thr, -1, &len); - if (duk_is_symbol(enc_ctx->thr, -1)) { - /* Symbols, encode as an empty table for now. This matches - * the behavior of cbor-js. - * - * XXX: Maybe encode String() coercion with a tag? - * XXX: Option to keep enough information to recover - * Symbols when decoding (this is not always desirable). - */ - p = enc_ctx->ptr; - *p++ = 0xa0U; - enc_ctx->ptr = p; - return; - } - - duk__cbor_encode_sizet_uint32_check(enc_ctx, len); -#if defined(DUK_CBOR_TEXT_STRINGS) - duk__cbor_encode_uint32(enc_ctx, (duk_uint32_t) len, 0x60U); -#elif defined(DUK_CBOR_BYTE_STRINGS) - duk__cbor_encode_uint32(enc_ctx, (duk_uint32_t) len, 0x40U); -#else - duk__cbor_encode_uint32(enc_ctx, - (duk_uint32_t) len, - (DUK_LIKELY(duk_unicode_is_utf8_compatible(str, len) != 0) ? 0x60U : 0x40U)); -#endif - duk__cbor_encode_ensure(enc_ctx, len); - p = enc_ctx->ptr; - duk_memcpy((void *) p, (const void *) str, len); - p += len; - enc_ctx->ptr = p; -} - -DUK_LOCAL void duk__cbor_encode_object(duk_cbor_encode_context *enc_ctx) { - duk_uint8_t *buf; - duk_size_t len; - duk_uint8_t *p; - duk_size_t i; - duk_size_t off_ib; - duk_uint32_t count; - - /* Caller must ensure space. */ - DUK_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 8); - - duk__cbor_encode_objarr_entry(enc_ctx); - - /* XXX: Support for specific built-ins like Date and RegExp. */ - if (duk_is_array(enc_ctx->thr, -1)) { - /* Shortest encoding for arrays >= 256 in length is actually - * the indefinite length one (3 or more bytes vs. 2 bytes). - * We still use the definite length version because it is - * more decoding friendly. - */ - len = duk_get_length(enc_ctx->thr, -1); - duk__cbor_encode_sizet_uint32_check(enc_ctx, len); - duk__cbor_encode_uint32(enc_ctx, (duk_uint32_t) len, 0x80U); - for (i = 0; i < len; i++) { - duk_get_prop_index(enc_ctx->thr, -1, (duk_uarridx_t) i); - duk__cbor_encode_value(enc_ctx); - } - } else if (duk_is_buffer_data(enc_ctx->thr, -1)) { - /* XXX: Tag buffer data? - * XXX: Encode typed arrays as integer arrays rather - * than buffer data as is? - */ - buf = (duk_uint8_t *) duk_require_buffer_data(enc_ctx->thr, -1, &len); - duk__cbor_encode_sizet_uint32_check(enc_ctx, len); - duk__cbor_encode_uint32(enc_ctx, (duk_uint32_t) len, 0x40U); - duk__cbor_encode_ensure(enc_ctx, len); - p = enc_ctx->ptr; - duk_memcpy_unsafe((void *) p, (const void *) buf, len); - p += len; - enc_ctx->ptr = p; - } else { - /* We don't know the number of properties in advance - * but would still like to encode at least small - * objects without indefinite length. Emit an - * indefinite length byte initially, and if the final - * property count is small enough to also fit in one - * byte, backpatch it later. Otherwise keep the - * indefinite length. This works well up to 23 - * properties which is practical and good enough. - */ - off_ib = (duk_size_t) (enc_ctx->ptr - enc_ctx->buf); /* XXX: get_offset? */ - count = 0U; - p = enc_ctx->ptr; - *p++ = 0xa0U + 0x1fU; /* indefinite length */ - enc_ctx->ptr = p; - duk_enum(enc_ctx->thr, -1, DUK_ENUM_OWN_PROPERTIES_ONLY); - while (duk_next(enc_ctx->thr, -1, 1 /*get_value*/)) { - duk_insert(enc_ctx->thr, -2); /* [ ... key value ] -> [ ... value key ] */ - duk__cbor_encode_value(enc_ctx); - duk__cbor_encode_value(enc_ctx); - count++; - if (count == 0U) { - duk__cbor_encode_error(enc_ctx); - } - } - duk_pop(enc_ctx->thr); - if (count <= 0x17U) { - DUK_ASSERT(off_ib < enc_ctx->len); - enc_ctx->buf[off_ib] = 0xa0U + (duk_uint8_t) count; - } else { - duk__cbor_encode_ensure(enc_ctx, 1); - p = enc_ctx->ptr; - *p++ = 0xffU; /* break */ - enc_ctx->ptr = p; - } - } - - duk__cbor_encode_objarr_exit(enc_ctx); -} - -DUK_LOCAL void duk__cbor_encode_buffer(duk_cbor_encode_context *enc_ctx) { - duk_uint8_t *buf; - duk_size_t len; - duk_uint8_t *p; - - /* Caller must ensure space. */ - DUK_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 8); - - /* Tag buffer data? */ - buf = (duk_uint8_t *) duk_require_buffer(enc_ctx->thr, -1, &len); - duk__cbor_encode_sizet_uint32_check(enc_ctx, len); - duk__cbor_encode_uint32(enc_ctx, (duk_uint32_t) len, 0x40U); - duk__cbor_encode_ensure(enc_ctx, len); - p = enc_ctx->ptr; - duk_memcpy_unsafe((void *) p, (const void *) buf, len); - p += len; - enc_ctx->ptr = p; -} - -DUK_LOCAL void duk__cbor_encode_pointer(duk_cbor_encode_context *enc_ctx) { - /* Pointers (void *) are challenging to encode. They can't - * be relied to be even 64-bit integer compatible (there are - * pointer models larger than that), nor can floats encode - * them. They could be encoded as strings (%p format) but - * that's not portable. They could be encoded as direct memory - * representations. Recovering pointers is non-portable in any - * case but it would be nice to be able to detect and recover - * compatible pointers. - * - * For now, encode as "(%p)" string, matching JX. There doesn't - * seem to be an appropriate tag, so pointers don't currently - * survive a CBOR encode/decode roundtrip intact. - */ - const char *ptr; - - ptr = duk_to_string(enc_ctx->thr, -1); - DUK_ASSERT(ptr != NULL); - duk_push_sprintf(enc_ctx->thr, "(%s)", ptr); - duk_remove(enc_ctx->thr, -2); - duk__cbor_encode_string_top(enc_ctx); -} - -DUK_LOCAL void duk__cbor_encode_lightfunc(duk_cbor_encode_context *enc_ctx) { - duk_uint8_t *p; - - /* Caller must ensure space. */ - DUK_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 8); - - /* For now encode as an empty object. */ - p = enc_ctx->ptr; - *p++ = 0xa0U; - enc_ctx->ptr = p; -} - -DUK_LOCAL void duk__cbor_encode_value(duk_cbor_encode_context *enc_ctx) { - duk_uint8_t *p; - - /* Encode/decode cycle currently loses some type information. - * This can be improved by registering custom tags with IANA. - */ - - /* Reserve space for up to 64-bit types (1 initial byte + 8 - * followup bytes). This allows encoding of integers, floats, - * string/buffer length fields, etc without separate checks - * in each code path. - */ - duk__cbor_encode_ensure(enc_ctx, 1 + 8); - - switch (duk_get_type(enc_ctx->thr, -1)) { - case DUK_TYPE_UNDEFINED: { - p = enc_ctx->ptr; - *p++ = 0xf7; - enc_ctx->ptr = p; - break; - } - case DUK_TYPE_NULL: { - p = enc_ctx->ptr; - *p++ = 0xf6; - enc_ctx->ptr = p; - break; - } - case DUK_TYPE_BOOLEAN: { - duk_uint8_t u8 = duk_get_boolean(enc_ctx->thr, -1) ? 0xf5U : 0xf4U; - p = enc_ctx->ptr; - *p++ = u8; - enc_ctx->ptr = p; - break; - } - case DUK_TYPE_NUMBER: { - duk__cbor_encode_double(enc_ctx, duk_get_number(enc_ctx->thr, -1)); - break; - } - case DUK_TYPE_STRING: { - duk__cbor_encode_string_top(enc_ctx); - break; - } - case DUK_TYPE_OBJECT: { - duk__cbor_encode_object(enc_ctx); - break; - } - case DUK_TYPE_BUFFER: { - duk__cbor_encode_buffer(enc_ctx); - break; - } - case DUK_TYPE_POINTER: { - duk__cbor_encode_pointer(enc_ctx); - break; - } - case DUK_TYPE_LIGHTFUNC: { - duk__cbor_encode_lightfunc(enc_ctx); - break; - } - case DUK_TYPE_NONE: - default: - goto fail; - } - - duk_pop(enc_ctx->thr); - return; - -fail: - duk__cbor_encode_error(enc_ctx); -} - -/* - * Decoding - */ - -DUK_LOCAL void duk__cbor_decode_error(duk_cbor_decode_context *dec_ctx) { - (void) duk_type_error(dec_ctx->thr, "cbor decode error"); -} - -DUK_LOCAL void duk__cbor_decode_req_stack(duk_cbor_decode_context *dec_ctx) { - duk_require_stack(dec_ctx->thr, 4); -} - -DUK_LOCAL void duk__cbor_decode_objarr_entry(duk_cbor_decode_context *dec_ctx) { - duk_hthread *thr = dec_ctx->thr; - - /* Native stack check in object/array recursion. */ - duk_native_stack_check(thr); - - duk__cbor_decode_req_stack(dec_ctx); - - DUK_ASSERT(dec_ctx->recursion_depth <= dec_ctx->recursion_limit); - if (dec_ctx->recursion_depth >= dec_ctx->recursion_limit) { - DUK_ERROR_RANGE(thr, DUK_STR_DEC_RECLIMIT); - DUK_WO_NORETURN(return;); - } - dec_ctx->recursion_depth++; -} - -DUK_LOCAL void duk__cbor_decode_objarr_exit(duk_cbor_decode_context *dec_ctx) { - DUK_ASSERT(dec_ctx->recursion_depth > 0); - dec_ctx->recursion_depth--; -} - -DUK_LOCAL duk_uint8_t duk__cbor_decode_readbyte(duk_cbor_decode_context *dec_ctx) { - DUK_ASSERT(dec_ctx->off <= dec_ctx->len); - if (DUK_UNLIKELY(dec_ctx->len - dec_ctx->off < 1U)) { - duk__cbor_decode_error(dec_ctx); - } - return dec_ctx->buf[dec_ctx->off++]; -} - -DUK_LOCAL duk_uint16_t duk__cbor_decode_read_u16(duk_cbor_decode_context *dec_ctx) { - duk_uint16_t res; - - DUK_ASSERT(dec_ctx->off <= dec_ctx->len); - if (DUK_UNLIKELY(dec_ctx->len - dec_ctx->off < 2U)) { - duk__cbor_decode_error(dec_ctx); - } - res = DUK_RAW_READ_U16_BE(dec_ctx->buf + dec_ctx->off); - dec_ctx->off += 2; - return res; -} - -DUK_LOCAL duk_uint32_t duk__cbor_decode_read_u32(duk_cbor_decode_context *dec_ctx) { - duk_uint32_t res; - - DUK_ASSERT(dec_ctx->off <= dec_ctx->len); - if (DUK_UNLIKELY(dec_ctx->len - dec_ctx->off < 4U)) { - duk__cbor_decode_error(dec_ctx); - } - res = DUK_RAW_READ_U32_BE(dec_ctx->buf + dec_ctx->off); - dec_ctx->off += 4; - return res; -} - -DUK_LOCAL duk_uint8_t duk__cbor_decode_peekbyte(duk_cbor_decode_context *dec_ctx) { - if (DUK_UNLIKELY(dec_ctx->off >= dec_ctx->len)) { - duk__cbor_decode_error(dec_ctx); - } - return dec_ctx->buf[dec_ctx->off]; -} - -DUK_LOCAL void duk__cbor_decode_rewind(duk_cbor_decode_context *dec_ctx, duk_size_t len) { - DUK_ASSERT(len <= dec_ctx->off); /* Caller must ensure. */ - dec_ctx->off -= len; -} - -#if 0 -DUK_LOCAL void duk__cbor_decode_ensure(duk_cbor_decode_context *dec_ctx, duk_size_t len) { - if (dec_ctx->off + len > dec_ctx->len) { - duk__cbor_decode_error(dec_ctx); - } -} -#endif - -DUK_LOCAL const duk_uint8_t *duk__cbor_decode_consume(duk_cbor_decode_context *dec_ctx, duk_size_t len) { - DUK_ASSERT(dec_ctx->off <= dec_ctx->len); - if (DUK_LIKELY(dec_ctx->len - dec_ctx->off >= len)) { - const duk_uint8_t *res = dec_ctx->buf + dec_ctx->off; - dec_ctx->off += len; - return res; - } - - duk__cbor_decode_error(dec_ctx); /* Not enough input. */ - return NULL; -} - -DUK_LOCAL int duk__cbor_decode_checkbreak(duk_cbor_decode_context *dec_ctx) { - if (duk__cbor_decode_peekbyte(dec_ctx) == 0xffU) { - DUK_ASSERT(dec_ctx->off < dec_ctx->len); - dec_ctx->off++; -#if 0 - (void) duk__cbor_decode_readbyte(dec_ctx); -#endif - return 1; - } - return 0; -} - -DUK_LOCAL void duk__cbor_decode_push_aival_int(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib, duk_bool_t negative) { - duk_uint8_t ai; - duk_uint32_t t, t1, t2; -#if 0 - duk_uint64_t t3; -#endif - duk_double_t d1, d2; - duk_double_t d; - - ai = ib & 0x1fU; - if (ai <= 0x17U) { - t = ai; - goto shared_exit; - } - - switch (ai) { - case 0x18U: /* 1 byte */ - t = (duk_uint32_t) duk__cbor_decode_readbyte(dec_ctx); - goto shared_exit; - case 0x19U: /* 2 byte */ - t = (duk_uint32_t) duk__cbor_decode_read_u16(dec_ctx); - goto shared_exit; - case 0x1aU: /* 4 byte */ - t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx); - goto shared_exit; - case 0x1bU: /* 8 byte */ - /* For uint64 it's important to handle the -1.0 part before - * casting to double: otherwise the adjustment might be lost - * in the cast. Uses: -1.0 - d <=> -(d + 1.0). - */ - t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx); - t2 = t; - t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx); - t1 = t; -#if 0 - t3 = (duk_uint64_t) t2 * DUK_U64_CONSTANT(0x100000000) + (duk_uint64_t) t1; - if (negative) { - if (t3 == DUK_UINT64_MAX) { - /* -(0xffff'ffff'ffff'ffffULL + 1) = - * -0x1'0000'0000'0000'0000 - * - * >>> -0x10000000000000000 - * -18446744073709551616L - */ - return -18446744073709551616.0; - } else { - return -((duk_double_t) (t3 + DUK_U64_CONSTANT(1))); - } - } else { - return (duk_double_t) t3; /* XXX: cast helper */ - } -#endif -#if 0 - t3 = (duk_uint64_t) t2 * DUK_U64_CONSTANT(0x100000000) + (duk_uint64_t) t1; - if (negative) { - /* Simpler version: take advantage of the fact that - * 0xffff'ffff'ffff'ffff and 0x1'0000'0000'0000'0000 - * both round to 0x1'0000'0000'0000'0000: - * > (0xffffffffffffffff).toString(16) - * '10000000000000000' - * > (0x10000000000000000).toString(16) - * '10000000000000000' - * - * For the DUK_UINT64_MAX case we just skip the +1 - * increment to avoid wrapping; the result still - * comes out right for an IEEE double cast. - */ - if (t3 != DUK_UINT64_MAX) { - t3++; - } - return -((duk_double_t) t3); - } else { - return (duk_double_t) t3; /* XXX: cast helper */ - } -#endif -#if 1 - /* Use two double parts, avoids dependency on 64-bit type. - * Avoid precision loss carefully, especially when dealing - * with the required +1 for negative values. - * - * No fastint check for this path at present. - */ - d1 = (duk_double_t) t1; /* XXX: cast helpers */ - d2 = (duk_double_t) t2 * 4294967296.0; - if (negative) { - d1 += 1.0; - } - d = d2 + d1; - if (negative) { - d = -d; - } -#endif - /* XXX: a push and check for fastint API would be nice */ - duk_push_number(dec_ctx->thr, d); - return; - } - - duk__cbor_decode_error(dec_ctx); - return; - -shared_exit: - if (negative) { - /* XXX: a push and check for fastint API would be nice */ - if ((duk_uint_t) t <= (duk_uint_t) - (DUK_INT_MIN + 1)) { - duk_push_int(dec_ctx->thr, -1 - ((duk_int_t) t)); - } else { - duk_push_number(dec_ctx->thr, -1.0 - (duk_double_t) t); - } - } else { - duk_push_uint(dec_ctx->thr, (duk_uint_t) t); - } -} - -DUK_LOCAL void duk__cbor_decode_skip_aival_int(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib) { - const duk_int8_t skips[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 8, -1, -1, -1, -1 }; - duk_uint8_t ai; - duk_int8_t skip; - - ai = ib & 0x1fU; - skip = skips[ai]; - if (DUK_UNLIKELY(skip < 0)) { - duk__cbor_decode_error(dec_ctx); - } - duk__cbor_decode_consume(dec_ctx, (duk_size_t) skip); - return; -} - -DUK_LOCAL duk_uint32_t duk__cbor_decode_aival_uint32(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib) { - duk_uint8_t ai; - duk_uint32_t t; - - ai = ib & 0x1fU; - if (ai <= 0x17U) { - return (duk_uint32_t) ai; - } - - switch (ai) { - case 0x18U: /* 1 byte */ - t = (duk_uint32_t) duk__cbor_decode_readbyte(dec_ctx); - return t; - case 0x19U: /* 2 byte */ - t = (duk_uint32_t) duk__cbor_decode_read_u16(dec_ctx); - return t; - case 0x1aU: /* 4 byte */ - t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx); - return t; - case 0x1bU: /* 8 byte */ - t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx); - if (t != 0U) { - break; - } - t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx); - return t; - } - - duk__cbor_decode_error(dec_ctx); - return 0U; -} - -DUK_LOCAL void duk__cbor_decode_buffer(duk_cbor_decode_context *dec_ctx, duk_uint8_t expected_base) { - duk_uint32_t len; - duk_uint8_t *buf; - const duk_uint8_t *inp; - duk_uint8_t ib; - - ib = duk__cbor_decode_readbyte(dec_ctx); - if ((ib & 0xe0U) != expected_base) { - duk__cbor_decode_error(dec_ctx); - } - /* Indefinite format is rejected by the following on purpose. */ - len = duk__cbor_decode_aival_uint32(dec_ctx, ib); - inp = duk__cbor_decode_consume(dec_ctx, len); - /* XXX: duk_push_fixed_buffer_with_data() would be a nice API addition. */ - buf = (duk_uint8_t *) duk_push_fixed_buffer(dec_ctx->thr, (duk_size_t) len); - duk_memcpy((void *) buf, (const void *) inp, (size_t) len); -} - -DUK_LOCAL void duk__cbor_decode_join_buffers(duk_cbor_decode_context *dec_ctx, duk_idx_t count) { - duk_size_t total_size = 0; - duk_idx_t top = duk_get_top(dec_ctx->thr); - duk_idx_t base = top - count; /* count is >= 1 */ - duk_idx_t idx; - duk_uint8_t *p = NULL; - - DUK_ASSERT(count >= 1); - DUK_ASSERT(top >= count); - - for (;;) { - /* First round: compute total size. - * Second round: copy into place. - */ - for (idx = base; idx < top; idx++) { - duk_uint8_t *buf_data; - duk_size_t buf_size; - - buf_data = (duk_uint8_t *) duk_require_buffer(dec_ctx->thr, idx, &buf_size); - if (p != NULL) { - duk_memcpy_unsafe((void *) p, (const void *) buf_data, buf_size); - p += buf_size; - } else { - total_size += buf_size; - if (DUK_UNLIKELY(total_size < buf_size)) { /* Wrap check. */ - duk__cbor_decode_error(dec_ctx); - } - } - } - - if (p != NULL) { - break; - } else { - p = (duk_uint8_t *) duk_push_fixed_buffer(dec_ctx->thr, total_size); - DUK_ASSERT(p != NULL); - } - } - - duk_replace(dec_ctx->thr, base); - duk_pop_n(dec_ctx->thr, count - 1); -} - -DUK_LOCAL void duk__cbor_decode_and_join_strbuf(duk_cbor_decode_context *dec_ctx, duk_uint8_t expected_base) { - duk_idx_t count = 0; - for (;;) { - if (duk__cbor_decode_checkbreak(dec_ctx)) { - break; - } - duk_require_stack(dec_ctx->thr, 1); - duk__cbor_decode_buffer(dec_ctx, expected_base); - count++; - if (DUK_UNLIKELY(count <= 0)) { /* Wrap check. */ - duk__cbor_decode_error(dec_ctx); - } - } - if (count == 0) { - (void) duk_push_fixed_buffer(dec_ctx->thr, 0); - } else if (count > 1) { - duk__cbor_decode_join_buffers(dec_ctx, count); - } -} - -DUK_LOCAL duk_double_t duk__cbor_decode_half_float(duk_cbor_decode_context *dec_ctx) { - duk_double_union u; - const duk_uint8_t *inp; - duk_int_t expt; - duk_uint_t u16; - duk_uint_t tmp; - duk_double_t res; - - inp = duk__cbor_decode_consume(dec_ctx, 2); - u16 = ((duk_uint_t) inp[0] << 8) + (duk_uint_t) inp[1]; - expt = (duk_int_t) ((u16 >> 10) & 0x1fU) - 15; - - /* Reconstruct IEEE double into little endian order first, then convert - * to host order. - */ - - duk_memzero((void *) &u, sizeof(u)); - - if (expt == -15) { - /* Zero or denormal; but note that half float - * denormals become double normals. - */ - if ((u16 & 0x03ffU) == 0) { - u.uc[7] = inp[0] & 0x80U; - } else { - /* Create denormal by first creating a double that - * contains the denormal bits and a leading implicit - * 1-bit. Then subtract away the implicit 1-bit. - * - * 0.mmmmmmmmmm * 2^-14 - * 1.mmmmmmmmmm 0.... * 2^-14 - * -1.0000000000 0.... * 2^-14 - * - * Double exponent: -14 + 1023 = 0x3f1 - */ - u.uc[7] = 0x3fU; - u.uc[6] = 0x10U + (duk_uint8_t) ((u16 >> 6) & 0x0fU); - u.uc[5] = (duk_uint8_t) ((u16 << 2) & 0xffU); /* Mask is really 0xfcU */ - - duk_dblunion_little_to_host(&u); - res = u.d - 0.00006103515625; /* 2^(-14) */ - if (u16 & 0x8000U) { - res = -res; - } - return res; - } - } else if (expt == 16) { - /* +/- Inf or NaN. */ - if ((u16 & 0x03ffU) == 0) { - u.uc[7] = (inp[0] & 0x80U) + 0x7fU; - u.uc[6] = 0xf0U; - } else { - /* Create a 'quiet NaN' with highest - * bit set (there are some platforms - * where the NaN payload convention is - * the opposite). Keep sign. - */ - u.uc[7] = (inp[0] & 0x80U) + 0x7fU; - u.uc[6] = 0xf8U; - } - } else { - /* Normal. */ - tmp = (inp[0] & 0x80U) ? 0x80000000UL : 0UL; - tmp += (duk_uint_t) (expt + 1023) << 20; - tmp += (duk_uint_t) (inp[0] & 0x03U) << 18; - tmp += (duk_uint_t) (inp[1] & 0xffU) << 10; - u.uc[7] = (tmp >> 24) & 0xffU; - u.uc[6] = (tmp >> 16) & 0xffU; - u.uc[5] = (tmp >> 8) & 0xffU; - u.uc[4] = (tmp >> 0) & 0xffU; - } - - duk_dblunion_little_to_host(&u); - return u.d; -} - -DUK_LOCAL void duk__cbor_decode_string(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib, duk_uint8_t ai) { - /* If the CBOR string data is not valid UTF-8 it is technically - * invalid CBOR. Possible behaviors at least: - * - * 1. Reject the input, i.e. throw TypeError. - * - * 2. Accept the input, but sanitize non-UTF-8 data into UTF-8 - * using U+FFFD replacements. Also it might make sense to - * decode non-BMP codepoints into surrogates for better - * ECMAScript compatibility. - * - * 3. Accept the input as a Duktape string (which are not always - * valid UTF-8), but reject any input that would create a - * Symbol representation. - * - * Current behavior is 3. - */ - - if (ai == 0x1fU) { - duk_uint8_t *buf_data; - duk_size_t buf_size; - - duk__cbor_decode_and_join_strbuf(dec_ctx, 0x60U); - buf_data = (duk_uint8_t *) duk_require_buffer(dec_ctx->thr, -1, &buf_size); - (void) duk_push_lstring(dec_ctx->thr, (const char *) buf_data, buf_size); - duk_remove(dec_ctx->thr, -2); - } else { - duk_uint32_t len; - const duk_uint8_t *inp; - - len = duk__cbor_decode_aival_uint32(dec_ctx, ib); - inp = duk__cbor_decode_consume(dec_ctx, len); - (void) duk_push_lstring(dec_ctx->thr, (const char *) inp, (duk_size_t) len); - } - if (duk_is_symbol(dec_ctx->thr, -1)) { - /* Refuse to create Symbols when decoding. */ - duk__cbor_decode_error(dec_ctx); - } - - /* XXX: Here a Duktape API call to convert input -> utf-8 with - * replacements would be nice. - */ -} - -DUK_LOCAL duk_bool_t duk__cbor_decode_array(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib, duk_uint8_t ai) { - duk_uint32_t idx, len; - - duk__cbor_decode_objarr_entry(dec_ctx); - - /* Support arrays up to 0xfffffffeU in length. 0xffffffff is - * used as an indefinite length marker. - */ - if (ai == 0x1fU) { - len = 0xffffffffUL; - } else { - len = duk__cbor_decode_aival_uint32(dec_ctx, ib); - if (len == 0xffffffffUL) { - goto failure; - } - } - - /* XXX: use bare array? */ - duk_push_array(dec_ctx->thr); - for (idx = 0U;;) { - if (len == 0xffffffffUL && duk__cbor_decode_checkbreak(dec_ctx)) { - break; - } - if (idx == len) { - if (ai == 0x1fU) { - goto failure; - } - break; - } - duk__cbor_decode_value(dec_ctx); - duk_put_prop_index(dec_ctx->thr, -2, (duk_uarridx_t) idx); - idx++; - if (idx == 0U) { - goto failure; /* wrapped */ - } - } - -#if 0 - success: -#endif - duk__cbor_decode_objarr_exit(dec_ctx); - return 1; - -failure: - /* No need to unwind recursion checks, caller will throw. */ - return 0; -} - -DUK_LOCAL duk_bool_t duk__cbor_decode_map(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib, duk_uint8_t ai) { - duk_uint32_t count; - - duk__cbor_decode_objarr_entry(dec_ctx); - - if (ai == 0x1fU) { - count = 0xffffffffUL; - } else { - count = duk__cbor_decode_aival_uint32(dec_ctx, ib); - if (count == 0xffffffffUL) { - goto failure; - } - } - - /* XXX: use bare object? */ - duk_push_object(dec_ctx->thr); - for (;;) { - if (count == 0xffffffffUL) { - if (duk__cbor_decode_checkbreak(dec_ctx)) { - break; - } - } else { - if (count == 0UL) { - break; - } - count--; - } - - /* Non-string keys are coerced to strings, - * possibly leading to overwriting previous - * keys. Last key of a certain coerced name - * wins. If key is an object, it will coerce - * to '[object Object]' which is consistent - * but potentially misleading. One alternative - * would be to skip non-string keys. - */ - duk__cbor_decode_value(dec_ctx); - duk__cbor_decode_value(dec_ctx); - duk_put_prop(dec_ctx->thr, -3); - } - -#if 0 - success: -#endif - duk__cbor_decode_objarr_exit(dec_ctx); - return 1; - -failure: - /* No need to unwind recursion checks, caller will throw. */ - return 0; -} - -DUK_LOCAL duk_double_t duk__cbor_decode_float(duk_cbor_decode_context *dec_ctx) { - duk_float_union u; - const duk_uint8_t *inp; - inp = duk__cbor_decode_consume(dec_ctx, 4); - duk_memcpy((void *) u.uc, (const void *) inp, 4); - duk_fltunion_big_to_host(&u); - return (duk_double_t) u.f; -} - -DUK_LOCAL duk_double_t duk__cbor_decode_double(duk_cbor_decode_context *dec_ctx) { - duk_double_union u; - const duk_uint8_t *inp; - inp = duk__cbor_decode_consume(dec_ctx, 8); - duk_memcpy((void *) u.uc, (const void *) inp, 8); - duk_dblunion_big_to_host(&u); - return u.d; -} - -#if defined(DUK_CBOR_DECODE_FASTPATH) -#define DUK__CBOR_AI (ib & 0x1fU) - -DUK_LOCAL void duk__cbor_decode_value(duk_cbor_decode_context *dec_ctx) { - duk_uint8_t ib; - - /* Any paths potentially recursing back to duk__cbor_decode_value() - * must perform a Duktape value stack growth check. Avoid the check - * here for simple paths like primitive values. - */ - -reread_initial_byte: - DUK_DDD(DUK_DDDPRINT("cbor decode off=%ld len=%ld", (long) dec_ctx->off, (long) dec_ctx->len)); - - ib = duk__cbor_decode_readbyte(dec_ctx); - - /* Full initial byte switch, footprint cost over baseline is ~+1kB. */ - /* XXX: Force full switch with no range check. */ - - switch (ib) { - case 0x00U: - case 0x01U: - case 0x02U: - case 0x03U: - case 0x04U: - case 0x05U: - case 0x06U: - case 0x07U: - case 0x08U: - case 0x09U: - case 0x0aU: - case 0x0bU: - case 0x0cU: - case 0x0dU: - case 0x0eU: - case 0x0fU: - case 0x10U: - case 0x11U: - case 0x12U: - case 0x13U: - case 0x14U: - case 0x15U: - case 0x16U: - case 0x17U: - duk_push_uint(dec_ctx->thr, ib); - break; - case 0x18U: - case 0x19U: - case 0x1aU: - case 0x1bU: - duk__cbor_decode_push_aival_int(dec_ctx, ib, 0 /*negative*/); - break; - case 0x1cU: - case 0x1dU: - case 0x1eU: - case 0x1fU: - goto format_error; - case 0x20U: - case 0x21U: - case 0x22U: - case 0x23U: - case 0x24U: - case 0x25U: - case 0x26U: - case 0x27U: - case 0x28U: - case 0x29U: - case 0x2aU: - case 0x2bU: - case 0x2cU: - case 0x2dU: - case 0x2eU: - case 0x2fU: - case 0x30U: - case 0x31U: - case 0x32U: - case 0x33U: - case 0x34U: - case 0x35U: - case 0x36U: - case 0x37U: - duk_push_int(dec_ctx->thr, -((duk_int_t) ((ib - 0x20U) + 1U))); - break; - case 0x38U: - case 0x39U: - case 0x3aU: - case 0x3bU: - duk__cbor_decode_push_aival_int(dec_ctx, ib, 1 /*negative*/); - break; - case 0x3cU: - case 0x3dU: - case 0x3eU: - case 0x3fU: - goto format_error; - case 0x40U: - case 0x41U: - case 0x42U: - case 0x43U: - case 0x44U: - case 0x45U: - case 0x46U: - case 0x47U: - case 0x48U: - case 0x49U: - case 0x4aU: - case 0x4bU: - case 0x4cU: - case 0x4dU: - case 0x4eU: - case 0x4fU: - case 0x50U: - case 0x51U: - case 0x52U: - case 0x53U: - case 0x54U: - case 0x55U: - case 0x56U: - case 0x57U: - /* XXX: Avoid rewind, we know the length already. */ - DUK_ASSERT(dec_ctx->off > 0U); - dec_ctx->off--; - duk__cbor_decode_buffer(dec_ctx, 0x40U); - break; - case 0x58U: - case 0x59U: - case 0x5aU: - case 0x5bU: - /* XXX: Avoid rewind, decode length inline. */ - DUK_ASSERT(dec_ctx->off > 0U); - dec_ctx->off--; - duk__cbor_decode_buffer(dec_ctx, 0x40U); - break; - case 0x5cU: - case 0x5dU: - case 0x5eU: - goto format_error; - case 0x5fU: - duk__cbor_decode_and_join_strbuf(dec_ctx, 0x40U); - break; - case 0x60U: - case 0x61U: - case 0x62U: - case 0x63U: - case 0x64U: - case 0x65U: - case 0x66U: - case 0x67U: - case 0x68U: - case 0x69U: - case 0x6aU: - case 0x6bU: - case 0x6cU: - case 0x6dU: - case 0x6eU: - case 0x6fU: - case 0x70U: - case 0x71U: - case 0x72U: - case 0x73U: - case 0x74U: - case 0x75U: - case 0x76U: - case 0x77U: - /* XXX: Avoid double decode of length. */ - duk__cbor_decode_string(dec_ctx, ib, DUK__CBOR_AI); - break; - case 0x78U: - case 0x79U: - case 0x7aU: - case 0x7bU: - /* XXX: Avoid double decode of length. */ - duk__cbor_decode_string(dec_ctx, ib, DUK__CBOR_AI); - break; - case 0x7cU: - case 0x7dU: - case 0x7eU: - goto format_error; - case 0x7fU: - duk__cbor_decode_string(dec_ctx, ib, DUK__CBOR_AI); - break; - case 0x80U: - case 0x81U: - case 0x82U: - case 0x83U: - case 0x84U: - case 0x85U: - case 0x86U: - case 0x87U: - case 0x88U: - case 0x89U: - case 0x8aU: - case 0x8bU: - case 0x8cU: - case 0x8dU: - case 0x8eU: - case 0x8fU: - case 0x90U: - case 0x91U: - case 0x92U: - case 0x93U: - case 0x94U: - case 0x95U: - case 0x96U: - case 0x97U: - if (DUK_UNLIKELY(duk__cbor_decode_array(dec_ctx, ib, DUK__CBOR_AI) == 0)) { - goto format_error; - } - break; - case 0x98U: - case 0x99U: - case 0x9aU: - case 0x9bU: - if (DUK_UNLIKELY(duk__cbor_decode_array(dec_ctx, ib, DUK__CBOR_AI) == 0)) { - goto format_error; - } - break; - case 0x9cU: - case 0x9dU: - case 0x9eU: - goto format_error; - case 0x9fU: - if (DUK_UNLIKELY(duk__cbor_decode_array(dec_ctx, ib, DUK__CBOR_AI) == 0)) { - goto format_error; - } - break; - case 0xa0U: - case 0xa1U: - case 0xa2U: - case 0xa3U: - case 0xa4U: - case 0xa5U: - case 0xa6U: - case 0xa7U: - case 0xa8U: - case 0xa9U: - case 0xaaU: - case 0xabU: - case 0xacU: - case 0xadU: - case 0xaeU: - case 0xafU: - case 0xb0U: - case 0xb1U: - case 0xb2U: - case 0xb3U: - case 0xb4U: - case 0xb5U: - case 0xb6U: - case 0xb7U: - if (DUK_UNLIKELY(duk__cbor_decode_map(dec_ctx, ib, DUK__CBOR_AI) == 0)) { - goto format_error; - } - break; - case 0xb8U: - case 0xb9U: - case 0xbaU: - case 0xbbU: - if (DUK_UNLIKELY(duk__cbor_decode_map(dec_ctx, ib, DUK__CBOR_AI) == 0)) { - goto format_error; - } - break; - case 0xbcU: - case 0xbdU: - case 0xbeU: - goto format_error; - case 0xbfU: - if (DUK_UNLIKELY(duk__cbor_decode_map(dec_ctx, ib, DUK__CBOR_AI) == 0)) { - goto format_error; - } - break; - case 0xc0U: - case 0xc1U: - case 0xc2U: - case 0xc3U: - case 0xc4U: - case 0xc5U: - case 0xc6U: - case 0xc7U: - case 0xc8U: - case 0xc9U: - case 0xcaU: - case 0xcbU: - case 0xccU: - case 0xcdU: - case 0xceU: - case 0xcfU: - case 0xd0U: - case 0xd1U: - case 0xd2U: - case 0xd3U: - case 0xd4U: - case 0xd5U: - case 0xd6U: - case 0xd7U: - /* Tag 0-23: drop. */ - goto reread_initial_byte; - case 0xd8U: - case 0xd9U: - case 0xdaU: - case 0xdbU: - duk__cbor_decode_skip_aival_int(dec_ctx, ib); - goto reread_initial_byte; - case 0xdcU: - case 0xddU: - case 0xdeU: - case 0xdfU: - goto format_error; - case 0xe0U: - goto format_error; - case 0xe1U: - goto format_error; - case 0xe2U: - goto format_error; - case 0xe3U: - goto format_error; - case 0xe4U: - goto format_error; - case 0xe5U: - goto format_error; - case 0xe6U: - goto format_error; - case 0xe7U: - goto format_error; - case 0xe8U: - goto format_error; - case 0xe9U: - goto format_error; - case 0xeaU: - goto format_error; - case 0xebU: - goto format_error; - case 0xecU: - goto format_error; - case 0xedU: - goto format_error; - case 0xeeU: - goto format_error; - case 0xefU: - goto format_error; - case 0xf0U: - goto format_error; - case 0xf1U: - goto format_error; - case 0xf2U: - goto format_error; - case 0xf3U: - goto format_error; - case 0xf4U: - duk_push_false(dec_ctx->thr); - break; - case 0xf5U: - duk_push_true(dec_ctx->thr); - break; - case 0xf6U: - duk_push_null(dec_ctx->thr); - break; - case 0xf7U: - duk_push_undefined(dec_ctx->thr); - break; - case 0xf8U: - /* Simple value 32-255, nothing defined yet, so reject. */ - goto format_error; - case 0xf9U: { - duk_double_t d; - d = duk__cbor_decode_half_float(dec_ctx); - duk_push_number(dec_ctx->thr, d); - break; - } - case 0xfaU: { - duk_double_t d; - d = duk__cbor_decode_float(dec_ctx); - duk_push_number(dec_ctx->thr, d); - break; - } - case 0xfbU: { - duk_double_t d; - d = duk__cbor_decode_double(dec_ctx); - duk_push_number(dec_ctx->thr, d); - break; - } - case 0xfcU: - case 0xfdU: - case 0xfeU: - case 0xffU: - goto format_error; - } /* end switch */ - - return; - -format_error: - duk__cbor_decode_error(dec_ctx); -} -#else /* DUK_CBOR_DECODE_FASTPATH */ -DUK_LOCAL void duk__cbor_decode_value(duk_cbor_decode_context *dec_ctx) { - duk_uint8_t ib, mt, ai; - - /* Any paths potentially recursing back to duk__cbor_decode_value() - * must perform a Duktape value stack growth check. Avoid the check - * here for simple paths like primitive values. - */ - -reread_initial_byte: - DUK_DDD(DUK_DDDPRINT("cbor decode off=%ld len=%ld", (long) dec_ctx->off, (long) dec_ctx->len)); - - ib = duk__cbor_decode_readbyte(dec_ctx); - mt = ib >> 5U; - ai = ib & 0x1fU; - - /* Additional information in [24,27] = [0x18,0x1b] has relatively - * uniform handling for all major types: read 1/2/4/8 additional - * bytes. For major type 7 the 1-byte value is a 'simple type', and - * 2/4/8-byte values are floats. For other major types the 1/2/4/8 - * byte values are integers. The lengths are uniform, but the typing - * is not. - */ - - switch (mt) { - case 0U: { /* unsigned integer */ - duk__cbor_decode_push_aival_int(dec_ctx, ib, 0 /*negative*/); - break; - } - case 1U: { /* negative integer */ - duk__cbor_decode_push_aival_int(dec_ctx, ib, 1 /*negative*/); - break; - } - case 2U: { /* byte string */ - if (ai == 0x1fU) { - duk__cbor_decode_and_join_strbuf(dec_ctx, 0x40U); - } else { - duk__cbor_decode_rewind(dec_ctx, 1U); - duk__cbor_decode_buffer(dec_ctx, 0x40U); - } - break; - } - case 3U: { /* text string */ - duk__cbor_decode_string(dec_ctx, ib, ai); - break; - } - case 4U: { /* array of data items */ - if (DUK_UNLIKELY(duk__cbor_decode_array(dec_ctx, ib, ai) == 0)) { - goto format_error; - } - break; - } - case 5U: { /* map of pairs of data items */ - if (DUK_UNLIKELY(duk__cbor_decode_map(dec_ctx, ib, ai) == 0)) { - goto format_error; - } - break; - } - case 6U: { /* semantic tagging */ - /* Tags are ignored now, re-read initial byte. A tagged - * value may itself be tagged (an unlimited number of times) - * so keep on peeling away tags. - */ - duk__cbor_decode_skip_aival_int(dec_ctx, ib); - goto reread_initial_byte; - } - case 7U: { /* floating point numbers, simple data types, break; other */ - switch (ai) { - case 0x14U: { - duk_push_false(dec_ctx->thr); - break; - } - case 0x15U: { - duk_push_true(dec_ctx->thr); - break; - } - case 0x16U: { - duk_push_null(dec_ctx->thr); - break; - } - case 0x17U: { - duk_push_undefined(dec_ctx->thr); - break; - } - case 0x18U: { /* more simple values (1 byte) */ - /* Simple value encoded in additional byte (none - * are defined so far). RFC 7049 states that the - * follow-up byte must be 32-255 to minimize - * confusion. So, a non-shortest encoding like - * f815 (= true, shortest encoding f5) must be - * rejected. cbor.me tester rejects f815, but - * e.g. Python CBOR binding decodes it as true. - */ - goto format_error; - } - case 0x19U: { /* half-float (2 bytes) */ - duk_double_t d; - d = duk__cbor_decode_half_float(dec_ctx); - duk_push_number(dec_ctx->thr, d); - break; - } - case 0x1aU: { /* float (4 bytes) */ - duk_double_t d; - d = duk__cbor_decode_float(dec_ctx); - duk_push_number(dec_ctx->thr, d); - break; - } - case 0x1bU: { /* double (8 bytes) */ - duk_double_t d; - d = duk__cbor_decode_double(dec_ctx); - duk_push_number(dec_ctx->thr, d); - break; - } - case 0xffU: /* unexpected break */ - default: { - goto format_error; - } - } /* end switch */ - break; - } - default: { - goto format_error; /* will never actually occur */ - } - } /* end switch */ - - return; - -format_error: - duk__cbor_decode_error(dec_ctx); -} -#endif /* DUK_CBOR_DECODE_FASTPATH */ - -DUK_LOCAL void duk__cbor_encode(duk_hthread *thr, duk_idx_t idx, duk_uint_t encode_flags) { - duk_cbor_encode_context enc_ctx; - duk_uint8_t *buf; - - DUK_UNREF(encode_flags); - - idx = duk_require_normalize_index(thr, idx); - - enc_ctx.thr = thr; - enc_ctx.idx_buf = duk_get_top(thr); - - enc_ctx.len = 64; - buf = (duk_uint8_t *) duk_push_dynamic_buffer(thr, enc_ctx.len); - enc_ctx.ptr = buf; - enc_ctx.buf = buf; - enc_ctx.buf_end = buf + enc_ctx.len; - - enc_ctx.recursion_depth = 0; - enc_ctx.recursion_limit = DUK_USE_CBOR_ENC_RECLIMIT; - - duk_dup(thr, idx); - duk__cbor_encode_req_stack(&enc_ctx); - duk__cbor_encode_value(&enc_ctx); - DUK_ASSERT(enc_ctx.recursion_depth == 0); - duk_resize_buffer(enc_ctx.thr, enc_ctx.idx_buf, (duk_size_t) (enc_ctx.ptr - enc_ctx.buf)); - duk_replace(thr, idx); -} - -DUK_LOCAL void duk__cbor_decode(duk_hthread *thr, duk_idx_t idx, duk_uint_t decode_flags) { - duk_cbor_decode_context dec_ctx; - - DUK_UNREF(decode_flags); - - /* Suppress compile warnings for functions only needed with e.g. - * asserts enabled. - */ - DUK_UNREF(duk__cbor_get_reserve); - - idx = duk_require_normalize_index(thr, idx); - - dec_ctx.thr = thr; - dec_ctx.buf = (const duk_uint8_t *) duk_require_buffer_data(thr, idx, &dec_ctx.len); - dec_ctx.off = 0; - /* dec_ctx.len: set above */ - - dec_ctx.recursion_depth = 0; - dec_ctx.recursion_limit = DUK_USE_CBOR_DEC_RECLIMIT; - - duk__cbor_decode_req_stack(&dec_ctx); - duk__cbor_decode_value(&dec_ctx); - DUK_ASSERT(dec_ctx.recursion_depth == 0); - if (dec_ctx.off != dec_ctx.len) { - (void) duk_type_error(thr, "trailing garbage"); - } - - duk_replace(thr, idx); -} - -#else /* DUK_USE_CBOR_SUPPORT */ - -DUK_LOCAL void duk__cbor_encode(duk_hthread *thr, duk_idx_t idx, duk_uint_t encode_flags) { - DUK_UNREF(idx); - DUK_UNREF(encode_flags); - DUK_ERROR_UNSUPPORTED(thr); -} - -DUK_LOCAL void duk__cbor_decode(duk_hthread *thr, duk_idx_t idx, duk_uint_t decode_flags) { - DUK_UNREF(idx); - DUK_UNREF(decode_flags); - DUK_ERROR_UNSUPPORTED(thr); -} - -#endif /* DUK_USE_CBOR_SUPPORT */ - -/* - * Public APIs - */ - -DUK_EXTERNAL void duk_cbor_encode(duk_hthread *thr, duk_idx_t idx, duk_uint_t encode_flags) { - DUK_ASSERT_API_ENTRY(thr); - duk__cbor_encode(thr, idx, encode_flags); -} -DUK_EXTERNAL void duk_cbor_decode(duk_hthread *thr, duk_idx_t idx, duk_uint_t decode_flags) { - DUK_ASSERT_API_ENTRY(thr); - duk__cbor_decode(thr, idx, decode_flags); -} - -#if defined(DUK_USE_CBOR_BUILTIN) -#if defined(DUK_USE_CBOR_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_cbor_encode(duk_hthread *thr) { - DUK_ASSERT_TOP(thr, 1); - - duk__cbor_encode(thr, -1, 0 /*flags*/); - - /* Produce an ArrayBuffer by first decoding into a plain buffer which - * mimics a Uint8Array and gettings its .buffer property. - */ - /* XXX: shortcut */ - (void) duk_get_prop_stridx(thr, -1, DUK_STRIDX_LC_BUFFER); - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_cbor_decode(duk_hthread *thr) { - DUK_ASSERT_TOP(thr, 1); - - duk__cbor_decode(thr, -1, 0 /*flags*/); - return 1; -} -#else /* DUK_USE_CBOR_SUPPORT */ -DUK_INTERNAL duk_ret_t duk_bi_cbor_encode(duk_hthread *thr) { - DUK_ERROR_UNSUPPORTED(thr); - DUK_WO_NORETURN(return 0;); -} -DUK_INTERNAL duk_ret_t duk_bi_cbor_decode(duk_hthread *thr) { - DUK_ERROR_UNSUPPORTED(thr); - DUK_WO_NORETURN(return 0;); -} -#endif /* DUK_USE_CBOR_SUPPORT */ -#endif /* DUK_USE_CBOR_BUILTIN */ - -/* automatic undefs */ -#undef DUK__CBOR_AI -/* - * Date built-ins - * - * Unlike most built-ins, Date has some platform dependencies for getting - * UTC time, converting between UTC and local time, and parsing and - * formatting time values. These are all abstracted behind DUK_USE_xxx - * config options. There are built-in platform specific providers for - * POSIX and Windows, but external providers can also be used. - * - * See doc/datetime.rst. - * - */ - -/* #include duk_internal.h -> already included */ - -/* XXX: currently defines unnecessary symbols when DUK_USE_DATE_BUILTIN is disabled. */ - -/* - * Forward declarations - */ - -DUK_LOCAL_DECL duk_double_t duk__push_this_get_timeval_tzoffset(duk_hthread *thr, duk_small_uint_t flags, duk_int_t *out_tzoffset); -DUK_LOCAL_DECL duk_double_t duk__push_this_get_timeval(duk_hthread *thr, duk_small_uint_t flags); -DUK_LOCAL_DECL void duk__twodigit_year_fixup(duk_hthread *thr, duk_idx_t idx_val); -DUK_LOCAL_DECL duk_ret_t duk__set_this_timeval_from_dparts(duk_hthread *thr, duk_double_t *dparts, duk_small_uint_t flags); - -/* - * Other file level defines - */ - -/* Debug macro to print all parts and dparts (used manually because of debug level). */ -#define DUK__DPRINT_PARTS_AND_DPARTS(parts, dparts) \ - do { \ - DUK_D(DUK_DPRINT("parts: %ld %ld %ld %ld %ld %ld %ld %ld, dparts: %lf %lf %lf %lf %lf %lf %lf %lf", \ - (long) (parts)[0], \ - (long) (parts)[1], \ - (long) (parts)[2], \ - (long) (parts)[3], \ - (long) (parts)[4], \ - (long) (parts)[5], \ - (long) (parts)[6], \ - (long) (parts)[7], \ - (double) (dparts)[0], \ - (double) (dparts)[1], \ - (double) (dparts)[2], \ - (double) (dparts)[3], \ - (double) (dparts)[4], \ - (double) (dparts)[5], \ - (double) (dparts)[6], \ - (double) (dparts)[7])); \ - } while (0) -#define DUK__DPRINT_PARTS(parts) \ - do { \ - DUK_D(DUK_DPRINT("parts: %ld %ld %ld %ld %ld %ld %ld %ld", \ - (long) (parts)[0], \ - (long) (parts)[1], \ - (long) (parts)[2], \ - (long) (parts)[3], \ - (long) (parts)[4], \ - (long) (parts)[5], \ - (long) (parts)[6], \ - (long) (parts)[7])); \ - } while (0) -#define DUK__DPRINT_DPARTS(dparts) \ - do { \ - DUK_D(DUK_DPRINT("dparts: %lf %lf %lf %lf %lf %lf %lf %lf", \ - (double) (dparts)[0], \ - (double) (dparts)[1], \ - (double) (dparts)[2], \ - (double) (dparts)[3], \ - (double) (dparts)[4], \ - (double) (dparts)[5], \ - (double) (dparts)[6], \ - (double) (dparts)[7])); \ - } while (0) - -/* Equivalent year for DST calculations outside [1970,2038[ range, see - * E5 Section 15.9.1.8. Equivalent year has the same leap-year-ness and - * starts with the same weekday on Jan 1. - * https://bugzilla.mozilla.org/show_bug.cgi?id=351066 - */ -#define DUK__YEAR(x) ((duk_uint8_t) ((x) -1970)) -DUK_LOCAL duk_uint8_t duk__date_equivyear[14] = { -#if 1 - /* This is based on V8 EquivalentYear() algorithm (see util/genequivyear.py): - * http://code.google.com/p/v8/source/browse/trunk/src/date.h#146 - */ - - /* non-leap year: sunday, monday, ... */ - DUK__YEAR(2023), - DUK__YEAR(2035), - DUK__YEAR(2019), - DUK__YEAR(2031), - DUK__YEAR(2015), - DUK__YEAR(2027), - DUK__YEAR(2011), - - /* leap year: sunday, monday, ... */ - DUK__YEAR(2012), - DUK__YEAR(2024), - DUK__YEAR(2008), - DUK__YEAR(2020), - DUK__YEAR(2032), - DUK__YEAR(2016), - DUK__YEAR(2028) -#endif - -#if 0 - /* This is based on Rhino EquivalentYear() algorithm: - * https://github.com/mozilla/rhino/blob/f99cc11d616f0cdda2c42bde72b3484df6182947/src/org/mozilla/javascript/NativeDate.java - */ - - /* non-leap year: sunday, monday, ... */ - DUK__YEAR(1978), DUK__YEAR(1973), DUK__YEAR(1985), DUK__YEAR(1986), - DUK__YEAR(1981), DUK__YEAR(1971), DUK__YEAR(1977), - - /* leap year: sunday, monday, ... */ - DUK__YEAR(1984), DUK__YEAR(1996), DUK__YEAR(1980), DUK__YEAR(1992), - DUK__YEAR(1976), DUK__YEAR(1988), DUK__YEAR(1972) -#endif -}; - -/* - * ISO 8601 subset parser. - */ - -/* Parser part count. */ -#define DUK__NUM_ISO8601_PARSER_PARTS 9 - -/* Parser part indices. */ -#define DUK__PI_YEAR 0 -#define DUK__PI_MONTH 1 -#define DUK__PI_DAY 2 -#define DUK__PI_HOUR 3 -#define DUK__PI_MINUTE 4 -#define DUK__PI_SECOND 5 -#define DUK__PI_MILLISECOND 6 -#define DUK__PI_TZHOUR 7 -#define DUK__PI_TZMINUTE 8 - -/* Parser part masks. */ -#define DUK__PM_YEAR (1 << DUK__PI_YEAR) -#define DUK__PM_MONTH (1 << DUK__PI_MONTH) -#define DUK__PM_DAY (1 << DUK__PI_DAY) -#define DUK__PM_HOUR (1 << DUK__PI_HOUR) -#define DUK__PM_MINUTE (1 << DUK__PI_MINUTE) -#define DUK__PM_SECOND (1 << DUK__PI_SECOND) -#define DUK__PM_MILLISECOND (1 << DUK__PI_MILLISECOND) -#define DUK__PM_TZHOUR (1 << DUK__PI_TZHOUR) -#define DUK__PM_TZMINUTE (1 << DUK__PI_TZMINUTE) - -/* Parser separator indices. */ -#define DUK__SI_PLUS 0 -#define DUK__SI_MINUS 1 -#define DUK__SI_T 2 -#define DUK__SI_SPACE 3 -#define DUK__SI_COLON 4 -#define DUK__SI_PERIOD 5 -#define DUK__SI_Z 6 -#define DUK__SI_NUL 7 - -/* Parser separator masks. */ -#define DUK__SM_PLUS (1 << DUK__SI_PLUS) -#define DUK__SM_MINUS (1 << DUK__SI_MINUS) -#define DUK__SM_T (1 << DUK__SI_T) -#define DUK__SM_SPACE (1 << DUK__SI_SPACE) -#define DUK__SM_COLON (1 << DUK__SI_COLON) -#define DUK__SM_PERIOD (1 << DUK__SI_PERIOD) -#define DUK__SM_Z (1 << DUK__SI_Z) -#define DUK__SM_NUL (1 << DUK__SI_NUL) - -/* Rule control flags. */ -#define DUK__CF_NEG (1 << 0) /* continue matching, set neg_tzoffset flag */ -#define DUK__CF_ACCEPT (1 << 1) /* accept string */ -#define DUK__CF_ACCEPT_NUL (1 << 2) /* accept string if next char is NUL (otherwise reject) */ - -#define DUK__PACK_RULE(partmask, sepmask, nextpart, flags) \ - ((duk_uint32_t) (partmask) + (((duk_uint32_t) (sepmask)) << 9) + (((duk_uint32_t) (nextpart)) << 17) + \ - (((duk_uint32_t) (flags)) << 21)) - -#define DUK__UNPACK_RULE(rule, var_nextidx, var_flags) \ - do { \ - (var_nextidx) = (duk_small_uint_t) (((rule) >> 17) & 0x0f); \ - (var_flags) = (duk_small_uint_t) ((rule) >> 21); \ - } while (0) - -#define DUK__RULE_MASK_PART_SEP 0x1ffffUL - -/* Matching separator index is used in the control table */ -DUK_LOCAL const duk_uint8_t duk__parse_iso8601_seps[] = { - DUK_ASC_PLUS /*0*/, DUK_ASC_MINUS /*1*/, DUK_ASC_UC_T /*2*/, DUK_ASC_SPACE /*3*/, - DUK_ASC_COLON /*4*/, DUK_ASC_PERIOD /*5*/, DUK_ASC_UC_Z /*6*/, DUK_ASC_NUL /*7*/ -}; - -/* Rule table: first matching rule is used to determine what to do next. */ -DUK_LOCAL const duk_uint32_t duk__parse_iso8601_control[] = { - DUK__PACK_RULE(DUK__PM_YEAR, DUK__SM_MINUS, DUK__PI_MONTH, 0), - DUK__PACK_RULE(DUK__PM_MONTH, DUK__SM_MINUS, DUK__PI_DAY, 0), - DUK__PACK_RULE(DUK__PM_YEAR | DUK__PM_MONTH | DUK__PM_DAY, DUK__SM_T | DUK__SM_SPACE, DUK__PI_HOUR, 0), - DUK__PACK_RULE(DUK__PM_HOUR, DUK__SM_COLON, DUK__PI_MINUTE, 0), - DUK__PACK_RULE(DUK__PM_MINUTE, DUK__SM_COLON, DUK__PI_SECOND, 0), - DUK__PACK_RULE(DUK__PM_SECOND, DUK__SM_PERIOD, DUK__PI_MILLISECOND, 0), - DUK__PACK_RULE(DUK__PM_TZHOUR, DUK__SM_COLON, DUK__PI_TZMINUTE, 0), - DUK__PACK_RULE(DUK__PM_YEAR | DUK__PM_MONTH | DUK__PM_DAY | DUK__PM_HOUR /*Note1*/ | DUK__PM_MINUTE | DUK__PM_SECOND | - DUK__PM_MILLISECOND, - DUK__SM_PLUS, - DUK__PI_TZHOUR, - 0), - DUK__PACK_RULE(DUK__PM_YEAR | DUK__PM_MONTH | DUK__PM_DAY | DUK__PM_HOUR /*Note1*/ | DUK__PM_MINUTE | DUK__PM_SECOND | - DUK__PM_MILLISECOND, - DUK__SM_MINUS, - DUK__PI_TZHOUR, - DUK__CF_NEG), - DUK__PACK_RULE(DUK__PM_YEAR | DUK__PM_MONTH | DUK__PM_DAY | DUK__PM_HOUR /*Note1*/ | DUK__PM_MINUTE | DUK__PM_SECOND | - DUK__PM_MILLISECOND, - DUK__SM_Z, - 0, - DUK__CF_ACCEPT_NUL), - DUK__PACK_RULE(DUK__PM_YEAR | DUK__PM_MONTH | DUK__PM_DAY | DUK__PM_HOUR /*Note1*/ | DUK__PM_MINUTE | DUK__PM_SECOND | - DUK__PM_MILLISECOND | DUK__PM_TZHOUR /*Note2*/ | DUK__PM_TZMINUTE, - DUK__SM_NUL, - 0, - DUK__CF_ACCEPT) - - /* Note1: the specification doesn't require matching a time form with - * just hours ("HH"), but we accept it here, e.g. "2012-01-02T12Z". - * - * Note2: the specification doesn't require matching a timezone offset - * with just hours ("HH"), but accept it here, e.g. "2012-01-02T03:04:05+02" - */ -}; - -DUK_LOCAL duk_bool_t duk__parse_string_iso8601_subset(duk_hthread *thr, const char *str) { - duk_int_t parts[DUK__NUM_ISO8601_PARSER_PARTS]; - duk_double_t dparts[DUK_DATE_IDX_NUM_PARTS]; - duk_double_t d; - const duk_uint8_t *p; - duk_small_uint_t part_idx = 0; - duk_int_t accum = 0; - duk_small_uint_t ndigits = 0; - duk_bool_t neg_year = 0; - duk_bool_t neg_tzoffset = 0; - duk_uint_fast8_t ch; - duk_small_uint_t i; - - /* During parsing, month and day are one-based; set defaults here. */ - duk_memzero(parts, sizeof(parts)); - DUK_ASSERT(parts[DUK_DATE_IDX_YEAR] == 0); /* don't care value, year is mandatory */ - parts[DUK_DATE_IDX_MONTH] = 1; - parts[DUK_DATE_IDX_DAY] = 1; - - /* Special handling for year sign. */ - p = (const duk_uint8_t *) str; - ch = p[0]; - if (ch == DUK_ASC_PLUS) { - p++; - } else if (ch == DUK_ASC_MINUS) { - neg_year = 1; - p++; - } - - for (;;) { - ch = *p++; - DUK_DDD(DUK_DDDPRINT("parsing, part_idx=%ld, char=%ld ('%c')", - (long) part_idx, - (long) ch, - (int) ((ch >= 0x20 && ch <= 0x7e) ? ch : DUK_ASC_QUESTION))); - - if (ch >= DUK_ASC_0 && ch <= DUK_ASC_9) { - if (ndigits >= 9) { - DUK_DDD(DUK_DDDPRINT("too many digits -> reject")); - goto reject; - } - if (part_idx == DUK__PI_MILLISECOND && ndigits >= 3) { - /* ignore millisecond fractions after 3 */ - } else { - accum = accum * 10 + ((duk_int_t) ch) - ((duk_int_t) DUK_ASC_0) + 0x00; - ndigits++; - } - } else { - duk_uint_fast32_t match_val; - duk_small_uint_t sep_idx; - - if (ndigits <= 0) { - goto reject; - } - if (part_idx == DUK__PI_MILLISECOND) { - /* complete the millisecond field */ - while (ndigits < 3) { - accum *= 10; - ndigits++; - } - } - parts[part_idx] = accum; - DUK_DDD(DUK_DDDPRINT("wrote part %ld -> value %ld", (long) part_idx, (long) accum)); - - accum = 0; - ndigits = 0; - - for (i = 0; i < (duk_small_uint_t) (sizeof(duk__parse_iso8601_seps) / sizeof(duk_uint8_t)); i++) { - if (duk__parse_iso8601_seps[i] == ch) { - break; - } - } - if (i == (duk_small_uint_t) (sizeof(duk__parse_iso8601_seps) / sizeof(duk_uint8_t))) { - DUK_DDD(DUK_DDDPRINT("separator character doesn't match -> reject")); - goto reject; - } - - sep_idx = i; - match_val = (1UL << part_idx) + (1UL << (sep_idx + 9)); /* match against rule part/sep bits */ - - for (i = 0; i < (duk_small_uint_t) (sizeof(duk__parse_iso8601_control) / sizeof(duk_uint32_t)); i++) { - duk_uint_fast32_t rule = duk__parse_iso8601_control[i]; - duk_small_uint_t nextpart; - duk_small_uint_t cflags; - - DUK_DDD(DUK_DDDPRINT("part_idx=%ld, sep_idx=%ld, match_val=0x%08lx, considering rule=0x%08lx", - (long) part_idx, - (long) sep_idx, - (unsigned long) match_val, - (unsigned long) rule)); - - if ((rule & match_val) != match_val) { - continue; - } - - DUK__UNPACK_RULE(rule, nextpart, cflags); - - DUK_DDD(DUK_DDDPRINT("rule match -> part_idx=%ld, sep_idx=%ld, match_val=0x%08lx, " - "rule=0x%08lx -> nextpart=%ld, cflags=0x%02lx", - (long) part_idx, - (long) sep_idx, - (unsigned long) match_val, - (unsigned long) rule, - (long) nextpart, - (unsigned long) cflags)); - - if (cflags & DUK__CF_NEG) { - neg_tzoffset = 1; - } - - if (cflags & DUK__CF_ACCEPT) { - goto accept; - } - - if (cflags & DUK__CF_ACCEPT_NUL) { - DUK_ASSERT(*(p - 1) != (char) 0); - if (*p == DUK_ASC_NUL) { - goto accept; - } - goto reject; - } - - part_idx = nextpart; - break; - } /* rule match */ - - if (i == (duk_small_uint_t) (sizeof(duk__parse_iso8601_control) / sizeof(duk_uint32_t))) { - DUK_DDD(DUK_DDDPRINT("no rule matches -> reject")); - goto reject; - } - - if (ch == 0) { - /* This shouldn't be necessary, but check just in case - * to avoid any chance of overruns. - */ - DUK_DDD(DUK_DDDPRINT("NUL after rule matching (should not happen) -> reject")); - goto reject; - } - } /* if-digit-else-ctrl */ - } /* char loop */ - - /* We should never exit the loop above. */ - DUK_UNREACHABLE(); - -reject: - DUK_DDD(DUK_DDDPRINT("reject")); - return 0; - -accept: - DUK_DDD(DUK_DDDPRINT("accept")); - - /* Apply timezone offset to get the main parts in UTC */ - if (neg_year) { - parts[DUK__PI_YEAR] = -parts[DUK__PI_YEAR]; - } - if (neg_tzoffset) { - parts[DUK__PI_HOUR] += parts[DUK__PI_TZHOUR]; - parts[DUK__PI_MINUTE] += parts[DUK__PI_TZMINUTE]; - } else { - parts[DUK__PI_HOUR] -= parts[DUK__PI_TZHOUR]; - parts[DUK__PI_MINUTE] -= parts[DUK__PI_TZMINUTE]; - } - parts[DUK__PI_MONTH] -= 1; /* zero-based month */ - parts[DUK__PI_DAY] -= 1; /* zero-based day */ - - /* Use double parts, they tolerate unnormalized time. - * - * Note: DUK_DATE_IDX_WEEKDAY is initialized with a bogus value (DUK__PI_TZHOUR) - * on purpose. It won't be actually used by duk_bi_date_get_timeval_from_dparts(), - * but will make the value initialized just in case, and avoid any - * potential for Valgrind issues. - */ - for (i = 0; i < DUK_DATE_IDX_NUM_PARTS; i++) { - DUK_DDD(DUK_DDDPRINT("part[%ld] = %ld", (long) i, (long) parts[i])); - dparts[i] = parts[i]; - } - - d = duk_bi_date_get_timeval_from_dparts(dparts, 0 /*flags*/); - duk_push_number(thr, d); - return 1; -} - -/* - * Date/time parsing helper. - * - * Parse a datetime string into a time value. We must first try to parse - * the input according to the standard format in E5.1 Section 15.9.1.15. - * If that fails, we can try to parse using custom parsing, which can - * either be platform neutral (custom code) or platform specific (using - * existing platform API calls). - * - * Note in particular that we must parse whatever toString(), toUTCString(), - * and toISOString() can produce; see E5.1 Section 15.9.4.2. - * - * Returns 1 to allow tail calling. - * - * There is much room for improvement here with respect to supporting - * alternative datetime formats. For instance, V8 parses '2012-01-01' as - * UTC and '2012/01/01' as local time. - */ - -DUK_LOCAL duk_ret_t duk__parse_string(duk_hthread *thr, const char *str) { - /* XXX: there is a small risk here: because the ISO 8601 parser is - * very loose, it may end up parsing some datetime values which - * would be better parsed with a platform specific parser. - */ - - DUK_ASSERT(str != NULL); - DUK_DDD(DUK_DDDPRINT("parse datetime from string '%s'", (const char *) str)); - - if (duk__parse_string_iso8601_subset(thr, str) != 0) { - return 1; - } - -#if defined(DUK_USE_DATE_PARSE_STRING) - /* Contract, either: - * - Push value on stack and return 1 - * - Don't push anything on stack and return 0 - */ - - if (DUK_USE_DATE_PARSE_STRING(thr, str) != 0) { - return 1; - } -#else - /* No platform-specific parsing, this is not an error. */ -#endif - - duk_push_nan(thr); - return 1; -} - -/* - * Calendar helpers - * - * Some helpers are used for getters and can operate on normalized values - * which can be represented with 32-bit signed integers. Other helpers are - * needed by setters and operate on un-normalized double values, must watch - * out for non-finite numbers etc. - */ - -DUK_LOCAL duk_uint8_t duk__days_in_month[12] = { (duk_uint8_t) 31, (duk_uint8_t) 28, (duk_uint8_t) 31, (duk_uint8_t) 30, - (duk_uint8_t) 31, (duk_uint8_t) 30, (duk_uint8_t) 31, (duk_uint8_t) 31, - (duk_uint8_t) 30, (duk_uint8_t) 31, (duk_uint8_t) 30, (duk_uint8_t) 31 }; - -/* Maximum iteration count for computing UTC-to-local time offset when - * creating an ECMAScript time value from local parts. - */ -#define DUK__LOCAL_TZOFFSET_MAXITER 4 - -/* Because 'day since epoch' can be negative and is used to compute weekday - * using a modulo operation, add this multiple of 7 to avoid negative values - * when year is below 1970 epoch. ECMAScript time values are restricted to - * +/- 100 million days from epoch, so this adder fits nicely into 32 bits. - * Round to a multiple of 7 (= floor(100000000 / 7) * 7) and add margin. - */ -#define DUK__WEEKDAY_MOD_ADDER (20000000 * 7) /* 0x08583b00 */ - -DUK_INTERNAL duk_bool_t duk_bi_date_is_leap_year(duk_int_t year) { - if ((year % 4) != 0) { - return 0; - } - if ((year % 100) != 0) { - return 1; - } - if ((year % 400) != 0) { - return 0; - } - return 1; -} - -DUK_INTERNAL duk_bool_t duk_bi_date_timeval_in_valid_range(duk_double_t x) { - return (x >= -DUK_DATE_MSEC_100M_DAYS && x <= DUK_DATE_MSEC_100M_DAYS); -} - -DUK_INTERNAL duk_bool_t duk_bi_date_timeval_in_leeway_range(duk_double_t x) { - return (x >= -DUK_DATE_MSEC_100M_DAYS_LEEWAY && x <= DUK_DATE_MSEC_100M_DAYS_LEEWAY); -} - -DUK_INTERNAL duk_bool_t duk_bi_date_year_in_valid_range(duk_double_t x) { - return (x >= DUK_DATE_MIN_ECMA_YEAR && x <= DUK_DATE_MAX_ECMA_YEAR); -} - -DUK_LOCAL duk_double_t duk__timeclip(duk_double_t x) { - if (!DUK_ISFINITE(x)) { - return DUK_DOUBLE_NAN; - } - - if (!duk_bi_date_timeval_in_valid_range(x)) { - return DUK_DOUBLE_NAN; - } - - x = duk_js_tointeger_number(x); - - /* Here we'd have the option to normalize -0 to +0. */ - return x; -} - -/* Integer division which floors also negative values correctly. */ -DUK_LOCAL duk_int_t duk__div_floor(duk_int_t a, duk_int_t b) { - DUK_ASSERT(b > 0); - if (a >= 0) { - return a / b; - } else { - /* e.g. a = -4, b = 5 --> -4 - 5 + 1 / 5 --> -8 / 5 --> -1 - * a = -5, b = 5 --> -5 - 5 + 1 / 5 --> -9 / 5 --> -1 - * a = -6, b = 5 --> -6 - 5 + 1 / 5 --> -10 / 5 --> -2 - */ - return (a - b + 1) / b; - } -} - -/* Compute day number of the first day of a given year. */ -DUK_LOCAL duk_int_t duk__day_from_year(duk_int_t year) { - /* Note: in integer arithmetic, (x / 4) is same as floor(x / 4) for non-negative - * values, but is incorrect for negative ones. - */ - return 365 * (year - 1970) + duk__div_floor(year - 1969, 4) - duk__div_floor(year - 1901, 100) + - duk__div_floor(year - 1601, 400); -} - -/* Given a day number, determine year and day-within-year. */ -DUK_LOCAL duk_int_t duk__year_from_day(duk_int_t day, duk_small_int_t *out_day_within_year) { - duk_int_t year; - duk_int_t diff_days; - - /* estimate year upwards (towards positive infinity), then back down; - * two iterations should be enough - */ - - if (day >= 0) { - year = 1970 + day / 365; - } else { - year = 1970 + day / 366; - } - - for (;;) { - diff_days = duk__day_from_year(year) - day; - DUK_DDD(DUK_DDDPRINT("year=%ld day=%ld, diff_days=%ld", (long) year, (long) day, (long) diff_days)); - if (diff_days <= 0) { - DUK_ASSERT(-diff_days < 366); /* fits into duk_small_int_t */ - *out_day_within_year = -diff_days; - DUK_DDD(DUK_DDDPRINT("--> year=%ld, day-within-year=%ld", (long) year, (long) *out_day_within_year)); - DUK_ASSERT(*out_day_within_year >= 0); - DUK_ASSERT(*out_day_within_year < (duk_bi_date_is_leap_year(year) ? 366 : 365)); - return year; - } - - /* Note: this is very tricky; we must never 'overshoot' the - * correction downwards. - */ - year -= 1 + (diff_days - 1) / 366; /* conservative */ - } -} - -/* Given a (year, month, day-within-month) triple, compute day number. - * The input triple is un-normalized and may contain non-finite values. - */ -DUK_LOCAL duk_double_t duk__make_day(duk_double_t year, duk_double_t month, duk_double_t day) { - duk_int_t day_num; - duk_bool_t is_leap; - duk_small_int_t i, n; - - /* Assume that year, month, day are all coerced to whole numbers. - * They may also be NaN or infinity, in which case this function - * must return NaN or infinity to ensure time value becomes NaN. - * If 'day' is NaN, the final return will end up returning a NaN, - * so it doesn't need to be checked here. - */ - - if (!DUK_ISFINITE(year) || !DUK_ISFINITE(month)) { - return DUK_DOUBLE_NAN; - } - - year += DUK_FLOOR(month / 12.0); - - month = DUK_FMOD(month, 12.0); - if (month < 0.0) { - /* handle negative values */ - month += 12.0; - } - - /* The algorithm in E5.1 Section 15.9.1.12 normalizes month, but - * does not normalize the day-of-month (nor check whether or not - * it is finite) because it's not necessary for finding the day - * number which matches the (year,month) pair. - * - * We assume that duk__day_from_year() is exact here. - * - * Without an explicit infinity / NaN check in the beginning, - * day_num would be a bogus integer here. - * - * It's possible for 'year' to be out of integer range here. - * If so, we need to return NaN without integer overflow. - * This fixes test-bug-setyear-overflow.js. - */ - - if (!duk_bi_date_year_in_valid_range(year)) { - DUK_DD(DUK_DDPRINT("year not in ecmascript valid range, avoid integer overflow: %lf", (double) year)); - return DUK_DOUBLE_NAN; - } - day_num = duk__day_from_year((duk_int_t) year); - is_leap = duk_bi_date_is_leap_year((duk_int_t) year); - - n = (duk_small_int_t) month; - for (i = 0; i < n; i++) { - day_num += duk__days_in_month[i]; - if (i == 1 && is_leap) { - day_num++; - } - } - - /* If 'day' is NaN, returns NaN. */ - return (duk_double_t) day_num + day; -} - -/* Split time value into parts. The time value may contain fractions (it may - * come from duk_time_to_components() API call) which are truncated. Possible - * local time adjustment has already been applied when reading the time value. - */ -DUK_INTERNAL void duk_bi_date_timeval_to_parts(duk_double_t d, duk_int_t *parts, duk_double_t *dparts, duk_small_uint_t flags) { - duk_double_t d1, d2; - duk_int_t t1, t2; - duk_int_t day_since_epoch; - duk_int_t year; /* does not fit into 16 bits */ - duk_small_int_t day_in_year; - duk_small_int_t month; - duk_small_int_t day; - duk_small_int_t dim; - duk_int_t jan1_since_epoch; - duk_small_int_t jan1_weekday; - duk_int_t equiv_year; - duk_small_uint_t i; - duk_bool_t is_leap; - duk_small_int_t arridx; - - DUK_ASSERT(DUK_ISFINITE(d)); /* caller checks */ - d = DUK_FLOOR(d); /* remove fractions if present */ - DUK_ASSERT(duk_double_equals(DUK_FLOOR(d), d)); - - /* The timevalue must be in valid ECMAScript range, but since a local - * time offset can be applied, we need to allow a +/- 24h leeway to - * the value. In other words, although the UTC time is within the - * ECMAScript range, the local part values can be just outside of it. - */ - DUK_UNREF(duk_bi_date_timeval_in_leeway_range); - DUK_ASSERT(duk_bi_date_timeval_in_leeway_range(d)); - - /* These computations are guaranteed to be exact for the valid - * E5 time value range, assuming milliseconds without fractions. - */ - d1 = (duk_double_t) DUK_FMOD(d, (double) DUK_DATE_MSEC_DAY); - if (d1 < 0.0) { - /* deal with negative values */ - d1 += (duk_double_t) DUK_DATE_MSEC_DAY; - } - d2 = DUK_FLOOR((double) (d / (duk_double_t) DUK_DATE_MSEC_DAY)); - DUK_ASSERT(duk_double_equals(d2 * ((duk_double_t) DUK_DATE_MSEC_DAY) + d1, d)); - /* now expected to fit into a 32-bit integer */ - t1 = (duk_int_t) d1; - t2 = (duk_int_t) d2; - day_since_epoch = t2; - DUK_ASSERT(duk_double_equals((duk_double_t) t1, d1)); - DUK_ASSERT(duk_double_equals((duk_double_t) t2, d2)); - - /* t1 = milliseconds within day (fits 32 bit) - * t2 = day number from epoch (fits 32 bit, may be negative) - */ - - parts[DUK_DATE_IDX_MILLISECOND] = t1 % 1000; - t1 /= 1000; - parts[DUK_DATE_IDX_SECOND] = t1 % 60; - t1 /= 60; - parts[DUK_DATE_IDX_MINUTE] = t1 % 60; - t1 /= 60; - parts[DUK_DATE_IDX_HOUR] = t1; - DUK_ASSERT(parts[DUK_DATE_IDX_MILLISECOND] >= 0 && parts[DUK_DATE_IDX_MILLISECOND] <= 999); - DUK_ASSERT(parts[DUK_DATE_IDX_SECOND] >= 0 && parts[DUK_DATE_IDX_SECOND] <= 59); - DUK_ASSERT(parts[DUK_DATE_IDX_MINUTE] >= 0 && parts[DUK_DATE_IDX_MINUTE] <= 59); - DUK_ASSERT(parts[DUK_DATE_IDX_HOUR] >= 0 && parts[DUK_DATE_IDX_HOUR] <= 23); - - DUK_DDD(DUK_DDDPRINT("d=%lf, d1=%lf, d2=%lf, t1=%ld, t2=%ld, parts: hour=%ld min=%ld sec=%ld msec=%ld", - (double) d, - (double) d1, - (double) d2, - (long) t1, - (long) t2, - (long) parts[DUK_DATE_IDX_HOUR], - (long) parts[DUK_DATE_IDX_MINUTE], - (long) parts[DUK_DATE_IDX_SECOND], - (long) parts[DUK_DATE_IDX_MILLISECOND])); - - /* This assert depends on the input parts representing time inside - * the ECMAScript range. - */ - DUK_ASSERT(t2 + DUK__WEEKDAY_MOD_ADDER >= 0); - parts[DUK_DATE_IDX_WEEKDAY] = (t2 + 4 + DUK__WEEKDAY_MOD_ADDER) % 7; /* E5.1 Section 15.9.1.6 */ - DUK_ASSERT(parts[DUK_DATE_IDX_WEEKDAY] >= 0 && parts[DUK_DATE_IDX_WEEKDAY] <= 6); - - year = duk__year_from_day(t2, &day_in_year); - day = day_in_year; - is_leap = duk_bi_date_is_leap_year(year); - for (month = 0; month < 12; month++) { - dim = duk__days_in_month[month]; - if (month == 1 && is_leap) { - dim++; - } - DUK_DDD(DUK_DDDPRINT("month=%ld, dim=%ld, day=%ld", (long) month, (long) dim, (long) day)); - if (day < dim) { - break; - } - day -= dim; - } - DUK_DDD(DUK_DDDPRINT("final month=%ld", (long) month)); - DUK_ASSERT(month >= 0 && month <= 11); - DUK_ASSERT(day >= 0 && day <= 31); - - /* Equivalent year mapping, used to avoid DST trouble when platform - * may fail to provide reasonable DST answers for dates outside the - * ordinary range (e.g. 1970-2038). An equivalent year has the same - * leap-year-ness as the original year and begins on the same weekday - * (Jan 1). - * - * The year 2038 is avoided because there seem to be problems with it - * on some platforms. The year 1970 is also avoided as there were - * practical problems with it; an equivalent year is used for it too, - * which breaks some DST computations for 1970 right now, see e.g. - * test-bi-date-tzoffset-brute-fi.js. - */ - if ((flags & DUK_DATE_FLAG_EQUIVYEAR) && (year < 1971 || year > 2037)) { - DUK_ASSERT(is_leap == 0 || is_leap == 1); - - jan1_since_epoch = day_since_epoch - day_in_year; /* day number for Jan 1 since epoch */ - DUK_ASSERT(jan1_since_epoch + DUK__WEEKDAY_MOD_ADDER >= 0); - jan1_weekday = (jan1_since_epoch + 4 + DUK__WEEKDAY_MOD_ADDER) % 7; /* E5.1 Section 15.9.1.6 */ - DUK_ASSERT(jan1_weekday >= 0 && jan1_weekday <= 6); - arridx = jan1_weekday; - if (is_leap) { - arridx += 7; - } - DUK_ASSERT(arridx >= 0 && arridx < (duk_small_int_t) (sizeof(duk__date_equivyear) / sizeof(duk_uint8_t))); - - equiv_year = (duk_int_t) duk__date_equivyear[arridx] + 1970; - year = equiv_year; - DUK_DDD(DUK_DDDPRINT("equiv year mapping, year=%ld, day_in_year=%ld, day_since_epoch=%ld, " - "jan1_since_epoch=%ld, jan1_weekday=%ld -> equiv year %ld", - (long) year, - (long) day_in_year, - (long) day_since_epoch, - (long) jan1_since_epoch, - (long) jan1_weekday, - (long) equiv_year)); - } - - parts[DUK_DATE_IDX_YEAR] = year; - parts[DUK_DATE_IDX_MONTH] = month; - parts[DUK_DATE_IDX_DAY] = day; - - if (flags & DUK_DATE_FLAG_ONEBASED) { - parts[DUK_DATE_IDX_MONTH]++; /* zero-based -> one-based */ - parts[DUK_DATE_IDX_DAY]++; /* -""- */ - } - - if (dparts != NULL) { - for (i = 0; i < DUK_DATE_IDX_NUM_PARTS; i++) { - dparts[i] = (duk_double_t) parts[i]; - } - } -} - -/* Compute time value from (double) parts. The parts can be either UTC - * or local time; if local, they need to be (conceptually) converted into - * UTC time. The parts may represent valid or invalid time, and may be - * wildly out of range (but may cancel each other and still come out in - * the valid Date range). - */ -DUK_INTERNAL duk_double_t duk_bi_date_get_timeval_from_dparts(duk_double_t *dparts, duk_small_uint_t flags) { -#if defined(DUK_USE_PARANOID_DATE_COMPUTATION) - /* See comments below on MakeTime why these are volatile. */ - volatile duk_double_t tmp_time; - volatile duk_double_t tmp_day; - volatile duk_double_t d; -#else - duk_double_t tmp_time; - duk_double_t tmp_day; - duk_double_t d; -#endif - duk_small_uint_t i; - duk_int_t tzoff, tzoffprev1, tzoffprev2; - - /* Expects 'this' at top of stack on entry. */ - - /* Coerce all finite parts with ToInteger(). ToInteger() must not - * be called for NaN/Infinity because it will convert e.g. NaN to - * zero. If ToInteger() has already been called, this has no side - * effects and is idempotent. - * - * Don't read dparts[DUK_DATE_IDX_WEEKDAY]; it will cause Valgrind - * issues if the value is uninitialized. - */ - for (i = 0; i <= DUK_DATE_IDX_MILLISECOND; i++) { - /* SCANBUILD: scan-build complains here about assigned value - * being garbage or undefined. This is correct but operating - * on undefined values has no ill effect and is ignored by the - * caller in the case where this happens. - */ - d = dparts[i]; - if (DUK_ISFINITE(d)) { - dparts[i] = duk_js_tointeger_number(d); - } - } - - /* Use explicit steps in computation to try to ensure that - * computation happens with intermediate results coerced to - * double values (instead of using something more accurate). - * E.g. E5.1 Section 15.9.1.11 requires use of IEEE 754 - * rules (= ECMAScript '+' and '*' operators). - * - * Without 'volatile' even this approach fails on some platform - * and compiler combinations. For instance, gcc 4.8.1 on Ubuntu - * 64-bit, with -m32 and without -std=c99, test-bi-date-canceling.js - * would fail because of some optimizations when computing tmp_time - * (MakeTime below). Adding 'volatile' to tmp_time solved this - * particular problem (annoyingly, also adding debug prints or - * running the executable under valgrind hides it). - */ - - /* MakeTime */ - tmp_time = 0.0; - tmp_time += dparts[DUK_DATE_IDX_HOUR] * ((duk_double_t) DUK_DATE_MSEC_HOUR); - tmp_time += dparts[DUK_DATE_IDX_MINUTE] * ((duk_double_t) DUK_DATE_MSEC_MINUTE); - tmp_time += dparts[DUK_DATE_IDX_SECOND] * ((duk_double_t) DUK_DATE_MSEC_SECOND); - tmp_time += dparts[DUK_DATE_IDX_MILLISECOND]; - - /* MakeDay */ - tmp_day = duk__make_day(dparts[DUK_DATE_IDX_YEAR], dparts[DUK_DATE_IDX_MONTH], dparts[DUK_DATE_IDX_DAY]); - - /* MakeDate */ - d = tmp_day * ((duk_double_t) DUK_DATE_MSEC_DAY) + tmp_time; - - DUK_DDD(DUK_DDDPRINT("time=%lf day=%lf --> timeval=%lf", (double) tmp_time, (double) tmp_day, (double) d)); - - /* Optional UTC conversion. */ - if (flags & DUK_DATE_FLAG_LOCALTIME) { - /* DUK_USE_DATE_GET_LOCAL_TZOFFSET() needs to be called with a - * time value computed from UTC parts. At this point we only - * have 'd' which is a time value computed from local parts, so - * it is off by the UTC-to-local time offset which we don't know - * yet. The current solution for computing the UTC-to-local - * time offset is to iterate a few times and detect a fixed - * point or a two-cycle loop (or a sanity iteration limit), - * see test-bi-date-local-parts.js and test-bi-date-tzoffset-basic-fi.js. - * - * E5.1 Section 15.9.1.9: - * UTC(t) = t - LocalTZA - DaylightSavingTA(t - LocalTZA) - * - * For NaN/inf, DUK_USE_DATE_GET_LOCAL_TZOFFSET() returns 0. - */ - -#if 0 - /* Old solution: don't iterate, incorrect */ - tzoff = DUK_USE_DATE_GET_LOCAL_TZOFFSET(d); - DUK_DDD(DUK_DDDPRINT("tzoffset w/o iteration, tzoff=%ld", (long) tzoff)); - d -= tzoff * 1000L; - DUK_UNREF(tzoffprev1); - DUK_UNREF(tzoffprev2); -#endif - - /* Iteration solution */ - tzoff = 0; - tzoffprev1 = 999999999L; /* invalid value which never matches */ - for (i = 0; i < DUK__LOCAL_TZOFFSET_MAXITER; i++) { - tzoffprev2 = tzoffprev1; - tzoffprev1 = tzoff; - tzoff = DUK_USE_DATE_GET_LOCAL_TZOFFSET(d - tzoff * 1000L); - DUK_DDD(DUK_DDDPRINT("tzoffset iteration, i=%d, tzoff=%ld, tzoffprev1=%ld tzoffprev2=%ld", - (int) i, - (long) tzoff, - (long) tzoffprev1, - (long) tzoffprev2)); - if (tzoff == tzoffprev1) { - DUK_DDD(DUK_DDDPRINT("tzoffset iteration finished, i=%d, tzoff=%ld, tzoffprev1=%ld, tzoffprev2=%ld", - (int) i, - (long) tzoff, - (long) tzoffprev1, - (long) tzoffprev2)); - break; - } else if (tzoff == tzoffprev2) { - /* Two value cycle, see e.g. test-bi-date-tzoffset-basic-fi.js. - * In these cases, favor a higher tzoffset to get a consistent - * result which is independent of iteration count. Not sure if - * this is a generically correct solution. - */ - DUK_DDD(DUK_DDDPRINT( - "tzoffset iteration two-value cycle, i=%d, tzoff=%ld, tzoffprev1=%ld, tzoffprev2=%ld", - (int) i, - (long) tzoff, - (long) tzoffprev1, - (long) tzoffprev2)); - if (tzoffprev1 > tzoff) { - tzoff = tzoffprev1; - } - break; - } - } - DUK_DDD(DUK_DDDPRINT("tzoffset iteration, tzoff=%ld", (long) tzoff)); - d -= tzoff * 1000L; - } - - /* TimeClip(), which also handles Infinity -> NaN conversion */ - d = duk__timeclip(d); - - return d; -} - -/* - * API oriented helpers - */ - -/* Push 'this' binding, check that it is a Date object; then push the - * internal time value. At the end, stack is: [ ... this timeval ]. - * Returns the time value. Local time adjustment is done if requested. - */ -DUK_LOCAL duk_double_t duk__push_this_get_timeval_tzoffset(duk_hthread *thr, duk_small_uint_t flags, duk_int_t *out_tzoffset) { - duk_hobject *h; - duk_double_t d; - duk_int_t tzoffset = 0; - - duk_push_this(thr); - h = duk_get_hobject(thr, -1); /* XXX: getter with class check, useful in built-ins */ - if (h == NULL || DUK_HOBJECT_GET_CLASS_NUMBER(h) != DUK_HOBJECT_CLASS_DATE) { - DUK_ERROR_TYPE(thr, "expected Date"); - DUK_WO_NORETURN(return 0.0;); - } - - duk_xget_owndataprop_stridx_short(thr, -1, DUK_STRIDX_INT_VALUE); - d = duk_to_number_m1(thr); - duk_pop(thr); - - if (DUK_ISNAN(d)) { - if (flags & DUK_DATE_FLAG_NAN_TO_ZERO) { - d = 0.0; - } - if (flags & DUK_DATE_FLAG_NAN_TO_RANGE_ERROR) { - DUK_ERROR_RANGE(thr, "Invalid Date"); - DUK_WO_NORETURN(return 0.0;); - } - } - /* if no NaN handling flag, may still be NaN here, but not Inf */ - DUK_ASSERT(!DUK_ISINF(d)); - - if (flags & DUK_DATE_FLAG_LOCALTIME) { - /* Note: DST adjustment is determined using UTC time. - * If 'd' is NaN, tzoffset will be 0. - */ - tzoffset = DUK_USE_DATE_GET_LOCAL_TZOFFSET(d); /* seconds */ - d += tzoffset * 1000L; - } - if (out_tzoffset) { - *out_tzoffset = tzoffset; - } - - /* [ ... this ] */ - return d; -} - -DUK_LOCAL duk_double_t duk__push_this_get_timeval(duk_hthread *thr, duk_small_uint_t flags) { - return duk__push_this_get_timeval_tzoffset(thr, flags, NULL); -} - -/* Set timeval to 'this' from dparts, push the new time value onto the - * value stack and return 1 (caller can then tail call us). Expects - * the value stack to contain 'this' on the stack top. - */ -DUK_LOCAL duk_ret_t duk__set_this_timeval_from_dparts(duk_hthread *thr, duk_double_t *dparts, duk_small_uint_t flags) { - duk_double_t d; - - /* [ ... this ] */ - - d = duk_bi_date_get_timeval_from_dparts(dparts, flags); - duk_push_number(thr, d); /* -> [ ... this timeval_new ] */ - duk_dup_top(thr); /* -> [ ... this timeval_new timeval_new ] */ - - /* Must force write because e.g. .setYear() must work even when - * the Date instance is frozen. - */ - duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_W); - - /* Stack top: new time value, return 1 to allow tail calls. */ - return 1; -} - -/* 'out_buf' must be at least DUK_BI_DATE_ISO8601_BUFSIZE long. */ -DUK_LOCAL void duk__format_parts_iso8601(duk_int_t *parts, duk_int_t tzoffset, duk_small_uint_t flags, duk_uint8_t *out_buf) { - char yearstr[8]; /* "-123456\0" */ - char tzstr[8]; /* "+11:22\0" */ - char sep = (flags & DUK_DATE_FLAG_SEP_T) ? DUK_ASC_UC_T : DUK_ASC_SPACE; - - DUK_ASSERT(parts[DUK_DATE_IDX_MONTH] >= 1 && parts[DUK_DATE_IDX_MONTH] <= 12); - DUK_ASSERT(parts[DUK_DATE_IDX_DAY] >= 1 && parts[DUK_DATE_IDX_DAY] <= 31); - DUK_ASSERT(parts[DUK_DATE_IDX_YEAR] >= -999999 && parts[DUK_DATE_IDX_YEAR] <= 999999); - - /* Note: %06d for positive value, %07d for negative value to include - * sign and 6 digits. - */ - DUK_SNPRINTF(yearstr, - sizeof(yearstr), - (parts[DUK_DATE_IDX_YEAR] >= 0 && parts[DUK_DATE_IDX_YEAR] <= 9999) ? - "%04ld" : - ((parts[DUK_DATE_IDX_YEAR] >= 0) ? "+%06ld" : "%07ld"), - (long) parts[DUK_DATE_IDX_YEAR]); - yearstr[sizeof(yearstr) - 1] = (char) 0; - - if (flags & DUK_DATE_FLAG_LOCALTIME) { - /* tzoffset seconds are dropped; 16 bits suffice for - * time offset in minutes - */ - const char *fmt; - duk_small_int_t tmp, arg_hours, arg_minutes; - - if (tzoffset >= 0) { - tmp = tzoffset; - fmt = "+%02d:%02d"; - } else { - tmp = -tzoffset; - fmt = "-%02d:%02d"; - } - tmp = tmp / 60; - arg_hours = tmp / 60; - arg_minutes = tmp % 60; - DUK_ASSERT(arg_hours <= 24); /* Even less is actually guaranteed for a valid tzoffset. */ - arg_hours = arg_hours & 0x3f; /* For [0,24] this is a no-op, but fixes GCC 7 warning, see - https://github.com/svaarala/duktape/issues/1602. */ - - DUK_SNPRINTF(tzstr, sizeof(tzstr), fmt, (int) arg_hours, (int) arg_minutes); - tzstr[sizeof(tzstr) - 1] = (char) 0; - } else { - tzstr[0] = DUK_ASC_UC_Z; - tzstr[1] = (char) 0; - } - - /* Unlike year, the other parts fit into 16 bits so %d format - * is portable. - */ - if ((flags & DUK_DATE_FLAG_TOSTRING_DATE) && (flags & DUK_DATE_FLAG_TOSTRING_TIME)) { - DUK_SPRINTF((char *) out_buf, - "%s-%02d-%02d%c%02d:%02d:%02d.%03d%s", - (const char *) yearstr, - (int) parts[DUK_DATE_IDX_MONTH], - (int) parts[DUK_DATE_IDX_DAY], - (int) sep, - (int) parts[DUK_DATE_IDX_HOUR], - (int) parts[DUK_DATE_IDX_MINUTE], - (int) parts[DUK_DATE_IDX_SECOND], - (int) parts[DUK_DATE_IDX_MILLISECOND], - (const char *) tzstr); - } else if (flags & DUK_DATE_FLAG_TOSTRING_DATE) { - DUK_SPRINTF((char *) out_buf, - "%s-%02d-%02d", - (const char *) yearstr, - (int) parts[DUK_DATE_IDX_MONTH], - (int) parts[DUK_DATE_IDX_DAY]); - } else { - DUK_ASSERT(flags & DUK_DATE_FLAG_TOSTRING_TIME); - DUK_SPRINTF((char *) out_buf, - "%02d:%02d:%02d.%03d%s", - (int) parts[DUK_DATE_IDX_HOUR], - (int) parts[DUK_DATE_IDX_MINUTE], - (int) parts[DUK_DATE_IDX_SECOND], - (int) parts[DUK_DATE_IDX_MILLISECOND], - (const char *) tzstr); - } -} - -/* Helper for string conversion calls: check 'this' binding, get the - * internal time value, and format date and/or time in a few formats. - * Return value allows tail calls. - */ -DUK_LOCAL duk_ret_t duk__to_string_helper(duk_hthread *thr, duk_small_uint_t flags) { - duk_double_t d; - duk_int_t parts[DUK_DATE_IDX_NUM_PARTS]; - duk_int_t tzoffset; /* seconds, doesn't fit into 16 bits */ - duk_bool_t rc; - duk_uint8_t buf[DUK_BI_DATE_ISO8601_BUFSIZE]; - - DUK_UNREF(rc); /* unreferenced with some options */ - - d = duk__push_this_get_timeval_tzoffset(thr, flags, &tzoffset); - if (DUK_ISNAN(d)) { - duk_push_hstring_stridx(thr, DUK_STRIDX_INVALID_DATE); - return 1; - } - DUK_ASSERT(DUK_ISFINITE(d)); - - /* formatters always get one-based month/day-of-month */ - duk_bi_date_timeval_to_parts(d, parts, NULL, DUK_DATE_FLAG_ONEBASED); - DUK_ASSERT(parts[DUK_DATE_IDX_MONTH] >= 1 && parts[DUK_DATE_IDX_MONTH] <= 12); - DUK_ASSERT(parts[DUK_DATE_IDX_DAY] >= 1 && parts[DUK_DATE_IDX_DAY] <= 31); - - if (flags & DUK_DATE_FLAG_TOSTRING_LOCALE) { - /* try locale specific formatter; if it refuses to format the - * string, fall back to an ISO 8601 formatted value in local - * time. - */ -#if defined(DUK_USE_DATE_FORMAT_STRING) - /* Contract, either: - * - Push string to value stack and return 1 - * - Don't push anything and return 0 - */ - - rc = DUK_USE_DATE_FORMAT_STRING(thr, parts, tzoffset, flags); - if (rc != 0) { - return 1; - } -#else - /* No locale specific formatter; this is OK, we fall back - * to ISO 8601. - */ -#endif - } - - /* Different calling convention than above used because the helper - * is shared. - */ - duk__format_parts_iso8601(parts, tzoffset, flags, buf); - duk_push_string(thr, (const char *) buf); - return 1; -} - -/* Helper for component getter calls: check 'this' binding, get the - * internal time value, split it into parts (either as UTC time or - * local time), push a specified component as a return value to the - * value stack and return 1 (caller can then tail call us). - */ -DUK_LOCAL duk_ret_t duk__get_part_helper(duk_hthread *thr, duk_small_uint_t flags_and_idx) { - duk_double_t d; - duk_int_t parts[DUK_DATE_IDX_NUM_PARTS]; - duk_small_uint_t idx_part = (duk_small_uint_t) (flags_and_idx >> DUK_DATE_FLAG_VALUE_SHIFT); /* unpack args */ - - DUK_ASSERT_DISABLE(idx_part >= 0); /* unsigned */ - DUK_ASSERT(idx_part < DUK_DATE_IDX_NUM_PARTS); - - d = duk__push_this_get_timeval(thr, flags_and_idx); - if (DUK_ISNAN(d)) { - duk_push_nan(thr); - return 1; - } - DUK_ASSERT(DUK_ISFINITE(d)); - - duk_bi_date_timeval_to_parts(d, parts, NULL, flags_and_idx); /* no need to mask idx portion */ - - /* Setter APIs detect special year numbers (0...99) and apply a +1900 - * only in certain cases. The legacy getYear() getter applies -1900 - * unconditionally. - */ - duk_push_int(thr, (flags_and_idx & DUK_DATE_FLAG_SUB1900) ? parts[idx_part] - 1900 : parts[idx_part]); - return 1; -} - -/* Helper for component setter calls: check 'this' binding, get the - * internal time value, split it into parts (either as UTC time or - * local time), modify one or more components as specified, recompute - * the time value, set it as the internal value. Finally, push the - * new time value as a return value to the value stack and return 1 - * (caller can then tail call us). - */ -DUK_LOCAL duk_ret_t duk__set_part_helper(duk_hthread *thr, duk_small_uint_t flags_and_maxnargs) { - duk_double_t d; - duk_int_t parts[DUK_DATE_IDX_NUM_PARTS]; - duk_double_t dparts[DUK_DATE_IDX_NUM_PARTS]; - duk_idx_t nargs; - duk_small_uint_t maxnargs = (duk_small_uint_t) (flags_and_maxnargs >> DUK_DATE_FLAG_VALUE_SHIFT); /* unpack args */ - duk_small_uint_t idx_first, idx; - duk_small_uint_t i; - - nargs = duk_get_top(thr); - d = duk__push_this_get_timeval(thr, flags_and_maxnargs); - DUK_ASSERT(DUK_ISFINITE(d) || DUK_ISNAN(d)); - - if (DUK_ISFINITE(d)) { - duk_bi_date_timeval_to_parts(d, parts, dparts, flags_and_maxnargs); - } else { - /* NaN timevalue: we need to coerce the arguments, but - * the resulting internal timestamp needs to remain NaN. - * This works but is not pretty: parts and dparts will - * be partially uninitialized, but we only write to them. - */ - } - - /* - * Determining which datetime components to overwrite based on - * stack arguments is a bit complicated, but important to factor - * out from setters themselves for compactness. - * - * If DUK_DATE_FLAG_TIMESETTER, maxnargs indicates setter type: - * - * 1 -> millisecond - * 2 -> second, [millisecond] - * 3 -> minute, [second], [millisecond] - * 4 -> hour, [minute], [second], [millisecond] - * - * Else: - * - * 1 -> date - * 2 -> month, [date] - * 3 -> year, [month], [date] - * - * By comparing nargs and maxnargs (and flags) we know which - * components to override. We rely on part index ordering. - */ - - if (flags_and_maxnargs & DUK_DATE_FLAG_TIMESETTER) { - DUK_ASSERT(maxnargs >= 1 && maxnargs <= 4); - idx_first = DUK_DATE_IDX_MILLISECOND - (maxnargs - 1); - } else { - DUK_ASSERT(maxnargs >= 1 && maxnargs <= 3); - idx_first = DUK_DATE_IDX_DAY - (maxnargs - 1); - } - DUK_ASSERT_DISABLE(idx_first >= 0); /* unsigned */ - DUK_ASSERT(idx_first < DUK_DATE_IDX_NUM_PARTS); - - for (i = 0; i < maxnargs; i++) { - if ((duk_idx_t) i >= nargs) { - /* no argument given -> leave components untouched */ - break; - } - idx = idx_first + i; - DUK_ASSERT_DISABLE(idx >= 0); /* unsigned */ - DUK_ASSERT(idx < DUK_DATE_IDX_NUM_PARTS); - - if (idx == DUK_DATE_IDX_YEAR && (flags_and_maxnargs & DUK_DATE_FLAG_YEAR_FIXUP)) { - duk__twodigit_year_fixup(thr, (duk_idx_t) i); - } - - dparts[idx] = duk_to_number(thr, (duk_idx_t) i); - - if (idx == DUK_DATE_IDX_DAY) { - /* Day-of-month is one-based in the API, but zero-based - * internally, so fix here. Note that month is zero-based - * both in the API and internally. - */ - /* SCANBUILD: complains about use of uninitialized values. - * The complaint is correct, but operating in undefined - * values here is intentional in some cases and the caller - * ignores the results. - */ - dparts[idx] -= 1.0; - } - } - - /* Leaves new timevalue on stack top and returns 1, which is correct - * for part setters. - */ - if (DUK_ISFINITE(d)) { - return duk__set_this_timeval_from_dparts(thr, dparts, flags_and_maxnargs); - } else { - /* Internal timevalue is already NaN, so don't touch it. */ - duk_push_nan(thr); - return 1; - } -} - -/* Apply ToNumber() to specified index; if ToInteger(val) in [0,99], add - * 1900 and replace value at idx_val. - */ -DUK_LOCAL void duk__twodigit_year_fixup(duk_hthread *thr, duk_idx_t idx_val) { - duk_double_t d; - - /* XXX: idx_val would fit into 16 bits, but using duk_small_uint_t - * might not generate better code due to casting. - */ - - /* E5 Sections 15.9.3.1, B.2.4, B.2.5 */ - duk_to_number(thr, idx_val); - if (duk_is_nan(thr, idx_val)) { - return; - } - duk_dup(thr, idx_val); - duk_to_int(thr, -1); - d = duk_get_number(thr, -1); /* get as double to handle huge numbers correctly */ - if (d >= 0.0 && d <= 99.0) { - d += 1900.0; - duk_push_number(thr, d); - duk_replace(thr, idx_val); - } - duk_pop(thr); -} - -/* Set datetime parts from stack arguments, defaulting any missing values. - * Day-of-week is not set; it is not required when setting the time value. - */ -DUK_LOCAL void duk__set_parts_from_args(duk_hthread *thr, duk_double_t *dparts, duk_idx_t nargs) { - duk_double_t d; - duk_small_uint_t i; - duk_small_uint_t idx; - - /* Causes a ToNumber() coercion, but doesn't break coercion order since - * year is coerced first anyway. - */ - duk__twodigit_year_fixup(thr, 0); - - /* There are at most 7 args, but we use 8 here so that also - * DUK_DATE_IDX_WEEKDAY gets initialized (to zero) to avoid the potential - * for any Valgrind gripes later. - */ - for (i = 0; i < 8; i++) { - /* Note: rely on index ordering */ - idx = DUK_DATE_IDX_YEAR + i; - if ((duk_idx_t) i < nargs) { - d = duk_to_number(thr, (duk_idx_t) i); - if (idx == DUK_DATE_IDX_DAY) { - /* Convert day from one-based to zero-based (internal). This may - * cause the day part to be negative, which is OK. - */ - d -= 1.0; - } - } else { - /* All components default to 0 except day-of-month which defaults - * to 1. However, because our internal day-of-month is zero-based, - * it also defaults to zero here. - */ - d = 0.0; - } - dparts[idx] = d; - } - - DUK_DDD(DUK_DDDPRINT("parts from args -> %lf %lf %lf %lf %lf %lf %lf %lf", - (double) dparts[0], - (double) dparts[1], - (double) dparts[2], - (double) dparts[3], - (double) dparts[4], - (double) dparts[5], - (double) dparts[6], - (double) dparts[7])); -} - -/* - * Indirect magic value lookup for Date methods. - * - * Date methods don't put their control flags into the function magic value - * because they wouldn't fit into a LIGHTFUNC's magic field. Instead, the - * magic value is set to an index pointing to the array of control flags - * below. - * - * This must be kept in strict sync with genbuiltins.py! - */ - -static duk_uint16_t duk__date_magics[] = { - /* 0: toString */ - DUK_DATE_FLAG_TOSTRING_DATE + DUK_DATE_FLAG_TOSTRING_TIME + DUK_DATE_FLAG_LOCALTIME, - - /* 1: toDateString */ - DUK_DATE_FLAG_TOSTRING_DATE + DUK_DATE_FLAG_LOCALTIME, - - /* 2: toTimeString */ - DUK_DATE_FLAG_TOSTRING_TIME + DUK_DATE_FLAG_LOCALTIME, - - /* 3: toLocaleString */ - DUK_DATE_FLAG_TOSTRING_DATE + DUK_DATE_FLAG_TOSTRING_TIME + DUK_DATE_FLAG_TOSTRING_LOCALE + DUK_DATE_FLAG_LOCALTIME, - - /* 4: toLocaleDateString */ - DUK_DATE_FLAG_TOSTRING_DATE + DUK_DATE_FLAG_TOSTRING_LOCALE + DUK_DATE_FLAG_LOCALTIME, - - /* 5: toLocaleTimeString */ - DUK_DATE_FLAG_TOSTRING_TIME + DUK_DATE_FLAG_TOSTRING_LOCALE + DUK_DATE_FLAG_LOCALTIME, - - /* 6: toUTCString */ - DUK_DATE_FLAG_TOSTRING_DATE + DUK_DATE_FLAG_TOSTRING_TIME, - - /* 7: toISOString */ - DUK_DATE_FLAG_TOSTRING_DATE + DUK_DATE_FLAG_TOSTRING_TIME + DUK_DATE_FLAG_NAN_TO_RANGE_ERROR + DUK_DATE_FLAG_SEP_T, - - /* 8: getFullYear */ - DUK_DATE_FLAG_LOCALTIME + (DUK_DATE_IDX_YEAR << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 9: getUTCFullYear */ - 0 + (DUK_DATE_IDX_YEAR << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 10: getMonth */ - DUK_DATE_FLAG_LOCALTIME + (DUK_DATE_IDX_MONTH << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 11: getUTCMonth */ - 0 + (DUK_DATE_IDX_MONTH << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 12: getDate */ - DUK_DATE_FLAG_ONEBASED + DUK_DATE_FLAG_LOCALTIME + (DUK_DATE_IDX_DAY << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 13: getUTCDate */ - DUK_DATE_FLAG_ONEBASED + (DUK_DATE_IDX_DAY << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 14: getDay */ - DUK_DATE_FLAG_LOCALTIME + (DUK_DATE_IDX_WEEKDAY << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 15: getUTCDay */ - 0 + (DUK_DATE_IDX_WEEKDAY << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 16: getHours */ - DUK_DATE_FLAG_LOCALTIME + (DUK_DATE_IDX_HOUR << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 17: getUTCHours */ - 0 + (DUK_DATE_IDX_HOUR << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 18: getMinutes */ - DUK_DATE_FLAG_LOCALTIME + (DUK_DATE_IDX_MINUTE << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 19: getUTCMinutes */ - 0 + (DUK_DATE_IDX_MINUTE << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 20: getSeconds */ - DUK_DATE_FLAG_LOCALTIME + (DUK_DATE_IDX_SECOND << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 21: getUTCSeconds */ - 0 + (DUK_DATE_IDX_SECOND << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 22: getMilliseconds */ - DUK_DATE_FLAG_LOCALTIME + (DUK_DATE_IDX_MILLISECOND << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 23: getUTCMilliseconds */ - 0 + (DUK_DATE_IDX_MILLISECOND << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 24: setMilliseconds */ - DUK_DATE_FLAG_TIMESETTER + DUK_DATE_FLAG_LOCALTIME + (1 << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 25: setUTCMilliseconds */ - DUK_DATE_FLAG_TIMESETTER + (1 << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 26: setSeconds */ - DUK_DATE_FLAG_TIMESETTER + DUK_DATE_FLAG_LOCALTIME + (2 << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 27: setUTCSeconds */ - DUK_DATE_FLAG_TIMESETTER + (2 << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 28: setMinutes */ - DUK_DATE_FLAG_TIMESETTER + DUK_DATE_FLAG_LOCALTIME + (3 << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 29: setUTCMinutes */ - DUK_DATE_FLAG_TIMESETTER + (3 << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 30: setHours */ - DUK_DATE_FLAG_TIMESETTER + DUK_DATE_FLAG_LOCALTIME + (4 << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 31: setUTCHours */ - DUK_DATE_FLAG_TIMESETTER + (4 << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 32: setDate */ - DUK_DATE_FLAG_LOCALTIME + (1 << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 33: setUTCDate */ - 0 + (1 << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 34: setMonth */ - DUK_DATE_FLAG_LOCALTIME + (2 << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 35: setUTCMonth */ - 0 + (2 << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 36: setFullYear */ - DUK_DATE_FLAG_NAN_TO_ZERO + DUK_DATE_FLAG_LOCALTIME + (3 << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 37: setUTCFullYear */ - DUK_DATE_FLAG_NAN_TO_ZERO + (3 << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 38: getYear */ - DUK_DATE_FLAG_LOCALTIME + DUK_DATE_FLAG_SUB1900 + (DUK_DATE_IDX_YEAR << DUK_DATE_FLAG_VALUE_SHIFT), - - /* 39: setYear */ - DUK_DATE_FLAG_NAN_TO_ZERO + DUK_DATE_FLAG_YEAR_FIXUP + (3 << DUK_DATE_FLAG_VALUE_SHIFT), -}; - -DUK_LOCAL duk_small_uint_t duk__date_get_indirect_magic(duk_hthread *thr) { - duk_small_uint_t magicidx = (duk_small_uint_t) duk_get_current_magic(thr); - DUK_ASSERT(magicidx < (duk_small_int_t) (sizeof(duk__date_magics) / sizeof(duk_uint16_t))); - return (duk_small_uint_t) duk__date_magics[magicidx]; -} - -#if defined(DUK_USE_DATE_BUILTIN) -/* - * Constructor calls - */ - -DUK_INTERNAL duk_ret_t duk_bi_date_constructor(duk_hthread *thr) { - duk_idx_t nargs = duk_get_top(thr); - duk_bool_t is_cons = duk_is_constructor_call(thr); - duk_double_t dparts[DUK_DATE_IDX_NUM_PARTS]; - duk_double_t d; - - DUK_DDD(DUK_DDDPRINT("Date constructor, nargs=%ld, is_cons=%ld", (long) nargs, (long) is_cons)); - - (void) duk_push_object_helper(thr, - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_FASTREFS | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DATE), - DUK_BIDX_DATE_PROTOTYPE); - - /* Unlike most built-ins, the internal [[PrimitiveValue]] of a Date - * is mutable. - */ - - if (nargs == 0 || !is_cons) { - d = duk__timeclip(duk_time_get_ecmascript_time_nofrac(thr)); - duk_push_number(thr, d); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_W); - if (!is_cons) { - /* called as a normal function: return new Date().toString() */ - duk_to_string(thr, -1); - } - return 1; - } else if (nargs == 1) { - const char *str; - duk_to_primitive(thr, 0, DUK_HINT_NONE); - str = duk_get_string_notsymbol(thr, 0); - if (str) { - duk__parse_string(thr, str); - duk_replace(thr, 0); /* may be NaN */ - } - d = duk__timeclip(duk_to_number(thr, 0)); /* symbols fail here */ - duk_push_number(thr, d); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_W); - return 1; - } - - duk__set_parts_from_args(thr, dparts, nargs); - - /* Parts are in local time, convert when setting. */ - - (void) duk__set_this_timeval_from_dparts(thr, dparts, DUK_DATE_FLAG_LOCALTIME /*flags*/); /* -> [ ... this timeval ] */ - duk_pop(thr); /* -> [ ... this ] */ - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_date_constructor_parse(duk_hthread *thr) { - return duk__parse_string(thr, duk_to_string(thr, 0)); -} - -DUK_INTERNAL duk_ret_t duk_bi_date_constructor_utc(duk_hthread *thr) { - duk_idx_t nargs = duk_get_top(thr); - duk_double_t dparts[DUK_DATE_IDX_NUM_PARTS]; - duk_double_t d; - - /* Behavior for nargs < 2 is implementation dependent: currently we'll - * set a NaN time value (matching V8 behavior) in this case. - */ - - if (nargs < 2) { - duk_push_nan(thr); - } else { - duk__set_parts_from_args(thr, dparts, nargs); - d = duk_bi_date_get_timeval_from_dparts(dparts, 0 /*flags*/); - duk_push_number(thr, d); - } - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_date_constructor_now(duk_hthread *thr) { - duk_double_t d; - - d = duk_time_get_ecmascript_time_nofrac(thr); - DUK_ASSERT(duk_double_equals(duk__timeclip(d), d)); /* TimeClip() should never be necessary */ - duk_push_number(thr, d); - return 1; -} - -/* - * String/JSON conversions - * - * Human readable conversions are now basically ISO 8601 with a space - * (instead of 'T') as the date/time separator. This is a good baseline - * and is platform independent. - * - * A shared native helper to provide many conversions. Magic value contains - * a set of flags. The helper provides: - * - * toString() - * toDateString() - * toTimeString() - * toLocaleString() - * toLocaleDateString() - * toLocaleTimeString() - * toUTCString() - * toISOString() - * - * Notes: - * - * - Date.prototype.toGMTString() and Date.prototype.toUTCString() are - * required to be the same ECMAScript function object (!), so it is - * omitted from here. - * - * - Date.prototype.toUTCString(): E5.1 specification does not require a - * specific format, but result should be human readable. The - * specification suggests using ISO 8601 format with a space (instead - * of 'T') separator if a more human readable format is not available. - * - * - Date.prototype.toISOString(): unlike other conversion functions, - * toISOString() requires a RangeError for invalid date values. - */ - -DUK_INTERNAL duk_ret_t duk_bi_date_prototype_tostring_shared(duk_hthread *thr) { - duk_small_uint_t flags = duk__date_get_indirect_magic(thr); - return duk__to_string_helper(thr, flags); -} - -DUK_INTERNAL duk_ret_t duk_bi_date_prototype_value_of(duk_hthread *thr) { - /* This native function is also used for Date.prototype.getTime() - * as their behavior is identical. - */ - - duk_double_t d = duk__push_this_get_timeval(thr, 0 /*flags*/); /* -> [ this ] */ - DUK_ASSERT(DUK_ISFINITE(d) || DUK_ISNAN(d)); - duk_push_number(thr, d); - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_date_prototype_to_json(duk_hthread *thr) { - /* Note: toJSON() is a generic function which works even if 'this' - * is not a Date. The sole argument is ignored. - */ - - duk_push_this(thr); - duk_to_object(thr, -1); - - duk_dup_top(thr); - duk_to_primitive(thr, -1, DUK_HINT_NUMBER); - if (duk_is_number(thr, -1)) { - duk_double_t d = duk_get_number(thr, -1); - if (!DUK_ISFINITE(d)) { - duk_push_null(thr); - return 1; - } - } - duk_pop(thr); - - duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_TO_ISO_STRING); - duk_dup_m2(thr); /* -> [ O toIsoString O ] */ - duk_call_method(thr, 0); - return 1; -} - -/* - * Getters. - * - * Implementing getters is quite easy. The internal time value is either - * NaN, or represents milliseconds (without fractions) from Jan 1, 1970. - * The internal time value can be converted to integer parts, and each - * part will be normalized and will fit into a 32-bit signed integer. - * - * A shared native helper to provide all getters. Magic value contains - * a set of flags and also packs the date component index argument. The - * helper provides: - * - * getFullYear() - * getUTCFullYear() - * getMonth() - * getUTCMonth() - * getDate() - * getUTCDate() - * getDay() - * getUTCDay() - * getHours() - * getUTCHours() - * getMinutes() - * getUTCMinutes() - * getSeconds() - * getUTCSeconds() - * getMilliseconds() - * getUTCMilliseconds() - * getYear() - * - * Notes: - * - * - Date.prototype.getDate(): 'date' means day-of-month, and is - * zero-based in internal calculations but public API expects it to - * be one-based. - * - * - Date.prototype.getTime() and Date.prototype.valueOf() have identical - * behavior. They have separate function objects, but share the same C - * function (duk_bi_date_prototype_value_of). - */ - -DUK_INTERNAL duk_ret_t duk_bi_date_prototype_get_shared(duk_hthread *thr) { - duk_small_uint_t flags_and_idx = duk__date_get_indirect_magic(thr); - return duk__get_part_helper(thr, flags_and_idx); -} - -DUK_INTERNAL duk_ret_t duk_bi_date_prototype_get_timezone_offset(duk_hthread *thr) { - /* - * Return (t - LocalTime(t)) in minutes: - * - * t - LocalTime(t) = t - (t + LocalTZA + DaylightSavingTA(t)) - * = -(LocalTZA + DaylightSavingTA(t)) - * - * where DaylightSavingTA() is checked for time 't'. - * - * Note that the sign of the result is opposite to common usage, - * e.g. for EE(S)T which normally is +2h or +3h from UTC, this - * function returns -120 or -180. - * - */ - - duk_double_t d; - duk_int_t tzoffset; - - /* Note: DST adjustment is determined using UTC time. */ - d = duk__push_this_get_timeval(thr, 0 /*flags*/); - DUK_ASSERT(DUK_ISFINITE(d) || DUK_ISNAN(d)); - if (DUK_ISNAN(d)) { - duk_push_nan(thr); - } else { - DUK_ASSERT(DUK_ISFINITE(d)); - tzoffset = DUK_USE_DATE_GET_LOCAL_TZOFFSET(d); - duk_push_int(thr, -tzoffset / 60); - } - return 1; -} - -/* - * Setters. - * - * Setters are a bit more complicated than getters. Component setters - * break down the current time value into its (normalized) component - * parts, replace one or more components with -unnormalized- new values, - * and the components are then converted back into a time value. As an - * example of using unnormalized values: - * - * var d = new Date(1234567890); - * - * is equivalent to: - * - * var d = new Date(0); - * d.setUTCMilliseconds(1234567890); - * - * A shared native helper to provide almost all setters. Magic value - * contains a set of flags and also packs the "maxnargs" argument. The - * helper provides: - * - * setMilliseconds() - * setUTCMilliseconds() - * setSeconds() - * setUTCSeconds() - * setMinutes() - * setUTCMinutes() - * setHours() - * setUTCHours() - * setDate() - * setUTCDate() - * setMonth() - * setUTCMonth() - * setFullYear() - * setUTCFullYear() - * setYear() - * - * Notes: - * - * - Date.prototype.setYear() (Section B addition): special year check - * is omitted. NaN / Infinity will just flow through and ultimately - * result in a NaN internal time value. - * - * - Date.prototype.setYear() does not have optional arguments for - * setting month and day-in-month (like setFullYear()), but we indicate - * 'maxnargs' to be 3 to get the year written to the correct component - * index in duk__set_part_helper(). The function has nargs == 1, so only - * the year will be set regardless of actual argument count. - */ - -DUK_INTERNAL duk_ret_t duk_bi_date_prototype_set_shared(duk_hthread *thr) { - duk_small_uint_t flags_and_maxnargs = duk__date_get_indirect_magic(thr); - return duk__set_part_helper(thr, flags_and_maxnargs); -} - -DUK_INTERNAL duk_ret_t duk_bi_date_prototype_set_time(duk_hthread *thr) { - duk_double_t d; - - (void) duk__push_this_get_timeval(thr, 0 /*flags*/); /* -> [ timeval this ] */ - d = duk__timeclip(duk_to_number(thr, 0)); - duk_push_number(thr, d); - duk_dup_top(thr); - /* Must force write because .setTime() must work even when - * the Date instance is frozen. - */ - duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_W); - /* -> [ timeval this timeval ] */ - - return 1; -} - -/* - * Misc. - */ - -#if defined(DUK_USE_SYMBOL_BUILTIN) -DUK_INTERNAL duk_ret_t duk_bi_date_prototype_toprimitive(duk_hthread *thr) { - duk_size_t hintlen; - const char *hintstr; - duk_int_t hint; - - /* Invokes OrdinaryToPrimitive() with suitable hint. Note that the - * method is generic, and works on non-Date arguments too. - * - * https://www.ecma-international.org/ecma-262/6.0/#sec-date.prototype-@@toprimitive - */ - - duk_push_this(thr); - duk_require_object(thr, -1); - DUK_ASSERT_TOP(thr, 2); - - hintstr = duk_require_lstring(thr, 0, &hintlen); - if ((hintlen == 6 && DUK_STRCMP(hintstr, "string") == 0) || (hintlen == 7 && DUK_STRCMP(hintstr, "default") == 0)) { - hint = DUK_HINT_STRING; - } else if (hintlen == 6 && DUK_STRCMP(hintstr, "number") == 0) { - hint = DUK_HINT_NUMBER; - } else { - DUK_DCERROR_TYPE_INVALID_ARGS(thr); - } - - duk_to_primitive_ordinary(thr, -1, hint); - return 1; -} -#endif /* DUK_USE_SYMBOL_BUILTIN */ - -#endif /* DUK_USE_DATE_BUILTIN */ - -/* automatic undefs */ -#undef DUK__CF_ACCEPT -#undef DUK__CF_ACCEPT_NUL -#undef DUK__CF_NEG -#undef DUK__DPRINT_DPARTS -#undef DUK__DPRINT_PARTS -#undef DUK__DPRINT_PARTS_AND_DPARTS -#undef DUK__LOCAL_TZOFFSET_MAXITER -#undef DUK__NUM_ISO8601_PARSER_PARTS -#undef DUK__PACK_RULE -#undef DUK__PI_DAY -#undef DUK__PI_HOUR -#undef DUK__PI_MILLISECOND -#undef DUK__PI_MINUTE -#undef DUK__PI_MONTH -#undef DUK__PI_SECOND -#undef DUK__PI_TZHOUR -#undef DUK__PI_TZMINUTE -#undef DUK__PI_YEAR -#undef DUK__PM_DAY -#undef DUK__PM_HOUR -#undef DUK__PM_MILLISECOND -#undef DUK__PM_MINUTE -#undef DUK__PM_MONTH -#undef DUK__PM_SECOND -#undef DUK__PM_TZHOUR -#undef DUK__PM_TZMINUTE -#undef DUK__PM_YEAR -#undef DUK__RULE_MASK_PART_SEP -#undef DUK__SI_COLON -#undef DUK__SI_MINUS -#undef DUK__SI_NUL -#undef DUK__SI_PERIOD -#undef DUK__SI_PLUS -#undef DUK__SI_SPACE -#undef DUK__SI_T -#undef DUK__SI_Z -#undef DUK__SM_COLON -#undef DUK__SM_MINUS -#undef DUK__SM_NUL -#undef DUK__SM_PERIOD -#undef DUK__SM_PLUS -#undef DUK__SM_SPACE -#undef DUK__SM_T -#undef DUK__SM_Z -#undef DUK__UNPACK_RULE -#undef DUK__WEEKDAY_MOD_ADDER -#undef DUK__YEAR -/* - * Unix-like Date providers - * - * Generally useful Unix / POSIX / ANSI Date providers. - */ - -/* #include duk_internal.h -> already included */ - -/* The necessary #includes are in place in duk_config.h. */ - -/* Buffer sizes for some UNIX calls. Larger than strictly necessary - * to avoid Valgrind errors. - */ -#define DUK__STRPTIME_BUF_SIZE 64 -#define DUK__STRFTIME_BUF_SIZE 64 - -#if defined(DUK_USE_DATE_NOW_GETTIMEOFDAY) -/* Get current ECMAScript time (= UNIX/Posix time, but in milliseconds). */ -DUK_INTERNAL duk_double_t duk_bi_date_get_now_gettimeofday(void) { - struct timeval tv; - duk_double_t d; - - if (gettimeofday(&tv, NULL) != 0) { - DUK_D(DUK_DPRINT("gettimeofday() failed")); - return 0.0; - } - - /* As of Duktape 2.2.0 allow fractions. */ - d = ((duk_double_t) tv.tv_sec) * 1000.0 + ((duk_double_t) tv.tv_usec) / 1000.0; - - return d; -} -#endif /* DUK_USE_DATE_NOW_GETTIMEOFDAY */ - -#if defined(DUK_USE_DATE_NOW_TIME) -/* Not a very good provider: only full seconds are available. */ -DUK_INTERNAL duk_double_t duk_bi_date_get_now_time(void) { - time_t t; - - t = time(NULL); - if (t == (time_t) -1) { - DUK_D(DUK_DPRINT("time() failed")); - return 0.0; - } - return ((duk_double_t) t) * 1000.0; -} -#endif /* DUK_USE_DATE_NOW_TIME */ - -#if defined(DUK_USE_DATE_TZO_GMTIME) || defined(DUK_USE_DATE_TZO_GMTIME_R) || defined(DUK_USE_DATE_TZO_GMTIME_S) -/* Get local time offset (in seconds) for a certain (UTC) instant 'd'. */ -DUK_INTERNAL duk_int_t duk_bi_date_get_local_tzoffset_gmtime(duk_double_t d) { - time_t t, t1, t2; - duk_int_t parts[DUK_DATE_IDX_NUM_PARTS]; - duk_double_t dparts[DUK_DATE_IDX_NUM_PARTS]; - struct tm tms[2]; -#if defined(DUK_USE_DATE_TZO_GMTIME) - struct tm *tm_ptr; -#endif - - /* For NaN/inf, the return value doesn't matter. */ - if (!DUK_ISFINITE(d)) { - return 0; - } - - /* If not within ECMAScript range, some integer time calculations - * won't work correctly (and some asserts will fail), so bail out - * if so. This fixes test-bug-date-insane-setyear.js. There is - * a +/- 24h leeway in this range check to avoid a test262 corner - * case documented in test-bug-date-timeval-edges.js. - */ - if (!duk_bi_date_timeval_in_leeway_range(d)) { - DUK_DD(DUK_DDPRINT("timeval not within valid range, skip tzoffset computation to avoid integer overflows")); - return 0; - } - - /* - * This is a bit tricky to implement portably. The result depends - * on the timestamp (specifically, DST depends on the timestamp). - * If e.g. UNIX APIs are used, they'll have portability issues with - * very small and very large years. - * - * Current approach: - * - * - Stay within portable UNIX limits by using equivalent year mapping. - * Avoid year 1970 and 2038 as some conversions start to fail, at - * least on some platforms. Avoiding 1970 means that there are - * currently DST discrepancies for 1970. - * - * - Create a UTC and local time breakdowns from 't'. Then create - * a time_t using gmtime() and localtime() and compute the time - * difference between the two. - * - * Equivalent year mapping (E5 Section 15.9.1.8): - * - * If the host environment provides functionality for determining - * daylight saving time, the implementation of ECMAScript is free - * to map the year in question to an equivalent year (same - * leap-year-ness and same starting week day for the year) for which - * the host environment provides daylight saving time information. - * The only restriction is that all equivalent years should produce - * the same result. - * - * This approach is quite reasonable but not entirely correct, e.g. - * the specification also states (E5 Section 15.9.1.8): - * - * The implementation of ECMAScript should not try to determine - * whether the exact time was subject to daylight saving time, but - * just whether daylight saving time would have been in effect if - * the _current daylight saving time algorithm_ had been used at the - * time. This avoids complications such as taking into account the - * years that the locale observed daylight saving time year round. - * - * Since we rely on the platform APIs for conversions between local - * time and UTC, we can't guarantee the above. Rather, if the platform - * has historical DST rules they will be applied. This seems to be the - * general preferred direction in ECMAScript standardization (or at least - * implementations) anyway, and even the equivalent year mapping should - * be disabled if the platform is known to handle DST properly for the - * full ECMAScript range. - * - * The following has useful discussion and links: - * - * https://bugzilla.mozilla.org/show_bug.cgi?id=351066 - */ - - duk_bi_date_timeval_to_parts(d, parts, dparts, DUK_DATE_FLAG_EQUIVYEAR /*flags*/); - DUK_ASSERT(parts[DUK_DATE_IDX_YEAR] >= 1970 && parts[DUK_DATE_IDX_YEAR] <= 2038); - - d = duk_bi_date_get_timeval_from_dparts(dparts, 0 /*flags*/); - DUK_ASSERT(d >= 0 && d < 2147483648.0 * 1000.0); /* unsigned 31-bit range */ - t = (time_t) (d / 1000.0); - DUK_DDD(DUK_DDDPRINT("timeval: %lf -> time_t %ld", (double) d, (long) t)); - - duk_memzero((void *) tms, sizeof(struct tm) * 2); - -#if defined(DUK_USE_DATE_TZO_GMTIME_R) - (void) gmtime_r(&t, &tms[0]); - (void) localtime_r(&t, &tms[1]); -#elif defined(DUK_USE_DATE_TZO_GMTIME_S) - (void) gmtime_s(&t, &tms[0]); - (void) localtime_s(&t, &tms[1]); -#elif defined(DUK_USE_DATE_TZO_GMTIME) - tm_ptr = gmtime(&t); - duk_memcpy((void *) &tms[0], tm_ptr, sizeof(struct tm)); - tm_ptr = localtime(&t); - duk_memcpy((void *) &tms[1], tm_ptr, sizeof(struct tm)); -#else -#error internal error -#endif - DUK_DDD(DUK_DDDPRINT("gmtime result: tm={sec:%ld,min:%ld,hour:%ld,mday:%ld,mon:%ld,year:%ld," - "wday:%ld,yday:%ld,isdst:%ld}", - (long) tms[0].tm_sec, - (long) tms[0].tm_min, - (long) tms[0].tm_hour, - (long) tms[0].tm_mday, - (long) tms[0].tm_mon, - (long) tms[0].tm_year, - (long) tms[0].tm_wday, - (long) tms[0].tm_yday, - (long) tms[0].tm_isdst)); - DUK_DDD(DUK_DDDPRINT("localtime result: tm={sec:%ld,min:%ld,hour:%ld,mday:%ld,mon:%ld,year:%ld," - "wday:%ld,yday:%ld,isdst:%ld}", - (long) tms[1].tm_sec, - (long) tms[1].tm_min, - (long) tms[1].tm_hour, - (long) tms[1].tm_mday, - (long) tms[1].tm_mon, - (long) tms[1].tm_year, - (long) tms[1].tm_wday, - (long) tms[1].tm_yday, - (long) tms[1].tm_isdst)); - - /* tm_isdst is both an input and an output to mktime(), use 0 to - * avoid DST handling in mktime(): - * - https://github.com/svaarala/duktape/issues/406 - * - http://stackoverflow.com/questions/8558919/mktime-and-tm-isdst - */ - tms[0].tm_isdst = 0; - tms[1].tm_isdst = 0; - t1 = mktime(&tms[0]); /* UTC */ - t2 = mktime(&tms[1]); /* local */ - if (t1 == (time_t) -1 || t2 == (time_t) -1) { - /* This check used to be for (t < 0) but on some platforms - * time_t is unsigned and apparently the proper way to detect - * an mktime() error return is the cast above. See e.g.: - * http://pubs.opengroup.org/onlinepubs/009695299/functions/mktime.html - */ - goto mktime_error; - } - DUK_DDD(DUK_DDDPRINT("t1=%ld (utc), t2=%ld (local)", (long) t1, (long) t2)); - - /* Compute final offset in seconds, positive if local time ahead of - * UTC (returned value is UTC-to-local offset). - * - * difftime() returns a double, so coercion to int generates quite - * a lot of code. Direct subtraction is not portable, however. - * XXX: allow direct subtraction on known platforms. - */ -#if 0 - return (duk_int_t) (t2 - t1); -#endif - return (duk_int_t) difftime(t2, t1); - -mktime_error: - /* XXX: return something more useful, so that caller can throw? */ - DUK_D(DUK_DPRINT("mktime() failed, d=%lf", (double) d)); - return 0; -} -#endif /* DUK_USE_DATE_TZO_GMTIME */ - -#if defined(DUK_USE_DATE_PRS_STRPTIME) -DUK_INTERNAL duk_bool_t duk_bi_date_parse_string_strptime(duk_hthread *thr, const char *str) { - struct tm tm; - time_t t; - char buf[DUK__STRPTIME_BUF_SIZE]; - - /* Copy to buffer with slack to avoid Valgrind gripes from strptime. */ - DUK_ASSERT(str != NULL); - duk_memzero(buf, sizeof(buf)); /* valgrind whine without this */ - DUK_SNPRINTF(buf, sizeof(buf), "%s", (const char *) str); - buf[sizeof(buf) - 1] = (char) 0; - - DUK_DDD(DUK_DDDPRINT("parsing: '%s'", (const char *) buf)); - - duk_memzero(&tm, sizeof(tm)); - if (strptime((const char *) buf, "%c", &tm) != NULL) { - DUK_DDD(DUK_DDDPRINT("before mktime: tm={sec:%ld,min:%ld,hour:%ld,mday:%ld,mon:%ld,year:%ld," - "wday:%ld,yday:%ld,isdst:%ld}", - (long) tm.tm_sec, - (long) tm.tm_min, - (long) tm.tm_hour, - (long) tm.tm_mday, - (long) tm.tm_mon, - (long) tm.tm_year, - (long) tm.tm_wday, - (long) tm.tm_yday, - (long) tm.tm_isdst)); - tm.tm_isdst = -1; /* negative: dst info not available */ - - t = mktime(&tm); - DUK_DDD(DUK_DDDPRINT("mktime() -> %ld", (long) t)); - if (t >= 0) { - duk_push_number(thr, ((duk_double_t) t) * 1000.0); - return 1; - } - } - - return 0; -} -#endif /* DUK_USE_DATE_PRS_STRPTIME */ - -#if defined(DUK_USE_DATE_PRS_GETDATE) -DUK_INTERNAL duk_bool_t duk_bi_date_parse_string_getdate(duk_hthread *thr, const char *str) { - struct tm tm; - duk_small_int_t rc; - time_t t; - - /* For this to work, DATEMSK must be set, so this is not very - * convenient for an embeddable interpreter. - */ - - duk_memzero(&tm, sizeof(struct tm)); - rc = (duk_small_int_t) getdate_r(str, &tm); - DUK_DDD(DUK_DDDPRINT("getdate_r() -> %ld", (long) rc)); - - if (rc == 0) { - t = mktime(&tm); - DUK_DDD(DUK_DDDPRINT("mktime() -> %ld", (long) t)); - if (t >= 0) { - duk_push_number(thr, (duk_double_t) t); - return 1; - } - } - - return 0; -} -#endif /* DUK_USE_DATE_PRS_GETDATE */ - -#if defined(DUK_USE_DATE_FMT_STRFTIME) -DUK_INTERNAL duk_bool_t duk_bi_date_format_parts_strftime(duk_hthread *thr, - duk_int_t *parts, - duk_int_t tzoffset, - duk_small_uint_t flags) { - char buf[DUK__STRFTIME_BUF_SIZE]; - struct tm tm; - const char *fmt; - - DUK_UNREF(tzoffset); - - /* If the platform doesn't support the entire ECMAScript range, we need - * to return 0 so that the caller can fall back to the default formatter. - * - * For now, assume that if time_t is 8 bytes or more, the whole ECMAScript - * range is supported. For smaller time_t values (4 bytes in practice), - * assumes that the signed 32-bit range is supported. - * - * XXX: detect this more correctly per platform. The size of time_t is - * probably not an accurate guarantee of strftime() supporting or not - * supporting a large time range (the full ECMAScript range). - */ - if (sizeof(time_t) < 8 && (parts[DUK_DATE_IDX_YEAR] < 1970 || parts[DUK_DATE_IDX_YEAR] > 2037)) { - /* be paranoid for 32-bit time values (even avoiding negative ones) */ - return 0; - } - - duk_memzero(&tm, sizeof(tm)); - tm.tm_sec = parts[DUK_DATE_IDX_SECOND]; - tm.tm_min = parts[DUK_DATE_IDX_MINUTE]; - tm.tm_hour = parts[DUK_DATE_IDX_HOUR]; - tm.tm_mday = parts[DUK_DATE_IDX_DAY]; /* already one-based */ - tm.tm_mon = parts[DUK_DATE_IDX_MONTH] - 1; /* one-based -> zero-based */ - tm.tm_year = parts[DUK_DATE_IDX_YEAR] - 1900; - tm.tm_wday = parts[DUK_DATE_IDX_WEEKDAY]; - tm.tm_isdst = 0; - - duk_memzero(buf, sizeof(buf)); - if ((flags & DUK_DATE_FLAG_TOSTRING_DATE) && (flags & DUK_DATE_FLAG_TOSTRING_TIME)) { - fmt = "%c"; - } else if (flags & DUK_DATE_FLAG_TOSTRING_DATE) { - fmt = "%x"; - } else { - DUK_ASSERT(flags & DUK_DATE_FLAG_TOSTRING_TIME); - fmt = "%X"; - } - (void) strftime(buf, sizeof(buf) - 1, fmt, &tm); - DUK_ASSERT(buf[sizeof(buf) - 1] == 0); - - duk_push_string(thr, buf); - return 1; -} -#endif /* DUK_USE_DATE_FMT_STRFTIME */ - -#if defined(DUK_USE_GET_MONOTONIC_TIME_CLOCK_GETTIME) -DUK_INTERNAL duk_double_t duk_bi_date_get_monotonic_time_clock_gettime(void) { - struct timespec ts; - - if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { - return (duk_double_t) ts.tv_sec * 1000.0 + (duk_double_t) ts.tv_nsec / 1000000.0; - } else { - DUK_D(DUK_DPRINT("clock_gettime(CLOCK_MONOTONIC) failed")); - return 0.0; - } -} -#endif - -/* automatic undefs */ -#undef DUK__STRFTIME_BUF_SIZE -#undef DUK__STRPTIME_BUF_SIZE -/* - * Windows Date providers - * - * Platform specific links: - * - * - http://msdn.microsoft.com/en-us/library/windows/desktop/ms725473(v=vs.85).aspx - */ - -/* #include duk_internal.h -> already included */ - -/* The necessary #includes are in place in duk_config.h. */ - -#if defined(DUK_USE_DATE_NOW_WINDOWS) || defined(DUK_USE_DATE_TZO_WINDOWS) -/* Shared Windows helpers. */ -DUK_LOCAL void duk__convert_systime_to_ularge(const SYSTEMTIME *st, ULARGE_INTEGER *res) { - FILETIME ft; - if (SystemTimeToFileTime(st, &ft) == 0) { - DUK_D(DUK_DPRINT("SystemTimeToFileTime() failed, returning 0")); - res->QuadPart = 0; - } else { - res->LowPart = ft.dwLowDateTime; - res->HighPart = ft.dwHighDateTime; - } -} - -#if defined(DUK_USE_DATE_NOW_WINDOWS_SUBMS) -DUK_LOCAL void duk__convert_filetime_to_ularge(const FILETIME *ft, ULARGE_INTEGER *res) { - res->LowPart = ft->dwLowDateTime; - res->HighPart = ft->dwHighDateTime; -} -#endif /* DUK_USE_DATE_NOW_WINDOWS_SUBMS */ - -DUK_LOCAL void duk__set_systime_jan1970(SYSTEMTIME *st) { - duk_memzero((void *) st, sizeof(*st)); - st->wYear = 1970; - st->wMonth = 1; - st->wDayOfWeek = 4; /* not sure whether or not needed; Thursday */ - st->wDay = 1; - DUK_ASSERT(st->wHour == 0); - DUK_ASSERT(st->wMinute == 0); - DUK_ASSERT(st->wSecond == 0); - DUK_ASSERT(st->wMilliseconds == 0); -} -#endif /* defined(DUK_USE_DATE_NOW_WINDOWS) || defined(DUK_USE_DATE_TZO_WINDOWS) */ - -#if defined(DUK_USE_DATE_NOW_WINDOWS) -DUK_INTERNAL duk_double_t duk_bi_date_get_now_windows(void) { - /* Suggested step-by-step method from documentation of RtlTimeToSecondsSince1970: - * http://msdn.microsoft.com/en-us/library/windows/desktop/ms724928(v=vs.85).aspx - */ - SYSTEMTIME st1, st2; - ULARGE_INTEGER tmp1, tmp2; - - GetSystemTime(&st1); - duk__convert_systime_to_ularge((const SYSTEMTIME *) &st1, &tmp1); - - duk__set_systime_jan1970(&st2); - duk__convert_systime_to_ularge((const SYSTEMTIME *) &st2, &tmp2); - - /* Difference is in 100ns units, convert to milliseconds, keeping - * fractions since Duktape 2.2.0. This is only theoretical because - * SYSTEMTIME is limited to milliseconds. - */ - return (duk_double_t) ((LONGLONG) tmp1.QuadPart - (LONGLONG) tmp2.QuadPart) / 10000.0; -} -#endif /* DUK_USE_DATE_NOW_WINDOWS */ - -#if defined(DUK_USE_DATE_NOW_WINDOWS_SUBMS) -DUK_INTERNAL duk_double_t duk_bi_date_get_now_windows_subms(void) { - /* Variant of the basic algorithm using GetSystemTimePreciseAsFileTime() - * for more accuracy. - */ - FILETIME ft1; - SYSTEMTIME st2; - ULARGE_INTEGER tmp1, tmp2; - - GetSystemTimePreciseAsFileTime(&ft1); - duk__convert_filetime_to_ularge((const FILETIME *) &ft1, &tmp1); - - duk__set_systime_jan1970(&st2); - duk__convert_systime_to_ularge((const SYSTEMTIME *) &st2, &tmp2); - - /* Difference is in 100ns units, convert to milliseconds, keeping - * fractions since Duktape 2.2.0. - */ - return (duk_double_t) ((LONGLONG) tmp1.QuadPart - (LONGLONG) tmp2.QuadPart) / 10000.0; -} -#endif /* DUK_USE_DATE_NOW_WINDOWS */ - -#if defined(DUK_USE_DATE_TZO_WINDOWS) -DUK_INTERNAL duk_int_t duk_bi_date_get_local_tzoffset_windows(duk_double_t d) { - SYSTEMTIME st1; - SYSTEMTIME st2; - SYSTEMTIME st3; - ULARGE_INTEGER tmp1; - ULARGE_INTEGER tmp2; - ULARGE_INTEGER tmp3; - FILETIME ft1; - - /* XXX: handling of timestamps outside Windows supported range. - * How does Windows deal with dates before 1600? Does windows - * support all ECMAScript years (like -200000 and +200000)? - * Should equivalent year mapping be used here too? If so, use - * a shared helper (currently integrated into timeval-to-parts). - */ - - /* Use the approach described in "Remarks" of FileTimeToLocalFileTime: - * http://msdn.microsoft.com/en-us/library/windows/desktop/ms724277(v=vs.85).aspx - */ - - duk__set_systime_jan1970(&st1); - duk__convert_systime_to_ularge((const SYSTEMTIME *) &st1, &tmp1); - tmp2.QuadPart = (ULONGLONG) (d * 10000.0); /* millisec -> 100ns units since jan 1, 1970 */ - tmp2.QuadPart += tmp1.QuadPart; /* input 'd' in Windows UTC, 100ns units */ - - ft1.dwLowDateTime = tmp2.LowPart; - ft1.dwHighDateTime = tmp2.HighPart; - if (FileTimeToSystemTime((const FILETIME *) &ft1, &st2) == 0) { - DUK_D(DUK_DPRINT("FileTimeToSystemTime() failed, return tzoffset 0")); - return 0; - } - if (SystemTimeToTzSpecificLocalTime((LPTIME_ZONE_INFORMATION) NULL, &st2, &st3) == 0) { - DUK_D(DUK_DPRINT("SystemTimeToTzSpecificLocalTime() failed, return tzoffset 0")); - return 0; - } - duk__convert_systime_to_ularge((const SYSTEMTIME *) &st3, &tmp3); - - /* Positive if local time ahead of UTC. */ - return (duk_int_t) (((LONGLONG) tmp3.QuadPart - (LONGLONG) tmp2.QuadPart) / DUK_I64_CONSTANT(10000000)); /* seconds */ -} -#endif /* DUK_USE_DATE_TZO_WINDOWS */ - -#if defined(DUK_USE_DATE_TZO_WINDOWS_NO_DST) -DUK_INTERNAL duk_int_t duk_bi_date_get_local_tzoffset_windows_no_dst(duk_double_t d) { - SYSTEMTIME st1; - SYSTEMTIME st2; - FILETIME ft1; - FILETIME ft2; - ULARGE_INTEGER tmp1; - ULARGE_INTEGER tmp2; - - /* Do a similar computation to duk_bi_date_get_local_tzoffset_windows - * but without accounting for daylight savings time. Use this on - * Windows platforms (like Durango) that don't support the - * SystemTimeToTzSpecificLocalTime() call. - */ - - /* current time not needed for this computation */ - DUK_UNREF(d); - - duk__set_systime_jan1970(&st1); - duk__convert_systime_to_ularge((const SYSTEMTIME *) &st1, &tmp1); - - ft1.dwLowDateTime = tmp1.LowPart; - ft1.dwHighDateTime = tmp1.HighPart; - if (FileTimeToLocalFileTime((const FILETIME *) &ft1, &ft2) == 0) { - DUK_D(DUK_DPRINT("FileTimeToLocalFileTime() failed, return tzoffset 0")); - return 0; - } - if (FileTimeToSystemTime((const FILETIME *) &ft2, &st2) == 0) { - DUK_D(DUK_DPRINT("FileTimeToSystemTime() failed, return tzoffset 0")); - return 0; - } - duk__convert_systime_to_ularge((const SYSTEMTIME *) &st2, &tmp2); - - return (duk_int_t) (((LONGLONG) tmp2.QuadPart - (LONGLONG) tmp1.QuadPart) / DUK_I64_CONSTANT(10000000)); /* seconds */ -} -#endif /* DUK_USE_DATE_TZO_WINDOWS_NO_DST */ - -#if defined(DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC) -DUK_INTERNAL duk_double_t duk_bi_date_get_monotonic_time_windows_qpc(void) { - LARGE_INTEGER count, freq; - - /* There are legacy issues with QueryPerformanceCounter(): - * - Potential jumps: - * https://support.microsoft.com/en-us/help/274323/performance-counter-value-may-unexpectedly-leap-forward - * - Differences between cores (XP): - * https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx#qpc_support_in_windows_versions - * - * We avoid these by enabling QPC by default only for Vista or later. - */ - - if (QueryPerformanceCounter(&count) && QueryPerformanceFrequency(&freq)) { - /* XXX: QueryPerformanceFrequency() can be cached */ - return (duk_double_t) count.QuadPart / (duk_double_t) freq.QuadPart * 1000.0; - } else { - /* MSDN: "On systems that run Windows XP or later, the function - * will always succeed and will thus never return zero." - * Provide minimal error path just in case user enables this - * feature in pre-XP Windows. - */ - return 0.0; - } -} -#endif /* DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC */ -/* - * Duktape built-ins - * - * Size optimization note: it might seem that vararg multipurpose functions - * like fin(), enc(), and dec() are not very size optimal, but using a single - * user-visible ECMAScript function saves a lot of run-time footprint; each - * Function instance takes >100 bytes. Using a shared native helper and a - * 'magic' value won't save much if there are multiple Function instances - * anyway. - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_DUKTAPE_BUILTIN) - -DUK_INTERNAL duk_ret_t duk_bi_duktape_object_info(duk_hthread *thr) { - duk_inspect_value(thr, -1); - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_duktape_object_act(duk_hthread *thr) { - duk_int_t level; - - level = duk_to_int(thr, 0); - duk_inspect_callstack_entry(thr, level); - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_duktape_object_gc(duk_hthread *thr) { - duk_small_uint_t flags; - - flags = (duk_small_uint_t) duk_get_uint(thr, 0); - duk_heap_mark_and_sweep(thr->heap, flags); - - /* XXX: Not sure what the best return value would be in the API. - * Return true for now. - */ - duk_push_true(thr); - return 1; -} - -#if defined(DUK_USE_FINALIZER_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_duktape_object_fin(duk_hthread *thr) { - (void) duk_require_hobject(thr, 0); - if (duk_get_top(thr) >= 2) { - /* Set: currently a finalizer is disabled by setting it to - * undefined; this does not remove the property at the moment. - * The value could be type checked to be either a function - * or something else; if something else, the property could - * be deleted. Must use duk_set_finalizer() to keep - * DUK_HOBJECT_FLAG_HAVE_FINALIZER in sync. - */ - duk_set_top(thr, 2); - duk_set_finalizer(thr, 0); - return 0; - } else { - /* Get. */ - DUK_ASSERT(duk_get_top(thr) == 1); - duk_get_finalizer(thr, 0); - return 1; - } -} -#endif /* DUK_USE_FINALIZER_SUPPORT */ - -DUK_INTERNAL duk_ret_t duk_bi_duktape_object_enc(duk_hthread *thr) { - duk_hstring *h_str; - - /* Vararg function: must be careful to check/require arguments. - * The JSON helpers accept invalid indices and treat them like - * non-existent optional parameters. - */ - - h_str = duk_require_hstring(thr, 0); /* Could reject symbols, but no point: won't match comparisons. */ - duk_require_valid_index(thr, 1); - - if (h_str == DUK_HTHREAD_STRING_HEX(thr)) { - duk_set_top(thr, 2); - duk_hex_encode(thr, 1); - DUK_ASSERT_TOP(thr, 2); - } else if (h_str == DUK_HTHREAD_STRING_BASE64(thr)) { - duk_set_top(thr, 2); - duk_base64_encode(thr, 1); - DUK_ASSERT_TOP(thr, 2); -#if defined(DUK_USE_JSON_SUPPORT) && defined(DUK_USE_JX) - } else if (h_str == DUK_HTHREAD_STRING_JX(thr)) { - duk_bi_json_stringify_helper(thr, - 1 /*idx_value*/, - 2 /*idx_replacer*/, - 3 /*idx_space*/, - DUK_JSON_FLAG_EXT_CUSTOM | DUK_JSON_FLAG_ASCII_ONLY | - DUK_JSON_FLAG_AVOID_KEY_QUOTES /*flags*/); -#endif -#if defined(DUK_USE_JSON_SUPPORT) && defined(DUK_USE_JC) - } else if (h_str == DUK_HTHREAD_STRING_JC(thr)) { - duk_bi_json_stringify_helper(thr, - 1 /*idx_value*/, - 2 /*idx_replacer*/, - 3 /*idx_space*/, - DUK_JSON_FLAG_EXT_COMPATIBLE | DUK_JSON_FLAG_ASCII_ONLY /*flags*/); -#endif - } else { - DUK_DCERROR_TYPE_INVALID_ARGS(thr); - } - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_duktape_object_dec(duk_hthread *thr) { - duk_hstring *h_str; - - /* Vararg function: must be careful to check/require arguments. - * The JSON helpers accept invalid indices and treat them like - * non-existent optional parameters. - */ - - h_str = duk_require_hstring(thr, 0); /* Could reject symbols, but no point: won't match comparisons */ - duk_require_valid_index(thr, 1); - - if (h_str == DUK_HTHREAD_STRING_HEX(thr)) { - duk_set_top(thr, 2); - duk_hex_decode(thr, 1); - DUK_ASSERT_TOP(thr, 2); - } else if (h_str == DUK_HTHREAD_STRING_BASE64(thr)) { - duk_set_top(thr, 2); - duk_base64_decode(thr, 1); - DUK_ASSERT_TOP(thr, 2); -#if defined(DUK_USE_JSON_SUPPORT) && defined(DUK_USE_JX) - } else if (h_str == DUK_HTHREAD_STRING_JX(thr)) { - duk_bi_json_parse_helper(thr, 1 /*idx_value*/, 2 /*idx_replacer*/, DUK_JSON_FLAG_EXT_CUSTOM /*flags*/); -#endif -#if defined(DUK_USE_JSON_SUPPORT) && defined(DUK_USE_JC) - } else if (h_str == DUK_HTHREAD_STRING_JC(thr)) { - duk_bi_json_parse_helper(thr, 1 /*idx_value*/, 2 /*idx_replacer*/, DUK_JSON_FLAG_EXT_COMPATIBLE /*flags*/); -#endif - } else { - DUK_DCERROR_TYPE_INVALID_ARGS(thr); - } - return 1; -} - -/* - * Compact an object - */ - -DUK_INTERNAL duk_ret_t duk_bi_duktape_object_compact(duk_hthread *thr) { - DUK_ASSERT_TOP(thr, 1); - duk_compact(thr, 0); - return 1; /* return the argument object */ -} - -#endif /* DUK_USE_DUKTAPE_BUILTIN */ -/* - * WHATWG Encoding API built-ins - * - * API specification: https://encoding.spec.whatwg.org/#api - * Web IDL: https://www.w3.org/TR/WebIDL/ - */ - -/* #include duk_internal.h -> already included */ - -/* - * Data structures for encoding/decoding - */ - -typedef struct { - duk_uint8_t *out; /* where to write next byte(s) */ - duk_codepoint_t lead; /* lead surrogate */ -} duk__encode_context; - -typedef struct { - /* UTF-8 decoding state */ - duk_codepoint_t codepoint; /* built up incrementally */ - duk_uint8_t upper; /* max value of next byte (decode error otherwise) */ - duk_uint8_t lower; /* min value of next byte (ditto) */ - duk_uint8_t needed; /* how many more bytes we need */ - duk_uint8_t bom_handled; /* BOM seen or no longer expected */ - - /* Decoder configuration */ - duk_uint8_t fatal; - duk_uint8_t ignore_bom; -} duk__decode_context; - -/* The signed duk_codepoint_t type is used to signal a decoded codepoint - * (>= 0) or various other states using negative values. - */ -#define DUK__CP_CONTINUE (-1) /* continue to next byte, no completed codepoint */ -#define DUK__CP_ERROR (-2) /* decoding error */ -#define DUK__CP_RETRY (-3) /* decoding error; retry last byte */ - -/* - * Raw helpers for encoding/decoding - */ - -/* Emit UTF-8 (= CESU-8) encoded U+FFFD (replacement char), i.e. ef bf bd. */ -DUK_LOCAL duk_uint8_t *duk__utf8_emit_repl(duk_uint8_t *ptr) { - *ptr++ = 0xef; - *ptr++ = 0xbf; - *ptr++ = 0xbd; - return ptr; -} - -DUK_LOCAL void duk__utf8_decode_init(duk__decode_context *dec_ctx) { - /* (Re)init the decoding state of 'dec_ctx' but leave decoder - * configuration fields untouched. - */ - dec_ctx->codepoint = 0x0000L; - dec_ctx->upper = 0xbf; - dec_ctx->lower = 0x80; - dec_ctx->needed = 0; - dec_ctx->bom_handled = 0; -} - -DUK_LOCAL duk_codepoint_t duk__utf8_decode_next(duk__decode_context *dec_ctx, duk_uint8_t x) { - /* - * UTF-8 algorithm based on the Encoding specification: - * https://encoding.spec.whatwg.org/#utf-8-decoder - * - * Two main states: decoding initial byte vs. decoding continuation - * bytes. Shortest length encoding is validated by restricting the - * allowed range of first continuation byte using 'lower' and 'upper'. - */ - - if (dec_ctx->needed == 0) { - /* process initial byte */ - if (x <= 0x7f) { - /* U+0000-U+007F, 1 byte (ASCII) */ - return (duk_codepoint_t) x; - } else if (x >= 0xc2 && x <= 0xdf) { - /* U+0080-U+07FF, 2 bytes */ - dec_ctx->needed = 1; - dec_ctx->codepoint = x & 0x1f; - DUK_ASSERT(dec_ctx->lower == 0x80); - DUK_ASSERT(dec_ctx->upper == 0xbf); - return DUK__CP_CONTINUE; - } else if (x >= 0xe0 && x <= 0xef) { - /* U+0800-U+FFFF, 3 bytes */ - if (x == 0xe0) { - dec_ctx->lower = 0xa0; - DUK_ASSERT(dec_ctx->upper == 0xbf); - } else if (x == 0xed) { - DUK_ASSERT(dec_ctx->lower == 0x80); - dec_ctx->upper = 0x9f; - } - dec_ctx->needed = 2; - dec_ctx->codepoint = x & 0x0f; - return DUK__CP_CONTINUE; - } else if (x >= 0xf0 && x <= 0xf4) { - /* U+010000-U+10FFFF, 4 bytes */ - if (x == 0xf0) { - dec_ctx->lower = 0x90; - DUK_ASSERT(dec_ctx->upper == 0xbf); - } else if (x == 0xf4) { - DUK_ASSERT(dec_ctx->lower == 0x80); - dec_ctx->upper = 0x8f; - } - dec_ctx->needed = 3; - dec_ctx->codepoint = x & 0x07; - return DUK__CP_CONTINUE; - } else { - /* not a legal initial byte */ - return DUK__CP_ERROR; - } - } else { - /* process continuation byte */ - if (x >= dec_ctx->lower && x <= dec_ctx->upper) { - dec_ctx->lower = 0x80; - dec_ctx->upper = 0xbf; - dec_ctx->codepoint = (dec_ctx->codepoint << 6) | (x & 0x3f); - if (--dec_ctx->needed > 0) { - /* need more bytes */ - return DUK__CP_CONTINUE; - } else { - /* got a codepoint */ - duk_codepoint_t ret; - DUK_ASSERT(dec_ctx->codepoint <= 0x10ffffL); /* Decoding rules guarantee. */ - ret = dec_ctx->codepoint; - dec_ctx->codepoint = 0x0000L; - dec_ctx->needed = 0; - return ret; - } - } else { - /* We just encountered an illegal UTF-8 continuation byte. This might - * be the initial byte of the next character; if we return a plain - * error status and the decoder is in replacement mode, the character - * will be masked. We still need to alert the caller to the error - * though. - */ - dec_ctx->codepoint = 0x0000L; - dec_ctx->needed = 0; - dec_ctx->lower = 0x80; - dec_ctx->upper = 0xbf; - return DUK__CP_RETRY; - } - } -} - -#if defined(DUK_USE_ENCODING_BUILTINS) -DUK_LOCAL void duk__utf8_encode_char(void *udata, duk_codepoint_t codepoint) { - duk__encode_context *enc_ctx; - - DUK_ASSERT(codepoint >= 0); - enc_ctx = (duk__encode_context *) udata; - DUK_ASSERT(enc_ctx != NULL); - -#if !defined(DUK_USE_PREFER_SIZE) - if (codepoint <= 0x7f && enc_ctx->lead == 0x0000L) { - /* Fast path for ASCII. */ - *enc_ctx->out++ = (duk_uint8_t) codepoint; - return; - } -#endif - - if (DUK_UNLIKELY(codepoint > 0x10ffffL)) { - /* cannot legally encode in UTF-8 */ - codepoint = DUK_UNICODE_CP_REPLACEMENT_CHARACTER; - } else if (codepoint >= 0xd800L && codepoint <= 0xdfffL) { - if (codepoint <= 0xdbffL) { - /* high surrogate */ - duk_codepoint_t prev_lead = enc_ctx->lead; - enc_ctx->lead = codepoint; - if (prev_lead == 0x0000L) { - /* high surrogate, no output */ - return; - } else { - /* consecutive high surrogates, consider first one unpaired */ - codepoint = DUK_UNICODE_CP_REPLACEMENT_CHARACTER; - } - } else { - /* low surrogate */ - if (enc_ctx->lead != 0x0000L) { - codepoint = - (duk_codepoint_t) (0x010000L + ((enc_ctx->lead - 0xd800L) << 10) + (codepoint - 0xdc00L)); - enc_ctx->lead = 0x0000L; - } else { - /* unpaired low surrogate */ - DUK_ASSERT(enc_ctx->lead == 0x0000L); - codepoint = DUK_UNICODE_CP_REPLACEMENT_CHARACTER; - } - } - } else { - if (enc_ctx->lead != 0x0000L) { - /* unpaired high surrogate: emit replacement character and the input codepoint */ - enc_ctx->lead = 0x0000L; - enc_ctx->out = duk__utf8_emit_repl(enc_ctx->out); - } - } - - /* Codepoint may be original input, a decoded surrogate pair, or may - * have been replaced with U+FFFD. - */ - enc_ctx->out += duk_unicode_encode_xutf8((duk_ucodepoint_t) codepoint, enc_ctx->out); -} -#endif /* DUK_USE_ENCODING_BUILTINS */ - -/* Shared helper for buffer-to-string using a TextDecoder() compatible UTF-8 - * decoder. - */ -DUK_LOCAL duk_ret_t duk__decode_helper(duk_hthread *thr, duk__decode_context *dec_ctx) { - const duk_uint8_t *input; - duk_size_t len = 0; - duk_size_t len_tmp; - duk_bool_t stream = 0; - duk_codepoint_t codepoint; - duk_uint8_t *output; - const duk_uint8_t *in; - duk_uint8_t *out; - - DUK_ASSERT(dec_ctx != NULL); - - /* Careful with input buffer pointer: any side effects involving - * code execution (e.g. getters, coercion calls, and finalizers) - * may cause a resize and invalidate a pointer we've read. This - * is why the pointer is actually looked up at the last minute. - * Argument validation must still happen first to match WHATWG - * required side effect order. - */ - - if (duk_is_undefined(thr, 0)) { - duk_push_fixed_buffer_nozero(thr, 0); - duk_replace(thr, 0); - } - (void) duk_require_buffer_data(thr, 0, &len); /* Need 'len', avoid pointer. */ - - if (duk_check_type_mask(thr, 1, DUK_TYPE_MASK_UNDEFINED | DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_NONE)) { - /* Use defaults, treat missing value like undefined. */ - } else { - duk_require_type_mask(thr, - 1, - DUK_TYPE_MASK_UNDEFINED | DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_LIGHTFUNC | - DUK_TYPE_MASK_BUFFER | DUK_TYPE_MASK_OBJECT); - if (duk_get_prop_literal(thr, 1, "stream")) { - stream = duk_to_boolean(thr, -1); - } - } - - /* Allowance is 3*len in the general case because all bytes may potentially - * become U+FFFD. If the first byte completes a non-BMP codepoint it will - * decode to a CESU-8 surrogate pair (6 bytes) so we allow 3 extra bytes to - * compensate: (1*3)+3 = 6. Non-BMP codepoints are safe otherwise because - * the 4->6 expansion is well under the 3x allowance. - * - * XXX: As with TextEncoder, need a better buffer allocation strategy here. - */ - if (len >= (DUK_HBUFFER_MAX_BYTELEN / 3) - 3) { - DUK_ERROR_TYPE(thr, DUK_STR_RESULT_TOO_LONG); - DUK_WO_NORETURN(return 0;); - } - output = - (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, 3 + (3 * len)); /* used parts will be always manually written over */ - - input = (const duk_uint8_t *) duk_get_buffer_data(thr, 0, &len_tmp); - DUK_ASSERT(input != NULL || len == 0); - if (DUK_UNLIKELY(len != len_tmp)) { - /* Very unlikely but possible: source buffer was resized by - * a side effect when fixed buffer was pushed. Output buffer - * may not be large enough to hold output, so just fail if - * length has changed. - */ - DUK_D(DUK_DPRINT("input buffer resized by side effect, fail")); - goto fail_type; - } - - /* From this point onwards it's critical that no side effect occur - * which may disturb 'input': finalizer execution, property accesses, - * active coercions, etc. Even an allocation related mark-and-sweep - * may affect the pointer because it may trigger a pending finalizer. - */ - - in = input; - out = output; - while (in < input + len) { - codepoint = duk__utf8_decode_next(dec_ctx, *in++); - if (codepoint < 0) { - if (codepoint == DUK__CP_CONTINUE) { - continue; - } - - /* Decoding error with or without retry. */ - DUK_ASSERT(codepoint == DUK__CP_ERROR || codepoint == DUK__CP_RETRY); - if (codepoint == DUK__CP_RETRY) { - --in; /* retry last byte */ - } - /* replacement mode: replace with U+FFFD */ - codepoint = DUK_UNICODE_CP_REPLACEMENT_CHARACTER; - if (dec_ctx->fatal) { - /* fatal mode: throw a TypeError */ - goto fail_type; - } - /* Continue with 'codepoint', Unicode replacement. */ - } - DUK_ASSERT(codepoint >= 0x0000L && codepoint <= 0x10ffffL); - - if (!dec_ctx->bom_handled) { - dec_ctx->bom_handled = 1; - if (codepoint == 0xfeffL && !dec_ctx->ignore_bom) { - continue; - } - } - - out += duk_unicode_encode_cesu8((duk_ucodepoint_t) codepoint, out); - DUK_ASSERT(out <= output + (3 + (3 * len))); - } - - if (!stream) { - if (dec_ctx->needed != 0) { - /* truncated sequence at end of buffer */ - if (dec_ctx->fatal) { - goto fail_type; - } else { - out += duk_unicode_encode_cesu8(DUK_UNICODE_CP_REPLACEMENT_CHARACTER, out); - DUK_ASSERT(out <= output + (3 + (3 * len))); - } - } - duk__utf8_decode_init(dec_ctx); /* Initialize decoding state for potential reuse. */ - } - - /* Output buffer is fixed and thus stable even if there had been - * side effects (which there shouldn't be). - */ - duk_push_lstring(thr, (const char *) output, (duk_size_t) (out - output)); - return 1; - -fail_type: - DUK_ERROR_TYPE(thr, DUK_STR_UTF8_DECODE_FAILED); - DUK_WO_NORETURN(return 0;); -} - -/* - * Built-in bindings - */ - -#if defined(DUK_USE_ENCODING_BUILTINS) -DUK_INTERNAL duk_ret_t duk_bi_textencoder_constructor(duk_hthread *thr) { - /* TextEncoder currently requires no persistent state, so the constructor - * does nothing on purpose. - */ - - duk_require_constructor_call(thr); - return 0; -} - -DUK_INTERNAL duk_ret_t duk_bi_textencoder_prototype_encoding_getter(duk_hthread *thr) { - duk_push_literal(thr, "utf-8"); - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_textencoder_prototype_encode(duk_hthread *thr) { - duk__encode_context enc_ctx; - duk_size_t len; - duk_size_t final_len; - duk_uint8_t *output; - - DUK_ASSERT_TOP(thr, 1); - if (duk_is_undefined(thr, 0)) { - len = 0; - } else { - duk_hstring *h_input; - - h_input = duk_to_hstring(thr, 0); - DUK_ASSERT(h_input != NULL); - - len = (duk_size_t) DUK_HSTRING_GET_CHARLEN(h_input); - if (len >= DUK_HBUFFER_MAX_BYTELEN / 3) { - DUK_ERROR_TYPE(thr, DUK_STR_RESULT_TOO_LONG); - DUK_WO_NORETURN(return 0;); - } - } - - /* Allowance is 3*len because all bytes can potentially be replaced with - * U+FFFD -- which rather inconveniently encodes to 3 bytes in UTF-8. - * Rely on dynamic buffer data pointer stability: no other code has - * access to the data pointer. - * - * XXX: The buffer allocation strategy used here is rather inefficient. - * Maybe switch to a chunk-based strategy, or preprocess the string to - * figure out the space needed ahead of time? - */ - DUK_ASSERT(3 * len >= len); - output = (duk_uint8_t *) duk_push_dynamic_buffer(thr, 3 * len); - - if (len > 0) { - DUK_ASSERT(duk_is_string(thr, 0)); /* True if len > 0. */ - - /* XXX: duk_decode_string() is used to process the input - * string. For standard ECMAScript strings, represented - * internally as CESU-8, this is fine. However, behavior - * beyond CESU-8 is not very strict: codepoints using an - * extended form of UTF-8 are also accepted, and invalid - * codepoint sequences (which are allowed in Duktape strings) - * are not handled as well as they could (e.g. invalid - * continuation bytes may mask following codepoints). - * This is how ECMAScript code would also see such strings. - * Maybe replace duk_decode_string() with an explicit strict - * CESU-8 decoder here? - */ - enc_ctx.lead = 0x0000L; - enc_ctx.out = output; - duk_decode_string(thr, 0, duk__utf8_encode_char, (void *) &enc_ctx); - if (enc_ctx.lead != 0x0000L) { - /* unpaired high surrogate at end of string */ - enc_ctx.out = duk__utf8_emit_repl(enc_ctx.out); - DUK_ASSERT(enc_ctx.out <= output + (3 * len)); - } - - /* The output buffer is usually very much oversized, so shrink it to - * actually needed size. Pointer stability assumed up to this point. - */ - DUK_ASSERT_TOP(thr, 2); - DUK_ASSERT(output == (duk_uint8_t *) duk_get_buffer_data(thr, -1, NULL)); - - final_len = (duk_size_t) (enc_ctx.out - output); - duk_resize_buffer(thr, -1, final_len); - /* 'output' and 'enc_ctx.out' are potentially invalidated by the resize. */ - } else { - final_len = 0; - } - - /* Standard WHATWG output is a Uint8Array. Here the Uint8Array will - * be backed by a dynamic buffer which differs from e.g. Uint8Arrays - * created as 'new Uint8Array(N)'. ECMAScript code won't see the - * difference but C code will. When bufferobjects are not supported, - * returns a plain dynamic buffer. - */ -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) - duk_push_buffer_object(thr, -1, 0, final_len, DUK_BUFOBJ_UINT8ARRAY); -#endif - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_textdecoder_constructor(duk_hthread *thr) { - duk__decode_context *dec_ctx; - duk_bool_t fatal = 0; - duk_bool_t ignore_bom = 0; - - DUK_ASSERT_TOP(thr, 2); - duk_require_constructor_call(thr); - if (!duk_is_undefined(thr, 0)) { - /* XXX: For now ignore 'label' (encoding identifier). */ - duk_to_string(thr, 0); - } - if (!duk_is_null_or_undefined(thr, 1)) { - if (duk_get_prop_literal(thr, 1, "fatal")) { - fatal = duk_to_boolean(thr, -1); - } - if (duk_get_prop_literal(thr, 1, "ignoreBOM")) { - ignore_bom = duk_to_boolean(thr, -1); - } - } - - duk_push_this(thr); - - /* The decode context is not assumed to be zeroed; all fields are - * initialized explicitly. - */ - dec_ctx = (duk__decode_context *) duk_push_fixed_buffer(thr, sizeof(duk__decode_context)); - dec_ctx->fatal = (duk_uint8_t) fatal; - dec_ctx->ignore_bom = (duk_uint8_t) ignore_bom; - duk__utf8_decode_init(dec_ctx); /* Initializes remaining fields. */ - - duk_put_prop_literal(thr, -2, DUK_INTERNAL_SYMBOL("Context")); - return 0; -} - -/* Get TextDecoder context from 'this'; leaves garbage on stack. */ -DUK_LOCAL duk__decode_context *duk__get_textdecoder_context(duk_hthread *thr) { - duk__decode_context *dec_ctx; - duk_push_this(thr); - duk_get_prop_literal(thr, -1, DUK_INTERNAL_SYMBOL("Context")); - dec_ctx = (duk__decode_context *) duk_require_buffer(thr, -1, NULL); - DUK_ASSERT(dec_ctx != NULL); - return dec_ctx; -} - -DUK_INTERNAL duk_ret_t duk_bi_textdecoder_prototype_shared_getter(duk_hthread *thr) { - duk__decode_context *dec_ctx; - duk_int_t magic; - - dec_ctx = duk__get_textdecoder_context(thr); - magic = duk_get_current_magic(thr); - switch (magic) { - case 0: - /* Encoding is now fixed, so _Context lookup is only needed to - * validate the 'this' binding (TypeError if not TextDecoder-like). - */ - duk_push_literal(thr, "utf-8"); - break; - case 1: - duk_push_boolean(thr, dec_ctx->fatal); - break; - default: - duk_push_boolean(thr, dec_ctx->ignore_bom); - break; - } - - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_textdecoder_prototype_decode(duk_hthread *thr) { - duk__decode_context *dec_ctx; - - dec_ctx = duk__get_textdecoder_context(thr); - return duk__decode_helper(thr, dec_ctx); -} -#endif /* DUK_USE_ENCODING_BUILTINS */ - -/* - * Internal helper for Node.js Buffer - */ - -/* Internal helper used for Node.js Buffer .toString(). Value stack convention - * is currently odd: it mimics TextDecoder .decode() so that argument must be at - * index 0, and decode options (not present for Buffer) at index 1. Return value - * is a Duktape/C function return value. - */ -DUK_INTERNAL duk_ret_t duk_textdecoder_decode_utf8_nodejs(duk_hthread *thr) { - duk__decode_context dec_ctx; - - dec_ctx.fatal = 0; /* use replacement chars */ - dec_ctx.ignore_bom = 1; /* ignore BOMs (matches Node.js Buffer .toString()) */ - duk__utf8_decode_init(&dec_ctx); - - return duk__decode_helper(thr, &dec_ctx); -} - -/* automatic undefs */ -#undef DUK__CP_CONTINUE -#undef DUK__CP_ERROR -#undef DUK__CP_RETRY -/* - * Error built-ins - */ - -/* #include duk_internal.h -> already included */ - -DUK_INTERNAL duk_ret_t duk_bi_error_constructor_shared(duk_hthread *thr) { - /* Behavior for constructor and non-constructor call is - * the same except for augmenting the created error. When - * called as a constructor, the caller (duk_new()) will handle - * augmentation; when called as normal function, we need to do - * it here. - */ - - duk_small_int_t bidx_prototype = duk_get_current_magic(thr); - - /* same for both error and each subclass like TypeError */ - duk_uint_t flags_and_class = - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ERROR); - - (void) duk_push_object_helper(thr, flags_and_class, bidx_prototype); - - /* If message is undefined, the own property 'message' is not set at - * all to save property space. An empty message is inherited anyway. - */ - if (!duk_is_undefined(thr, 0)) { - duk_to_string(thr, 0); - duk_dup_0(thr); /* [ message error message ] */ - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_MESSAGE, DUK_PROPDESC_FLAGS_WC); - } - - /* Augment the error if called as a normal function. __FILE__ and __LINE__ - * are not desirable in this case. - */ - -#if defined(DUK_USE_AUGMENT_ERROR_CREATE) - if (!duk_is_constructor_call(thr)) { - duk_err_augment_error_create(thr, thr, NULL, 0, DUK_AUGMENT_FLAG_NOBLAME_FILELINE); - } -#endif - - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_error_prototype_to_string(duk_hthread *thr) { - /* XXX: optimize with more direct internal access */ - - duk_push_this(thr); - (void) duk_require_hobject_promote_mask(thr, -1, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER); - - /* [ ... this ] */ - - duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_NAME); - if (duk_is_undefined(thr, -1)) { - duk_pop(thr); - duk_push_literal(thr, "Error"); - } else { - duk_to_string(thr, -1); - } - - /* [ ... this name ] */ - - /* XXX: Are steps 6 and 7 in E5 Section 15.11.4.4 duplicated by - * accident or are they actually needed? The first ToString() - * could conceivably return 'undefined'. - */ - duk_get_prop_stridx_short(thr, -2, DUK_STRIDX_MESSAGE); - if (duk_is_undefined(thr, -1)) { - duk_pop(thr); - duk_push_hstring_empty(thr); - } else { - duk_to_string(thr, -1); - } - - /* [ ... this name message ] */ - - if (duk_get_length(thr, -2) == 0) { - /* name is empty -> return message */ - return 1; - } - if (duk_get_length(thr, -1) == 0) { - /* message is empty -> return name */ - duk_pop(thr); - return 1; - } - duk_push_literal(thr, ": "); - duk_insert(thr, -2); /* ... name ': ' message */ - duk_concat(thr, 3); - - return 1; -} - -#if defined(DUK_USE_TRACEBACKS) - -/* - * Traceback handling - * - * The unified helper decodes the traceback and produces various requested - * outputs. It should be optimized for size, and may leave garbage on stack, - * only the topmost return value matters. For instance, traceback separator - * and decoded strings are pushed even when looking for filename only. - * - * NOTE: although _Tracedata is an internal property, user code can currently - * write to the array (or replace it with something other than an array). - * The code below must tolerate arbitrary _Tracedata. It can throw errors - * etc, but cannot cause a segfault or memory unsafe behavior. - */ - -/* constants arbitrary, chosen for small loads */ -#define DUK__OUTPUT_TYPE_TRACEBACK (-1) -#define DUK__OUTPUT_TYPE_FILENAME 0 -#define DUK__OUTPUT_TYPE_LINENUMBER 1 - -DUK_LOCAL duk_ret_t duk__error_getter_helper(duk_hthread *thr, duk_small_int_t output_type) { - duk_idx_t idx_td; - duk_small_int_t i; /* traceback depth fits into 16 bits */ - duk_small_int_t t; /* stack type fits into 16 bits */ - duk_small_int_t count_func = 0; /* traceback depth ensures fits into 16 bits */ - const char *str_tailcall = " tailcall"; - const char *str_strict = " strict"; - const char *str_construct = " construct"; - const char *str_prevyield = " preventsyield"; - const char *str_directeval = " directeval"; - const char *str_empty = ""; - - DUK_ASSERT_TOP(thr, 0); /* fixed arg count */ - - duk_push_this(thr); - duk_xget_owndataprop_stridx_short(thr, -1, DUK_STRIDX_INT_TRACEDATA); - idx_td = duk_get_top_index(thr); - - duk_push_hstring_stridx(thr, DUK_STRIDX_NEWLINE_4SPACE); - duk_push_this(thr); - - /* [ ... this tracedata sep this ] */ - - /* XXX: skip null filename? */ - - if (duk_check_type(thr, idx_td, DUK_TYPE_OBJECT)) { - /* Current tracedata contains 2 entries per callstack entry. */ - for (i = 0;; i += 2) { - duk_int_t pc; - duk_uint_t line; - duk_uint_t flags; - duk_double_t d; - const char *funcname; - const char *filename; - duk_hobject *h_func; - duk_hstring *h_name; - - duk_require_stack(thr, 5); - duk_get_prop_index(thr, idx_td, (duk_uarridx_t) i); - duk_get_prop_index(thr, idx_td, (duk_uarridx_t) (i + 1)); - d = duk_to_number_m1(thr); - pc = duk_double_to_int_t(DUK_FMOD(d, DUK_DOUBLE_2TO32)); - flags = duk_double_to_uint_t(DUK_FLOOR(d / DUK_DOUBLE_2TO32)); - t = (duk_small_int_t) duk_get_type(thr, -2); - - if (t == DUK_TYPE_OBJECT || t == DUK_TYPE_LIGHTFUNC) { - /* - * ECMAScript/native function call or lightfunc call - */ - - count_func++; - - /* [ ... v1(func) v2(pc+flags) ] */ - - /* These may be systematically omitted by Duktape - * with certain config options, but allow user to - * set them on a case-by-case basis. - */ - duk_get_prop_stridx_short(thr, -2, DUK_STRIDX_NAME); - duk_get_prop_stridx_short(thr, -3, DUK_STRIDX_FILE_NAME); - -#if defined(DUK_USE_PC2LINE) - line = (duk_uint_t) duk_hobject_pc2line_query(thr, -4, (duk_uint_fast32_t) pc); -#else - line = 0; -#endif - - /* [ ... v1 v2 name filename ] */ - - /* When looking for .fileName/.lineNumber, blame first - * function which has a .fileName. - */ - if (duk_is_string_notsymbol(thr, -1)) { - if (output_type == DUK__OUTPUT_TYPE_FILENAME) { - return 1; - } else if (output_type == DUK__OUTPUT_TYPE_LINENUMBER) { - duk_push_uint(thr, line); - return 1; - } - } - - /* XXX: Change 'anon' handling here too, to use empty string for anonymous functions? */ - /* XXX: Could be improved by coercing to a readable duk_tval (especially string escaping) */ - h_name = duk_get_hstring_notsymbol(thr, -2); /* may be NULL */ - funcname = (h_name == NULL || h_name == DUK_HTHREAD_STRING_EMPTY_STRING(thr)) ? - "[anon]" : - (const char *) DUK_HSTRING_GET_DATA(h_name); - filename = duk_get_string_notsymbol(thr, -1); - filename = filename ? filename : ""; - DUK_ASSERT(funcname != NULL); - DUK_ASSERT(filename != NULL); - - h_func = duk_get_hobject(thr, -4); /* NULL for lightfunc */ - - if (h_func == NULL) { - duk_push_sprintf( - thr, - "at %s light%s%s%s%s%s", - (const char *) funcname, - (const char *) ((flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty), - (const char *) ((flags & DUK_ACT_FLAG_TAILCALLED) ? str_tailcall : str_empty), - (const char *) ((flags & DUK_ACT_FLAG_CONSTRUCT) ? str_construct : str_empty), - (const char *) ((flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty), - (const char *) ((flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty)); - } else if (DUK_HOBJECT_HAS_NATFUNC(h_func)) { - duk_push_sprintf( - thr, - "at %s (%s) native%s%s%s%s%s", - (const char *) funcname, - (const char *) filename, - (const char *) ((flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty), - (const char *) ((flags & DUK_ACT_FLAG_TAILCALLED) ? str_tailcall : str_empty), - (const char *) ((flags & DUK_ACT_FLAG_CONSTRUCT) ? str_construct : str_empty), - (const char *) ((flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty), - (const char *) ((flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty)); - } else { - duk_push_sprintf( - thr, - "at %s (%s:%lu)%s%s%s%s%s", - (const char *) funcname, - (const char *) filename, - (unsigned long) line, - (const char *) ((flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty), - (const char *) ((flags & DUK_ACT_FLAG_TAILCALLED) ? str_tailcall : str_empty), - (const char *) ((flags & DUK_ACT_FLAG_CONSTRUCT) ? str_construct : str_empty), - (const char *) ((flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty), - (const char *) ((flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty)); - } - duk_replace(thr, -5); /* [ ... v1 v2 name filename str ] -> [ ... str v2 name filename ] */ - duk_pop_3(thr); /* -> [ ... str ] */ - } else if (t == DUK_TYPE_STRING) { - const char *str_file; - - /* - * __FILE__ / __LINE__ entry, here 'pc' is line number directly. - * Sometimes __FILE__ / __LINE__ is reported as the source for - * the error (fileName, lineNumber), sometimes not. - */ - - /* [ ... v1(filename) v2(line+flags) ] */ - - /* When looking for .fileName/.lineNumber, blame compilation - * or C call site unless flagged not to do so. - */ - if (!(flags & DUK_TB_FLAG_NOBLAME_FILELINE)) { - if (output_type == DUK__OUTPUT_TYPE_FILENAME) { - duk_pop(thr); - return 1; - } else if (output_type == DUK__OUTPUT_TYPE_LINENUMBER) { - duk_push_int(thr, pc); - return 1; - } - } - - /* Tracedata is trusted but avoid any risk of using a NULL - * for %s format because it has undefined behavior. Symbols - * don't need to be explicitly rejected as they pose no memory - * safety issues. - */ - str_file = (const char *) duk_get_string(thr, -2); - duk_push_sprintf(thr, - "at [anon] (%s:%ld) internal", - (const char *) (str_file ? str_file : "null"), - (long) pc); - duk_replace(thr, -3); /* [ ... v1 v2 str ] -> [ ... str v2 ] */ - duk_pop(thr); /* -> [ ... str ] */ - } else { - /* unknown, ignore */ - duk_pop_2(thr); - break; - } - } - - if (count_func >= DUK_USE_TRACEBACK_DEPTH) { - /* Possibly truncated; there is no explicit truncation - * marker so this is the best we can do. - */ - - duk_push_hstring_stridx(thr, DUK_STRIDX_BRACKETED_ELLIPSIS); - } - } - - /* [ ... this tracedata sep this str1 ... strN ] */ - - if (output_type != DUK__OUTPUT_TYPE_TRACEBACK) { - return 0; - } else { - /* The 'this' after 'sep' will get ToString() coerced by - * duk_join() automatically. We don't want to do that - * coercion when providing .fileName or .lineNumber (GH-254). - */ - duk_join(thr, duk_get_top(thr) - (idx_td + 2) /*count, not including sep*/); - return 1; - } -} - -/* XXX: Output type could be encoded into native function 'magic' value to - * save space. For setters the stridx could be encoded into 'magic'. - */ - -DUK_INTERNAL duk_ret_t duk_bi_error_prototype_stack_getter(duk_hthread *thr) { - return duk__error_getter_helper(thr, DUK__OUTPUT_TYPE_TRACEBACK); -} - -DUK_INTERNAL duk_ret_t duk_bi_error_prototype_filename_getter(duk_hthread *thr) { - return duk__error_getter_helper(thr, DUK__OUTPUT_TYPE_FILENAME); -} - -DUK_INTERNAL duk_ret_t duk_bi_error_prototype_linenumber_getter(duk_hthread *thr) { - return duk__error_getter_helper(thr, DUK__OUTPUT_TYPE_LINENUMBER); -} - -#else /* DUK_USE_TRACEBACKS */ - -/* - * Traceback handling when tracebacks disabled. - * - * The fileName / lineNumber stubs are now necessary because built-in - * data will include the accessor properties in Error.prototype. If those - * are removed for builds without tracebacks, these can also be removed. - * 'stack' should still be present and produce a ToString() equivalent: - * this is useful for user code which prints a stacktrace and expects to - * see something useful. A normal stacktrace also begins with a ToString() - * of the error so this makes sense. - */ - -DUK_INTERNAL duk_ret_t duk_bi_error_prototype_stack_getter(duk_hthread *thr) { - /* XXX: remove this native function and map 'stack' accessor - * to the toString() implementation directly. - */ - return duk_bi_error_prototype_to_string(thr); -} - -DUK_INTERNAL duk_ret_t duk_bi_error_prototype_filename_getter(duk_hthread *thr) { - DUK_UNREF(thr); - return 0; -} - -DUK_INTERNAL duk_ret_t duk_bi_error_prototype_linenumber_getter(duk_hthread *thr) { - DUK_UNREF(thr); - return 0; -} - -#endif /* DUK_USE_TRACEBACKS */ - -DUK_LOCAL duk_ret_t duk__error_setter_helper(duk_hthread *thr, duk_small_uint_t stridx_key) { - /* Attempt to write 'stack', 'fileName', 'lineNumber' works as if - * user code called Object.defineProperty() to create an overriding - * own property. This allows user code to overwrite .fileName etc - * intuitively as e.g. "err.fileName = 'dummy'" as one might expect. - * See https://github.com/svaarala/duktape/issues/387. - */ - - DUK_ASSERT_TOP(thr, 1); /* fixed arg count: value */ - - duk_push_this(thr); - duk_push_hstring_stridx(thr, stridx_key); - duk_dup_0(thr); - - /* [ ... obj key value ] */ - - DUK_DD(DUK_DDPRINT("error setter: %!T %!T %!T", duk_get_tval(thr, -3), duk_get_tval(thr, -2), duk_get_tval(thr, -1))); - - duk_def_prop(thr, - -3, - DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_WRITABLE | - DUK_DEFPROP_HAVE_ENUMERABLE | /*not enumerable*/ - DUK_DEFPROP_HAVE_CONFIGURABLE | DUK_DEFPROP_CONFIGURABLE); - return 0; -} - -DUK_INTERNAL duk_ret_t duk_bi_error_prototype_stack_setter(duk_hthread *thr) { - return duk__error_setter_helper(thr, DUK_STRIDX_STACK); -} - -DUK_INTERNAL duk_ret_t duk_bi_error_prototype_filename_setter(duk_hthread *thr) { - return duk__error_setter_helper(thr, DUK_STRIDX_FILE_NAME); -} - -DUK_INTERNAL duk_ret_t duk_bi_error_prototype_linenumber_setter(duk_hthread *thr) { - return duk__error_setter_helper(thr, DUK_STRIDX_LINE_NUMBER); -} - -/* automatic undefs */ -#undef DUK__OUTPUT_TYPE_FILENAME -#undef DUK__OUTPUT_TYPE_LINENUMBER -#undef DUK__OUTPUT_TYPE_TRACEBACK -/* - * Function built-ins - */ - -/* #include duk_internal.h -> already included */ - -/* Needed even when Function built-in is disabled. */ -DUK_INTERNAL duk_ret_t duk_bi_function_prototype(duk_hthread *thr) { - /* ignore arguments, return undefined (E5 Section 15.3.4) */ - DUK_UNREF(thr); - return 0; -} - -#if defined(DUK_USE_FUNCTION_BUILTIN) -DUK_INTERNAL duk_ret_t duk_bi_function_constructor(duk_hthread *thr) { - duk_hstring *h_sourcecode; - duk_idx_t nargs; - duk_idx_t i; - duk_small_uint_t comp_flags; - duk_hcompfunc *func; - duk_hobject *outer_lex_env; - duk_hobject *outer_var_env; - - /* normal and constructor calls have identical semantics */ - - nargs = duk_get_top(thr); - for (i = 0; i < nargs; i++) { - duk_to_string(thr, i); /* Rejects Symbols during coercion. */ - } - - if (nargs == 0) { - duk_push_hstring_empty(thr); - duk_push_hstring_empty(thr); - } else if (nargs == 1) { - /* XXX: cover this with the generic >1 case? */ - duk_push_hstring_empty(thr); - } else { - duk_insert(thr, 0); /* [ arg1 ... argN-1 body] -> [body arg1 ... argN-1] */ - duk_push_literal(thr, ","); - duk_insert(thr, 1); - duk_join(thr, nargs - 1); - } - - /* [ body formals ], formals is comma separated list that needs to be parsed */ - - DUK_ASSERT_TOP(thr, 2); - - /* XXX: this placeholder is not always correct, but use for now. - * It will fail in corner cases; see test-dev-func-cons-args.js. - */ - duk_push_literal(thr, "function("); - duk_dup_1(thr); - duk_push_literal(thr, "){"); - duk_dup_0(thr); - duk_push_literal(thr, "\n}"); /* Newline is important to handle trailing // comment. */ - duk_concat(thr, 5); - - /* [ body formals source ] */ - - DUK_ASSERT_TOP(thr, 3); - - /* strictness is not inherited, intentional */ - comp_flags = DUK_COMPILE_FUNCEXPR; - - duk_push_hstring_stridx(thr, DUK_STRIDX_COMPILE); /* XXX: copy from caller? */ /* XXX: ignored now */ - h_sourcecode = duk_require_hstring(thr, -2); /* no symbol check needed; -2 is concat'd code */ - duk_js_compile(thr, - (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_sourcecode), - (duk_size_t) DUK_HSTRING_GET_BYTELEN(h_sourcecode), - comp_flags); - - /* Force .name to 'anonymous' (ES2015). */ - duk_push_literal(thr, "anonymous"); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C); - - func = (duk_hcompfunc *) duk_known_hobject(thr, -1); - DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC((duk_hobject *) func)); - DUK_ASSERT(DUK_HOBJECT_HAS_CONSTRUCTABLE((duk_hobject *) func)); - - /* [ body formals source template ] */ - - /* only outer_lex_env matters, as functions always get a new - * variable declaration environment. - */ - - outer_lex_env = thr->builtins[DUK_BIDX_GLOBAL_ENV]; - outer_var_env = thr->builtins[DUK_BIDX_GLOBAL_ENV]; - - duk_js_push_closure(thr, func, outer_var_env, outer_lex_env, 1 /*add_auto_proto*/); - - /* [ body formals source template closure ] */ - - return 1; -} -#endif /* DUK_USE_FUNCTION_BUILTIN */ - -#if defined(DUK_USE_FUNCTION_BUILTIN) -DUK_INTERNAL duk_ret_t duk_bi_function_prototype_to_string(duk_hthread *thr) { - duk_tval *tv; - - /* - * E5 Section 15.3.4.2 places few requirements on the output of - * this function: the result is implementation dependent, must - * follow FunctionDeclaration syntax (in particular, must have a - * name even for anonymous functions or functions with empty name). - * The output does NOT need to compile into anything useful. - * - * E6 Section 19.2.3.5 changes the requirements completely: the - * result must either eval() to a functionally equivalent object - * OR eval() to a SyntaxError. - * - * We opt for the SyntaxError approach for now, with a syntax that - * mimics V8's native function syntax: - * - * 'function cos() { [native code] }' - * - * but extended with [ecmascript code], [bound code], and - * [lightfunc code]. - */ - - duk_push_this(thr); - tv = DUK_GET_TVAL_NEGIDX(thr, -1); - DUK_ASSERT(tv != NULL); - - if (DUK_TVAL_IS_OBJECT(tv)) { - duk_hobject *obj = DUK_TVAL_GET_OBJECT(tv); - const char *func_name; - - /* Function name: missing/undefined is mapped to empty string, - * otherwise coerce to string. No handling for invalid identifier - * characters or e.g. '{' in the function name. This doesn't - * really matter as long as a SyntaxError results. Technically - * if the name contained a suitable prefix followed by '//' it - * might cause the result to parse without error. - */ - duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_NAME); - if (duk_is_undefined(thr, -1)) { - func_name = ""; - } else { - func_name = duk_to_string(thr, -1); - DUK_ASSERT(func_name != NULL); - } - - if (DUK_HOBJECT_IS_COMPFUNC(obj)) { - duk_push_sprintf(thr, "function %s() { [ecmascript code] }", (const char *) func_name); - } else if (DUK_HOBJECT_IS_NATFUNC(obj)) { - duk_push_sprintf(thr, "function %s() { [native code] }", (const char *) func_name); - } else if (DUK_HOBJECT_IS_BOUNDFUNC(obj)) { - duk_push_sprintf(thr, "function %s() { [bound code] }", (const char *) func_name); - } else { - goto type_error; - } - } else if (DUK_TVAL_IS_LIGHTFUNC(tv)) { - duk_push_lightfunc_tostring(thr, tv); - } else { - goto type_error; - } - - return 1; - -type_error: - DUK_DCERROR_TYPE_INVALID_ARGS(thr); -} -#endif - -/* Always present because the native function pointer is needed in call - * handling. - */ -DUK_INTERNAL duk_ret_t duk_bi_function_prototype_call(duk_hthread *thr) { - /* .call() is dealt with in call handling by simulating its - * effects so this function is actually never called. - */ - DUK_UNREF(thr); - return DUK_RET_TYPE_ERROR; -} - -DUK_INTERNAL duk_ret_t duk_bi_function_prototype_apply(duk_hthread *thr) { - /* Like .call(), never actually called. */ - DUK_UNREF(thr); - return DUK_RET_TYPE_ERROR; -} - -DUK_INTERNAL duk_ret_t duk_bi_reflect_apply(duk_hthread *thr) { - /* Like .call(), never actually called. */ - DUK_UNREF(thr); - return DUK_RET_TYPE_ERROR; -} - -DUK_INTERNAL duk_ret_t duk_bi_reflect_construct(duk_hthread *thr) { - /* Like .call(), never actually called. */ - DUK_UNREF(thr); - return DUK_RET_TYPE_ERROR; -} - -#if defined(DUK_USE_FUNCTION_BUILTIN) -/* Create a bound function which points to a target function which may - * be bound or non-bound. If the target is bound, the argument lists - * and 'this' binding of the functions are merged and the resulting - * function points directly to the non-bound target. - */ -DUK_INTERNAL duk_ret_t duk_bi_function_prototype_bind(duk_hthread *thr) { - duk_hboundfunc *h_bound; - duk_idx_t nargs; /* bound args, not counting 'this' binding */ - duk_idx_t bound_nargs; - duk_int_t bound_len; - duk_tval *tv_prevbound; - duk_idx_t n_prevbound; - duk_tval *tv_res; - duk_tval *tv_tmp; - - /* XXX: C API call, e.g. duk_push_bound_function(thr, target_idx, nargs); */ - - /* Vararg function, careful arg handling, e.g. thisArg may not - * be present. - */ - nargs = duk_get_top(thr) - 1; /* actual args, not counting 'this' binding */ - if (nargs < 0) { - nargs++; - duk_push_undefined(thr); - } - DUK_ASSERT(nargs >= 0); - - /* Limit 'nargs' for bound functions to guarantee arithmetic - * below will never wrap. - */ - if (nargs > (duk_idx_t) DUK_HBOUNDFUNC_MAX_ARGS) { - DUK_DCERROR_RANGE_INVALID_COUNT(thr); - } - - duk_push_this(thr); - duk_require_callable(thr, -1); - - /* [ thisArg arg1 ... argN func ] (thisArg+args == nargs+1 total) */ - DUK_ASSERT_TOP(thr, nargs + 2); - - /* Create bound function object. */ - h_bound = duk_push_hboundfunc(thr); - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&h_bound->target)); - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&h_bound->this_binding)); - DUK_ASSERT(h_bound->args == NULL); - DUK_ASSERT(h_bound->nargs == 0); - DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) h_bound) == NULL); - - /* [ thisArg arg1 ... argN func boundFunc ] */ - - /* If the target is a bound function, argument lists must be - * merged. The 'this' binding closest to the target function - * wins because in call handling the 'this' gets replaced over - * and over again until we call the non-bound function. - */ - tv_prevbound = NULL; - n_prevbound = 0; - tv_tmp = DUK_GET_TVAL_POSIDX(thr, 0); - DUK_TVAL_SET_TVAL(&h_bound->this_binding, tv_tmp); - tv_tmp = DUK_GET_TVAL_NEGIDX(thr, -2); - DUK_TVAL_SET_TVAL(&h_bound->target, tv_tmp); - - if (DUK_TVAL_IS_OBJECT(tv_tmp)) { - duk_hobject *h_target; - duk_hobject *bound_proto; - - h_target = DUK_TVAL_GET_OBJECT(tv_tmp); - DUK_ASSERT(DUK_HOBJECT_IS_CALLABLE(h_target)); - - /* Internal prototype must be copied from the target. - * For lightfuncs Function.prototype is used and is already - * in place. - */ - bound_proto = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_target); - DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) h_bound, bound_proto); - - /* The 'strict' flag is copied to get the special [[Get]] of E5.1 - * Section 15.3.5.4 to apply when a 'caller' value is a strict bound - * function. Not sure if this is correct, because the specification - * is a bit ambiguous on this point but it would make sense. - */ - /* Strictness is inherited from target. */ - if (DUK_HOBJECT_HAS_STRICT(h_target)) { - DUK_HOBJECT_SET_STRICT((duk_hobject *) h_bound); - } - - if (DUK_HOBJECT_HAS_BOUNDFUNC(h_target)) { - duk_hboundfunc *h_boundtarget; - - h_boundtarget = (duk_hboundfunc *) (void *) h_target; - - /* The final function should always be non-bound, unless - * there's a bug in the internals. Assert for it. - */ - DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(&h_boundtarget->target) || - (DUK_TVAL_IS_OBJECT(&h_boundtarget->target) && - DUK_HOBJECT_IS_CALLABLE(DUK_TVAL_GET_OBJECT(&h_boundtarget->target)) && - !DUK_HOBJECT_IS_BOUNDFUNC(DUK_TVAL_GET_OBJECT(&h_boundtarget->target)))); - - DUK_TVAL_SET_TVAL(&h_bound->target, &h_boundtarget->target); - DUK_TVAL_SET_TVAL(&h_bound->this_binding, &h_boundtarget->this_binding); - - tv_prevbound = h_boundtarget->args; - n_prevbound = h_boundtarget->nargs; - } - } else { - /* Lightfuncs are always strict. */ - duk_hobject *bound_proto; - - DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv_tmp)); - DUK_HOBJECT_SET_STRICT((duk_hobject *) h_bound); - bound_proto = thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]; - DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) h_bound, bound_proto); - } - - DUK_TVAL_INCREF(thr, &h_bound->target); /* old values undefined, no decref needed */ - DUK_TVAL_INCREF(thr, &h_bound->this_binding); - - bound_nargs = n_prevbound + nargs; - if (bound_nargs > (duk_idx_t) DUK_HBOUNDFUNC_MAX_ARGS) { - DUK_DCERROR_RANGE_INVALID_COUNT(thr); - } - tv_res = (duk_tval *) DUK_ALLOC_CHECKED(thr, ((duk_size_t) bound_nargs) * sizeof(duk_tval)); - DUK_ASSERT(tv_res != NULL || bound_nargs == 0); - DUK_ASSERT(h_bound->args == NULL); - DUK_ASSERT(h_bound->nargs == 0); - h_bound->args = tv_res; - h_bound->nargs = bound_nargs; - - DUK_ASSERT(n_prevbound >= 0); - duk_copy_tvals_incref(thr, tv_res, tv_prevbound, (duk_size_t) n_prevbound); - DUK_ASSERT(nargs >= 0); - duk_copy_tvals_incref(thr, tv_res + n_prevbound, DUK_GET_TVAL_POSIDX(thr, 1), (duk_size_t) nargs); - - /* [ thisArg arg1 ... argN func boundFunc ] */ - - /* Bound function 'length' property is interesting. - * For lightfuncs, simply read the virtual property. - */ - duk_get_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH); - bound_len = duk_get_int(thr, -1); /* ES2015: no coercion */ - if (bound_len < nargs) { - bound_len = 0; - } else { - bound_len -= nargs; - } - if (sizeof(duk_int_t) > 4 && bound_len > (duk_int_t) DUK_UINT32_MAX) { - bound_len = (duk_int_t) DUK_UINT32_MAX; - } - duk_pop(thr); - DUK_ASSERT(bound_len >= 0); - tv_tmp = thr->valstack_top++; - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv_tmp)); - DUK_ASSERT(!DUK_TVAL_NEEDS_REFCOUNT_UPDATE(tv_tmp)); - DUK_TVAL_SET_U32(tv_tmp, (duk_uint32_t) bound_len); /* in-place update, fastint */ - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_C); /* attrs in E6 Section 9.2.4 */ - - /* XXX: could these be virtual? */ - /* Caller and arguments must use the same thrower, [[ThrowTypeError]]. */ - duk_xdef_prop_stridx_thrower(thr, -1, DUK_STRIDX_CALLER); - duk_xdef_prop_stridx_thrower(thr, -1, DUK_STRIDX_LC_ARGUMENTS); - - /* Function name and fileName (non-standard). */ - duk_push_literal(thr, "bound "); /* ES2015 19.2.3.2. */ - duk_get_prop_stridx(thr, -3, DUK_STRIDX_NAME); - if (!duk_is_string_notsymbol(thr, -1)) { - /* ES2015 has requirement to check that .name of target is a string - * (also must check for Symbol); if not, targetName should be the - * empty string. ES2015 19.2.3.2. - */ - duk_pop(thr); - duk_push_hstring_empty(thr); - } - duk_concat(thr, 2); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C); -#if defined(DUK_USE_FUNC_FILENAME_PROPERTY) - duk_get_prop_stridx_short(thr, -2, DUK_STRIDX_FILE_NAME); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_C); -#endif - - DUK_DDD(DUK_DDDPRINT("created bound function: %!iT", (duk_tval *) duk_get_tval(thr, -1))); - - return 1; -} -#endif /* DUK_USE_FUNCTION_BUILTIN */ - -/* %NativeFunctionPrototype% .length getter. */ -DUK_INTERNAL duk_ret_t duk_bi_native_function_length(duk_hthread *thr) { - duk_tval *tv; - duk_hnatfunc *h; - duk_int16_t func_nargs; - - tv = duk_get_borrowed_this_tval(thr); - DUK_ASSERT(tv != NULL); - - if (DUK_TVAL_IS_OBJECT(tv)) { - h = (duk_hnatfunc *) DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - if (!DUK_HOBJECT_IS_NATFUNC((duk_hobject *) h)) { - goto fail_type; - } - func_nargs = h->nargs; - duk_push_int(thr, func_nargs == DUK_HNATFUNC_NARGS_VARARGS ? 0 : func_nargs); - } else if (DUK_TVAL_IS_LIGHTFUNC(tv)) { - duk_small_uint_t lf_flags; - duk_small_uint_t lf_len; - - lf_flags = DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv); - lf_len = DUK_LFUNC_FLAGS_GET_LENGTH(lf_flags); - duk_push_uint(thr, lf_len); - } else { - goto fail_type; - } - return 1; - -fail_type: - DUK_DCERROR_TYPE_INVALID_ARGS(thr); -} - -/* %NativeFunctionPrototype% .name getter. */ -DUK_INTERNAL duk_ret_t duk_bi_native_function_name(duk_hthread *thr) { - duk_tval *tv; - duk_hnatfunc *h; - - tv = duk_get_borrowed_this_tval(thr); - DUK_ASSERT(tv != NULL); - - if (DUK_TVAL_IS_OBJECT(tv)) { - h = (duk_hnatfunc *) DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - if (!DUK_HOBJECT_IS_NATFUNC((duk_hobject *) h)) { - goto fail_type; - } -#if 0 - duk_push_hnatfunc_name(thr, h); -#endif - duk_push_hstring_empty(thr); - } else if (DUK_TVAL_IS_LIGHTFUNC(tv)) { - duk_push_lightfunc_name(thr, tv); - } else { - goto fail_type; - } - return 1; - -fail_type: - DUK_DCERROR_TYPE_INVALID_ARGS(thr); -} - -#if defined(DUK_USE_SYMBOL_BUILTIN) -DUK_INTERNAL duk_ret_t duk_bi_function_prototype_hasinstance(duk_hthread *thr) { - /* This binding: RHS, stack index 0: LHS. */ - duk_bool_t ret; - - ret = duk_js_instanceof_ordinary(thr, DUK_GET_TVAL_POSIDX(thr, 0), DUK_GET_THIS_TVAL_PTR(thr)); - duk_push_boolean(thr, ret); - return 1; -} -#endif /* DUK_USE_SYMBOL_BUILTIN */ -/* - * Global object built-ins - */ - -/* #include duk_internal.h -> already included */ - -/* - * Encoding/decoding helpers - */ - -/* XXX: Could add fast path (for each transform callback) with direct byte - * lookups (no shifting) and no explicit check for x < 0x80 before table - * lookup. - */ - -/* Macros for creating and checking bitmasks for character encoding. - * Bit number is a bit counterintuitive, but minimizes code size. - */ -#define DUK__MKBITS(a, b, c, d, e, f, g, h) \ - ((duk_uint8_t) (((a) << 0) | ((b) << 1) | ((c) << 2) | ((d) << 3) | ((e) << 4) | ((f) << 5) | ((g) << 6) | ((h) << 7))) -#define DUK__CHECK_BITMASK(table, cp) ((table)[(cp) >> 3] & (1 << ((cp) &0x07))) - -/* E5.1 Section 15.1.3.3: uriReserved + uriUnescaped + '#' */ -DUK_LOCAL const duk_uint8_t duk__encode_uriunescaped_table[16] = { - DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x00-0x0f */ - DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x10-0x1f */ - DUK__MKBITS(0, 1, 0, 1, 1, 0, 1, 1), DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), /* 0x20-0x2f */ - DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 1, 0, 1, 0, 1), /* 0x30-0x3f */ - DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), /* 0x40-0x4f */ - DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 0, 0, 0, 0, 1), /* 0x50-0x5f */ - DUK__MKBITS(0, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), /* 0x60-0x6f */ - DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 0, 0, 0, 1, 0), /* 0x70-0x7f */ -}; - -/* E5.1 Section 15.1.3.4: uriUnescaped */ -DUK_LOCAL const duk_uint8_t duk__encode_uricomponent_unescaped_table[16] = { - DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x00-0x0f */ - DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x10-0x1f */ - DUK__MKBITS(0, 1, 0, 0, 0, 0, 0, 1), DUK__MKBITS(1, 1, 1, 0, 0, 1, 1, 0), /* 0x20-0x2f */ - DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 0, 0, 0, 0, 0, 0), /* 0x30-0x3f */ - DUK__MKBITS(0, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), /* 0x40-0x4f */ - DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 0, 0, 0, 0, 1), /* 0x50-0x5f */ - DUK__MKBITS(0, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), /* 0x60-0x6f */ - DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 0, 0, 0, 1, 0), /* 0x70-0x7f */ -}; - -/* E5.1 Section 15.1.3.1: uriReserved + '#' */ -DUK_LOCAL const duk_uint8_t duk__decode_uri_reserved_table[16] = { - DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x00-0x0f */ - DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x10-0x1f */ - DUK__MKBITS(0, 0, 0, 1, 1, 0, 1, 0), DUK__MKBITS(0, 0, 0, 1, 1, 0, 0, 1), /* 0x20-0x2f */ - DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 1, 1, 0, 1, 0, 1), /* 0x30-0x3f */ - DUK__MKBITS(1, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x40-0x4f */ - DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x50-0x5f */ - DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x60-0x6f */ - DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x70-0x7f */ -}; - -/* E5.1 Section 15.1.3.2: empty */ -DUK_LOCAL const duk_uint8_t duk__decode_uri_component_reserved_table[16] = { - DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x00-0x0f */ - DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x10-0x1f */ - DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x20-0x2f */ - DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x30-0x3f */ - DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x40-0x4f */ - DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x50-0x5f */ - DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x60-0x6f */ - DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x70-0x7f */ -}; - -#if defined(DUK_USE_SECTION_B) -/* E5.1 Section B.2.2, step 7. */ -DUK_LOCAL const duk_uint8_t duk__escape_unescaped_table[16] = { - DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x00-0x0f */ - DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x10-0x1f */ - DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 1, 1, 0, 1, 1, 1), /* 0x20-0x2f */ - DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 0, 0, 0, 0, 0, 0), /* 0x30-0x3f */ - DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), /* 0x40-0x4f */ - DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 0, 0, 0, 0, 1), /* 0x50-0x5f */ - DUK__MKBITS(0, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), /* 0x60-0x6f */ - DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 0, 0, 0, 0, 0) /* 0x70-0x7f */ -}; -#endif /* DUK_USE_SECTION_B */ - -typedef struct { - duk_hthread *thr; - duk_hstring *h_str; - duk_bufwriter_ctx bw; - const duk_uint8_t *p; - const duk_uint8_t *p_start; - const duk_uint8_t *p_end; -} duk__transform_context; - -typedef void (*duk__transform_callback)(duk__transform_context *tfm_ctx, const void *udata, duk_codepoint_t cp); - -/* XXX: refactor and share with other code */ -DUK_LOCAL duk_small_int_t duk__decode_hex_escape(const duk_uint8_t *p, duk_small_int_t n) { - duk_small_int_t ch; - duk_small_int_t t = 0; - - while (n > 0) { - t = t * 16; - ch = (duk_small_int_t) duk_hex_dectab[*p++]; - if (DUK_LIKELY(ch >= 0)) { - t += ch; - } else { - return -1; - } - n--; - } - return t; -} - -DUK_LOCAL int duk__transform_helper(duk_hthread *thr, duk__transform_callback callback, const void *udata) { - duk__transform_context tfm_ctx_alloc; - duk__transform_context *tfm_ctx = &tfm_ctx_alloc; - duk_codepoint_t cp; - - tfm_ctx->thr = thr; - - tfm_ctx->h_str = duk_to_hstring(thr, 0); - DUK_ASSERT(tfm_ctx->h_str != NULL); - - DUK_BW_INIT_PUSHBUF(thr, &tfm_ctx->bw, DUK_HSTRING_GET_BYTELEN(tfm_ctx->h_str)); /* initial size guess */ - - tfm_ctx->p_start = DUK_HSTRING_GET_DATA(tfm_ctx->h_str); - tfm_ctx->p_end = tfm_ctx->p_start + DUK_HSTRING_GET_BYTELEN(tfm_ctx->h_str); - tfm_ctx->p = tfm_ctx->p_start; - - while (tfm_ctx->p < tfm_ctx->p_end) { - cp = (duk_codepoint_t) duk_unicode_decode_xutf8_checked(thr, &tfm_ctx->p, tfm_ctx->p_start, tfm_ctx->p_end); - callback(tfm_ctx, udata, cp); - } - - DUK_BW_COMPACT(thr, &tfm_ctx->bw); - - (void) duk_buffer_to_string(thr, -1); /* Safe if transform is safe. */ - return 1; -} - -DUK_LOCAL void duk__transform_callback_encode_uri(duk__transform_context *tfm_ctx, const void *udata, duk_codepoint_t cp) { - duk_uint8_t xutf8_buf[DUK_UNICODE_MAX_XUTF8_LENGTH]; - duk_small_int_t len; - duk_codepoint_t cp1, cp2; - duk_small_int_t i, t; - const duk_uint8_t *unescaped_table = (const duk_uint8_t *) udata; - - /* UTF-8 encoded bytes escaped as %xx%xx%xx... -> 3 * nbytes. - * Codepoint range is restricted so this is a slightly too large - * but doesn't matter. - */ - DUK_BW_ENSURE(tfm_ctx->thr, &tfm_ctx->bw, 3 * DUK_UNICODE_MAX_XUTF8_LENGTH); - - if (cp < 0) { - goto uri_error; - } else if ((cp < 0x80L) && DUK__CHECK_BITMASK(unescaped_table, cp)) { - DUK_BW_WRITE_RAW_U8(tfm_ctx->thr, &tfm_ctx->bw, (duk_uint8_t) cp); - return; - } else if (cp >= 0xdc00L && cp <= 0xdfffL) { - goto uri_error; - } else if (cp >= 0xd800L && cp <= 0xdbffL) { - /* Needs lookahead */ - if (duk_unicode_decode_xutf8(tfm_ctx->thr, - &tfm_ctx->p, - tfm_ctx->p_start, - tfm_ctx->p_end, - (duk_ucodepoint_t *) &cp2) == 0) { - goto uri_error; - } - if (!(cp2 >= 0xdc00L && cp2 <= 0xdfffL)) { - goto uri_error; - } - cp1 = cp; - cp = (duk_codepoint_t) (((cp1 - 0xd800L) << 10) + (cp2 - 0xdc00L) + 0x10000L); - } else if (cp > 0x10ffffL) { - /* Although we can allow non-BMP characters (they'll decode - * back into surrogate pairs), we don't allow extended UTF-8 - * characters; they would encode to URIs which won't decode - * back because of strict UTF-8 checks in URI decoding. - * (However, we could just as well allow them here.) - */ - goto uri_error; - } else { - /* Non-BMP characters within valid UTF-8 range: encode as is. - * They'll decode back into surrogate pairs if the escaped - * output is decoded. - */ - ; - } - - len = duk_unicode_encode_xutf8((duk_ucodepoint_t) cp, xutf8_buf); - for (i = 0; i < len; i++) { - t = (duk_small_int_t) xutf8_buf[i]; - DUK_BW_WRITE_RAW_U8_3(tfm_ctx->thr, - &tfm_ctx->bw, - DUK_ASC_PERCENT, - (duk_uint8_t) duk_uc_nybbles[t >> 4], - (duk_uint8_t) duk_uc_nybbles[t & 0x0f]); - } - - return; - -uri_error: - DUK_ERROR_URI(tfm_ctx->thr, DUK_STR_INVALID_INPUT); - DUK_WO_NORETURN(return;); -} - -DUK_LOCAL void duk__transform_callback_decode_uri(duk__transform_context *tfm_ctx, const void *udata, duk_codepoint_t cp) { - const duk_uint8_t *reserved_table = (const duk_uint8_t *) udata; - duk_small_uint_t utf8_blen; - duk_codepoint_t min_cp; - duk_small_int_t t; /* must be signed */ - duk_small_uint_t i; - - /* Maximum write size: XUTF8 path writes max DUK_UNICODE_MAX_XUTF8_LENGTH, - * percent escape path writes max two times CESU-8 encoded BMP length. - */ - DUK_BW_ENSURE(tfm_ctx->thr, - &tfm_ctx->bw, - (DUK_UNICODE_MAX_XUTF8_LENGTH >= 2 * DUK_UNICODE_MAX_CESU8_BMP_LENGTH ? DUK_UNICODE_MAX_XUTF8_LENGTH : - DUK_UNICODE_MAX_CESU8_BMP_LENGTH)); - - if (cp == (duk_codepoint_t) '%') { - const duk_uint8_t *p = tfm_ctx->p; - duk_size_t left = (duk_size_t) (tfm_ctx->p_end - p); /* bytes left */ - - DUK_DDD(DUK_DDDPRINT("percent encoding, left=%ld", (long) left)); - - if (left < 2) { - goto uri_error; - } - - t = duk__decode_hex_escape(p, 2); - DUK_DDD(DUK_DDDPRINT("first byte: %ld", (long) t)); - if (t < 0) { - goto uri_error; - } - - if (t < 0x80) { - if (DUK__CHECK_BITMASK(reserved_table, t)) { - /* decode '%xx' to '%xx' if decoded char in reserved set */ - DUK_ASSERT(tfm_ctx->p - 1 >= tfm_ctx->p_start); - DUK_BW_WRITE_RAW_U8_3(tfm_ctx->thr, &tfm_ctx->bw, DUK_ASC_PERCENT, p[0], p[1]); - } else { - DUK_BW_WRITE_RAW_U8(tfm_ctx->thr, &tfm_ctx->bw, (duk_uint8_t) t); - } - tfm_ctx->p += 2; - return; - } - - /* Decode UTF-8 codepoint from a sequence of hex escapes. The - * first byte of the sequence has been decoded to 't'. - * - * Note that UTF-8 validation must be strict according to the - * specification: E5.1 Section 15.1.3, decode algorithm step - * 4.d.vii.8. URIError from non-shortest encodings is also - * specifically noted in the spec. - */ - - DUK_ASSERT(t >= 0x80); - if (t < 0xc0) { - /* continuation byte */ - goto uri_error; - } else if (t < 0xe0) { - /* 110x xxxx; 2 bytes */ - utf8_blen = 2; - min_cp = 0x80L; - cp = t & 0x1f; - } else if (t < 0xf0) { - /* 1110 xxxx; 3 bytes */ - utf8_blen = 3; - min_cp = 0x800L; - cp = t & 0x0f; - } else if (t < 0xf8) { - /* 1111 0xxx; 4 bytes */ - utf8_blen = 4; - min_cp = 0x10000L; - cp = t & 0x07; - } else { - /* extended utf-8 not allowed for URIs */ - goto uri_error; - } - - if (left < utf8_blen * 3 - 1) { - /* '%xx%xx...%xx', p points to char after first '%' */ - goto uri_error; - } - - p += 3; - for (i = 1; i < utf8_blen; i++) { - /* p points to digit part ('%xy', p points to 'x') */ - t = duk__decode_hex_escape(p, 2); - DUK_DDD(DUK_DDDPRINT("i=%ld utf8_blen=%ld cp=%ld t=0x%02lx", - (long) i, - (long) utf8_blen, - (long) cp, - (unsigned long) t)); - if (t < 0) { - goto uri_error; - } - if ((t & 0xc0) != 0x80) { - goto uri_error; - } - cp = (cp << 6) + (t & 0x3f); - p += 3; - } - p--; /* p overshoots */ - tfm_ctx->p = p; - - DUK_DDD(DUK_DDDPRINT("final cp=%ld, min_cp=%ld", (long) cp, (long) min_cp)); - - if (cp < min_cp || cp > 0x10ffffL || (cp >= 0xd800L && cp <= 0xdfffL)) { - goto uri_error; - } - - /* The E5.1 algorithm checks whether or not a decoded codepoint - * is below 0x80 and perhaps may be in the "reserved" set. - * This seems pointless because the single byte UTF-8 case is - * handled separately, and non-shortest encodings are rejected. - * So, 'cp' cannot be below 0x80 here, and thus cannot be in - * the reserved set. - */ - - /* utf-8 validation ensures these */ - DUK_ASSERT(cp >= 0x80L && cp <= 0x10ffffL); - - if (cp >= 0x10000L) { - cp -= 0x10000L; - DUK_ASSERT(cp < 0x100000L); - - DUK_BW_WRITE_RAW_XUTF8(tfm_ctx->thr, &tfm_ctx->bw, ((cp >> 10) + 0xd800L)); - DUK_BW_WRITE_RAW_XUTF8(tfm_ctx->thr, &tfm_ctx->bw, ((cp & 0x03ffL) + 0xdc00L)); - } else { - DUK_BW_WRITE_RAW_XUTF8(tfm_ctx->thr, &tfm_ctx->bw, cp); - } - } else { - DUK_BW_WRITE_RAW_XUTF8(tfm_ctx->thr, &tfm_ctx->bw, cp); - } - return; - -uri_error: - DUK_ERROR_URI(tfm_ctx->thr, DUK_STR_INVALID_INPUT); - DUK_WO_NORETURN(return;); -} - -#if defined(DUK_USE_SECTION_B) -DUK_LOCAL void duk__transform_callback_escape(duk__transform_context *tfm_ctx, const void *udata, duk_codepoint_t cp) { - DUK_UNREF(udata); - - DUK_BW_ENSURE(tfm_ctx->thr, &tfm_ctx->bw, 6); - - if (cp < 0) { - goto esc_error; - } else if ((cp < 0x80L) && DUK__CHECK_BITMASK(duk__escape_unescaped_table, cp)) { - DUK_BW_WRITE_RAW_U8(tfm_ctx->thr, &tfm_ctx->bw, (duk_uint8_t) cp); - } else if (cp < 0x100L) { - DUK_BW_WRITE_RAW_U8_3(tfm_ctx->thr, - &tfm_ctx->bw, - (duk_uint8_t) DUK_ASC_PERCENT, - (duk_uint8_t) duk_uc_nybbles[cp >> 4], - (duk_uint8_t) duk_uc_nybbles[cp & 0x0f]); - } else if (cp < 0x10000L) { - DUK_BW_WRITE_RAW_U8_6(tfm_ctx->thr, - &tfm_ctx->bw, - (duk_uint8_t) DUK_ASC_PERCENT, - (duk_uint8_t) DUK_ASC_LC_U, - (duk_uint8_t) duk_uc_nybbles[cp >> 12], - (duk_uint8_t) duk_uc_nybbles[(cp >> 8) & 0x0f], - (duk_uint8_t) duk_uc_nybbles[(cp >> 4) & 0x0f], - (duk_uint8_t) duk_uc_nybbles[cp & 0x0f]); - } else { - /* Characters outside BMP cannot be escape()'d. We could - * encode them as surrogate pairs (for codepoints inside - * valid UTF-8 range, but not extended UTF-8). Because - * escape() and unescape() are legacy functions, we don't. - */ - goto esc_error; - } - - return; - -esc_error: - DUK_ERROR_TYPE(tfm_ctx->thr, DUK_STR_INVALID_INPUT); - DUK_WO_NORETURN(return;); -} - -DUK_LOCAL void duk__transform_callback_unescape(duk__transform_context *tfm_ctx, const void *udata, duk_codepoint_t cp) { - duk_small_int_t t; - - DUK_UNREF(udata); - - if (cp == (duk_codepoint_t) '%') { - const duk_uint8_t *p = tfm_ctx->p; - duk_size_t left = (duk_size_t) (tfm_ctx->p_end - p); /* bytes left */ - - if (left >= 5 && p[0] == 'u' && ((t = duk__decode_hex_escape(p + 1, 4)) >= 0)) { - cp = (duk_codepoint_t) t; - tfm_ctx->p += 5; - } else if (left >= 2 && ((t = duk__decode_hex_escape(p, 2)) >= 0)) { - cp = (duk_codepoint_t) t; - tfm_ctx->p += 2; - } - } - - DUK_BW_WRITE_ENSURE_XUTF8(tfm_ctx->thr, &tfm_ctx->bw, cp); -} -#endif /* DUK_USE_SECTION_B */ - -/* - * Eval - * - * Eval needs to handle both a "direct eval" and an "indirect eval". - * Direct eval handling needs access to the caller's activation so that its - * lexical environment can be accessed. A direct eval is only possible from - * ECMAScript code; an indirect eval call is possible also from C code. - * When an indirect eval call is made from C code, there may not be a - * calling activation at all which needs careful handling. - */ - -DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_hthread *thr) { - duk_hstring *h; - duk_activation *act_caller; - duk_activation *act_eval; - duk_hcompfunc *func; - duk_hobject *outer_lex_env; - duk_hobject *outer_var_env; - duk_bool_t this_to_global = 1; - duk_small_uint_t comp_flags; - duk_int_t level = -2; - duk_small_uint_t call_flags; - - DUK_ASSERT(duk_get_top(thr) == 1 || duk_get_top(thr) == 2); /* 2 when called by debugger */ - DUK_ASSERT(thr->callstack_top >= 1); /* at least this function exists */ - DUK_ASSERT(thr->callstack_curr != NULL); - DUK_ASSERT((thr->callstack_curr->flags & DUK_ACT_FLAG_DIRECT_EVAL) == 0 || /* indirect eval */ - (thr->callstack_top >= 2)); /* if direct eval, calling activation must exist */ - - /* - * callstack_top - 1 --> this function - * callstack_top - 2 --> caller (may not exist) - * - * If called directly from C, callstack_top might be 1. If calling - * activation doesn't exist, call must be indirect. - */ - - h = duk_get_hstring_notsymbol(thr, 0); - if (!h) { - /* Symbol must be returned as is, like any non-string values. */ - return 1; /* return arg as-is */ - } - -#if defined(DUK_USE_DEBUGGER_SUPPORT) - /* NOTE: level is used only by the debugger and should never be present - * for an ECMAScript eval(). - */ - DUK_ASSERT(level == -2); /* by default, use caller's environment */ - if (duk_get_top(thr) >= 2 && duk_is_number(thr, 1)) { - level = duk_get_int(thr, 1); - } - DUK_ASSERT(level <= -2); /* This is guaranteed by debugger code. */ -#endif - - /* [ source ] */ - - comp_flags = DUK_COMPILE_EVAL; - act_eval = thr->callstack_curr; /* this function */ - DUK_ASSERT(act_eval != NULL); - act_caller = duk_hthread_get_activation_for_level(thr, level); - if (act_caller != NULL) { - /* Have a calling activation, check for direct eval (otherwise - * assume indirect eval. - */ - if ((act_caller->flags & DUK_ACT_FLAG_STRICT) && (act_eval->flags & DUK_ACT_FLAG_DIRECT_EVAL)) { - /* Only direct eval inherits strictness from calling code - * (E5.1 Section 10.1.1). - */ - comp_flags |= DUK_COMPILE_STRICT; - } - } else { - DUK_ASSERT((act_eval->flags & DUK_ACT_FLAG_DIRECT_EVAL) == 0); - } - - duk_push_hstring_stridx(thr, DUK_STRIDX_INPUT); /* XXX: copy from caller? */ - duk_js_compile(thr, (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h), (duk_size_t) DUK_HSTRING_GET_BYTELEN(h), comp_flags); - func = (duk_hcompfunc *) duk_known_hobject(thr, -1); - DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC((duk_hobject *) func)); - - /* [ source template ] */ - - /* E5 Section 10.4.2 */ - - if (act_eval->flags & DUK_ACT_FLAG_DIRECT_EVAL) { - DUK_ASSERT(thr->callstack_top >= 2); - DUK_ASSERT(act_caller != NULL); - if (act_caller->lex_env == NULL) { - DUK_ASSERT(act_caller->var_env == NULL); - DUK_DDD(DUK_DDDPRINT("delayed environment initialization")); - - /* this may have side effects, so re-lookup act */ - duk_js_init_activation_environment_records_delayed(thr, act_caller); - } - DUK_ASSERT(act_caller->lex_env != NULL); - DUK_ASSERT(act_caller->var_env != NULL); - - this_to_global = 0; - - if (DUK_HOBJECT_HAS_STRICT((duk_hobject *) func)) { - duk_hdecenv *new_env; - duk_hobject *act_lex_env; - - DUK_DDD(DUK_DDDPRINT("direct eval call to a strict function -> " - "var_env and lex_env to a fresh env, " - "this_binding to caller's this_binding")); - - act_lex_env = act_caller->lex_env; - - new_env = - duk_hdecenv_alloc(thr, - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV)); - DUK_ASSERT(new_env != NULL); - duk_push_hobject(thr, (duk_hobject *) new_env); - - DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) new_env) == NULL); - DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) new_env, act_lex_env); - DUK_HOBJECT_INCREF_ALLOWNULL(thr, act_lex_env); - DUK_DDD(DUK_DDDPRINT("new_env allocated: %!iO", (duk_heaphdr *) new_env)); - - outer_lex_env = (duk_hobject *) new_env; - outer_var_env = (duk_hobject *) new_env; - - duk_insert(thr, 0); /* stash to bottom of value stack to keep new_env reachable for duration of eval */ - - /* compiler's responsibility */ - DUK_ASSERT(DUK_HOBJECT_HAS_NEWENV((duk_hobject *) func)); - } else { - DUK_DDD(DUK_DDDPRINT("direct eval call to a non-strict function -> " - "var_env and lex_env to caller's envs, " - "this_binding to caller's this_binding")); - - outer_lex_env = act_caller->lex_env; - outer_var_env = act_caller->var_env; - - /* compiler's responsibility */ - DUK_ASSERT(!DUK_HOBJECT_HAS_NEWENV((duk_hobject *) func)); - } - } else { - DUK_DDD(DUK_DDDPRINT("indirect eval call -> var_env and lex_env to " - "global object, this_binding to global object")); - - this_to_global = 1; - outer_lex_env = thr->builtins[DUK_BIDX_GLOBAL_ENV]; - outer_var_env = thr->builtins[DUK_BIDX_GLOBAL_ENV]; - } - - /* Eval code doesn't need an automatic .prototype object. */ - duk_js_push_closure(thr, func, outer_var_env, outer_lex_env, 0 /*add_auto_proto*/); - - /* [ env? source template closure ] */ - - if (this_to_global) { - DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL); - duk_push_hobject_bidx(thr, DUK_BIDX_GLOBAL); - } else { - duk_tval *tv; - DUK_ASSERT(thr->callstack_top >= 2); - DUK_ASSERT(act_caller != NULL); - tv = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + act_caller->bottom_byteoff - - sizeof(duk_tval)); /* this is just beneath bottom */ - DUK_ASSERT(tv >= thr->valstack); - duk_push_tval(thr, tv); - } - - DUK_DDD(DUK_DDDPRINT("eval -> lex_env=%!iO, var_env=%!iO, this_binding=%!T", - (duk_heaphdr *) outer_lex_env, - (duk_heaphdr *) outer_var_env, - duk_get_tval(thr, -1))); - - /* [ env? source template closure this ] */ - - call_flags = 0; - if (act_eval->flags & DUK_ACT_FLAG_DIRECT_EVAL) { - /* Set DIRECT_EVAL flag for the call; it's not strictly - * needed for the 'inner' eval call (the eval body) but - * current new.target implementation expects to find it - * so it can traverse direct eval chains up to the real - * calling function. - */ - call_flags |= DUK_CALL_FLAG_DIRECT_EVAL; - } - duk_handle_call_unprotected_nargs(thr, 0, call_flags); - - /* [ env? source template result ] */ - - return 1; -} - -/* - * Parsing of ints and floats - */ - -#if defined(DUK_USE_GLOBAL_BUILTIN) -DUK_INTERNAL duk_ret_t duk_bi_global_object_parse_int(duk_hthread *thr) { - duk_int32_t radix; - duk_small_uint_t s2n_flags; - - DUK_ASSERT_TOP(thr, 2); - duk_to_string(thr, 0); /* Reject symbols. */ - - radix = duk_to_int32(thr, 1); - - /* While parseInt() recognizes 0xdeadbeef, it doesn't recognize - * ES2015 0o123 or 0b10001. - */ - s2n_flags = DUK_S2N_FLAG_TRIM_WHITE | DUK_S2N_FLAG_ALLOW_GARBAGE | DUK_S2N_FLAG_ALLOW_PLUS | DUK_S2N_FLAG_ALLOW_MINUS | - DUK_S2N_FLAG_ALLOW_LEADING_ZERO | DUK_S2N_FLAG_ALLOW_AUTO_HEX_INT; - - /* Specification stripPrefix maps to DUK_S2N_FLAG_ALLOW_AUTO_HEX_INT. - * - * Don't autodetect octals (from leading zeroes), require user code to - * provide an explicit radix 8 for parsing octal. See write-up from Mozilla: - * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt#ECMAScript_5_Removes_Octal_Interpretation - */ - - if (radix != 0) { - if (radix < 2 || radix > 36) { - goto ret_nan; - } - if (radix != 16) { - s2n_flags &= ~DUK_S2N_FLAG_ALLOW_AUTO_HEX_INT; - } - } else { - radix = 10; - } - - duk_dup_0(thr); - duk_numconv_parse(thr, (duk_small_int_t) radix, s2n_flags); - return 1; - -ret_nan: - duk_push_nan(thr); - return 1; -} -#endif /* DUK_USE_GLOBAL_BUILTIN */ - -#if defined(DUK_USE_GLOBAL_BUILTIN) -DUK_INTERNAL duk_ret_t duk_bi_global_object_parse_float(duk_hthread *thr) { - duk_small_uint_t s2n_flags; - - DUK_ASSERT_TOP(thr, 1); - duk_to_string(thr, 0); /* Reject symbols. */ - - /* XXX: check flags */ - s2n_flags = DUK_S2N_FLAG_TRIM_WHITE | DUK_S2N_FLAG_ALLOW_EXP | DUK_S2N_FLAG_ALLOW_GARBAGE | DUK_S2N_FLAG_ALLOW_PLUS | - DUK_S2N_FLAG_ALLOW_MINUS | DUK_S2N_FLAG_ALLOW_INF | DUK_S2N_FLAG_ALLOW_FRAC | DUK_S2N_FLAG_ALLOW_NAKED_FRAC | - DUK_S2N_FLAG_ALLOW_EMPTY_FRAC | DUK_S2N_FLAG_ALLOW_LEADING_ZERO; - - duk_numconv_parse(thr, 10 /*radix*/, s2n_flags); - return 1; -} -#endif /* DUK_USE_GLOBAL_BUILTIN */ - -/* - * Number checkers - */ - -#if defined(DUK_USE_GLOBAL_BUILTIN) -DUK_INTERNAL duk_ret_t duk_bi_global_object_is_nan(duk_hthread *thr) { - duk_double_t d = duk_to_number(thr, 0); - duk_push_boolean(thr, (duk_bool_t) DUK_ISNAN(d)); - return 1; -} -#endif /* DUK_USE_GLOBAL_BUILTIN */ - -#if defined(DUK_USE_GLOBAL_BUILTIN) -DUK_INTERNAL duk_ret_t duk_bi_global_object_is_finite(duk_hthread *thr) { - duk_double_t d = duk_to_number(thr, 0); - duk_push_boolean(thr, (duk_bool_t) DUK_ISFINITE(d)); - return 1; -} -#endif /* DUK_USE_GLOBAL_BUILTIN */ - -/* - * URI handling - */ - -#if defined(DUK_USE_GLOBAL_BUILTIN) -DUK_INTERNAL duk_ret_t duk_bi_global_object_decode_uri(duk_hthread *thr) { - return duk__transform_helper(thr, duk__transform_callback_decode_uri, (const void *) duk__decode_uri_reserved_table); -} - -DUK_INTERNAL duk_ret_t duk_bi_global_object_decode_uri_component(duk_hthread *thr) { - return duk__transform_helper(thr, - duk__transform_callback_decode_uri, - (const void *) duk__decode_uri_component_reserved_table); -} - -DUK_INTERNAL duk_ret_t duk_bi_global_object_encode_uri(duk_hthread *thr) { - return duk__transform_helper(thr, duk__transform_callback_encode_uri, (const void *) duk__encode_uriunescaped_table); -} - -DUK_INTERNAL duk_ret_t duk_bi_global_object_encode_uri_component(duk_hthread *thr) { - return duk__transform_helper(thr, - duk__transform_callback_encode_uri, - (const void *) duk__encode_uricomponent_unescaped_table); -} - -#if defined(DUK_USE_SECTION_B) -DUK_INTERNAL duk_ret_t duk_bi_global_object_escape(duk_hthread *thr) { - return duk__transform_helper(thr, duk__transform_callback_escape, (const void *) NULL); -} - -DUK_INTERNAL duk_ret_t duk_bi_global_object_unescape(duk_hthread *thr) { - return duk__transform_helper(thr, duk__transform_callback_unescape, (const void *) NULL); -} -#endif /* DUK_USE_SECTION_B */ -#endif /* DUK_USE_GLOBAL_BUILTIN */ - -/* automatic undefs */ -#undef DUK__CHECK_BITMASK -#undef DUK__MKBITS -/* - * JSON built-ins. - * - * See doc/json.rst. - * - * Codepoints are handled as duk_uint_fast32_t to ensure that the full - * unsigned 32-bit range is supported. This matters to e.g. JX. - * - * Input parsing doesn't do an explicit end-of-input check at all. This is - * safe: input string data is always NUL-terminated (0x00) and valid JSON - * inputs never contain plain NUL characters, so that as long as syntax checks - * are correct, we'll never read past the NUL. This approach reduces code size - * and improves parsing performance, but it's critical that syntax checks are - * indeed correct! - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_JSON_SUPPORT) - -/* - * Local defines and forward declarations. - */ - -#define DUK__JSON_DECSTR_BUFSIZE 128 -#define DUK__JSON_DECSTR_CHUNKSIZE 64 -#define DUK__JSON_ENCSTR_CHUNKSIZE 64 -#define DUK__JSON_STRINGIFY_BUFSIZE 128 -#define DUK__JSON_MAX_ESC_LEN 10 /* '\Udeadbeef' */ - -DUK_LOCAL_DECL void duk__json_dec_syntax_error(duk_json_dec_ctx *js_ctx); -DUK_LOCAL_DECL void duk__json_dec_eat_white(duk_json_dec_ctx *js_ctx); -#if defined(DUK_USE_JX) -DUK_LOCAL_DECL duk_uint8_t duk__json_dec_peek(duk_json_dec_ctx *js_ctx); -#endif -DUK_LOCAL_DECL duk_uint8_t duk__json_dec_get(duk_json_dec_ctx *js_ctx); -DUK_LOCAL_DECL duk_uint8_t duk__json_dec_get_nonwhite(duk_json_dec_ctx *js_ctx); -DUK_LOCAL_DECL duk_uint_fast32_t duk__json_dec_decode_hex_escape(duk_json_dec_ctx *js_ctx, duk_small_uint_t n); -DUK_LOCAL_DECL void duk__json_dec_req_stridx(duk_json_dec_ctx *js_ctx, duk_small_uint_t stridx); -DUK_LOCAL_DECL void duk__json_dec_string(duk_json_dec_ctx *js_ctx); -#if defined(DUK_USE_JX) -DUK_LOCAL_DECL void duk__json_dec_plain_string(duk_json_dec_ctx *js_ctx); -DUK_LOCAL_DECL void duk__json_dec_pointer(duk_json_dec_ctx *js_ctx); -DUK_LOCAL_DECL void duk__json_dec_buffer(duk_json_dec_ctx *js_ctx); -#endif -DUK_LOCAL_DECL void duk__json_dec_number(duk_json_dec_ctx *js_ctx); -DUK_LOCAL_DECL void duk__json_dec_objarr_entry(duk_json_dec_ctx *js_ctx); -DUK_LOCAL_DECL void duk__json_dec_objarr_exit(duk_json_dec_ctx *js_ctx); -DUK_LOCAL_DECL void duk__json_dec_object(duk_json_dec_ctx *js_ctx); -DUK_LOCAL_DECL void duk__json_dec_array(duk_json_dec_ctx *js_ctx); -DUK_LOCAL_DECL void duk__json_dec_value(duk_json_dec_ctx *js_ctx); -DUK_LOCAL_DECL void duk__json_dec_reviver_walk(duk_json_dec_ctx *js_ctx); - -DUK_LOCAL_DECL void duk__emit_1(duk_json_enc_ctx *js_ctx, duk_uint_fast8_t ch); -DUK_LOCAL_DECL void duk__emit_2(duk_json_enc_ctx *js_ctx, duk_uint_fast8_t ch1, duk_uint_fast8_t ch2); -DUK_LOCAL_DECL void duk__unemit_1(duk_json_enc_ctx *js_ctx); -DUK_LOCAL_DECL void duk__emit_hstring(duk_json_enc_ctx *js_ctx, duk_hstring *h); -#if defined(DUK_USE_FASTINT) -DUK_LOCAL_DECL void duk__emit_cstring(duk_json_enc_ctx *js_ctx, const char *p); -#endif -DUK_LOCAL_DECL void duk__emit_stridx(duk_json_enc_ctx *js_ctx, duk_small_uint_t stridx); -DUK_LOCAL_DECL duk_uint8_t *duk__emit_esc_auto_fast(duk_json_enc_ctx *js_ctx, duk_uint_fast32_t cp, duk_uint8_t *q); -DUK_LOCAL_DECL void duk__json_enc_key_autoquote(duk_json_enc_ctx *js_ctx, duk_hstring *k); -DUK_LOCAL_DECL void duk__json_enc_quote_string(duk_json_enc_ctx *js_ctx, duk_hstring *h_str); -DUK_LOCAL_DECL void duk__json_enc_objarr_entry(duk_json_enc_ctx *js_ctx, duk_idx_t *entry_top); -DUK_LOCAL_DECL void duk__json_enc_objarr_exit(duk_json_enc_ctx *js_ctx, duk_idx_t *entry_top); -DUK_LOCAL_DECL void duk__json_enc_object(duk_json_enc_ctx *js_ctx); -DUK_LOCAL_DECL void duk__json_enc_array(duk_json_enc_ctx *js_ctx); -DUK_LOCAL_DECL duk_bool_t duk__json_enc_value(duk_json_enc_ctx *js_ctx, duk_idx_t idx_holder); -DUK_LOCAL_DECL duk_bool_t duk__json_enc_allow_into_proplist(duk_tval *tv); -DUK_LOCAL_DECL void duk__json_enc_double(duk_json_enc_ctx *js_ctx); -#if defined(DUK_USE_FASTINT) -DUK_LOCAL_DECL void duk__json_enc_fastint_tval(duk_json_enc_ctx *js_ctx, duk_tval *tv); -#endif -#if defined(DUK_USE_JX) || defined(DUK_USE_JC) -DUK_LOCAL_DECL void duk__json_enc_buffer_jx_jc(duk_json_enc_ctx *js_ctx, duk_hbuffer *h); -DUK_LOCAL_DECL void duk__json_enc_pointer(duk_json_enc_ctx *js_ctx, void *ptr); -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_LOCAL_DECL void duk__json_enc_bufobj(duk_json_enc_ctx *js_ctx, duk_hbufobj *h_bufobj); -#endif -#endif -#if defined(DUK_USE_JSON_STRINGIFY_FASTPATH) -DUK_LOCAL_DECL void duk__json_enc_buffer_json_fastpath(duk_json_enc_ctx *js_ctx, duk_hbuffer *h); -#endif -DUK_LOCAL_DECL void duk__json_enc_newline_indent(duk_json_enc_ctx *js_ctx, duk_uint_t depth); - -/* - * Helper tables - */ - -#if defined(DUK_USE_JSON_QUOTESTRING_FASTPATH) -DUK_LOCAL const duk_uint8_t duk__json_quotestr_lookup[256] = { - /* 0x00 ... 0x7f: as is - * 0x80: escape generically - * 0x81: slow path - * 0xa0 ... 0xff: backslash + one char - */ - - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xe2, 0xf4, 0xee, 0x80, 0xe6, 0xf2, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x20, 0x21, 0xa2, 0x23, 0x24, 0x25, 0x26, 0x27, - 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, - 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, - 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0xdc, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, - 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81 -}; -#else /* DUK_USE_JSON_QUOTESTRING_FASTPATH */ -DUK_LOCAL const duk_uint8_t duk__json_quotestr_esc[14] = { DUK_ASC_NUL, DUK_ASC_NUL, DUK_ASC_NUL, DUK_ASC_NUL, DUK_ASC_NUL, - DUK_ASC_NUL, DUK_ASC_NUL, DUK_ASC_NUL, DUK_ASC_LC_B, DUK_ASC_LC_T, - DUK_ASC_LC_N, DUK_ASC_NUL, DUK_ASC_LC_F, DUK_ASC_LC_R }; -#endif /* DUK_USE_JSON_QUOTESTRING_FASTPATH */ - -#if defined(DUK_USE_JSON_DECSTRING_FASTPATH) -DUK_LOCAL const duk_uint8_t duk__json_decstr_lookup[256] = { - /* 0x00: slow path - * other: as is - */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x21, 0x00, 0x23, 0x24, 0x25, 0x26, 0x27, - 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, - 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, - 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x00, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, - 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, - 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, - 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, - 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, - 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, - 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, - 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff -}; -#endif /* DUK_USE_JSON_DECSTRING_FASTPATH */ - -#if defined(DUK_USE_JSON_EATWHITE_FASTPATH) -DUK_LOCAL const duk_uint8_t duk__json_eatwhite_lookup[256] = { - /* 0x00: finish (non-white) - * 0x01: continue - */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -#endif /* DUK_USE_JSON_EATWHITE_FASTPATH */ - -#if defined(DUK_USE_JSON_DECNUMBER_FASTPATH) -DUK_LOCAL const duk_uint8_t duk__json_decnumber_lookup[256] = { - /* 0x00: finish (not part of number) - * 0x01: continue - */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -#endif /* DUK_USE_JSON_DECNUMBER_FASTPATH */ - -/* - * Parsing implementation. - * - * JSON lexer is now separate from duk_lexer.c because there are numerous - * small differences making it difficult to share the lexer. - * - * The parser here works with raw bytes directly; this works because all - * JSON delimiters are ASCII characters. Invalid xUTF-8 encoded values - * inside strings will be passed on without normalization; this is not a - * compliance concern because compliant inputs will always be valid - * CESU-8 encodings. - */ - -DUK_LOCAL void duk__json_dec_syntax_error(duk_json_dec_ctx *js_ctx) { - /* Shared handler to minimize parser size. Cause will be - * hidden, unfortunately, but we'll have an offset which - * is often quite enough. - */ - DUK_ERROR_FMT1(js_ctx->thr, DUK_ERR_SYNTAX_ERROR, DUK_STR_FMT_INVALID_JSON, (long) (js_ctx->p - js_ctx->p_start)); - DUK_WO_NORETURN(return;); -} - -DUK_LOCAL void duk__json_dec_eat_white(duk_json_dec_ctx *js_ctx) { - const duk_uint8_t *p; - duk_uint8_t t; - - p = js_ctx->p; - for (;;) { - DUK_ASSERT(p <= js_ctx->p_end); - t = *p; - -#if defined(DUK_USE_JSON_EATWHITE_FASTPATH) - /* This fast path is pretty marginal in practice. - * XXX: candidate for removal. - */ - DUK_ASSERT(duk__json_eatwhite_lookup[0x00] == 0x00); /* end-of-input breaks */ - if (duk__json_eatwhite_lookup[t] == 0) { - break; - } -#else /* DUK_USE_JSON_EATWHITE_FASTPATH */ - if (!(t == 0x20 || t == 0x0a || t == 0x0d || t == 0x09)) { - /* NUL also comes here. Comparison order matters, 0x20 - * is most common whitespace. - */ - break; - } -#endif /* DUK_USE_JSON_EATWHITE_FASTPATH */ - p++; - } - js_ctx->p = p; -} - -#if defined(DUK_USE_JX) -DUK_LOCAL duk_uint8_t duk__json_dec_peek(duk_json_dec_ctx *js_ctx) { - DUK_ASSERT(js_ctx->p <= js_ctx->p_end); - return *js_ctx->p; -} -#endif - -DUK_LOCAL duk_uint8_t duk__json_dec_get(duk_json_dec_ctx *js_ctx) { - DUK_ASSERT(js_ctx->p <= js_ctx->p_end); - return *js_ctx->p++; -} - -DUK_LOCAL duk_uint8_t duk__json_dec_get_nonwhite(duk_json_dec_ctx *js_ctx) { - duk__json_dec_eat_white(js_ctx); - return duk__json_dec_get(js_ctx); -} - -/* For JX, expressing the whole unsigned 32-bit range matters. */ -DUK_LOCAL duk_uint_fast32_t duk__json_dec_decode_hex_escape(duk_json_dec_ctx *js_ctx, duk_small_uint_t n) { - duk_small_uint_t i; - duk_uint_fast32_t res = 0; - duk_uint8_t x; - duk_small_int_t t; - - for (i = 0; i < n; i++) { - /* XXX: share helper from lexer; duk_lexer.c / hexval(). */ - - x = duk__json_dec_get(js_ctx); - DUK_DDD(DUK_DDDPRINT("decode_hex_escape: i=%ld, n=%ld, res=%ld, x=%ld", (long) i, (long) n, (long) res, (long) x)); - - /* x == 0x00 (EOF) causes syntax_error */ - DUK_ASSERT(duk_hex_dectab[0] == -1); - t = duk_hex_dectab[x & 0xff]; - if (DUK_LIKELY(t >= 0)) { - res = (res * 16) + (duk_uint_fast32_t) t; - } else { - /* catches EOF and invalid digits */ - goto syntax_error; - } - } - - DUK_DDD(DUK_DDDPRINT("final hex decoded value: %ld", (long) res)); - return res; - -syntax_error: - duk__json_dec_syntax_error(js_ctx); - DUK_UNREACHABLE(); - return 0; -} - -DUK_LOCAL void duk__json_dec_req_stridx(duk_json_dec_ctx *js_ctx, duk_small_uint_t stridx) { - duk_hstring *h; - const duk_uint8_t *p; - duk_uint8_t x, y; - - /* First character has already been eaten and checked by the caller. - * We can scan until a NUL in stridx string because no built-in strings - * have internal NULs. - */ - - DUK_ASSERT_STRIDX_VALID(stridx); - h = DUK_HTHREAD_GET_STRING(js_ctx->thr, stridx); - DUK_ASSERT(h != NULL); - - p = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h) + 1; - DUK_ASSERT(*(js_ctx->p - 1) == *(p - 1)); /* first character has been matched */ - - for (;;) { - x = *p; - if (x == 0) { - break; - } - y = duk__json_dec_get(js_ctx); - if (x != y) { - /* Catches EOF of JSON input. */ - goto syntax_error; - } - p++; - } - - return; - -syntax_error: - duk__json_dec_syntax_error(js_ctx); - DUK_UNREACHABLE(); -} - -DUK_LOCAL duk_small_int_t duk__json_dec_string_escape(duk_json_dec_ctx *js_ctx, duk_uint8_t **ext_p) { - duk_uint_fast32_t cp; - - /* EOF (-1) will be cast to an unsigned value first - * and then re-cast for the switch. In any case, it - * will match the default case (syntax error). - */ - cp = (duk_uint_fast32_t) duk__json_dec_get(js_ctx); - switch (cp) { - case DUK_ASC_BACKSLASH: - break; - case DUK_ASC_DOUBLEQUOTE: - break; - case DUK_ASC_SLASH: - break; - case DUK_ASC_LC_T: - cp = 0x09; - break; - case DUK_ASC_LC_N: - cp = 0x0a; - break; - case DUK_ASC_LC_R: - cp = 0x0d; - break; - case DUK_ASC_LC_F: - cp = 0x0c; - break; - case DUK_ASC_LC_B: - cp = 0x08; - break; - case DUK_ASC_LC_U: { - cp = duk__json_dec_decode_hex_escape(js_ctx, 4); - break; - } -#if defined(DUK_USE_JX) - case DUK_ASC_UC_U: { - if (js_ctx->flag_ext_custom) { - cp = duk__json_dec_decode_hex_escape(js_ctx, 8); - } else { - return 1; /* syntax error */ - } - break; - } - case DUK_ASC_LC_X: { - if (js_ctx->flag_ext_custom) { - cp = duk__json_dec_decode_hex_escape(js_ctx, 2); - } else { - return 1; /* syntax error */ - } - break; - } -#endif /* DUK_USE_JX */ - default: - /* catches EOF (0x00) */ - return 1; /* syntax error */ - } - - DUK_RAW_WRITEINC_XUTF8(*ext_p, cp); - - return 0; -} - -DUK_LOCAL void duk__json_dec_string(duk_json_dec_ctx *js_ctx) { - duk_hthread *thr = js_ctx->thr; - duk_bufwriter_ctx bw_alloc; - duk_bufwriter_ctx *bw; - duk_uint8_t *q; - - /* '"' was eaten by caller */ - - /* Note that we currently parse -bytes-, not codepoints. - * All non-ASCII extended UTF-8 will encode to bytes >= 0x80, - * so they'll simply pass through (valid UTF-8 or not). - */ - - bw = &bw_alloc; - DUK_BW_INIT_PUSHBUF(js_ctx->thr, bw, DUK__JSON_DECSTR_BUFSIZE); - q = DUK_BW_GET_PTR(js_ctx->thr, bw); - -#if defined(DUK_USE_JSON_DECSTRING_FASTPATH) - for (;;) { - duk_small_uint_t safe; - duk_uint8_t b, x; - const duk_uint8_t *p; - - /* Select a safe loop count where no output checks are - * needed assuming we won't encounter escapes. Input - * bound checks are not necessary as a NUL (guaranteed) - * will cause a SyntaxError before we read out of bounds. - */ - - safe = DUK__JSON_DECSTR_CHUNKSIZE; - - /* Ensure space for 1:1 output plus one escape. */ - q = DUK_BW_ENSURE_RAW(js_ctx->thr, bw, safe + DUK_UNICODE_MAX_XUTF8_LENGTH, q); - - p = js_ctx->p; /* temp copy, write back for next loop */ - for (;;) { - if (safe == 0) { - js_ctx->p = p; - break; - } - safe--; - - /* End of input (NUL) goes through slow path and causes SyntaxError. */ - DUK_ASSERT(duk__json_decstr_lookup[0] == 0x00); - - b = *p++; - x = (duk_small_int_t) duk__json_decstr_lookup[b]; - if (DUK_LIKELY(x != 0)) { - /* Fast path, decode as is. */ - *q++ = b; - } else if (b == DUK_ASC_DOUBLEQUOTE) { - js_ctx->p = p; - goto found_quote; - } else if (b == DUK_ASC_BACKSLASH) { - /* We've ensured space for one escaped input; then - * bail out and recheck (this makes escape handling - * quite slow but it's uncommon). - */ - js_ctx->p = p; - if (duk__json_dec_string_escape(js_ctx, &q) != 0) { - goto syntax_error; - } - break; - } else { - js_ctx->p = p; - goto syntax_error; - } - } - } -found_quote: -#else /* DUK_USE_JSON_DECSTRING_FASTPATH */ - for (;;) { - duk_uint8_t x; - - q = DUK_BW_ENSURE_RAW(js_ctx->thr, bw, DUK_UNICODE_MAX_XUTF8_LENGTH, q); - - x = duk__json_dec_get(js_ctx); - - if (x == DUK_ASC_DOUBLEQUOTE) { - break; - } else if (x == DUK_ASC_BACKSLASH) { - if (duk__json_dec_string_escape(js_ctx, &q) != 0) { - goto syntax_error; - } - } else if (x < 0x20) { - /* catches EOF (NUL) */ - goto syntax_error; - } else { - *q++ = (duk_uint8_t) x; - } - } -#endif /* DUK_USE_JSON_DECSTRING_FASTPATH */ - - DUK_BW_SETPTR_AND_COMPACT(js_ctx->thr, bw, q); - (void) duk_buffer_to_string(thr, -1); /* Safe if input string is safe. */ - - /* [ ... str ] */ - - return; - -syntax_error: - duk__json_dec_syntax_error(js_ctx); - DUK_UNREACHABLE(); -} - -#if defined(DUK_USE_JX) -/* Decode a plain string consisting entirely of identifier characters. - * Used to parse plain keys (e.g. "foo: 123"). - */ -DUK_LOCAL void duk__json_dec_plain_string(duk_json_dec_ctx *js_ctx) { - duk_hthread *thr = js_ctx->thr; - const duk_uint8_t *p; - duk_small_int_t x; - - /* Caller has already eaten the first char so backtrack one byte. */ - - js_ctx->p--; /* safe */ - p = js_ctx->p; - - /* Here again we parse bytes, and non-ASCII UTF-8 will cause end of - * parsing (which is correct except if there are non-shortest encodings). - * There is also no need to check explicitly for end of input buffer as - * the input is NUL padded and NUL will exit the parsing loop. - * - * Because no unescaping takes place, we can just scan to the end of the - * plain string and intern from the input buffer. - */ - - for (;;) { - x = *p; - - /* There is no need to check the first character specially here - * (i.e. reject digits): the caller only accepts valid initial - * characters and won't call us if the first character is a digit. - * This also ensures that the plain string won't be empty. - */ - - if (!duk_unicode_is_identifier_part((duk_codepoint_t) x)) { - break; - } - p++; - } - - duk_push_lstring(thr, (const char *) js_ctx->p, (duk_size_t) (p - js_ctx->p)); - js_ctx->p = p; - - /* [ ... str ] */ -} -#endif /* DUK_USE_JX */ - -#if defined(DUK_USE_JX) -DUK_LOCAL void duk__json_dec_pointer(duk_json_dec_ctx *js_ctx) { - duk_hthread *thr = js_ctx->thr; - const duk_uint8_t *p; - duk_small_int_t x; - void *voidptr; - - /* Caller has already eaten the first character ('(') which we don't need. */ - - p = js_ctx->p; - - for (;;) { - x = *p; - - /* Assume that the native representation never contains a closing - * parenthesis. - */ - - if (x == DUK_ASC_RPAREN) { - break; - } else if (x <= 0) { - /* NUL term or -1 (EOF), NUL check would suffice */ - goto syntax_error; - } - p++; - } - - /* There is no need to NUL delimit the sscanf() call: trailing garbage is - * ignored and there is always a NUL terminator which will force an error - * if no error is encountered before it. It's possible that the scan - * would scan further than between [js_ctx->p,p[ though and we'd advance - * by less than the scanned value. - * - * Because pointers are platform specific, a failure to scan a pointer - * results in a null pointer which is a better placeholder than a missing - * value or an error. - */ - - voidptr = NULL; - (void) DUK_SSCANF((const char *) js_ctx->p, DUK_STR_FMT_PTR, &voidptr); - duk_push_pointer(thr, voidptr); - js_ctx->p = p + 1; /* skip ')' */ - - /* [ ... ptr ] */ - - return; - -syntax_error: - duk__json_dec_syntax_error(js_ctx); - DUK_UNREACHABLE(); -} -#endif /* DUK_USE_JX */ - -#if defined(DUK_USE_JX) -DUK_LOCAL void duk__json_dec_buffer(duk_json_dec_ctx *js_ctx) { - duk_hthread *thr = js_ctx->thr; - const duk_uint8_t *p; - duk_uint8_t *buf; - duk_size_t src_len; - duk_small_int_t x; - - /* Caller has already eaten the first character ('|') which we don't need. */ - - p = js_ctx->p; - - /* XXX: Would be nice to share the fast path loop from duk_hex_decode() - * and avoid creating a temporary buffer. However, there are some - * differences which prevent trivial sharing: - * - * - Pipe char detection - * - EOF detection - * - Unknown length of input and output - * - * The best approach here would be a bufwriter and a reasonaly sized - * safe inner loop (e.g. 64 output bytes at a time). - */ - - for (;;) { - x = *p; - - /* This loop intentionally does not ensure characters are valid - * ([0-9a-fA-F]) because the hex decode call below will do that. - */ - if (x == DUK_ASC_PIPE) { - break; - } else if (x <= 0) { - /* NUL term or -1 (EOF), NUL check would suffice */ - goto syntax_error; - } - p++; - } - - /* XXX: this is not very nice; unnecessary copy is made. */ - src_len = (duk_size_t) (p - js_ctx->p); - buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, src_len); - DUK_ASSERT(buf != NULL); - duk_memcpy((void *) buf, (const void *) js_ctx->p, src_len); - duk_hex_decode(thr, -1); - - js_ctx->p = p + 1; /* skip '|' */ - - /* [ ... buf ] */ - - return; - -syntax_error: - duk__json_dec_syntax_error(js_ctx); - DUK_UNREACHABLE(); -} -#endif /* DUK_USE_JX */ - -/* Parse a number, other than NaN or +/- Infinity */ -DUK_LOCAL void duk__json_dec_number(duk_json_dec_ctx *js_ctx) { - duk_hthread *thr = js_ctx->thr; - const duk_uint8_t *p_start; - const duk_uint8_t *p; - duk_uint8_t x; - duk_small_uint_t s2n_flags; - - DUK_DDD(DUK_DDDPRINT("parse_number")); - - p_start = js_ctx->p; - - /* First pass parse is very lenient (e.g. allows '1.2.3') and extracts a - * string for strict number parsing. - */ - - p = js_ctx->p; - for (;;) { - x = *p; - - DUK_DDD(DUK_DDDPRINT("parse_number: p_start=%p, p=%p, p_end=%p, x=%ld", - (const void *) p_start, - (const void *) p, - (const void *) js_ctx->p_end, - (long) x)); - -#if defined(DUK_USE_JSON_DECNUMBER_FASTPATH) - /* This fast path is pretty marginal in practice. - * XXX: candidate for removal. - */ - DUK_ASSERT(duk__json_decnumber_lookup[0x00] == 0x00); /* end-of-input breaks */ - if (duk__json_decnumber_lookup[x] == 0) { - break; - } -#else /* DUK_USE_JSON_DECNUMBER_FASTPATH */ - if (!((x >= DUK_ASC_0 && x <= DUK_ASC_9) || - (x == DUK_ASC_PERIOD || x == DUK_ASC_LC_E || x == DUK_ASC_UC_E || x == DUK_ASC_MINUS || x == DUK_ASC_PLUS))) { - /* Plus sign must be accepted for positive exponents - * (e.g. '1.5e+2'). This clause catches NULs. - */ - break; - } -#endif /* DUK_USE_JSON_DECNUMBER_FASTPATH */ - p++; /* safe, because matched (NUL causes a break) */ - } - js_ctx->p = p; - - DUK_ASSERT(js_ctx->p > p_start); - duk_push_lstring(thr, (const char *) p_start, (duk_size_t) (p - p_start)); - - s2n_flags = DUK_S2N_FLAG_ALLOW_EXP | DUK_S2N_FLAG_ALLOW_MINUS | /* but don't allow leading plus */ - DUK_S2N_FLAG_ALLOW_FRAC; - - DUK_DDD(DUK_DDDPRINT("parse_number: string before parsing: %!T", (duk_tval *) duk_get_tval(thr, -1))); - duk_numconv_parse(thr, 10 /*radix*/, s2n_flags); - if (duk_is_nan(thr, -1)) { - duk__json_dec_syntax_error(js_ctx); - } - DUK_ASSERT(duk_is_number(thr, -1)); - DUK_DDD(DUK_DDDPRINT("parse_number: final number: %!T", (duk_tval *) duk_get_tval(thr, -1))); - - /* [ ... num ] */ -} - -DUK_LOCAL void duk__json_dec_objarr_entry(duk_json_dec_ctx *js_ctx) { - duk_hthread *thr = js_ctx->thr; - duk_require_stack(thr, DUK_JSON_DEC_REQSTACK); - - /* c recursion check */ - - duk_native_stack_check(thr); - - DUK_ASSERT_DISABLE(js_ctx->recursion_depth >= 0); /* unsigned */ - DUK_ASSERT(js_ctx->recursion_depth <= js_ctx->recursion_limit); - if (js_ctx->recursion_depth >= js_ctx->recursion_limit) { - DUK_ERROR_RANGE(thr, DUK_STR_DEC_RECLIMIT); - DUK_WO_NORETURN(return;); - } - js_ctx->recursion_depth++; -} - -DUK_LOCAL void duk__json_dec_objarr_exit(duk_json_dec_ctx *js_ctx) { - /* c recursion check */ - - DUK_ASSERT(js_ctx->recursion_depth > 0); - DUK_ASSERT(js_ctx->recursion_depth <= js_ctx->recursion_limit); - js_ctx->recursion_depth--; -} - -DUK_LOCAL void duk__json_dec_object(duk_json_dec_ctx *js_ctx) { - duk_hthread *thr = js_ctx->thr; - duk_int_t key_count; /* XXX: a "first" flag would suffice */ - duk_uint8_t x; - - DUK_DDD(DUK_DDDPRINT("parse_object")); - - duk__json_dec_objarr_entry(js_ctx); - - duk_push_object(thr); - - /* Initial '{' has been checked and eaten by caller. */ - - key_count = 0; - for (;;) { - x = duk__json_dec_get_nonwhite(js_ctx); - - DUK_DDD(DUK_DDDPRINT("parse_object: obj=%!T, x=%ld, key_count=%ld", - (duk_tval *) duk_get_tval(thr, -1), - (long) x, - (long) key_count)); - - /* handle comma and closing brace */ - - if (x == DUK_ASC_COMMA && key_count > 0) { - /* accept comma, expect new value */ - x = duk__json_dec_get_nonwhite(js_ctx); - } else if (x == DUK_ASC_RCURLY) { - /* eat closing brace */ - break; - } else if (key_count == 0) { - /* accept anything, expect first value (EOF will be - * caught by key parsing below. - */ - ; - } else { - /* catches EOF (NUL) and initial comma */ - goto syntax_error; - } - - /* parse key and value */ - - if (x == DUK_ASC_DOUBLEQUOTE) { - duk__json_dec_string(js_ctx); -#if defined(DUK_USE_JX) - } else if (js_ctx->flag_ext_custom && duk_unicode_is_identifier_start((duk_codepoint_t) x)) { - duk__json_dec_plain_string(js_ctx); -#endif - } else { - goto syntax_error; - } - - /* [ ... obj key ] */ - - x = duk__json_dec_get_nonwhite(js_ctx); - if (x != DUK_ASC_COLON) { - goto syntax_error; - } - - duk__json_dec_value(js_ctx); - - /* [ ... obj key val ] */ - - duk_xdef_prop_wec(thr, -3); - - /* [ ... obj ] */ - - key_count++; - } - - /* [ ... obj ] */ - - DUK_DDD(DUK_DDDPRINT("parse_object: final object is %!T", (duk_tval *) duk_get_tval(thr, -1))); - - duk__json_dec_objarr_exit(js_ctx); - return; - -syntax_error: - duk__json_dec_syntax_error(js_ctx); - DUK_UNREACHABLE(); -} - -DUK_LOCAL void duk__json_dec_array(duk_json_dec_ctx *js_ctx) { - duk_hthread *thr = js_ctx->thr; - duk_uarridx_t arr_idx; - duk_uint8_t x; - - DUK_DDD(DUK_DDDPRINT("parse_array")); - - duk__json_dec_objarr_entry(js_ctx); - - duk_push_array(thr); - - /* Initial '[' has been checked and eaten by caller. */ - - arr_idx = 0; - for (;;) { - x = duk__json_dec_get_nonwhite(js_ctx); - - DUK_DDD(DUK_DDDPRINT("parse_array: arr=%!T, x=%ld, arr_idx=%ld", - (duk_tval *) duk_get_tval(thr, -1), - (long) x, - (long) arr_idx)); - - /* handle comma and closing bracket */ - - if ((x == DUK_ASC_COMMA) && (arr_idx != 0)) { - /* accept comma, expect new value */ - ; - } else if (x == DUK_ASC_RBRACKET) { - /* eat closing bracket */ - break; - } else if (arr_idx == 0) { - /* accept anything, expect first value (EOF will be - * caught by duk__json_dec_value() below. - */ - js_ctx->p--; /* backtrack (safe) */ - } else { - /* catches EOF (NUL) and initial comma */ - goto syntax_error; - } - - /* parse value */ - - duk__json_dec_value(js_ctx); - - /* [ ... arr val ] */ - - duk_xdef_prop_index_wec(thr, -2, arr_idx); - arr_idx++; - } - - /* Must set 'length' explicitly when using duk_xdef_prop_xxx() to - * set the values. - */ - - duk_set_length(thr, -1, arr_idx); - - /* [ ... arr ] */ - - DUK_DDD(DUK_DDDPRINT("parse_array: final array is %!T", (duk_tval *) duk_get_tval(thr, -1))); - - duk__json_dec_objarr_exit(js_ctx); - return; - -syntax_error: - duk__json_dec_syntax_error(js_ctx); - DUK_UNREACHABLE(); -} - -DUK_LOCAL void duk__json_dec_value(duk_json_dec_ctx *js_ctx) { - duk_hthread *thr = js_ctx->thr; - duk_uint8_t x; - - x = duk__json_dec_get_nonwhite(js_ctx); - - DUK_DDD(DUK_DDDPRINT("parse_value: initial x=%ld", (long) x)); - - /* Note: duk__json_dec_req_stridx() backtracks one char */ - - if (x == DUK_ASC_DOUBLEQUOTE) { - duk__json_dec_string(js_ctx); - } else if ((x >= DUK_ASC_0 && x <= DUK_ASC_9) || (x == DUK_ASC_MINUS)) { -#if defined(DUK_USE_JX) - if (js_ctx->flag_ext_custom && x == DUK_ASC_MINUS && duk__json_dec_peek(js_ctx) == DUK_ASC_UC_I) { - duk__json_dec_req_stridx(js_ctx, DUK_STRIDX_MINUS_INFINITY); /* "-Infinity", '-' has been eaten */ - duk_push_number(thr, -DUK_DOUBLE_INFINITY); - } else { -#else - { /* unconditional block */ -#endif - /* We already ate 'x', so backup one byte. */ - js_ctx->p--; /* safe */ - duk__json_dec_number(js_ctx); - } - } else if (x == DUK_ASC_LC_T) { - duk__json_dec_req_stridx(js_ctx, DUK_STRIDX_TRUE); - duk_push_true(thr); - } else if (x == DUK_ASC_LC_F) { - duk__json_dec_req_stridx(js_ctx, DUK_STRIDX_FALSE); - duk_push_false(thr); - } else if (x == DUK_ASC_LC_N) { - duk__json_dec_req_stridx(js_ctx, DUK_STRIDX_LC_NULL); - duk_push_null(thr); -#if defined(DUK_USE_JX) - } else if (js_ctx->flag_ext_custom && x == DUK_ASC_LC_U) { - duk__json_dec_req_stridx(js_ctx, DUK_STRIDX_LC_UNDEFINED); - duk_push_undefined(thr); - } else if (js_ctx->flag_ext_custom && x == DUK_ASC_UC_N) { - duk__json_dec_req_stridx(js_ctx, DUK_STRIDX_NAN); - duk_push_nan(thr); - } else if (js_ctx->flag_ext_custom && x == DUK_ASC_UC_I) { - duk__json_dec_req_stridx(js_ctx, DUK_STRIDX_INFINITY); - duk_push_number(thr, DUK_DOUBLE_INFINITY); - } else if (js_ctx->flag_ext_custom && x == DUK_ASC_LPAREN) { - duk__json_dec_pointer(js_ctx); - } else if (js_ctx->flag_ext_custom && x == DUK_ASC_PIPE) { - duk__json_dec_buffer(js_ctx); -#endif - } else if (x == DUK_ASC_LCURLY) { - duk__json_dec_object(js_ctx); - } else if (x == DUK_ASC_LBRACKET) { - duk__json_dec_array(js_ctx); - } else { - /* catches EOF (NUL) */ - goto syntax_error; - } - - duk__json_dec_eat_white(js_ctx); - - /* [ ... val ] */ - return; - -syntax_error: - duk__json_dec_syntax_error(js_ctx); - DUK_UNREACHABLE(); -} - -/* Recursive value reviver, implements the Walk() algorithm. The parsing - * step ensures there is a reasonable depth limit to the input. However, - * the reviver may create more depth by editing object or array entries, so - * we have both C recursion limit and native stack checks here. - */ -DUK_LOCAL void duk__json_dec_reviver_walk(duk_json_dec_ctx *js_ctx) { - duk_hthread *thr = js_ctx->thr; - duk_hobject *h; - duk_uarridx_t i, arr_len; - - duk__json_dec_objarr_entry(js_ctx); - - DUK_DDD(DUK_DDDPRINT("walk: top=%ld, holder=%!T, name=%!T", - (long) duk_get_top(thr), - (duk_tval *) duk_get_tval(thr, -2), - (duk_tval *) duk_get_tval(thr, -1))); - - duk_dup_top(thr); - duk_get_prop(thr, -3); /* -> [ ... holder name val ] */ - - h = duk_get_hobject(thr, -1); - if (h != NULL) { - if (duk_js_isarray_hobject(h)) { - arr_len = (duk_uarridx_t) duk_get_length(thr, -1); - for (i = 0; i < arr_len; i++) { - /* [ ... holder name val ] */ - - DUK_DDD(DUK_DDDPRINT("walk: array, top=%ld, i=%ld, arr_len=%ld, holder=%!T, name=%!T, val=%!T", - (long) duk_get_top(thr), - (long) i, - (long) arr_len, - (duk_tval *) duk_get_tval(thr, -3), - (duk_tval *) duk_get_tval(thr, -2), - (duk_tval *) duk_get_tval(thr, -1))); - - duk_dup_top(thr); - (void) duk_push_uint_to_hstring(thr, - (duk_uint_t) i); /* -> [ ... holder name val val ToString(i) ] */ - duk__json_dec_reviver_walk(js_ctx); /* -> [ ... holder name val new_elem ] */ - - if (duk_is_undefined(thr, -1)) { - duk_pop(thr); - duk_del_prop_index(thr, -1, i); - } else { - /* XXX: duk_xdef_prop_index_wec() would be more appropriate - * here but it currently makes some assumptions that might - * not hold (e.g. that previous property is not an accessor). - */ - duk_put_prop_index(thr, -2, i); - } - } - } else { - /* [ ... holder name val ] */ - duk_enum(thr, -1, DUK_ENUM_OWN_PROPERTIES_ONLY /*flags*/); - while (duk_next(thr, -1 /*enum_index*/, 0 /*get_value*/)) { - DUK_DDD(DUK_DDDPRINT("walk: object, top=%ld, holder=%!T, name=%!T, val=%!T, enum=%!iT, obj_key=%!T", - (long) duk_get_top(thr), - (duk_tval *) duk_get_tval(thr, -5), - (duk_tval *) duk_get_tval(thr, -4), - (duk_tval *) duk_get_tval(thr, -3), - (duk_tval *) duk_get_tval(thr, -2), - (duk_tval *) duk_get_tval(thr, -1))); - - /* [ ... holder name val enum obj_key ] */ - duk_dup_m3(thr); - duk_dup_m2(thr); - - /* [ ... holder name val enum obj_key val obj_key ] */ - duk__json_dec_reviver_walk(js_ctx); - - /* [ ... holder name val enum obj_key new_elem ] */ - if (duk_is_undefined(thr, -1)) { - duk_pop(thr); - duk_del_prop(thr, -3); - } else { - /* XXX: duk_xdef_prop_index_wec() would be more appropriate - * here but it currently makes some assumptions that might - * not hold (e.g. that previous property is not an accessor). - * - * Using duk_put_prop() works incorrectly with '__proto__' - * if the own property with that name has been deleted. This - * does not happen normally, but a clever reviver can trigger - * that, see complex reviver case in: test-bug-json-parse-__proto__.js. - */ - duk_put_prop(thr, -4); - } - } - duk_pop(thr); /* pop enum */ - } - } - - /* [ ... holder name val ] */ - - duk_dup(thr, js_ctx->idx_reviver); - duk_insert(thr, -4); /* -> [ ... reviver holder name val ] */ - duk_call_method(thr, 2); /* -> [ ... res ] */ - - duk__json_dec_objarr_exit(js_ctx); - - DUK_DDD(DUK_DDDPRINT("walk: top=%ld, result=%!T", (long) duk_get_top(thr), (duk_tval *) duk_get_tval(thr, -1))); -} - -/* - * Stringify implementation. - */ - -#define DUK__EMIT_1(js_ctx, ch) duk__emit_1((js_ctx), (duk_uint_fast8_t) (ch)) -#define DUK__EMIT_2(js_ctx, ch1, ch2) duk__emit_2((js_ctx), (duk_uint_fast8_t) (ch1), (duk_uint_fast8_t) (ch2)) -#define DUK__EMIT_HSTR(js_ctx, h) duk__emit_hstring((js_ctx), (h)) -#if defined(DUK_USE_FASTINT) || defined(DUK_USE_JX) || defined(DUK_USE_JC) -#define DUK__EMIT_CSTR(js_ctx, p) duk__emit_cstring((js_ctx), (p)) -#endif -#define DUK__EMIT_STRIDX(js_ctx, i) duk__emit_stridx((js_ctx), (i)) -#define DUK__UNEMIT_1(js_ctx) duk__unemit_1((js_ctx)) - -DUK_LOCAL void duk__emit_1(duk_json_enc_ctx *js_ctx, duk_uint_fast8_t ch) { - DUK_BW_WRITE_ENSURE_U8(js_ctx->thr, &js_ctx->bw, ch); -} - -DUK_LOCAL void duk__emit_2(duk_json_enc_ctx *js_ctx, duk_uint_fast8_t ch1, duk_uint_fast8_t ch2) { - DUK_BW_WRITE_ENSURE_U8_2(js_ctx->thr, &js_ctx->bw, ch1, ch2); -} - -DUK_LOCAL void duk__emit_hstring(duk_json_enc_ctx *js_ctx, duk_hstring *h) { - DUK_BW_WRITE_ENSURE_HSTRING(js_ctx->thr, &js_ctx->bw, h); -} - -#if defined(DUK_USE_FASTINT) || defined(DUK_USE_JX) || defined(DUK_USE_JC) -DUK_LOCAL void duk__emit_cstring(duk_json_enc_ctx *js_ctx, const char *str) { - DUK_BW_WRITE_ENSURE_CSTRING(js_ctx->thr, &js_ctx->bw, str); -} -#endif - -DUK_LOCAL void duk__emit_stridx(duk_json_enc_ctx *js_ctx, duk_small_uint_t stridx) { - duk_hstring *h; - - DUK_ASSERT_STRIDX_VALID(stridx); - h = DUK_HTHREAD_GET_STRING(js_ctx->thr, stridx); - DUK_ASSERT(h != NULL); - - DUK_BW_WRITE_ENSURE_HSTRING(js_ctx->thr, &js_ctx->bw, h); -} - -DUK_LOCAL void duk__unemit_1(duk_json_enc_ctx *js_ctx) { - DUK_ASSERT(DUK_BW_GET_SIZE(js_ctx->thr, &js_ctx->bw) >= 1); - DUK_BW_ADD_PTR(js_ctx->thr, &js_ctx->bw, -1); -} - -#define DUK__MKESC(nybbles, esc1, esc2) \ - (((duk_uint_fast32_t) (nybbles)) << 16) | (((duk_uint_fast32_t) (esc1)) << 8) | ((duk_uint_fast32_t) (esc2)) - -DUK_LOCAL duk_uint8_t *duk__emit_esc_auto_fast(duk_json_enc_ctx *js_ctx, duk_uint_fast32_t cp, duk_uint8_t *q) { - duk_uint_fast32_t tmp; - duk_small_uint_t dig; - - DUK_UNREF(js_ctx); - - /* Caller ensures space for at least DUK__JSON_MAX_ESC_LEN. */ - - /* Select appropriate escape format automatically, and set 'tmp' to a - * value encoding both the escape format character and the nybble count: - * - * (nybble_count << 16) | (escape_char1) | (escape_char2) - */ - -#if defined(DUK_USE_JX) - if (DUK_LIKELY(cp < 0x100UL)) { - if (DUK_UNLIKELY(js_ctx->flag_ext_custom != 0U)) { - tmp = DUK__MKESC(2, DUK_ASC_BACKSLASH, DUK_ASC_LC_X); - } else { - tmp = DUK__MKESC(4, DUK_ASC_BACKSLASH, DUK_ASC_LC_U); - } - } else -#endif - if (DUK_LIKELY(cp < 0x10000UL)) { - tmp = DUK__MKESC(4, DUK_ASC_BACKSLASH, DUK_ASC_LC_U); - } else { -#if defined(DUK_USE_JX) - if (DUK_LIKELY(js_ctx->flag_ext_custom != 0U)) { - tmp = DUK__MKESC(8, DUK_ASC_BACKSLASH, DUK_ASC_UC_U); - } else -#endif - { - /* In compatible mode and standard JSON mode, output - * something useful for non-BMP characters. This won't - * roundtrip but will still be more or less readable and - * more useful than an error. - */ - tmp = DUK__MKESC(8, DUK_ASC_UC_U, DUK_ASC_PLUS); - } - } - - *q++ = (duk_uint8_t) ((tmp >> 8) & 0xff); - *q++ = (duk_uint8_t) (tmp & 0xff); - - tmp = tmp >> 16; - while (tmp > 0) { - tmp--; - dig = (duk_small_uint_t) ((cp >> (4 * tmp)) & 0x0f); - *q++ = duk_lc_digits[dig]; - } - - return q; -} - -DUK_LOCAL void duk__json_enc_key_autoquote(duk_json_enc_ctx *js_ctx, duk_hstring *k) { - const duk_int8_t *p, *p_start, *p_end; /* Note: intentionally signed. */ - duk_size_t k_len; - duk_codepoint_t cp; - - DUK_ASSERT(k != NULL); - - /* Accept ASCII strings which conform to identifier requirements - * as being emitted without key quotes. Since we only accept ASCII - * there's no need for actual decoding: 'p' is intentionally signed - * so that bytes >= 0x80 extend to negative values and are rejected - * as invalid identifier codepoints. - */ - - if (js_ctx->flag_avoid_key_quotes) { - k_len = DUK_HSTRING_GET_BYTELEN(k); - p_start = (const duk_int8_t *) DUK_HSTRING_GET_DATA(k); - p_end = p_start + k_len; - p = p_start; - - if (p == p_end) { - /* Zero length string is not accepted without quotes */ - goto quote_normally; - } - cp = (duk_codepoint_t) (*p++); - if (DUK_UNLIKELY(!duk_unicode_is_identifier_start(cp))) { - goto quote_normally; - } - while (p < p_end) { - cp = (duk_codepoint_t) (*p++); - if (DUK_UNLIKELY(!duk_unicode_is_identifier_part(cp))) { - goto quote_normally; - } - } - - /* This seems faster than emitting bytes one at a time and - * then potentially rewinding. - */ - DUK__EMIT_HSTR(js_ctx, k); - return; - } - -quote_normally: - duk__json_enc_quote_string(js_ctx, k); -} - -/* The Quote(value) operation: quote a string. - * - * Stack policy: [ ] -> [ ]. - */ - -DUK_LOCAL void duk__json_enc_quote_string(duk_json_enc_ctx *js_ctx, duk_hstring *h_str) { - duk_hthread *thr = js_ctx->thr; - const duk_uint8_t *p, *p_start, *p_end, *p_now, *p_tmp; - duk_uint8_t *q; - duk_ucodepoint_t cp; /* typed for duk_unicode_decode_xutf8() */ - - DUK_DDD(DUK_DDDPRINT("duk__json_enc_quote_string: h_str=%!O", (duk_heaphdr *) h_str)); - - DUK_ASSERT(h_str != NULL); - p_start = DUK_HSTRING_GET_DATA(h_str); - p_end = p_start + DUK_HSTRING_GET_BYTELEN(h_str); - p = p_start; - - DUK__EMIT_1(js_ctx, DUK_ASC_DOUBLEQUOTE); - - /* Encode string in small chunks, estimating the maximum expansion so that - * there's no need to ensure space while processing the chunk. - */ - - while (p < p_end) { - duk_size_t left, now, space; - - left = (duk_size_t) (p_end - p); - now = (left > DUK__JSON_ENCSTR_CHUNKSIZE ? DUK__JSON_ENCSTR_CHUNKSIZE : left); - - /* Maximum expansion per input byte is 6: - * - invalid UTF-8 byte causes "\uXXXX" to be emitted (6/1 = 6). - * - 2-byte UTF-8 encodes as "\uXXXX" (6/2 = 3). - * - 4-byte UTF-8 encodes as "\Uxxxxxxxx" (10/4 = 2.5). - */ - space = now * 6; - q = DUK_BW_ENSURE_GETPTR(thr, &js_ctx->bw, space); - - p_now = p + now; - - while (p < p_now) { -#if defined(DUK_USE_JSON_QUOTESTRING_FASTPATH) - duk_uint8_t b; - - b = duk__json_quotestr_lookup[*p++]; - if (DUK_LIKELY(b < 0x80)) { - /* Most input bytes go through here. */ - *q++ = b; - } else if (b >= 0xa0) { - *q++ = DUK_ASC_BACKSLASH; - *q++ = (duk_uint8_t) (b - 0x80); - } else if (b == 0x80) { - cp = (duk_ucodepoint_t) (*(p - 1)); - q = duk__emit_esc_auto_fast(js_ctx, cp, q); - } else if (b == 0x7f && js_ctx->flag_ascii_only) { - /* 0x7F is special */ - DUK_ASSERT(b == 0x81); - cp = (duk_ucodepoint_t) 0x7f; - q = duk__emit_esc_auto_fast(js_ctx, cp, q); - } else { - DUK_ASSERT(b == 0x81); - p--; - - /* slow path is shared */ -#else /* DUK_USE_JSON_QUOTESTRING_FASTPATH */ - cp = *p; - - if (DUK_LIKELY(cp <= 0x7f)) { - /* ascii fast path: avoid decoding utf-8 */ - p++; - if (cp == 0x22 || cp == 0x5c) { - /* double quote or backslash */ - *q++ = DUK_ASC_BACKSLASH; - *q++ = (duk_uint8_t) cp; - } else if (cp < 0x20) { - duk_uint_fast8_t esc_char; - - /* This approach is a bit shorter than a straight - * if-else-ladder and also a bit faster. - */ - if (cp < (sizeof(duk__json_quotestr_esc) / sizeof(duk_uint8_t)) && - (esc_char = duk__json_quotestr_esc[cp]) != 0) { - *q++ = DUK_ASC_BACKSLASH; - *q++ = (duk_uint8_t) esc_char; - } else { - q = duk__emit_esc_auto_fast(js_ctx, cp, q); - } - } else if (cp == 0x7f && js_ctx->flag_ascii_only) { - q = duk__emit_esc_auto_fast(js_ctx, cp, q); - } else { - /* any other printable -> as is */ - *q++ = (duk_uint8_t) cp; - } - } else { - /* slow path is shared */ -#endif /* DUK_USE_JSON_QUOTESTRING_FASTPATH */ - - /* slow path decode */ - - /* If XUTF-8 decoding fails, treat the offending byte as a codepoint directly - * and go forward one byte. This is of course very lossy, but allows some kind - * of output to be produced even for internal strings which don't conform to - * XUTF-8. All standard ECMAScript strings are always CESU-8, so this behavior - * does not violate the ECMAScript specification. The behavior is applied to - * all modes, including ECMAScript standard JSON. Because the current XUTF-8 - * decoding is not very strict, this behavior only really affects initial bytes - * and truncated codepoints. - * - * Another alternative would be to scan forwards to start of next codepoint - * (or end of input) and emit just one replacement codepoint. - */ - - p_tmp = p; - if (!duk_unicode_decode_xutf8(thr, &p, p_start, p_end, &cp)) { - /* Decode failed. */ - cp = *p_tmp; - p = p_tmp + 1; - } - -#if defined(DUK_USE_NONSTD_JSON_ESC_U2028_U2029) - if (js_ctx->flag_ascii_only || cp == 0x2028 || cp == 0x2029) { -#else - if (js_ctx->flag_ascii_only) { -#endif - q = duk__emit_esc_auto_fast(js_ctx, cp, q); - } else { - /* as is */ - DUK_RAW_WRITEINC_XUTF8(q, cp); - } - } - } - - DUK_BW_SET_PTR(thr, &js_ctx->bw, q); - } - - DUK__EMIT_1(js_ctx, DUK_ASC_DOUBLEQUOTE); -} - -/* Encode a double (checked by caller) from stack top. Stack top may be - * replaced by serialized string but is not popped (caller does that). - */ -DUK_LOCAL void duk__json_enc_double(duk_json_enc_ctx *js_ctx) { - duk_hthread *thr; - duk_tval *tv; - duk_double_t d; - duk_small_int_t c; - duk_small_int_t s; - duk_small_uint_t stridx; - duk_small_uint_t n2s_flags; - duk_hstring *h_str; - - DUK_ASSERT(js_ctx != NULL); - thr = js_ctx->thr; - DUK_ASSERT(thr != NULL); - - /* Caller must ensure 'tv' is indeed a double and not a fastint! */ - tv = DUK_GET_TVAL_NEGIDX(thr, -1); - DUK_ASSERT(DUK_TVAL_IS_DOUBLE(tv)); - d = DUK_TVAL_GET_DOUBLE(tv); - - c = (duk_small_int_t) DUK_FPCLASSIFY(d); - s = (duk_small_int_t) DUK_SIGNBIT(d); - DUK_UNREF(s); - - if (DUK_LIKELY(!(c == DUK_FP_INFINITE || c == DUK_FP_NAN))) { - DUK_ASSERT(DUK_ISFINITE(d)); - -#if defined(DUK_USE_JX) || defined(DUK_USE_JC) - /* Negative zero needs special handling in JX/JC because - * it would otherwise serialize to '0', not '-0'. - */ - if (DUK_UNLIKELY(c == DUK_FP_ZERO && s != 0 && (js_ctx->flag_ext_custom_or_compatible))) { - duk_push_hstring_stridx(thr, DUK_STRIDX_MINUS_ZERO); /* '-0' */ - } else -#endif /* DUK_USE_JX || DUK_USE_JC */ - { - n2s_flags = 0; - /* [ ... number ] -> [ ... string ] */ - duk_numconv_stringify(thr, 10 /*radix*/, 0 /*digits*/, n2s_flags); - } - h_str = duk_known_hstring(thr, -1); - DUK__EMIT_HSTR(js_ctx, h_str); - return; - } - -#if defined(DUK_USE_JX) || defined(DUK_USE_JC) - if (!(js_ctx->flags & (DUK_JSON_FLAG_EXT_CUSTOM | DUK_JSON_FLAG_EXT_COMPATIBLE))) { - stridx = DUK_STRIDX_LC_NULL; - } else if (c == DUK_FP_NAN) { - stridx = js_ctx->stridx_custom_nan; - } else if (s == 0) { - stridx = js_ctx->stridx_custom_posinf; - } else { - stridx = js_ctx->stridx_custom_neginf; - } -#else - stridx = DUK_STRIDX_LC_NULL; -#endif - DUK__EMIT_STRIDX(js_ctx, stridx); -} - -#if defined(DUK_USE_FASTINT) -/* Encode a fastint from duk_tval ptr, no value stack effects. */ -DUK_LOCAL void duk__json_enc_fastint_tval(duk_json_enc_ctx *js_ctx, duk_tval *tv) { - duk_int64_t v; - - /* Fastint range is signed 48-bit so longest value is -2^47 = -140737488355328 - * (16 chars long), longest signed 64-bit value is -2^63 = -9223372036854775808 - * (20 chars long). Alloc space for 64-bit range to be safe. - */ - duk_uint8_t buf[20 + 1]; - - /* Caller must ensure 'tv' is indeed a fastint! */ - DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv)); - v = DUK_TVAL_GET_FASTINT(tv); - - /* XXX: There are no format strings in duk_config.h yet, could add - * one for formatting duk_int64_t. For now, assumes "%lld" and that - * "long long" type exists. Could also rely on C99 directly but that - * won't work for older MSVC. - */ - DUK_SPRINTF((char *) buf, "%lld", (long long) v); - DUK__EMIT_CSTR(js_ctx, (const char *) buf); -} -#endif - -#if defined(DUK_USE_JX) || defined(DUK_USE_JC) -#if defined(DUK_USE_HEX_FASTPATH) -DUK_LOCAL duk_uint8_t *duk__json_enc_buffer_data_hex(const duk_uint8_t *src, duk_size_t src_len, duk_uint8_t *dst) { - duk_uint8_t *q; - duk_uint16_t *q16; - duk_small_uint_t x; - duk_size_t i, len_safe; -#if !defined(DUK_USE_UNALIGNED_ACCESSES_POSSIBLE) - duk_bool_t shift_dst; -#endif - - /* Unlike in duk_hex_encode() 'dst' is not necessarily aligned by 2. - * For platforms where unaligned accesses are not allowed, shift 'dst' - * ahead by 1 byte to get alignment and then duk_memmove() the result - * in place. The faster encoding loop makes up the difference. - * There's always space for one extra byte because a terminator always - * follows the hex data and that's been accounted for by the caller. - */ - -#if defined(DUK_USE_UNALIGNED_ACCESSES_POSSIBLE) - q16 = (duk_uint16_t *) (void *) dst; -#else - shift_dst = (duk_bool_t) (((duk_size_t) dst) & 0x01U); - if (shift_dst) { - DUK_DD(DUK_DDPRINT("unaligned accesses not possible, dst not aligned -> step to dst + 1")); - q16 = (duk_uint16_t *) (void *) (dst + 1); - } else { - DUK_DD(DUK_DDPRINT("unaligned accesses not possible, dst is aligned")); - q16 = (duk_uint16_t *) (void *) dst; - } - DUK_ASSERT((((duk_size_t) q16) & 0x01U) == 0); -#endif - - len_safe = src_len & ~0x03U; - for (i = 0; i < len_safe; i += 4) { - q16[0] = duk_hex_enctab[src[i]]; - q16[1] = duk_hex_enctab[src[i + 1]]; - q16[2] = duk_hex_enctab[src[i + 2]]; - q16[3] = duk_hex_enctab[src[i + 3]]; - q16 += 4; - } - q = (duk_uint8_t *) q16; - -#if !defined(DUK_USE_UNALIGNED_ACCESSES_POSSIBLE) - if (shift_dst) { - q--; - duk_memmove((void *) dst, (const void *) (dst + 1), 2 * len_safe); - DUK_ASSERT(dst + 2 * len_safe == q); - } -#endif - - for (; i < src_len; i++) { - x = src[i]; - *q++ = duk_lc_digits[x >> 4]; - *q++ = duk_lc_digits[x & 0x0f]; - } - - return q; -} -#else /* DUK_USE_HEX_FASTPATH */ -DUK_LOCAL duk_uint8_t *duk__json_enc_buffer_data_hex(const duk_uint8_t *src, duk_size_t src_len, duk_uint8_t *dst) { - const duk_uint8_t *p; - const duk_uint8_t *p_end; - duk_uint8_t *q; - duk_small_uint_t x; - - p = src; - p_end = src + src_len; - q = dst; - while (p != p_end) { - x = *p++; - *q++ = duk_lc_digits[x >> 4]; - *q++ = duk_lc_digits[x & 0x0f]; - } - - return q; -} -#endif /* DUK_USE_HEX_FASTPATH */ - -DUK_LOCAL void duk__json_enc_buffer_data(duk_json_enc_ctx *js_ctx, duk_uint8_t *buf_data, duk_size_t buf_len) { - duk_hthread *thr; - duk_uint8_t *q; - duk_size_t space; - - thr = js_ctx->thr; - - DUK_ASSERT(js_ctx->flag_ext_custom || js_ctx->flag_ext_compatible); /* caller checks */ - DUK_ASSERT(js_ctx->flag_ext_custom_or_compatible); - - /* Buffer values are encoded in (lowercase) hex to make the - * binary data readable. Base64 or similar would be more - * compact but less readable, and the point of JX/JC - * variants is to be as useful to a programmer as possible. - */ - - /* The #if defined() clutter here needs to handle the three - * cases: (1) JX+JC, (2) JX only, (3) JC only. - */ - - /* Note: space must cater for both JX and JC. */ - space = 9 + buf_len * 2 + 2; - DUK_ASSERT(DUK_HBUFFER_MAX_BYTELEN <= 0x7ffffffeUL); - DUK_ASSERT((space - 2) / 2 >= buf_len); /* overflow not possible, buffer limits */ - q = DUK_BW_ENSURE_GETPTR(thr, &js_ctx->bw, space); - -#if defined(DUK_USE_JX) && defined(DUK_USE_JC) - if (js_ctx->flag_ext_custom) -#endif -#if defined(DUK_USE_JX) - { - *q++ = DUK_ASC_PIPE; - q = duk__json_enc_buffer_data_hex(buf_data, buf_len, q); - *q++ = DUK_ASC_PIPE; - - } -#endif -#if defined(DUK_USE_JX) && defined(DUK_USE_JC) - else -#endif -#if defined(DUK_USE_JC) - { - DUK_ASSERT(js_ctx->flag_ext_compatible); - duk_memcpy((void *) q, (const void *) "{\"_buf\":\"", 9); /* len: 9 */ - q += 9; - q = duk__json_enc_buffer_data_hex(buf_data, buf_len, q); - *q++ = DUK_ASC_DOUBLEQUOTE; - *q++ = DUK_ASC_RCURLY; - } -#endif - - DUK_BW_SET_PTR(thr, &js_ctx->bw, q); -} - -DUK_LOCAL void duk__json_enc_buffer_jx_jc(duk_json_enc_ctx *js_ctx, duk_hbuffer *h) { - duk__json_enc_buffer_data(js_ctx, - (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(js_ctx->thr->heap, h), - (duk_size_t) DUK_HBUFFER_GET_SIZE(h)); -} -#endif /* DUK_USE_JX || DUK_USE_JC */ - -#if defined(DUK_USE_JSON_STRINGIFY_FASTPATH) -DUK_LOCAL void duk__json_enc_buffer_json_fastpath(duk_json_enc_ctx *js_ctx, duk_hbuffer *h) { - duk_size_t i, n; - const duk_uint8_t *buf; - duk_uint8_t *q; - - n = DUK_HBUFFER_GET_SIZE(h); - if (n == 0) { - DUK__EMIT_2(js_ctx, DUK_ASC_LCURLY, DUK_ASC_RCURLY); - return; - } - - DUK__EMIT_1(js_ctx, DUK_ASC_LCURLY); - - /* Maximum encoded length with 32-bit index: 1 + 10 + 2 + 3 + 1 + 1 = 18, - * with 64-bit index: 1 + 20 + 2 + 3 + 1 + 1 = 28. 32 has some slack. - * - * Note that because the output buffer is reallocated from time to time, - * side effects (such as finalizers) affecting the buffer 'h' must be - * disabled. This is the case in the JSON.stringify() fast path. - */ - - buf = (const duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(js_ctx->thr->heap, h); - if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) { - for (i = 0; i < n; i++) { - duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth + 1); - q = DUK_BW_ENSURE_GETPTR(js_ctx->thr, &js_ctx->bw, 32); - q += DUK_SPRINTF((char *) q, "\"%lu\": %u,", (unsigned long) i, (unsigned int) buf[i]); - DUK_BW_SET_PTR(js_ctx->thr, &js_ctx->bw, q); - } - } else { - q = DUK_BW_GET_PTR(js_ctx->thr, &js_ctx->bw); - for (i = 0; i < n; i++) { - q = DUK_BW_ENSURE_RAW(js_ctx->thr, &js_ctx->bw, 32, q); - q += DUK_SPRINTF((char *) q, "\"%lu\":%u,", (unsigned long) i, (unsigned int) buf[i]); - } - DUK_BW_SET_PTR(js_ctx->thr, &js_ctx->bw, q); - } - DUK__UNEMIT_1(js_ctx); /* eat trailing comma */ - - if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) { - duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth); - } - DUK__EMIT_1(js_ctx, DUK_ASC_RCURLY); -} -#endif /* DUK_USE_JSON_STRINGIFY_FASTPATH */ - -#if defined(DUK_USE_JX) || defined(DUK_USE_JC) -DUK_LOCAL void duk__json_enc_pointer(duk_json_enc_ctx *js_ctx, void *ptr) { - char buf[64]; /* XXX: how to figure correct size? */ - const char *fmt; - - DUK_ASSERT(js_ctx->flag_ext_custom || js_ctx->flag_ext_compatible); /* caller checks */ - DUK_ASSERT(js_ctx->flag_ext_custom_or_compatible); - - duk_memzero(buf, sizeof(buf)); - - /* The #if defined() clutter here needs to handle the three - * cases: (1) JX+JC, (2) JX only, (3) JC only. - */ -#if defined(DUK_USE_JX) && defined(DUK_USE_JC) - if (js_ctx->flag_ext_custom) -#endif -#if defined(DUK_USE_JX) - { - fmt = ptr ? "(%p)" : "(null)"; - } -#endif -#if defined(DUK_USE_JX) && defined(DUK_USE_JC) - else -#endif -#if defined(DUK_USE_JC) - { - DUK_ASSERT(js_ctx->flag_ext_compatible); - fmt = ptr ? "{\"_ptr\":\"%p\"}" : "{\"_ptr\":\"null\"}"; - } -#endif - - /* When ptr == NULL, the format argument is unused. */ - DUK_SNPRINTF(buf, sizeof(buf) - 1, fmt, ptr); /* must not truncate */ - DUK__EMIT_CSTR(js_ctx, buf); -} -#endif /* DUK_USE_JX || DUK_USE_JC */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -#if defined(DUK_USE_JX) || defined(DUK_USE_JC) -DUK_LOCAL void duk__json_enc_bufobj(duk_json_enc_ctx *js_ctx, duk_hbufobj *h_bufobj) { - DUK_HBUFOBJ_ASSERT_VALID(h_bufobj); - - if (h_bufobj->buf == NULL || !DUK_HBUFOBJ_VALID_SLICE(h_bufobj)) { - DUK__EMIT_STRIDX(js_ctx, DUK_STRIDX_LC_NULL); - } else { - /* Handle both full and partial slice (as long as covered). */ - duk__json_enc_buffer_data(js_ctx, - (duk_uint8_t *) DUK_HBUFOBJ_GET_SLICE_BASE(js_ctx->thr->heap, h_bufobj), - (duk_size_t) h_bufobj->length); - } -} -#endif /* DUK_USE_JX || DUK_USE_JC */ -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -/* Indent helper. Calling code relies on js_ctx->recursion_depth also being - * directly related to indent depth. - */ -#if defined(DUK_USE_PREFER_SIZE) -DUK_LOCAL void duk__json_enc_newline_indent(duk_json_enc_ctx *js_ctx, duk_uint_t depth) { - DUK_ASSERT(js_ctx->h_gap != NULL); - DUK_ASSERT(DUK_HSTRING_GET_BYTELEN(js_ctx->h_gap) > 0); /* caller guarantees */ - - DUK__EMIT_1(js_ctx, 0x0a); - while (depth-- > 0) { - DUK__EMIT_HSTR(js_ctx, js_ctx->h_gap); - } -} -#else /* DUK_USE_PREFER_SIZE */ -DUK_LOCAL void duk__json_enc_newline_indent(duk_json_enc_ctx *js_ctx, duk_uint_t depth) { - const duk_uint8_t *gap_data; - duk_size_t gap_len; - duk_size_t avail_bytes; /* bytes of indent available for copying */ - duk_size_t need_bytes; /* bytes of indent still needed */ - duk_uint8_t *p_start; - duk_uint8_t *p; - - DUK_ASSERT(js_ctx->h_gap != NULL); - DUK_ASSERT(DUK_HSTRING_GET_BYTELEN(js_ctx->h_gap) > 0); /* caller guarantees */ - - DUK__EMIT_1(js_ctx, 0x0a); - if (DUK_UNLIKELY(depth == 0)) { - return; - } - - /* To handle deeper indents efficiently, make use of copies we've - * already emitted. In effect we can emit a sequence of 1, 2, 4, - * 8, etc copies, and then finish the last run. Byte counters - * avoid multiply with gap_len on every loop. - */ - - gap_data = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(js_ctx->h_gap); - gap_len = (duk_size_t) DUK_HSTRING_GET_BYTELEN(js_ctx->h_gap); - DUK_ASSERT(gap_len > 0); - - need_bytes = gap_len * depth; - p = DUK_BW_ENSURE_GETPTR(js_ctx->thr, &js_ctx->bw, need_bytes); - p_start = p; - - duk_memcpy((void *) p, (const void *) gap_data, (size_t) gap_len); - p += gap_len; - avail_bytes = gap_len; - DUK_ASSERT(need_bytes >= gap_len); - need_bytes -= gap_len; - - while (need_bytes >= avail_bytes) { - duk_memcpy((void *) p, (const void *) p_start, (size_t) avail_bytes); - p += avail_bytes; - need_bytes -= avail_bytes; - avail_bytes <<= 1; - } - - DUK_ASSERT(need_bytes < avail_bytes); /* need_bytes may be zero */ - duk_memcpy((void *) p, (const void *) p_start, (size_t) need_bytes); - p += need_bytes; - /*avail_bytes += need_bytes*/ - - DUK_BW_SET_PTR(js_ctx->thr, &js_ctx->bw, p); -} -#endif /* DUK_USE_PREFER_SIZE */ - -/* Shared entry handling for object/array serialization. */ -DUK_LOCAL void duk__json_enc_objarr_entry(duk_json_enc_ctx *js_ctx, duk_idx_t *entry_top) { - duk_hthread *thr = js_ctx->thr; - duk_hobject *h_target; - duk_uint_fast32_t i, n; - - *entry_top = duk_get_top(thr); - - duk_native_stack_check(thr); - duk_require_stack(thr, DUK_JSON_ENC_REQSTACK); - - /* Loop check using a hybrid approach: a fixed-size visited[] array - * with overflow in a loop check object. - */ - - h_target = duk_known_hobject(thr, -1); /* object or array */ - - n = js_ctx->recursion_depth; - if (DUK_UNLIKELY(n > DUK_JSON_ENC_LOOPARRAY)) { - n = DUK_JSON_ENC_LOOPARRAY; - } - for (i = 0; i < n; i++) { - if (DUK_UNLIKELY(js_ctx->visiting[i] == h_target)) { - DUK_DD(DUK_DDPRINT("slow path loop detect")); - DUK_ERROR_TYPE(thr, DUK_STR_CYCLIC_INPUT); - DUK_WO_NORETURN(return;); - } - } - if (js_ctx->recursion_depth < DUK_JSON_ENC_LOOPARRAY) { - js_ctx->visiting[js_ctx->recursion_depth] = h_target; - } else { - duk_push_sprintf(thr, DUK_STR_FMT_PTR, (void *) h_target); - duk_dup_top(thr); /* -> [ ... voidp voidp ] */ - if (duk_has_prop(thr, js_ctx->idx_loop)) { - DUK_ERROR_TYPE(thr, DUK_STR_CYCLIC_INPUT); - DUK_WO_NORETURN(return;); - } - duk_push_true(thr); /* -> [ ... voidp true ] */ - duk_put_prop(thr, js_ctx->idx_loop); /* -> [ ... ] */ - } - - /* C recursion check. */ - - DUK_ASSERT_DISABLE(js_ctx->recursion_depth >= 0); /* unsigned */ - DUK_ASSERT(js_ctx->recursion_depth <= js_ctx->recursion_limit); - if (js_ctx->recursion_depth >= js_ctx->recursion_limit) { - DUK_ERROR_RANGE(thr, DUK_STR_ENC_RECLIMIT); - DUK_WO_NORETURN(return;); - } - js_ctx->recursion_depth++; - - DUK_DDD(DUK_DDDPRINT("shared entry finished: top=%ld, loop=%!T", - (long) duk_get_top(thr), - (duk_tval *) duk_get_tval(thr, js_ctx->idx_loop))); -} - -/* Shared exit handling for object/array serialization. */ -DUK_LOCAL void duk__json_enc_objarr_exit(duk_json_enc_ctx *js_ctx, duk_idx_t *entry_top) { - duk_hthread *thr = js_ctx->thr; - duk_hobject *h_target; - - /* C recursion check. */ - - DUK_ASSERT(js_ctx->recursion_depth > 0); - DUK_ASSERT(js_ctx->recursion_depth <= js_ctx->recursion_limit); - js_ctx->recursion_depth--; - - /* Loop check. */ - - h_target = duk_known_hobject(thr, *entry_top - 1); /* original target at entry_top - 1 */ - - if (js_ctx->recursion_depth < DUK_JSON_ENC_LOOPARRAY) { - /* Previous entry was inside visited[], nothing to do. */ - } else { - duk_push_sprintf(thr, DUK_STR_FMT_PTR, (void *) h_target); - duk_del_prop(thr, js_ctx->idx_loop); /* -> [ ... ] */ - } - - /* Restore stack top after unbalanced code paths. */ - duk_set_top(thr, *entry_top); - - DUK_DDD(DUK_DDDPRINT("shared entry finished: top=%ld, loop=%!T", - (long) duk_get_top(thr), - (duk_tval *) duk_get_tval(thr, js_ctx->idx_loop))); -} - -/* The JO(value) operation: encode object. - * - * Stack policy: [ object ] -> [ object ]. - */ -DUK_LOCAL void duk__json_enc_object(duk_json_enc_ctx *js_ctx) { - duk_hthread *thr = js_ctx->thr; - duk_hstring *h_key; - duk_idx_t entry_top; - duk_idx_t idx_obj; - duk_idx_t idx_keys; - duk_bool_t emitted; - duk_uarridx_t arr_len, i; - duk_size_t prev_size; - - DUK_DDD(DUK_DDDPRINT("duk__json_enc_object: obj=%!T", (duk_tval *) duk_get_tval(thr, -1))); - - duk__json_enc_objarr_entry(js_ctx, &entry_top); - - idx_obj = entry_top - 1; - - if (js_ctx->idx_proplist >= 0) { - idx_keys = js_ctx->idx_proplist; - } else { - /* XXX: would be nice to enumerate an object at specified index */ - duk_dup(thr, idx_obj); - (void) duk_hobject_get_enumerated_keys( - thr, - DUK_ENUM_OWN_PROPERTIES_ONLY /*flags*/); /* [ ... target ] -> [ ... target keys ] */ - idx_keys = duk_require_normalize_index(thr, -1); - /* leave stack unbalanced on purpose */ - } - - DUK_DDD(DUK_DDDPRINT("idx_keys=%ld, h_keys=%!T", (long) idx_keys, (duk_tval *) duk_get_tval(thr, idx_keys))); - - /* Steps 8-10 have been merged to avoid a "partial" variable. */ - - DUK__EMIT_1(js_ctx, DUK_ASC_LCURLY); - - /* XXX: keys is an internal object with all keys to be processed - * in its (gapless) array part. Because nobody can touch the keys - * object, we could iterate its array part directly (keeping in mind - * that it can be reallocated). - */ - - arr_len = (duk_uarridx_t) duk_get_length(thr, idx_keys); - emitted = 0; - for (i = 0; i < arr_len; i++) { - duk_get_prop_index(thr, idx_keys, i); /* -> [ ... key ] */ - - DUK_DDD(DUK_DDDPRINT("object property loop: holder=%!T, key=%!T", - (duk_tval *) duk_get_tval(thr, idx_obj), - (duk_tval *) duk_get_tval(thr, -1))); - - h_key = duk_known_hstring(thr, -1); - DUK_ASSERT(h_key != NULL); - DUK_ASSERT(!DUK_HSTRING_HAS_SYMBOL(h_key)); /* proplist filtering; enum options */ - - prev_size = DUK_BW_GET_SIZE(js_ctx->thr, &js_ctx->bw); - if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) { - duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth); - duk__json_enc_key_autoquote(js_ctx, h_key); - DUK__EMIT_2(js_ctx, DUK_ASC_COLON, DUK_ASC_SPACE); - } else { - duk__json_enc_key_autoquote(js_ctx, h_key); - DUK__EMIT_1(js_ctx, DUK_ASC_COLON); - } - - /* [ ... key ] */ - - if (DUK_UNLIKELY(duk__json_enc_value(js_ctx, idx_obj) == 0)) { - /* Value would yield 'undefined', so skip key altogether. - * Side effects have already happened. - */ - DUK_BW_SET_SIZE(js_ctx->thr, &js_ctx->bw, prev_size); - } else { - DUK__EMIT_1(js_ctx, DUK_ASC_COMMA); - emitted = 1; - } - - /* [ ... ] */ - } - - if (emitted) { - DUK_ASSERT(*((duk_uint8_t *) DUK_BW_GET_PTR(js_ctx->thr, &js_ctx->bw) - 1) == DUK_ASC_COMMA); - DUK__UNEMIT_1(js_ctx); /* eat trailing comma */ - if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) { - DUK_ASSERT(js_ctx->recursion_depth >= 1); - duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth - 1U); - } - } - DUK__EMIT_1(js_ctx, DUK_ASC_RCURLY); - - duk__json_enc_objarr_exit(js_ctx, &entry_top); - - DUK_ASSERT_TOP(thr, entry_top); -} - -/* The JA(value) operation: encode array. - * - * Stack policy: [ array ] -> [ array ]. - */ -DUK_LOCAL void duk__json_enc_array(duk_json_enc_ctx *js_ctx) { - duk_hthread *thr = js_ctx->thr; - duk_idx_t entry_top; - duk_idx_t idx_arr; - duk_bool_t emitted; - duk_uarridx_t i, arr_len; - - DUK_DDD(DUK_DDDPRINT("duk__json_enc_array: array=%!T", (duk_tval *) duk_get_tval(thr, -1))); - - duk__json_enc_objarr_entry(js_ctx, &entry_top); - - idx_arr = entry_top - 1; - - /* Steps 8-10 have been merged to avoid a "partial" variable. */ - - DUK__EMIT_1(js_ctx, DUK_ASC_LBRACKET); - - arr_len = (duk_uarridx_t) duk_get_length(thr, idx_arr); - emitted = 0; - for (i = 0; i < arr_len; i++) { - DUK_DDD(DUK_DDDPRINT("array entry loop: array=%!T, index=%ld, arr_len=%ld", - (duk_tval *) duk_get_tval(thr, idx_arr), - (long) i, - (long) arr_len)); - - if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) { - DUK_ASSERT(js_ctx->recursion_depth >= 1); - duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth); - } - - (void) duk_push_uint_to_hstring(thr, (duk_uint_t) i); /* -> [ ... key ] */ - - /* [ ... key ] */ - - if (DUK_UNLIKELY(duk__json_enc_value(js_ctx, idx_arr) == 0)) { - /* Value would normally be omitted, replace with 'null'. */ - DUK__EMIT_STRIDX(js_ctx, DUK_STRIDX_LC_NULL); - } else { - ; - } - - /* [ ... ] */ - - DUK__EMIT_1(js_ctx, DUK_ASC_COMMA); - emitted = 1; - } - - if (emitted) { - DUK_ASSERT(*((duk_uint8_t *) DUK_BW_GET_PTR(js_ctx->thr, &js_ctx->bw) - 1) == DUK_ASC_COMMA); - DUK__UNEMIT_1(js_ctx); /* eat trailing comma */ - if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) { - DUK_ASSERT(js_ctx->recursion_depth >= 1); - duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth - 1U); - } - } - DUK__EMIT_1(js_ctx, DUK_ASC_RBRACKET); - - duk__json_enc_objarr_exit(js_ctx, &entry_top); - - DUK_ASSERT_TOP(thr, entry_top); -} - -/* The Str(key, holder) operation. - * - * Stack policy: [ ... key ] -> [ ... ] - */ -DUK_LOCAL duk_bool_t duk__json_enc_value(duk_json_enc_ctx *js_ctx, duk_idx_t idx_holder) { - duk_hthread *thr = js_ctx->thr; - duk_tval *tv; - duk_tval *tv_holder; - duk_tval *tv_key; - duk_small_int_t c; - - DUK_DDD(DUK_DDDPRINT("duk__json_enc_value: idx_holder=%ld, holder=%!T, key=%!T", - (long) idx_holder, - (duk_tval *) duk_get_tval(thr, idx_holder), - (duk_tval *) duk_get_tval(thr, -1))); - - tv_holder = DUK_GET_TVAL_POSIDX(thr, idx_holder); - DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv_holder)); - tv_key = DUK_GET_TVAL_NEGIDX(thr, -1); - DUK_ASSERT(DUK_TVAL_IS_STRING(tv_key)); - DUK_ASSERT(!DUK_HSTRING_HAS_SYMBOL(DUK_TVAL_GET_STRING(tv_key))); /* Caller responsible. */ - (void) duk_hobject_getprop(thr, tv_holder, tv_key); - - /* -> [ ... key val ] */ - - DUK_DDD(DUK_DDDPRINT("value=%!T", (duk_tval *) duk_get_tval(thr, -1))); - - /* Standard JSON checks for .toJSON() only for actual objects; for - * example, setting Number.prototype.toJSON and then serializing a - * number won't invoke the .toJSON() method. However, lightfuncs and - * plain buffers mimic objects so we check for their .toJSON() method. - */ - if (duk_check_type_mask(thr, -1, DUK_TYPE_MASK_OBJECT | DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER)) { - duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_TO_JSON); - if (duk_is_callable(thr, -1)) { /* toJSON() can also be a lightfunc */ - DUK_DDD(DUK_DDDPRINT("value is object, has callable toJSON() -> call it")); - /* XXX: duk_dup_unvalidated(thr, -2) etc. */ - duk_dup_m2(thr); /* -> [ ... key val toJSON val ] */ - duk_dup_m4(thr); /* -> [ ... key val toJSON val key ] */ - duk_call_method(thr, 1); /* -> [ ... key val val' ] */ - duk_remove_m2(thr); /* -> [ ... key val' ] */ - } else { - duk_pop(thr); /* -> [ ... key val ] */ - } - } - - /* [ ... key val ] */ - - DUK_DDD(DUK_DDDPRINT("value=%!T", (duk_tval *) duk_get_tval(thr, -1))); - - if (js_ctx->h_replacer) { - /* XXX: Here a "slice copy" would be useful. */ - DUK_DDD(DUK_DDDPRINT("replacer is set, call replacer")); - duk_push_hobject(thr, js_ctx->h_replacer); /* -> [ ... key val replacer ] */ - duk_dup(thr, idx_holder); /* -> [ ... key val replacer holder ] */ - duk_dup_m4(thr); /* -> [ ... key val replacer holder key ] */ - duk_dup_m4(thr); /* -> [ ... key val replacer holder key val ] */ - duk_call_method(thr, 2); /* -> [ ... key val val' ] */ - duk_remove_m2(thr); /* -> [ ... key val' ] */ - } - - /* [ ... key val ] */ - - DUK_DDD(DUK_DDDPRINT("value=%!T", (duk_tval *) duk_get_tval(thr, -1))); - - tv = DUK_GET_TVAL_NEGIDX(thr, -1); - if (DUK_TVAL_IS_OBJECT(tv)) { - duk_hobject *h; - - h = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -#if defined(DUK_USE_JX) || defined(DUK_USE_JC) - if (DUK_HOBJECT_IS_BUFOBJ(h) && js_ctx->flags & (DUK_JSON_FLAG_EXT_CUSTOM | DUK_JSON_FLAG_EXT_COMPATIBLE)) { - /* With JX/JC a bufferobject gets serialized specially. */ - duk_hbufobj *h_bufobj; - h_bufobj = (duk_hbufobj *) h; - DUK_HBUFOBJ_ASSERT_VALID(h_bufobj); - duk__json_enc_bufobj(js_ctx, h_bufobj); - goto pop2_emitted; - } - /* Otherwise bufferobjects get serialized as normal objects. */ -#endif /* JX || JC */ -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - c = (duk_small_int_t) DUK_HOBJECT_GET_CLASS_NUMBER(h); - switch (c) { - case DUK_HOBJECT_CLASS_NUMBER: { - DUK_DDD(DUK_DDDPRINT("value is a Number object -> coerce with ToNumber()")); - duk_to_number_m1(thr); - /* The coercion potentially invokes user .valueOf() and .toString() - * but can't result in a function value because ToPrimitive() would - * reject such a result: test-dev-json-stringify-coercion-1.js. - */ - DUK_ASSERT(!duk_is_callable(thr, -1)); - break; - } - case DUK_HOBJECT_CLASS_STRING: { - DUK_DDD(DUK_DDDPRINT("value is a String object -> coerce with ToString()")); - duk_to_string(thr, -1); - /* Same coercion behavior as for Number. */ - DUK_ASSERT(!duk_is_callable(thr, -1)); - break; - } -#if defined(DUK_USE_JX) || defined(DUK_USE_JC) - case DUK_HOBJECT_CLASS_POINTER: -#endif - case DUK_HOBJECT_CLASS_BOOLEAN: { - DUK_DDD(DUK_DDDPRINT("value is a Boolean/Buffer/Pointer object -> get internal value")); - duk_xget_owndataprop_stridx_short(thr, -1, DUK_STRIDX_INT_VALUE); - duk_remove_m2(thr); - break; - } - default: { - /* Normal object which doesn't get automatically coerced to a - * primitive value. Functions are checked for specially. The - * primitive value coercions for Number, String, Pointer, and - * Boolean can't result in functions so suffices to check here. - * Symbol objects are handled like plain objects (their primitive - * value is NOT looked up like for e.g. String objects). - */ - DUK_ASSERT(h != NULL); - if (DUK_HOBJECT_IS_CALLABLE(h)) { -#if defined(DUK_USE_JX) || defined(DUK_USE_JC) - if (js_ctx->flags & (DUK_JSON_FLAG_EXT_CUSTOM | DUK_JSON_FLAG_EXT_COMPATIBLE)) { - /* We only get here when doing non-standard JSON encoding */ - DUK_DDD(DUK_DDDPRINT("-> function allowed, serialize to custom format")); - DUK_ASSERT(js_ctx->flag_ext_custom || js_ctx->flag_ext_compatible); - DUK__EMIT_STRIDX(js_ctx, js_ctx->stridx_custom_function); - goto pop2_emitted; - } else { - DUK_DDD(DUK_DDDPRINT("-> will result in undefined (function)")); - goto pop2_undef; - } -#else /* DUK_USE_JX || DUK_USE_JC */ - DUK_DDD(DUK_DDDPRINT("-> will result in undefined (function)")); - goto pop2_undef; -#endif /* DUK_USE_JX || DUK_USE_JC */ - } - } - } /* end switch */ - } - - /* [ ... key val ] */ - - DUK_DDD(DUK_DDDPRINT("value=%!T", (duk_tval *) duk_get_tval(thr, -1))); - - if (duk_check_type_mask(thr, -1, js_ctx->mask_for_undefined)) { - /* will result in undefined */ - DUK_DDD(DUK_DDDPRINT("-> will result in undefined (type mask check)")); - goto pop2_undef; - } - tv = DUK_GET_TVAL_NEGIDX(thr, -1); - - switch (DUK_TVAL_GET_TAG(tv)) { -#if defined(DUK_USE_JX) || defined(DUK_USE_JC) - /* When JX/JC not in use, the type mask above will avoid this case if needed. */ - case DUK_TAG_UNDEFINED: { - DUK__EMIT_STRIDX(js_ctx, js_ctx->stridx_custom_undefined); - break; - } -#endif - case DUK_TAG_NULL: { - DUK__EMIT_STRIDX(js_ctx, DUK_STRIDX_LC_NULL); - break; - } - case DUK_TAG_BOOLEAN: { - DUK__EMIT_STRIDX(js_ctx, DUK_TVAL_GET_BOOLEAN(tv) ? DUK_STRIDX_TRUE : DUK_STRIDX_FALSE); - break; - } -#if defined(DUK_USE_JX) || defined(DUK_USE_JC) - /* When JX/JC not in use, the type mask above will avoid this case if needed. */ - case DUK_TAG_POINTER: { - duk__json_enc_pointer(js_ctx, DUK_TVAL_GET_POINTER(tv)); - break; - } -#endif /* DUK_USE_JX || DUK_USE_JC */ - case DUK_TAG_STRING: { - duk_hstring *h = DUK_TVAL_GET_STRING(tv); - DUK_ASSERT(h != NULL); - if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) { - goto pop2_undef; - } - duk__json_enc_quote_string(js_ctx, h); - break; - } - case DUK_TAG_OBJECT: { - duk_hobject *h = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - - /* Function values are handled completely above (including - * coercion results): - */ - DUK_ASSERT(!DUK_HOBJECT_IS_CALLABLE(h)); - - if (duk_js_isarray_hobject(h)) { - duk__json_enc_array(js_ctx); - } else { - duk__json_enc_object(js_ctx); - } - break; - } - /* Because plain buffers mimics Uint8Array, they have enumerable - * index properties [0,byteLength[. Because JSON only serializes - * enumerable own properties, no properties can be serialized for - * plain buffers (all virtual properties are non-enumerable). However, - * there may be a .toJSON() method which was already handled above. - */ - case DUK_TAG_BUFFER: { -#if defined(DUK_USE_JX) || defined(DUK_USE_JC) - if (js_ctx->flag_ext_custom_or_compatible) { - duk__json_enc_buffer_jx_jc(js_ctx, DUK_TVAL_GET_BUFFER(tv)); - break; - } -#endif - - /* Could implement a fastpath, but the fast path would need - * to handle realloc side effects correctly. - */ - duk_to_object(thr, -1); - duk__json_enc_object(js_ctx); - break; - } - case DUK_TAG_LIGHTFUNC: { -#if defined(DUK_USE_JX) || defined(DUK_USE_JC) - /* We only get here when doing non-standard JSON encoding */ - DUK_ASSERT(js_ctx->flag_ext_custom || js_ctx->flag_ext_compatible); - DUK__EMIT_STRIDX(js_ctx, js_ctx->stridx_custom_function); -#else - /* Standard JSON omits functions */ - DUK_UNREACHABLE(); -#endif - break; - } -#if defined(DUK_USE_FASTINT) - case DUK_TAG_FASTINT: - /* Number serialization has a significant impact relative to - * other fast path code, so careful fast path for fastints. - */ - duk__json_enc_fastint_tval(js_ctx, tv); - break; -#endif - default: { - /* number */ - DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv)); - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); - /* XXX: A fast path for usual integers would be useful when - * fastint support is not enabled. - */ - duk__json_enc_double(js_ctx); - break; - } - } - -#if defined(DUK_USE_JX) || defined(DUK_USE_JC) -pop2_emitted: -#endif - duk_pop_2(thr); /* [ ... key val ] -> [ ... ] */ - return 1; /* emitted */ - -pop2_undef: - duk_pop_2(thr); /* [ ... key val ] -> [ ... ] */ - return 0; /* not emitted */ -} - -/* E5 Section 15.12.3, main algorithm, step 4.b.ii steps 1-4. */ -DUK_LOCAL duk_bool_t duk__json_enc_allow_into_proplist(duk_tval *tv) { - duk_small_int_t c; - - /* XXX: some kind of external internal type checker? - * - type mask; symbol flag; class mask - */ - DUK_ASSERT(tv != NULL); - if (DUK_TVAL_IS_STRING(tv)) { - duk_hstring *h; - h = DUK_TVAL_GET_STRING(tv); - DUK_ASSERT(h != NULL); - if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) { - return 0; - } - return 1; - } else if (DUK_TVAL_IS_NUMBER(tv)) { - return 1; - } else if (DUK_TVAL_IS_OBJECT(tv)) { - duk_hobject *h; - h = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - c = (duk_small_int_t) DUK_HOBJECT_GET_CLASS_NUMBER(h); - if (c == DUK_HOBJECT_CLASS_STRING || c == DUK_HOBJECT_CLASS_NUMBER) { - return 1; - } - } - - return 0; -} - -/* - * JSON.stringify() fast path - * - * Otherwise supports full JSON, JX, and JC features, but bails out on any - * possible side effect which might change the value being serialized. The - * fast path can take advantage of the fact that the value being serialized - * is unchanged so that we can walk directly through property tables etc. - */ - -#if defined(DUK_USE_JSON_STRINGIFY_FASTPATH) -DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, duk_tval *tv) { - duk_uint_fast32_t i, n; - - DUK_DDD(DUK_DDDPRINT("stringify fast: %!T", tv)); - - DUK_ASSERT(js_ctx != NULL); - DUK_ASSERT(js_ctx->thr != NULL); - -#if 0 /* disabled for now */ - restart_match: -#endif - - DUK_ASSERT(tv != NULL); - - switch (DUK_TVAL_GET_TAG(tv)) { - case DUK_TAG_UNDEFINED: { -#if defined(DUK_USE_JX) || defined(DUK_USE_JC) - if (js_ctx->flag_ext_custom || js_ctx->flag_ext_compatible) { - DUK__EMIT_STRIDX(js_ctx, js_ctx->stridx_custom_undefined); - break; - } else { - goto emit_undefined; - } -#else - goto emit_undefined; -#endif - } - case DUK_TAG_NULL: { - DUK__EMIT_STRIDX(js_ctx, DUK_STRIDX_LC_NULL); - break; - } - case DUK_TAG_BOOLEAN: { - DUK__EMIT_STRIDX(js_ctx, DUK_TVAL_GET_BOOLEAN(tv) ? DUK_STRIDX_TRUE : DUK_STRIDX_FALSE); - break; - } - case DUK_TAG_STRING: { - duk_hstring *h; - h = DUK_TVAL_GET_STRING(tv); - DUK_ASSERT(h != NULL); - if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) { - goto emit_undefined; - } - duk__json_enc_quote_string(js_ctx, h); - break; - } - case DUK_TAG_OBJECT: { - duk_hobject *obj; - duk_tval *tv_val; - duk_bool_t emitted = 0; - duk_uint32_t c_bit, c_all, c_array, c_unbox, c_undef, c_func, c_bufobj, c_object, c_abort; - - /* For objects JSON.stringify() only looks for own, enumerable - * properties which is nice for the fast path here. - * - * For arrays JSON.stringify() uses [[Get]] so it will actually - * inherit properties during serialization! This fast path - * supports gappy arrays as long as there's no actual inherited - * property (which might be a getter etc). - * - * Since recursion only happens for objects, we can have both - * recursion and loop checks here. We use a simple, depth-limited - * loop check in the fast path because the object-based tracking - * is very slow (when tested, it accounted for 50% of fast path - * execution time for input data with a lot of small objects!). - */ - - /* XXX: for real world code, could just ignore array inheritance - * and only look at array own properties. - */ - - /* We rely on a few object flag / class number relationships here, - * assert for them. - */ - - obj = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(obj != NULL); - DUK_HOBJECT_ASSERT_VALID(obj); - - /* Once recursion depth is increased, exit path must decrease - * it (though it's OK to abort the fast path). - */ - - DUK_ASSERT_DISABLE(js_ctx->recursion_depth >= 0); /* unsigned */ - DUK_ASSERT(js_ctx->recursion_depth <= js_ctx->recursion_limit); - if (js_ctx->recursion_depth >= js_ctx->recursion_limit) { - DUK_DD(DUK_DDPRINT("fast path recursion limit")); - DUK_ERROR_RANGE(js_ctx->thr, DUK_STR_DEC_RECLIMIT); - DUK_WO_NORETURN(return 0;); - } - - for (i = 0, n = (duk_uint_fast32_t) js_ctx->recursion_depth; i < n; i++) { - if (DUK_UNLIKELY(js_ctx->visiting[i] == obj)) { - DUK_DD(DUK_DDPRINT("fast path loop detect")); - DUK_ERROR_TYPE(js_ctx->thr, DUK_STR_CYCLIC_INPUT); - DUK_WO_NORETURN(return 0;); - } - } - - /* Guaranteed by recursion_limit setup so we don't have to - * check twice. - */ - DUK_ASSERT(js_ctx->recursion_depth < DUK_JSON_ENC_LOOPARRAY); - js_ctx->visiting[js_ctx->recursion_depth] = obj; - js_ctx->recursion_depth++; - - /* If object has a .toJSON() property, we can't be certain - * that it wouldn't mutate any value arbitrarily, so bail - * out of the fast path. - * - * If an object is a Proxy we also can't avoid side effects - * so abandon. - */ - /* XXX: non-callable .toJSON() doesn't need to cause an abort - * but does at the moment, probably not worth fixing. - */ - if (duk_hobject_hasprop_raw(js_ctx->thr, obj, DUK_HTHREAD_STRING_TO_JSON(js_ctx->thr)) || - DUK_HOBJECT_IS_PROXY(obj)) { - DUK_DD(DUK_DDPRINT("object has a .toJSON property or object is a Proxy, abort fast path")); - goto abort_fastpath; - } - - /* We could use a switch-case for the class number but it turns out - * a small if-else ladder on class masks is better. The if-ladder - * should be in order of relevancy. - */ - - /* XXX: move masks to js_ctx? they don't change during one - * fast path invocation. - */ - DUK_ASSERT(DUK_HOBJECT_CLASS_MAX <= 31); -#if defined(DUK_USE_JX) || defined(DUK_USE_JC) - if (js_ctx->flag_ext_custom_or_compatible) { - c_all = DUK_HOBJECT_CMASK_ALL; - c_array = DUK_HOBJECT_CMASK_ARRAY; - c_unbox = DUK_HOBJECT_CMASK_NUMBER | DUK_HOBJECT_CMASK_STRING | DUK_HOBJECT_CMASK_BOOLEAN | - DUK_HOBJECT_CMASK_POINTER; /* Symbols are not unboxed. */ - c_func = DUK_HOBJECT_CMASK_FUNCTION; - c_bufobj = DUK_HOBJECT_CMASK_ALL_BUFOBJS; - c_undef = 0; - c_abort = 0; - c_object = c_all & ~(c_array | c_unbox | c_func | c_bufobj | c_undef | c_abort); - } else -#endif - { - c_all = DUK_HOBJECT_CMASK_ALL; - c_array = DUK_HOBJECT_CMASK_ARRAY; - c_unbox = DUK_HOBJECT_CMASK_NUMBER | DUK_HOBJECT_CMASK_STRING | - DUK_HOBJECT_CMASK_BOOLEAN; /* Symbols are not unboxed. */ - c_func = 0; - c_bufobj = 0; - c_undef = DUK_HOBJECT_CMASK_FUNCTION | DUK_HOBJECT_CMASK_POINTER; - /* As the fast path doesn't currently properly support - * duk_hbufobj virtual properties, abort fast path if - * we encounter them in plain JSON mode. - */ - c_abort = DUK_HOBJECT_CMASK_ALL_BUFOBJS; - c_object = c_all & ~(c_array | c_unbox | c_func | c_bufobj | c_undef | c_abort); - } - - c_bit = (duk_uint32_t) DUK_HOBJECT_GET_CLASS_MASK(obj); - if (c_bit & c_object) { - /* All other object types. */ - DUK__EMIT_1(js_ctx, DUK_ASC_LCURLY); - - /* A non-Array object should not have an array part in practice. - * But since it is supported internally (and perhaps used at some - * point), check and abandon if that's the case. - */ - if (DUK_HOBJECT_HAS_ARRAY_PART(obj)) { - DUK_DD(DUK_DDPRINT("non-Array object has array part, abort fast path")); - goto abort_fastpath; - } - - for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(obj); i++) { - duk_hstring *k; - duk_size_t prev_size; - - k = DUK_HOBJECT_E_GET_KEY(js_ctx->thr->heap, obj, i); - if (!k) { - continue; - } - if (DUK_HSTRING_HAS_ARRIDX(k)) { - /* If an object has array index keys we would need - * to sort them into the ES2015 enumeration order to - * be consistent with the slow path. Abort the fast - * path and handle in the slow path for now. - */ - DUK_DD(DUK_DDPRINT("property key is an array index, abort fast path")); - goto abort_fastpath; - } - if (!DUK_HOBJECT_E_SLOT_IS_ENUMERABLE(js_ctx->thr->heap, obj, i)) { - continue; - } - if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(js_ctx->thr->heap, obj, i)) { - /* Getter might have arbitrary side effects, - * so bail out. - */ - DUK_DD(DUK_DDPRINT("property is an accessor, abort fast path")); - goto abort_fastpath; - } - if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(k))) { - continue; - } - - tv_val = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(js_ctx->thr->heap, obj, i); - - prev_size = DUK_BW_GET_SIZE(js_ctx->thr, &js_ctx->bw); - if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) { - duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth); - duk__json_enc_key_autoquote(js_ctx, k); - DUK__EMIT_2(js_ctx, DUK_ASC_COLON, DUK_ASC_SPACE); - } else { - duk__json_enc_key_autoquote(js_ctx, k); - DUK__EMIT_1(js_ctx, DUK_ASC_COLON); - } - - if (duk__json_stringify_fast_value(js_ctx, tv_val) == 0) { - DUK_DD(DUK_DDPRINT("prop value not supported, rewind key and colon")); - DUK_BW_SET_SIZE(js_ctx->thr, &js_ctx->bw, prev_size); - } else { - DUK__EMIT_1(js_ctx, DUK_ASC_COMMA); - emitted = 1; - } - } - - /* If any non-Array value had enumerable virtual own - * properties, they should be serialized here (actually, - * before the explicit properties). Standard types don't. - */ - - if (emitted) { - DUK_ASSERT(*((duk_uint8_t *) DUK_BW_GET_PTR(js_ctx->thr, &js_ctx->bw) - 1) == DUK_ASC_COMMA); - DUK__UNEMIT_1(js_ctx); /* eat trailing comma */ - if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) { - DUK_ASSERT(js_ctx->recursion_depth >= 1); - duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth - 1U); - } - } - DUK__EMIT_1(js_ctx, DUK_ASC_RCURLY); - } else if (c_bit & c_array) { - duk_uint_fast32_t arr_len; - duk_uint_fast32_t asize; - - DUK__EMIT_1(js_ctx, DUK_ASC_LBRACKET); - - /* Assume arrays are dense in the fast path. */ - if (!DUK_HOBJECT_HAS_ARRAY_PART(obj)) { - DUK_DD(DUK_DDPRINT("Array object is sparse, abort fast path")); - goto abort_fastpath; - } - - arr_len = (duk_uint_fast32_t) ((duk_harray *) obj)->length; - asize = (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(obj); - /* Array part may be larger than 'length'; if so, iterate - * only up to array 'length'. Array part may also be smaller - * than 'length' in some cases. - */ - for (i = 0; i < arr_len; i++) { - duk_tval *tv_arrval; - duk_hstring *h_tmp; - duk_bool_t has_inherited; - - if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) { - duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth); - } - - if (DUK_LIKELY(i < asize)) { - tv_arrval = DUK_HOBJECT_A_GET_VALUE_PTR(js_ctx->thr->heap, obj, i); - if (DUK_LIKELY(!DUK_TVAL_IS_UNUSED(tv_arrval))) { - /* Expected case: element is present. */ - if (duk__json_stringify_fast_value(js_ctx, tv_arrval) == 0) { - DUK__EMIT_STRIDX(js_ctx, DUK_STRIDX_LC_NULL); - } - goto elem_done; - } - } - - /* Gap in array; check for inherited property, - * bail out if one exists. This should be enough - * to support gappy arrays for all practical code. - */ - - h_tmp = duk_push_uint_to_hstring(js_ctx->thr, (duk_uint_t) i); - has_inherited = duk_hobject_hasprop_raw(js_ctx->thr, obj, h_tmp); - duk_pop(js_ctx->thr); - if (has_inherited) { - DUK_D(DUK_DPRINT("gap in array, conflicting inherited property, abort fast path")); - goto abort_fastpath; - } - - /* Ordinary gap, undefined encodes to 'null' in - * standard JSON, but JX/JC use their form for - * undefined to better preserve the typing. - */ - DUK_D(DUK_DPRINT("gap in array, no conflicting inherited property, remain on fast path")); -#if defined(DUK_USE_JX) - DUK__EMIT_STRIDX(js_ctx, js_ctx->stridx_custom_undefined); -#else - DUK__EMIT_STRIDX(js_ctx, DUK_STRIDX_LC_NULL); -#endif - /* fall through */ - - elem_done: - DUK__EMIT_1(js_ctx, DUK_ASC_COMMA); - emitted = 1; - } - - if (emitted) { - DUK_ASSERT(*((duk_uint8_t *) DUK_BW_GET_PTR(js_ctx->thr, &js_ctx->bw) - 1) == DUK_ASC_COMMA); - DUK__UNEMIT_1(js_ctx); /* eat trailing comma */ - if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) { - DUK_ASSERT(js_ctx->recursion_depth >= 1); - duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth - 1U); - } - } - DUK__EMIT_1(js_ctx, DUK_ASC_RBRACKET); - } else if (c_bit & c_unbox) { - /* Certain boxed types are required to go through - * automatic unboxing. Rely on internal value being - * sane (to avoid infinite recursion). - */ - DUK_ASSERT((c_bit & DUK_HOBJECT_CMASK_SYMBOL) == 0); /* Symbols are not unboxed. */ - -#if 1 - /* The code below is incorrect if .toString() or .valueOf() have - * have been overridden. The correct approach would be to look up - * the method(s) and if they resolve to the built-in function we - * can safely bypass it and look up the internal value directly. - * Unimplemented for now, abort fast path for boxed values. - */ - goto abort_fastpath; -#else /* disabled */ - /* Disabled until fixed, see above. */ - duk_tval *tv_internal; - - DUK_DD(DUK_DDPRINT("auto unboxing in fast path")); - - tv_internal = duk_hobject_get_internal_value_tval_ptr(js_ctx->thr->heap, obj); - DUK_ASSERT(tv_internal != NULL); - DUK_ASSERT(DUK_TVAL_IS_STRING(tv_internal) || DUK_TVAL_IS_NUMBER(tv_internal) || - DUK_TVAL_IS_BOOLEAN(tv_internal) || DUK_TVAL_IS_POINTER(tv_internal)); - - tv = tv_internal; - DUK_ASSERT(js_ctx->recursion_depth > 0); - js_ctx->recursion_depth--; /* required to keep recursion depth correct */ - goto restart_match; -#endif /* disabled */ -#if defined(DUK_USE_JX) || defined(DUK_USE_JC) - } else if (c_bit & c_func) { - DUK__EMIT_STRIDX(js_ctx, js_ctx->stridx_custom_function); -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) - } else if (c_bit & c_bufobj) { - duk__json_enc_bufobj(js_ctx, (duk_hbufobj *) obj); -#endif -#endif - } else if (c_bit & c_abort) { - DUK_DD(DUK_DDPRINT("abort fast path for unsupported type")); - goto abort_fastpath; - } else { - DUK_ASSERT((c_bit & c_undef) != 0); - - /* Must decrease recursion depth before returning. */ - DUK_ASSERT(js_ctx->recursion_depth > 0); - DUK_ASSERT(js_ctx->recursion_depth <= js_ctx->recursion_limit); - js_ctx->recursion_depth--; - goto emit_undefined; - } - - DUK_ASSERT(js_ctx->recursion_depth > 0); - DUK_ASSERT(js_ctx->recursion_depth <= js_ctx->recursion_limit); - js_ctx->recursion_depth--; - break; - } - case DUK_TAG_BUFFER: { - /* Plain buffers are treated like Uint8Arrays: they have - * enumerable indices. Other virtual properties are not - * enumerable, and inherited properties are not serialized. - * However, there can be a replacer (not relevant here) or - * a .toJSON() method (which we need to check for explicitly). - */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) - if (duk_hobject_hasprop_raw(js_ctx->thr, - js_ctx->thr->builtins[DUK_BIDX_UINT8ARRAY_PROTOTYPE], - DUK_HTHREAD_STRING_TO_JSON(js_ctx->thr))) { - DUK_DD(DUK_DDPRINT("value is a plain buffer and there's an inherited .toJSON, abort fast path")); - goto abort_fastpath; - } -#endif - -#if defined(DUK_USE_JX) || defined(DUK_USE_JC) - if (js_ctx->flag_ext_custom_or_compatible) { - duk__json_enc_buffer_jx_jc(js_ctx, DUK_TVAL_GET_BUFFER(tv)); - break; - } -#endif - - /* Plain buffers mimic Uint8Arrays, and have enumerable index - * properties. - */ - duk__json_enc_buffer_json_fastpath(js_ctx, DUK_TVAL_GET_BUFFER(tv)); - break; - } - case DUK_TAG_POINTER: { -#if defined(DUK_USE_JX) || defined(DUK_USE_JC) - if (js_ctx->flag_ext_custom_or_compatible) { - duk__json_enc_pointer(js_ctx, DUK_TVAL_GET_POINTER(tv)); - break; - } else { - goto emit_undefined; - } -#else - goto emit_undefined; -#endif - } - case DUK_TAG_LIGHTFUNC: { - /* A lightfunc might also inherit a .toJSON() so just bail out. */ - /* XXX: Could just lookup .toJSON() and continue in fast path, - * as it would almost never be defined. - */ - DUK_DD(DUK_DDPRINT("value is a lightfunc, abort fast path")); - goto abort_fastpath; - } -#if defined(DUK_USE_FASTINT) - case DUK_TAG_FASTINT: { - /* Number serialization has a significant impact relative to - * other fast path code, so careful fast path for fastints. - */ - duk__json_enc_fastint_tval(js_ctx, tv); - break; - } -#endif - default: { - /* XXX: A fast path for usual integers would be useful when - * fastint support is not enabled. - */ - DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv)); - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); - - /* XXX: Stack discipline is annoying, could be changed in numconv. */ - duk_push_tval(js_ctx->thr, tv); - duk__json_enc_double(js_ctx); - duk_pop(js_ctx->thr); - -#if 0 - /* Could also rely on native sprintf(), but it will handle - * values like NaN, Infinity, -0, exponent notation etc in - * a JSON-incompatible way. - */ - duk_double_t d; - char buf[64]; - - DUK_ASSERT(DUK_TVAL_IS_DOUBLE(tv)); - d = DUK_TVAL_GET_DOUBLE(tv); - DUK_SPRINTF(buf, "%lg", d); - DUK__EMIT_CSTR(js_ctx, buf); -#endif - } - } - return 1; /* not undefined */ - -emit_undefined: - return 0; /* value was undefined/unsupported */ - -abort_fastpath: - /* Error message doesn't matter: the error is ignored anyway. */ - DUK_DD(DUK_DDPRINT("aborting fast path")); - DUK_ERROR_INTERNAL(js_ctx->thr); - DUK_WO_NORETURN(return 0;); -} - -DUK_LOCAL duk_ret_t duk__json_stringify_fast(duk_hthread *thr, void *udata) { - duk_json_enc_ctx *js_ctx; - duk_tval *tv; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(udata != NULL); - - js_ctx = (duk_json_enc_ctx *) udata; - DUK_ASSERT(js_ctx != NULL); - - tv = DUK_GET_TVAL_NEGIDX(thr, -1); - if (duk__json_stringify_fast_value(js_ctx, tv) == 0) { - DUK_DD(DUK_DDPRINT("top level value not supported, fail fast path")); - DUK_DCERROR_TYPE_INVALID_ARGS(thr); /* Error message is ignored, so doesn't matter. */ - } - - return 0; -} -#endif /* DUK_USE_JSON_STRINGIFY_FASTPATH */ - -/* - * Top level wrappers - */ - -DUK_INTERNAL -void duk_bi_json_parse_helper(duk_hthread *thr, duk_idx_t idx_value, duk_idx_t idx_reviver, duk_small_uint_t flags) { - duk_json_dec_ctx js_ctx_alloc; - duk_json_dec_ctx *js_ctx = &js_ctx_alloc; - duk_hstring *h_text; -#if defined(DUK_USE_ASSERTIONS) - duk_idx_t entry_top = duk_get_top(thr); -#endif - - /* negative top-relative indices not allowed now */ - DUK_ASSERT(idx_value == DUK_INVALID_INDEX || idx_value >= 0); - DUK_ASSERT(idx_reviver == DUK_INVALID_INDEX || idx_reviver >= 0); - - DUK_DDD(DUK_DDDPRINT("JSON parse start: text=%!T, reviver=%!T, flags=0x%08lx, stack_top=%ld", - (duk_tval *) duk_get_tval(thr, idx_value), - (duk_tval *) duk_get_tval(thr, idx_reviver), - (unsigned long) flags, - (long) duk_get_top(thr))); - - duk_memzero(&js_ctx_alloc, sizeof(js_ctx_alloc)); - js_ctx->thr = thr; -#if defined(DUK_USE_EXPLICIT_NULL_INIT) - /* nothing now */ -#endif - js_ctx->recursion_limit = DUK_USE_JSON_DEC_RECLIMIT; - DUK_ASSERT(js_ctx->recursion_depth == 0); - - /* Flag handling currently assumes that flags are consistent. This is OK - * because the call sites are now strictly controlled. - */ - - js_ctx->flags = flags; -#if defined(DUK_USE_JX) - js_ctx->flag_ext_custom = flags & DUK_JSON_FLAG_EXT_CUSTOM; -#endif -#if defined(DUK_USE_JC) - js_ctx->flag_ext_compatible = flags & DUK_JSON_FLAG_EXT_COMPATIBLE; -#endif -#if defined(DUK_USE_JX) || defined(DUK_USE_JC) - js_ctx->flag_ext_custom_or_compatible = flags & (DUK_JSON_FLAG_EXT_CUSTOM | DUK_JSON_FLAG_EXT_COMPATIBLE); -#endif - - h_text = duk_to_hstring(thr, idx_value); /* coerce in-place; rejects Symbols */ - DUK_ASSERT(h_text != NULL); - - /* JSON parsing code is allowed to read [p_start,p_end]: p_end is - * valid and points to the string NUL terminator (which is always - * guaranteed for duk_hstrings. - */ - js_ctx->p_start = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_text); - js_ctx->p = js_ctx->p_start; - js_ctx->p_end = ((const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_text)) + DUK_HSTRING_GET_BYTELEN(h_text); - DUK_ASSERT(*(js_ctx->p_end) == 0x00); - - duk__json_dec_value(js_ctx); /* -> [ ... value ] */ - DUK_ASSERT(js_ctx->recursion_depth == 0); - - /* Trailing whitespace has been eaten by duk__json_dec_value(), so if - * we're not at end of input here, it's a SyntaxError. - */ - - if (js_ctx->p != js_ctx->p_end) { - duk__json_dec_syntax_error(js_ctx); - } - - if (duk_is_callable(thr, idx_reviver)) { - DUK_DDD(DUK_DDDPRINT("applying reviver: %!T", (duk_tval *) duk_get_tval(thr, idx_reviver))); - - js_ctx->idx_reviver = idx_reviver; - - duk_push_object(thr); - duk_dup_m2(thr); /* -> [ ... val root val ] */ - duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_EMPTY_STRING); /* default attrs ok */ - duk_push_hstring_stridx(thr, DUK_STRIDX_EMPTY_STRING); /* -> [ ... val root "" ] */ - - DUK_DDD(DUK_DDDPRINT("start reviver walk, root=%!T, name=%!T", - (duk_tval *) duk_get_tval(thr, -2), - (duk_tval *) duk_get_tval(thr, -1))); - - DUK_ASSERT(js_ctx->recursion_depth == 0); - duk__json_dec_reviver_walk(js_ctx); /* [ ... val root "" ] -> [ ... val val' ] */ - DUK_ASSERT(js_ctx->recursion_depth == 0); - duk_remove_m2(thr); /* -> [ ... val' ] */ - } else { - DUK_DDD( - DUK_DDDPRINT("reviver does not exist or is not callable: %!T", (duk_tval *) duk_get_tval(thr, idx_reviver))); - } - - /* Final result is at stack top. */ - - DUK_DDD(DUK_DDDPRINT("JSON parse end: text=%!T, reviver=%!T, flags=0x%08lx, result=%!T, stack_top=%ld", - (duk_tval *) duk_get_tval(thr, idx_value), - (duk_tval *) duk_get_tval(thr, idx_reviver), - (unsigned long) flags, - (duk_tval *) duk_get_tval(thr, -1), - (long) duk_get_top(thr))); - - DUK_ASSERT(duk_get_top(thr) == entry_top + 1); -} - -DUK_INTERNAL -void duk_bi_json_stringify_helper(duk_hthread *thr, - duk_idx_t idx_value, - duk_idx_t idx_replacer, - duk_idx_t idx_space, - duk_small_uint_t flags) { - duk_json_enc_ctx js_ctx_alloc; - duk_json_enc_ctx *js_ctx = &js_ctx_alloc; - duk_hobject *h; - duk_idx_t idx_holder; - duk_idx_t entry_top; - - /* negative top-relative indices not allowed now */ - DUK_ASSERT(idx_value == DUK_INVALID_INDEX || idx_value >= 0); - DUK_ASSERT(idx_replacer == DUK_INVALID_INDEX || idx_replacer >= 0); - DUK_ASSERT(idx_space == DUK_INVALID_INDEX || idx_space >= 0); - - DUK_DDD(DUK_DDDPRINT("JSON stringify start: value=%!T, replacer=%!T, space=%!T, flags=0x%08lx, stack_top=%ld", - (duk_tval *) duk_get_tval(thr, idx_value), - (duk_tval *) duk_get_tval(thr, idx_replacer), - (duk_tval *) duk_get_tval(thr, idx_space), - (unsigned long) flags, - (long) duk_get_top(thr))); - - entry_top = duk_get_top(thr); - - /* - * Context init - */ - - duk_memzero(&js_ctx_alloc, sizeof(js_ctx_alloc)); - js_ctx->thr = thr; -#if defined(DUK_USE_EXPLICIT_NULL_INIT) - js_ctx->h_replacer = NULL; - js_ctx->h_gap = NULL; -#endif - js_ctx->idx_proplist = -1; - - /* Flag handling currently assumes that flags are consistent. This is OK - * because the call sites are now strictly controlled. - */ - - js_ctx->flags = flags; - js_ctx->flag_ascii_only = flags & DUK_JSON_FLAG_ASCII_ONLY; - js_ctx->flag_avoid_key_quotes = flags & DUK_JSON_FLAG_AVOID_KEY_QUOTES; -#if defined(DUK_USE_JX) - js_ctx->flag_ext_custom = flags & DUK_JSON_FLAG_EXT_CUSTOM; -#endif -#if defined(DUK_USE_JC) - js_ctx->flag_ext_compatible = flags & DUK_JSON_FLAG_EXT_COMPATIBLE; -#endif -#if defined(DUK_USE_JX) || defined(DUK_USE_JC) - js_ctx->flag_ext_custom_or_compatible = flags & (DUK_JSON_FLAG_EXT_CUSTOM | DUK_JSON_FLAG_EXT_COMPATIBLE); -#endif - - /* The #if defined() clutter here handles the JX/JC enable/disable - * combinations properly. - */ -#if defined(DUK_USE_JX) || defined(DUK_USE_JC) - js_ctx->stridx_custom_undefined = DUK_STRIDX_LC_NULL; /* standard JSON; array gaps */ -#if defined(DUK_USE_JX) - if (flags & DUK_JSON_FLAG_EXT_CUSTOM) { - js_ctx->stridx_custom_undefined = DUK_STRIDX_LC_UNDEFINED; - js_ctx->stridx_custom_nan = DUK_STRIDX_NAN; - js_ctx->stridx_custom_neginf = DUK_STRIDX_MINUS_INFINITY; - js_ctx->stridx_custom_posinf = DUK_STRIDX_INFINITY; - js_ctx->stridx_custom_function = - (flags & DUK_JSON_FLAG_AVOID_KEY_QUOTES) ? DUK_STRIDX_JSON_EXT_FUNCTION2 : DUK_STRIDX_JSON_EXT_FUNCTION1; - } -#endif /* DUK_USE_JX */ -#if defined(DUK_USE_JX) && defined(DUK_USE_JC) - else -#endif /* DUK_USE_JX && DUK_USE_JC */ -#if defined(DUK_USE_JC) - if (js_ctx->flags & DUK_JSON_FLAG_EXT_COMPATIBLE) { - js_ctx->stridx_custom_undefined = DUK_STRIDX_JSON_EXT_UNDEFINED; - js_ctx->stridx_custom_nan = DUK_STRIDX_JSON_EXT_NAN; - js_ctx->stridx_custom_neginf = DUK_STRIDX_JSON_EXT_NEGINF; - js_ctx->stridx_custom_posinf = DUK_STRIDX_JSON_EXT_POSINF; - js_ctx->stridx_custom_function = DUK_STRIDX_JSON_EXT_FUNCTION1; - } -#endif /* DUK_USE_JC */ -#endif /* DUK_USE_JX || DUK_USE_JC */ - -#if defined(DUK_USE_JX) || defined(DUK_USE_JC) - if (js_ctx->flags & (DUK_JSON_FLAG_EXT_CUSTOM | DUK_JSON_FLAG_EXT_COMPATIBLE)) { - DUK_ASSERT(js_ctx->mask_for_undefined == 0); /* already zero */ - } else -#endif /* DUK_USE_JX || DUK_USE_JC */ - { - /* Plain buffer is treated like ArrayBuffer and serialized. - * Lightfuncs are treated like objects, but JSON explicitly - * skips serializing Function objects so we can just reject - * lightfuncs here. - */ - js_ctx->mask_for_undefined = DUK_TYPE_MASK_UNDEFINED | DUK_TYPE_MASK_POINTER | DUK_TYPE_MASK_LIGHTFUNC; - } - - DUK_BW_INIT_PUSHBUF(thr, &js_ctx->bw, DUK__JSON_STRINGIFY_BUFSIZE); - - js_ctx->idx_loop = duk_push_bare_object(thr); - DUK_ASSERT(js_ctx->idx_loop >= 0); - - /* [ ... buf loop ] */ - - /* - * Process replacer/proplist (2nd argument to JSON.stringify) - */ - - h = duk_get_hobject(thr, idx_replacer); - if (h != NULL) { - if (DUK_HOBJECT_IS_CALLABLE(h)) { - js_ctx->h_replacer = h; - } else if (duk_js_isarray_hobject(h)) { - /* Here the specification requires correct array index enumeration - * which is a bit tricky for sparse arrays (it is handled by the - * enum setup code). We now enumerate ancestors too, although the - * specification is not very clear on whether that is required. - */ - - duk_uarridx_t plist_idx = 0; - duk_small_uint_t enum_flags; - - js_ctx->idx_proplist = duk_push_bare_array(thr); - - enum_flags = DUK_ENUM_ARRAY_INDICES_ONLY | DUK_ENUM_SORT_ARRAY_INDICES; /* expensive flag */ - duk_enum(thr, idx_replacer, enum_flags); - while (duk_next(thr, -1 /*enum_index*/, 1 /*get_value*/)) { - /* [ ... proplist enum_obj key val ] */ - if (duk__json_enc_allow_into_proplist(duk_get_tval(thr, -1))) { - /* XXX: duplicates should be eliminated here */ - DUK_DDD(DUK_DDDPRINT("proplist enum: key=%!T, val=%!T --> accept", - (duk_tval *) duk_get_tval(thr, -2), - (duk_tval *) duk_get_tval(thr, -1))); - duk_to_string(thr, -1); /* extra coercion of strings is OK */ - duk_put_prop_index(thr, -4, plist_idx); /* -> [ ... proplist enum_obj key ] */ - plist_idx++; - duk_pop(thr); - } else { - DUK_DDD(DUK_DDDPRINT("proplist enum: key=%!T, val=%!T --> reject", - (duk_tval *) duk_get_tval(thr, -2), - (duk_tval *) duk_get_tval(thr, -1))); - duk_pop_2(thr); - } - } - duk_pop(thr); /* pop enum */ - - /* [ ... proplist ] */ - } - } - - /* [ ... buf loop (proplist) ] */ - - /* - * Process space (3rd argument to JSON.stringify) - */ - - h = duk_get_hobject(thr, idx_space); - if (h != NULL) { - duk_small_uint_t c = DUK_HOBJECT_GET_CLASS_NUMBER(h); - if (c == DUK_HOBJECT_CLASS_NUMBER) { - duk_to_number(thr, idx_space); - } else if (c == DUK_HOBJECT_CLASS_STRING) { - duk_to_string(thr, idx_space); - } - } - - if (duk_is_number(thr, idx_space)) { - duk_small_int_t nspace; - /* spaces[] must be static to allow initializer with old compilers like BCC */ - static const char spaces[10] = { - DUK_ASC_SPACE, DUK_ASC_SPACE, DUK_ASC_SPACE, DUK_ASC_SPACE, DUK_ASC_SPACE, - DUK_ASC_SPACE, DUK_ASC_SPACE, DUK_ASC_SPACE, DUK_ASC_SPACE, DUK_ASC_SPACE - }; /* XXX: helper */ - - /* ToInteger() coercion; NaN -> 0, infinities are clamped to 0 and 10 */ - nspace = (duk_small_int_t) duk_to_int_clamped(thr, idx_space, 0 /*minval*/, 10 /*maxval*/); - DUK_ASSERT(nspace >= 0 && nspace <= 10); - - duk_push_lstring(thr, spaces, (duk_size_t) nspace); - js_ctx->h_gap = duk_known_hstring(thr, -1); - DUK_ASSERT(js_ctx->h_gap != NULL); - } else if (duk_is_string_notsymbol(thr, idx_space)) { - duk_dup(thr, idx_space); - duk_substring(thr, -1, 0, 10); /* clamp to 10 chars */ - js_ctx->h_gap = duk_known_hstring(thr, -1); - } else { - /* nop */ - } - - if (js_ctx->h_gap != NULL) { - /* If gap is empty, behave as if not given at all. Check - * against byte length because character length is more - * expensive. - */ - if (DUK_HSTRING_GET_BYTELEN(js_ctx->h_gap) == 0) { - js_ctx->h_gap = NULL; - } - } - - /* [ ... buf loop (proplist) (gap) ] */ - - /* - * Fast path: assume no mutation, iterate object property tables - * directly; bail out if that assumption doesn't hold. - */ - -#if defined(DUK_USE_JSON_STRINGIFY_FASTPATH) - if (js_ctx->h_replacer == NULL && /* replacer is a mutation risk */ - js_ctx->idx_proplist == -1) { /* proplist is very rare */ - duk_int_t pcall_rc; - duk_small_uint_t prev_ms_base_flags; - - DUK_DD(DUK_DDPRINT("try JSON.stringify() fast path")); - - /* Use recursion_limit to ensure we don't overwrite js_ctx->visiting[] - * array so we don't need two counter checks in the fast path. The - * slow path has a much larger recursion limit which we'll use if - * necessary. - */ - DUK_ASSERT(DUK_USE_JSON_ENC_RECLIMIT >= DUK_JSON_ENC_LOOPARRAY); - js_ctx->recursion_limit = DUK_JSON_ENC_LOOPARRAY; - DUK_ASSERT(js_ctx->recursion_depth == 0); - - /* Execute the fast path in a protected call. If any error is thrown, - * fall back to the slow path. This includes e.g. recursion limit - * because the fast path has a smaller recursion limit (and simpler, - * limited loop detection). - */ - - duk_dup(thr, idx_value); - - /* Must prevent finalizers which may have arbitrary side effects. */ - prev_ms_base_flags = thr->heap->ms_base_flags; - thr->heap->ms_base_flags |= DUK_MS_FLAG_NO_OBJECT_COMPACTION; /* Avoid attempt to compact any objects. */ - thr->heap->pf_prevent_count++; /* Prevent finalizers. */ - DUK_ASSERT(thr->heap->pf_prevent_count != 0); /* Wrap. */ - - pcall_rc = duk_safe_call(thr, duk__json_stringify_fast, (void *) js_ctx /*udata*/, 1 /*nargs*/, 0 /*nret*/); - - DUK_ASSERT(thr->heap->pf_prevent_count > 0); - thr->heap->pf_prevent_count--; - thr->heap->ms_base_flags = prev_ms_base_flags; - - if (pcall_rc == DUK_EXEC_SUCCESS) { - DUK_DD(DUK_DDPRINT("fast path successful")); - DUK_BW_PUSH_AS_STRING(thr, &js_ctx->bw); - goto replace_finished; - } - - /* We come here for actual aborts (like encountering .toJSON()) - * but also for recursion/loop errors. Bufwriter size can be - * kept because we'll probably need at least as much as we've - * allocated so far. - */ - DUK_D(DUK_DPRINT("fast path failed, serialize using slow path instead")); - DUK_BW_RESET_SIZE(thr, &js_ctx->bw); - js_ctx->recursion_depth = 0; - } -#endif - - /* - * Create wrapper object and serialize - */ - - idx_holder = duk_push_object(thr); - duk_dup(thr, idx_value); - duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_EMPTY_STRING); - - DUK_DDD(DUK_DDDPRINT("before: flags=0x%08lx, loop=%!T, replacer=%!O, " - "proplist=%!T, gap=%!O, holder=%!T", - (unsigned long) js_ctx->flags, - (duk_tval *) duk_get_tval(thr, js_ctx->idx_loop), - (duk_heaphdr *) js_ctx->h_replacer, - (duk_tval *) (js_ctx->idx_proplist >= 0 ? duk_get_tval(thr, js_ctx->idx_proplist) : NULL), - (duk_heaphdr *) js_ctx->h_gap, - (duk_tval *) duk_get_tval(thr, -1))); - - /* serialize the wrapper with empty string key */ - - duk_push_hstring_empty(thr); - - /* [ ... buf loop (proplist) (gap) holder "" ] */ - - js_ctx->recursion_limit = DUK_USE_JSON_ENC_RECLIMIT; - DUK_ASSERT(js_ctx->recursion_depth == 0); - - if (DUK_UNLIKELY(duk__json_enc_value(js_ctx, idx_holder) == 0)) { /* [ ... holder key ] -> [ ... holder ] */ - /* Result is undefined. */ - duk_push_undefined(thr); - } else { - /* Convert buffer to result string. */ - DUK_BW_PUSH_AS_STRING(thr, &js_ctx->bw); - } - - DUK_DDD(DUK_DDDPRINT("after: flags=0x%08lx, loop=%!T, replacer=%!O, " - "proplist=%!T, gap=%!O, holder=%!T", - (unsigned long) js_ctx->flags, - (duk_tval *) duk_get_tval(thr, js_ctx->idx_loop), - (duk_heaphdr *) js_ctx->h_replacer, - (duk_tval *) (js_ctx->idx_proplist >= 0 ? duk_get_tval(thr, js_ctx->idx_proplist) : NULL), - (duk_heaphdr *) js_ctx->h_gap, - (duk_tval *) duk_get_tval(thr, idx_holder))); - - /* The stack has a variable shape here, so force it to the - * desired one explicitly. - */ - -#if defined(DUK_USE_JSON_STRINGIFY_FASTPATH) -replace_finished: -#endif - duk_replace(thr, entry_top); - duk_set_top(thr, entry_top + 1); - - DUK_DDD(DUK_DDDPRINT("JSON stringify end: value=%!T, replacer=%!T, space=%!T, " - "flags=0x%08lx, result=%!T, stack_top=%ld", - (duk_tval *) duk_get_tval(thr, idx_value), - (duk_tval *) duk_get_tval(thr, idx_replacer), - (duk_tval *) duk_get_tval(thr, idx_space), - (unsigned long) flags, - (duk_tval *) duk_get_tval(thr, -1), - (long) duk_get_top(thr))); - - DUK_ASSERT(duk_get_top(thr) == entry_top + 1); -} - -#if defined(DUK_USE_JSON_BUILTIN) - -/* - * Entry points - */ - -DUK_INTERNAL duk_ret_t duk_bi_json_object_parse(duk_hthread *thr) { - duk_bi_json_parse_helper(thr, 0 /*idx_value*/, 1 /*idx_replacer*/, 0 /*flags*/); - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_json_object_stringify(duk_hthread *thr) { - duk_bi_json_stringify_helper(thr, 0 /*idx_value*/, 1 /*idx_replacer*/, 2 /*idx_space*/, 0 /*flags*/); - return 1; -} - -#endif /* DUK_USE_JSON_BUILTIN */ - -#endif /* DUK_USE_JSON_SUPPORT */ - -/* automatic undefs */ -#undef DUK__EMIT_1 -#undef DUK__EMIT_2 -#undef DUK__EMIT_CSTR -#undef DUK__EMIT_HSTR -#undef DUK__EMIT_STRIDX -#undef DUK__JSON_DECSTR_BUFSIZE -#undef DUK__JSON_DECSTR_CHUNKSIZE -#undef DUK__JSON_ENCSTR_CHUNKSIZE -#undef DUK__JSON_MAX_ESC_LEN -#undef DUK__JSON_STRINGIFY_BUFSIZE -#undef DUK__MKESC -#undef DUK__UNEMIT_1 -/* - * Math built-ins - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_MATH_BUILTIN) - -/* - * Use static helpers which can work with math.h functions matching - * the following signatures. This is not portable if any of these math - * functions is actually a macro. - * - * Typing here is intentionally 'double' wherever values interact with - * the standard library APIs. - */ - -typedef double (*duk__one_arg_func)(double); -typedef double (*duk__two_arg_func)(double, double); - -DUK_LOCAL duk_ret_t duk__math_minmax(duk_hthread *thr, duk_double_t initial, duk__two_arg_func min_max) { - duk_idx_t n = duk_get_top(thr); - duk_idx_t i; - duk_double_t res = initial; - duk_double_t t; - - /* - * Note: fmax() does not match the E5 semantics. E5 requires - * that if -any- input to Math.max() is a NaN, the result is a - * NaN. fmax() will return a NaN only if -both- inputs are NaN. - * Same applies to fmin(). - * - * Note: every input value must be coerced with ToNumber(), even - * if we know the result will be a NaN anyway: ToNumber() may have - * side effects for which even order of evaluation matters. - */ - - for (i = 0; i < n; i++) { - t = duk_to_number(thr, i); - if (DUK_FPCLASSIFY(t) == DUK_FP_NAN || DUK_FPCLASSIFY(res) == DUK_FP_NAN) { - /* Note: not normalized, but duk_push_number() will normalize */ - res = (duk_double_t) DUK_DOUBLE_NAN; - } else { - res = (duk_double_t) min_max(res, (double) t); - } - } - - duk_push_number(thr, res); - return 1; -} - -DUK_LOCAL double duk__fmin_fixed(double x, double y) { - /* fmin() with args -0 and +0 is not guaranteed to return - * -0 as ECMAScript requires. - */ - if (duk_double_equals(x, 0.0) && duk_double_equals(y, 0.0)) { - duk_double_union du1, du2; - du1.d = x; - du2.d = y; - - /* Already checked to be zero so these must hold, and allow us - * to check for "x is -0 or y is -0" by ORing the high parts - * for comparison. - */ - DUK_ASSERT(du1.ui[DUK_DBL_IDX_UI0] == 0 || du1.ui[DUK_DBL_IDX_UI0] == 0x80000000UL); - DUK_ASSERT(du2.ui[DUK_DBL_IDX_UI0] == 0 || du2.ui[DUK_DBL_IDX_UI0] == 0x80000000UL); - - /* XXX: what's the safest way of creating a negative zero? */ - if ((du1.ui[DUK_DBL_IDX_UI0] | du2.ui[DUK_DBL_IDX_UI0]) != 0) { - /* Enter here if either x or y (or both) is -0. */ - return -0.0; - } else { - return +0.0; - } - } - return duk_double_fmin(x, y); -} - -DUK_LOCAL double duk__fmax_fixed(double x, double y) { - /* fmax() with args -0 and +0 is not guaranteed to return - * +0 as ECMAScript requires. - */ - if (duk_double_equals(x, 0.0) && duk_double_equals(y, 0.0)) { - if (DUK_SIGNBIT(x) == 0 || DUK_SIGNBIT(y) == 0) { - return +0.0; - } else { - return -0.0; - } - } - return duk_double_fmax(x, y); -} - -#if defined(DUK_USE_ES6) -DUK_LOCAL double duk__cbrt(double x) { - /* cbrt() is C99. To avoid hassling embedders with the need to provide a - * cube root function, we can get by with pow(). The result is not - * identical, but that's OK: ES2015 says it's implementation-dependent. - */ - -#if defined(DUK_CBRT) - /* cbrt() matches ES2015 requirements. */ - return DUK_CBRT(x); -#else - duk_small_int_t c = (duk_small_int_t) DUK_FPCLASSIFY(x); - - /* pow() does not, however. */ - if (c == DUK_FP_NAN || c == DUK_FP_INFINITE || c == DUK_FP_ZERO) { - return x; - } - if (DUK_SIGNBIT(x)) { - return -DUK_POW(-x, 1.0 / 3.0); - } else { - return DUK_POW(x, 1.0 / 3.0); - } -#endif -} - -DUK_LOCAL double duk__log2(double x) { -#if defined(DUK_LOG2) - return DUK_LOG2(x); -#else - return DUK_LOG(x) * DUK_DOUBLE_LOG2E; -#endif -} - -DUK_LOCAL double duk__log10(double x) { -#if defined(DUK_LOG10) - return DUK_LOG10(x); -#else - return DUK_LOG(x) * DUK_DOUBLE_LOG10E; -#endif -} - -DUK_LOCAL double duk__trunc(double x) { -#if defined(DUK_TRUNC) - return DUK_TRUNC(x); -#else - /* Handles -0 correctly: -0.0 matches 'x >= 0.0' but floor() - * is required to return -0 when the argument is -0. - */ - return x >= 0.0 ? DUK_FLOOR(x) : DUK_CEIL(x); -#endif -} -#endif /* DUK_USE_ES6 */ - -DUK_LOCAL double duk__round_fixed(double x) { - /* Numbers half-way between integers must be rounded towards +Infinity, - * e.g. -3.5 must be rounded to -3 (not -4). When rounded to zero, zero - * sign must be set appropriately. E5.1 Section 15.8.2.15. - * - * Note that ANSI C round() is "round to nearest integer, away from zero", - * which is incorrect for negative values. Here we make do with floor(). - */ - - duk_small_int_t c = (duk_small_int_t) DUK_FPCLASSIFY(x); - if (c == DUK_FP_NAN || c == DUK_FP_INFINITE || c == DUK_FP_ZERO) { - return x; - } - - /* - * x is finite and non-zero - * - * -1.6 -> floor(-1.1) -> -2 - * -1.5 -> floor(-1.0) -> -1 (towards +Inf) - * -1.4 -> floor(-0.9) -> -1 - * -0.5 -> -0.0 (special case) - * -0.1 -> -0.0 (special case) - * +0.1 -> +0.0 (special case) - * +0.5 -> floor(+1.0) -> 1 (towards +Inf) - * +1.4 -> floor(+1.9) -> 1 - * +1.5 -> floor(+2.0) -> 2 (towards +Inf) - * +1.6 -> floor(+2.1) -> 2 - */ - - if (x >= -0.5 && x < 0.5) { - /* +0.5 is handled by floor, this is on purpose */ - if (x < 0.0) { - return -0.0; - } else { - return +0.0; - } - } - - return DUK_FLOOR(x + 0.5); -} - -/* Wrappers for calling standard math library methods. These may be required - * on platforms where one or more of the math built-ins are defined as macros - * or inline functions and are thus not suitable to be used as function pointers. - */ -#if defined(DUK_USE_AVOID_PLATFORM_FUNCPTRS) -DUK_LOCAL double duk__fabs(double x) { - return DUK_FABS(x); -} -DUK_LOCAL double duk__acos(double x) { - return DUK_ACOS(x); -} -DUK_LOCAL double duk__asin(double x) { - return DUK_ASIN(x); -} -DUK_LOCAL double duk__atan(double x) { - return DUK_ATAN(x); -} -DUK_LOCAL double duk__ceil(double x) { - return DUK_CEIL(x); -} -DUK_LOCAL double duk__cos(double x) { - return DUK_COS(x); -} -DUK_LOCAL double duk__exp(double x) { - return DUK_EXP(x); -} -DUK_LOCAL double duk__floor(double x) { - return DUK_FLOOR(x); -} -DUK_LOCAL double duk__log(double x) { - return DUK_LOG(x); -} -DUK_LOCAL double duk__sin(double x) { - return DUK_SIN(x); -} -DUK_LOCAL double duk__sqrt(double x) { - return DUK_SQRT(x); -} -DUK_LOCAL double duk__tan(double x) { - return DUK_TAN(x); -} -DUK_LOCAL double duk__atan2_fixed(double x, double y) { -#if defined(DUK_USE_ATAN2_WORKAROUNDS) - /* Specific fixes to common atan2() implementation issues: - * - test-bug-mingw-math-issues.js - */ - if (DUK_ISINF(x) && DUK_ISINF(y)) { - if (DUK_SIGNBIT(x)) { - if (DUK_SIGNBIT(y)) { - return -2.356194490192345; - } else { - return -0.7853981633974483; - } - } else { - if (DUK_SIGNBIT(y)) { - return 2.356194490192345; - } else { - return 0.7853981633974483; - } - } - } -#else - /* Some ISO C assumptions. */ - - DUK_ASSERT(duk_double_equals(DUK_ATAN2(DUK_DOUBLE_INFINITY, DUK_DOUBLE_INFINITY), 0.7853981633974483)); - DUK_ASSERT(duk_double_equals(DUK_ATAN2(-DUK_DOUBLE_INFINITY, DUK_DOUBLE_INFINITY), -0.7853981633974483)); - DUK_ASSERT(duk_double_equals(DUK_ATAN2(DUK_DOUBLE_INFINITY, -DUK_DOUBLE_INFINITY), 2.356194490192345)); - DUK_ASSERT(duk_double_equals(DUK_ATAN2(-DUK_DOUBLE_INFINITY, -DUK_DOUBLE_INFINITY), -2.356194490192345)); -#endif - - return DUK_ATAN2(x, y); -} -#endif /* DUK_USE_AVOID_PLATFORM_FUNCPTRS */ - -/* order must match constants in genbuiltins.py */ -DUK_LOCAL const duk__one_arg_func duk__one_arg_funcs[] = { -#if defined(DUK_USE_AVOID_PLATFORM_FUNCPTRS) - duk__fabs, duk__acos, duk__asin, duk__atan, duk__ceil, duk__cos, duk__exp, - duk__floor, duk__log, duk__round_fixed, duk__sin, duk__sqrt, duk__tan, -#if defined(DUK_USE_ES6) - duk__cbrt, duk__log2, duk__log10, duk__trunc -#endif -#else /* DUK_USE_AVOID_PLATFORM_FUNCPTRS */ - DUK_FABS, DUK_ACOS, DUK_ASIN, DUK_ATAN, DUK_CEIL, DUK_COS, DUK_EXP, - DUK_FLOOR, DUK_LOG, duk__round_fixed, DUK_SIN, DUK_SQRT, DUK_TAN, -#if defined(DUK_USE_ES6) - duk__cbrt, duk__log2, duk__log10, duk__trunc -#endif -#endif /* DUK_USE_AVOID_PLATFORM_FUNCPTRS */ -}; - -/* order must match constants in genbuiltins.py */ -DUK_LOCAL const duk__two_arg_func duk__two_arg_funcs[] = { -#if defined(DUK_USE_AVOID_PLATFORM_FUNCPTRS) - duk__atan2_fixed, - duk_js_arith_pow -#else - duk__atan2_fixed, - duk_js_arith_pow -#endif -}; - -DUK_INTERNAL duk_ret_t duk_bi_math_object_onearg_shared(duk_hthread *thr) { - duk_small_int_t fun_idx = duk_get_current_magic(thr); - duk__one_arg_func fun; - duk_double_t arg1; - - DUK_ASSERT(fun_idx >= 0); - DUK_ASSERT(fun_idx < (duk_small_int_t) (sizeof(duk__one_arg_funcs) / sizeof(duk__one_arg_func))); - arg1 = duk_to_number(thr, 0); - fun = duk__one_arg_funcs[fun_idx]; - duk_push_number(thr, (duk_double_t) fun((double) arg1)); - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_math_object_twoarg_shared(duk_hthread *thr) { - duk_small_int_t fun_idx = duk_get_current_magic(thr); - duk__two_arg_func fun; - duk_double_t arg1; - duk_double_t arg2; - - DUK_ASSERT(fun_idx >= 0); - DUK_ASSERT(fun_idx < (duk_small_int_t) (sizeof(duk__two_arg_funcs) / sizeof(duk__two_arg_func))); - arg1 = duk_to_number(thr, 0); /* explicit ordered evaluation to match coercion semantics */ - arg2 = duk_to_number(thr, 1); - fun = duk__two_arg_funcs[fun_idx]; - duk_push_number(thr, (duk_double_t) fun((double) arg1, (double) arg2)); - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_math_object_max(duk_hthread *thr) { - return duk__math_minmax(thr, -DUK_DOUBLE_INFINITY, duk__fmax_fixed); -} - -DUK_INTERNAL duk_ret_t duk_bi_math_object_min(duk_hthread *thr) { - return duk__math_minmax(thr, DUK_DOUBLE_INFINITY, duk__fmin_fixed); -} - -DUK_INTERNAL duk_ret_t duk_bi_math_object_random(duk_hthread *thr) { - duk_push_number(thr, (duk_double_t) duk_util_get_random_double(thr)); - return 1; -} - -#if defined(DUK_USE_ES6) -DUK_INTERNAL duk_ret_t duk_bi_math_object_hypot(duk_hthread *thr) { - /* - * E6 Section 20.2.2.18: Math.hypot - * - * - If no arguments are passed, the result is +0. - * - If any argument is +inf, the result is +inf. - * - If any argument is -inf, the result is +inf. - * - If no argument is +inf or -inf, and any argument is NaN, the result is - * NaN. - * - If all arguments are either +0 or -0, the result is +0. - */ - - duk_idx_t nargs; - duk_idx_t i; - duk_bool_t found_nan; - duk_double_t max; - duk_double_t sum, summand; - duk_double_t comp, prelim; - duk_double_t t; - - nargs = duk_get_top(thr); - - /* Find the highest value. Also ToNumber() coerces. */ - max = 0.0; - found_nan = 0; - for (i = 0; i < nargs; i++) { - t = DUK_FABS(duk_to_number(thr, i)); - if (DUK_FPCLASSIFY(t) == DUK_FP_NAN) { - found_nan = 1; - } else { - max = duk_double_fmax(max, t); - } - } - - /* Early return cases. */ - if (duk_double_equals(max, DUK_DOUBLE_INFINITY)) { - duk_push_number(thr, DUK_DOUBLE_INFINITY); - return 1; - } else if (found_nan) { - duk_push_number(thr, DUK_DOUBLE_NAN); - return 1; - } else if (duk_double_equals(max, 0.0)) { - duk_push_number(thr, 0.0); - /* Otherwise we'd divide by zero. */ - return 1; - } - - /* Use Kahan summation and normalize to the highest value to minimize - * floating point rounding error and avoid overflow. - * - * https://en.wikipedia.org/wiki/Kahan_summation_algorithm - */ - sum = 0.0; - comp = 0.0; - for (i = 0; i < nargs; i++) { - t = DUK_FABS(duk_get_number(thr, i)) / max; - summand = (t * t) - comp; - prelim = sum + summand; - comp = (prelim - sum) - summand; - sum = prelim; - } - - duk_push_number(thr, (duk_double_t) DUK_SQRT(sum) * max); - return 1; -} -#endif /* DUK_USE_ES6 */ - -#if defined(DUK_USE_ES6) -DUK_INTERNAL duk_ret_t duk_bi_math_object_sign(duk_hthread *thr) { - duk_double_t d; - - d = duk_to_number(thr, 0); - if (duk_double_is_nan(d)) { - DUK_ASSERT(duk_is_nan(thr, -1)); - return 1; /* NaN input -> return NaN */ - } - if (duk_double_equals(d, 0.0)) { - /* Zero sign kept, i.e. -0 -> -0, +0 -> +0. */ - return 1; - } - duk_push_int(thr, (d > 0.0 ? 1 : -1)); - return 1; -} -#endif /* DUK_USE_ES6 */ - -#if defined(DUK_USE_ES6) -DUK_INTERNAL duk_ret_t duk_bi_math_object_clz32(duk_hthread *thr) { - duk_uint32_t x; - duk_small_uint_t i; - -#if defined(DUK_USE_PREFER_SIZE) - duk_uint32_t mask; - - x = duk_to_uint32(thr, 0); - for (i = 0, mask = 0x80000000UL; mask != 0; mask >>= 1) { - if (x & mask) { - break; - } - i++; - } - DUK_ASSERT(i <= 32); - duk_push_uint(thr, i); - return 1; -#else /* DUK_USE_PREFER_SIZE */ - i = 0; - x = duk_to_uint32(thr, 0); - if (x & 0xffff0000UL) { - x >>= 16; - } else { - i += 16; - } - if (x & 0x0000ff00UL) { - x >>= 8; - } else { - i += 8; - } - if (x & 0x000000f0UL) { - x >>= 4; - } else { - i += 4; - } - if (x & 0x0000000cUL) { - x >>= 2; - } else { - i += 2; - } - if (x & 0x00000002UL) { - x >>= 1; - } else { - i += 1; - } - if (x & 0x00000001UL) { - ; - } else { - i += 1; - } - DUK_ASSERT(i <= 32); - duk_push_uint(thr, i); - return 1; -#endif /* DUK_USE_PREFER_SIZE */ -} -#endif /* DUK_USE_ES6 */ - -#if defined(DUK_USE_ES6) -DUK_INTERNAL duk_ret_t duk_bi_math_object_imul(duk_hthread *thr) { - duk_uint32_t x, y, z; - - x = duk_to_uint32(thr, 0); - y = duk_to_uint32(thr, 1); - z = x * y; - - /* While arguments are ToUint32() coerced and the multiplication - * is unsigned as such, the final result is curiously interpreted - * as a signed 32-bit value. - */ - duk_push_i32(thr, (duk_int32_t) z); - return 1; -} -#endif /* DUK_USE_ES6 */ - -#endif /* DUK_USE_MATH_BUILTIN */ -/* - * Number built-ins - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_NUMBER_BUILTIN) - -DUK_LOCAL duk_double_t duk__push_this_number_plain(duk_hthread *thr) { - duk_hobject *h; - - /* Number built-in accepts a plain number or a Number object (whose - * internal value is operated on). Other types cause TypeError. - */ - - duk_push_this(thr); - if (duk_is_number(thr, -1)) { - DUK_DDD(DUK_DDDPRINT("plain number value: %!T", (duk_tval *) duk_get_tval(thr, -1))); - goto done; - } - h = duk_get_hobject(thr, -1); - if (!h || (DUK_HOBJECT_GET_CLASS_NUMBER(h) != DUK_HOBJECT_CLASS_NUMBER)) { - DUK_DDD(DUK_DDDPRINT("unacceptable this value: %!T", (duk_tval *) duk_get_tval(thr, -1))); - DUK_ERROR_TYPE(thr, "number expected"); - DUK_WO_NORETURN(return 0.0;); - } - duk_xget_owndataprop_stridx_short(thr, -1, DUK_STRIDX_INT_VALUE); - DUK_ASSERT(duk_is_number(thr, -1)); - DUK_DDD(DUK_DDDPRINT("number object: %!T, internal value: %!T", - (duk_tval *) duk_get_tval(thr, -2), - (duk_tval *) duk_get_tval(thr, -1))); - duk_remove_m2(thr); - -done: - return duk_get_number(thr, -1); -} - -DUK_INTERNAL duk_ret_t duk_bi_number_constructor(duk_hthread *thr) { - duk_idx_t nargs; - duk_hobject *h_this; - - /* - * The Number constructor uses ToNumber(arg) for number coercion - * (coercing an undefined argument to NaN). However, if the - * argument is not given at all, +0 must be used instead. To do - * this, a vararg function is used. - */ - - nargs = duk_get_top(thr); - if (nargs == 0) { - duk_push_int(thr, 0); - } - duk_to_number(thr, 0); - duk_set_top(thr, 1); - DUK_ASSERT_TOP(thr, 1); - - if (!duk_is_constructor_call(thr)) { - return 1; - } - - /* - * E5 Section 15.7.2.1 requires that the constructed object - * must have the original Number.prototype as its internal - * prototype. However, since Number.prototype is non-writable - * and non-configurable, this doesn't have to be enforced here: - * The default object (bound to 'this') is OK, though we have - * to change its class. - * - * Internal value set to ToNumber(arg) or +0; if no arg given, - * ToNumber(undefined) = NaN, so special treatment is needed - * (above). String internal value is immutable. - */ - - /* XXX: helper */ - duk_push_this(thr); - h_this = duk_known_hobject(thr, -1); - DUK_HOBJECT_SET_CLASS_NUMBER(h_this, DUK_HOBJECT_CLASS_NUMBER); - - DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_this) == thr->builtins[DUK_BIDX_NUMBER_PROTOTYPE]); - DUK_ASSERT(DUK_HOBJECT_GET_CLASS_NUMBER(h_this) == DUK_HOBJECT_CLASS_NUMBER); - DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(h_this)); - - duk_dup_0(thr); /* -> [ val obj val ] */ - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE); - return 0; /* no return value -> don't replace created value */ -} - -DUK_INTERNAL duk_ret_t duk_bi_number_prototype_value_of(duk_hthread *thr) { - (void) duk__push_this_number_plain(thr); - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_string(duk_hthread *thr) { - duk_small_int_t radix; - duk_small_uint_t n2s_flags; - - (void) duk__push_this_number_plain(thr); - if (duk_is_undefined(thr, 0)) { - radix = 10; - } else { - radix = (duk_small_int_t) duk_to_int_check_range(thr, 0, 2, 36); - } - DUK_DDD(DUK_DDDPRINT("radix=%ld", (long) radix)); - - n2s_flags = 0; - - duk_numconv_stringify(thr, radix /*radix*/, 0 /*digits*/, n2s_flags /*flags*/); - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_locale_string(duk_hthread *thr) { - /* XXX: just use toString() for now; permitted although not recommended. - * nargs==1, so radix is passed to toString(). - */ - return duk_bi_number_prototype_to_string(thr); -} - -/* - * toFixed(), toExponential(), toPrecision() - */ - -/* XXX: shared helper for toFixed(), toExponential(), toPrecision()? */ - -DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_fixed(duk_hthread *thr) { - duk_small_int_t frac_digits; - duk_double_t d; - duk_small_int_t c; - duk_small_uint_t n2s_flags; - - /* In ES5.1 frac_digits is coerced first; in ES2015 the 'this number - * value' check is done first. - */ - d = duk__push_this_number_plain(thr); - frac_digits = (duk_small_int_t) duk_to_int_check_range(thr, 0, 0, 20); - - c = (duk_small_int_t) DUK_FPCLASSIFY(d); - if (c == DUK_FP_NAN || c == DUK_FP_INFINITE) { - goto use_to_string; - } - - if (d >= 1.0e21 || d <= -1.0e21) { - goto use_to_string; - } - - n2s_flags = DUK_N2S_FLAG_FIXED_FORMAT | DUK_N2S_FLAG_FRACTION_DIGITS; - - duk_numconv_stringify(thr, 10 /*radix*/, frac_digits /*digits*/, n2s_flags /*flags*/); - return 1; - -use_to_string: - DUK_ASSERT_TOP(thr, 2); - duk_to_string(thr, -1); - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_exponential(duk_hthread *thr) { - duk_bool_t frac_undefined; - duk_small_int_t frac_digits; - duk_double_t d; - duk_small_int_t c; - duk_small_uint_t n2s_flags; - - d = duk__push_this_number_plain(thr); - - frac_undefined = duk_is_undefined(thr, 0); - duk_to_int(thr, 0); /* for side effects */ - - c = (duk_small_int_t) DUK_FPCLASSIFY(d); - if (c == DUK_FP_NAN || c == DUK_FP_INFINITE) { - goto use_to_string; - } - - frac_digits = (duk_small_int_t) duk_to_int_check_range(thr, 0, 0, 20); - - n2s_flags = DUK_N2S_FLAG_FORCE_EXP | (frac_undefined ? 0 : DUK_N2S_FLAG_FIXED_FORMAT); - - duk_numconv_stringify(thr, 10 /*radix*/, frac_digits + 1 /*leading digit + fractions*/, n2s_flags /*flags*/); - return 1; - -use_to_string: - DUK_ASSERT_TOP(thr, 2); - duk_to_string(thr, -1); - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_precision(duk_hthread *thr) { - /* The specification has quite awkward order of coercion and - * checks for toPrecision(). The operations below are a bit - * reordered, within constraints of observable side effects. - */ - - duk_double_t d; - duk_small_int_t prec; - duk_small_int_t c; - duk_small_uint_t n2s_flags; - - DUK_ASSERT_TOP(thr, 1); - - d = duk__push_this_number_plain(thr); - if (duk_is_undefined(thr, 0)) { - goto use_to_string; - } - DUK_ASSERT_TOP(thr, 2); - - duk_to_int(thr, 0); /* for side effects */ - - c = (duk_small_int_t) DUK_FPCLASSIFY(d); - if (c == DUK_FP_NAN || c == DUK_FP_INFINITE) { - goto use_to_string; - } - - prec = (duk_small_int_t) duk_to_int_check_range(thr, 0, 1, 21); - - n2s_flags = DUK_N2S_FLAG_FIXED_FORMAT | DUK_N2S_FLAG_NO_ZERO_PAD; - - duk_numconv_stringify(thr, 10 /*radix*/, prec /*digits*/, n2s_flags /*flags*/); - return 1; - -use_to_string: - /* Used when precision is undefined; also used for NaN (-> "NaN"), - * and +/- infinity (-> "Infinity", "-Infinity"). - */ - - DUK_ASSERT_TOP(thr, 2); - duk_to_string(thr, -1); - return 1; -} - -/* - * ES2015 isFinite() etc - */ - -#if defined(DUK_USE_ES6) -DUK_INTERNAL duk_ret_t duk_bi_number_check_shared(duk_hthread *thr) { - duk_int_t magic; - duk_bool_t ret = 0; - - if (duk_is_number(thr, 0)) { - duk_double_t d; - - magic = duk_get_current_magic(thr); - d = duk_get_number(thr, 0); - - switch (magic) { - case 0: /* isFinite() */ - ret = duk_double_is_finite(d); - break; - case 1: /* isInteger() */ - ret = duk_double_is_integer(d); - break; - case 2: /* isNaN() */ - ret = duk_double_is_nan(d); - break; - default: /* isSafeInteger() */ - DUK_ASSERT(magic == 3); - ret = duk_double_is_safe_integer(d); - } - } - - duk_push_boolean(thr, ret); - return 1; -} -#endif /* DUK_USE_ES6 */ - -#endif /* DUK_USE_NUMBER_BUILTIN */ -/* - * Object built-ins - */ - -/* #include duk_internal.h -> already included */ - -/* Needed even when Object built-in disabled. */ -DUK_INTERNAL duk_ret_t duk_bi_object_prototype_to_string(duk_hthread *thr) { - duk_tval *tv; - - tv = DUK_HTHREAD_THIS_PTR(thr); - duk_push_class_string_tval(thr, tv, 0 /*avoid_side_effects*/); - return 1; -} - -#if defined(DUK_USE_OBJECT_BUILTIN) -DUK_INTERNAL duk_ret_t duk_bi_object_constructor(duk_hthread *thr) { - duk_uint_t arg_mask; - - arg_mask = duk_get_type_mask(thr, 0); - - if (!duk_is_constructor_call(thr) && /* not a constructor call */ - ((arg_mask & (DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_UNDEFINED)) == 0)) { /* and argument not null or undefined */ - duk_to_object(thr, 0); - return 1; - } - - /* Pointer and buffer primitive values are treated like other - * primitives values which have a fully fledged object counterpart: - * promote to an object value. Lightfuncs and plain buffers are - * coerced with ToObject() even they could also be returned as is. - */ - if (arg_mask & (DUK_TYPE_MASK_OBJECT | DUK_TYPE_MASK_STRING | DUK_TYPE_MASK_BOOLEAN | DUK_TYPE_MASK_NUMBER | - DUK_TYPE_MASK_POINTER | DUK_TYPE_MASK_BUFFER | DUK_TYPE_MASK_LIGHTFUNC)) { - /* For DUK_TYPE_OBJECT the coercion is a no-op and could - * be checked for explicitly, but Object(obj) calls are - * not very common so opt for minimal footprint. - */ - duk_to_object(thr, 0); - return 1; - } - - (void) duk_push_object_helper(thr, - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_FASTREFS | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT), - DUK_BIDX_OBJECT_PROTOTYPE); - return 1; -} -#endif /* DUK_USE_OBJECT_BUILTIN */ - -#if defined(DUK_USE_OBJECT_BUILTIN) && defined(DUK_USE_ES6) -DUK_INTERNAL duk_ret_t duk_bi_object_constructor_assign(duk_hthread *thr) { - duk_idx_t nargs; - duk_int_t idx; - - nargs = duk_get_top_require_min(thr, 1 /*min_top*/); - - duk_to_object(thr, 0); - for (idx = 1; idx < nargs; idx++) { - /* E7 19.1.2.1 (step 4a) */ - if (duk_is_null_or_undefined(thr, idx)) { - continue; - } - - /* duk_enum() respects ES2015+ [[OwnPropertyKeys]] ordering, which is - * convenient here. - */ - duk_to_object(thr, idx); - duk_enum(thr, idx, DUK_ENUM_OWN_PROPERTIES_ONLY); - while (duk_next(thr, -1, 1 /*get_value*/)) { - /* [ target ... enum key value ] */ - duk_put_prop(thr, 0); - /* [ target ... enum ] */ - } - /* Could pop enumerator, but unnecessary because of duk_set_top() - * below. - */ - } - - duk_set_top(thr, 1); - return 1; -} -#endif - -#if defined(DUK_USE_OBJECT_BUILTIN) && defined(DUK_USE_ES6) -DUK_INTERNAL duk_ret_t duk_bi_object_constructor_is(duk_hthread *thr) { - DUK_ASSERT_TOP(thr, 2); - duk_push_boolean(thr, duk_samevalue(thr, 0, 1)); - return 1; -} -#endif - -#if defined(DUK_USE_OBJECT_BUILTIN) -DUK_INTERNAL duk_ret_t duk_bi_object_constructor_create(duk_hthread *thr) { - duk_hobject *proto; - - DUK_ASSERT_TOP(thr, 2); - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) - duk_hbufobj_promote_plain(thr, 0); -#endif - proto = duk_require_hobject_accept_mask(thr, 0, DUK_TYPE_MASK_NULL); - DUK_ASSERT(proto != NULL || duk_is_null(thr, 0)); - - (void) duk_push_object_helper_proto(thr, - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_FASTREFS | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT), - proto); - - if (!duk_is_undefined(thr, 1)) { - /* [ O Properties obj ] */ - - duk_replace(thr, 0); - - /* [ obj Properties ] */ - - /* Just call the "original" Object.defineProperties() to - * finish up. - */ - - return duk_bi_object_constructor_define_properties(thr); - } - - /* [ O Properties obj ] */ - - return 1; -} -#endif /* DUK_USE_OBJECT_BUILTIN */ - -#if defined(DUK_USE_OBJECT_BUILTIN) -DUK_INTERNAL duk_ret_t duk_bi_object_constructor_define_properties(duk_hthread *thr) { - duk_small_uint_t pass; - duk_uint_t defprop_flags; - duk_hobject *obj; - duk_idx_t idx_value; - duk_hobject *get; - duk_hobject *set; - - /* Lightfunc and plain buffer handling by ToObject() coercion. */ - obj = duk_require_hobject_promote_mask(thr, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER); - DUK_ASSERT(obj != NULL); - - duk_to_object(thr, 1); /* properties object */ - - DUK_DDD(DUK_DDDPRINT("target=%!iT, properties=%!iT", (duk_tval *) duk_get_tval(thr, 0), (duk_tval *) duk_get_tval(thr, 1))); - - /* - * Two pass approach to processing the property descriptors. - * On first pass validate and normalize all descriptors before - * any changes are made to the target object. On second pass - * make the actual modifications to the target object. - * - * Right now we'll just use the same normalize/validate helper - * on both passes, ignoring its outputs on the first pass. - */ - - for (pass = 0; pass < 2; pass++) { - duk_set_top(thr, 2); /* -> [ hobject props ] */ - duk_enum(thr, 1, DUK_ENUM_OWN_PROPERTIES_ONLY | DUK_ENUM_INCLUDE_SYMBOLS /*enum_flags*/); - - for (;;) { - duk_hstring *key; - - /* [ hobject props enum(props) ] */ - - duk_set_top(thr, 3); - - if (!duk_next(thr, 2, 1 /*get_value*/)) { - break; - } - - DUK_DDD(DUK_DDDPRINT("-> key=%!iT, desc=%!iT", - (duk_tval *) duk_get_tval(thr, -2), - (duk_tval *) duk_get_tval(thr, -1))); - - /* [ hobject props enum(props) key desc ] */ - - duk_hobject_prepare_property_descriptor(thr, 4 /*idx_desc*/, &defprop_flags, &idx_value, &get, &set); - - /* [ hobject props enum(props) key desc [multiple values] ] */ - - if (pass == 0) { - continue; - } - - /* This allows symbols on purpose. */ - key = duk_known_hstring(thr, 3); - DUK_ASSERT(key != NULL); - - duk_hobject_define_property_helper(thr, defprop_flags, obj, key, idx_value, get, set, 1 /*throw_flag*/); - } - } - - /* - * Return target object - */ - - duk_dup_0(thr); - return 1; -} -#endif /* DUK_USE_OBJECT_BUILTIN */ - -#if defined(DUK_USE_OBJECT_BUILTIN) -DUK_INTERNAL duk_ret_t duk_bi_object_constructor_seal_freeze_shared(duk_hthread *thr) { - DUK_ASSERT_TOP(thr, 1); - - duk_seal_freeze_raw(thr, 0, (duk_bool_t) duk_get_current_magic(thr) /*is_freeze*/); - return 1; -} -#endif /* DUK_USE_OBJECT_BUILTIN */ - -#if defined(DUK_USE_OBJECT_BUILTIN) -DUK_INTERNAL duk_ret_t duk_bi_object_constructor_is_sealed_frozen_shared(duk_hthread *thr) { - duk_hobject *h; - duk_bool_t is_frozen; - duk_uint_t mask; - - is_frozen = (duk_bool_t) duk_get_current_magic(thr); - mask = duk_get_type_mask(thr, 0); - if (mask & (DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER)) { - DUK_ASSERT(is_frozen == 0 || is_frozen == 1); - duk_push_boolean(thr, - (mask & DUK_TYPE_MASK_LIGHTFUNC) ? 1 : /* lightfunc always frozen and sealed */ - (is_frozen ^ 1)); /* buffer sealed but not frozen (index props writable) */ - } else { - /* ES2015 Sections 19.1.2.12, 19.1.2.13: anything other than an object - * is considered to be already sealed and frozen. - */ - h = duk_get_hobject(thr, 0); - duk_push_boolean(thr, (h == NULL) || duk_hobject_object_is_sealed_frozen_helper(thr, h, is_frozen /*is_frozen*/)); - } - return 1; -} -#endif /* DUK_USE_OBJECT_BUILTIN */ - -#if defined(DUK_USE_OBJECT_BUILTIN) -DUK_INTERNAL duk_ret_t duk_bi_object_prototype_to_locale_string(duk_hthread *thr) { - DUK_ASSERT_TOP(thr, 0); - (void) duk_push_this_coercible_to_object(thr); - duk_get_prop_stridx_short(thr, 0, DUK_STRIDX_TO_STRING); -#if 0 /* This is mentioned explicitly in the E5.1 spec, but duk_call_method() checks for it in practice. */ - duk_require_callable(thr, 1); -#endif - duk_dup_0(thr); /* -> [ O toString O ] */ - duk_call_method(thr, 0); /* XXX: call method tail call? */ - return 1; -} -#endif /* DUK_USE_OBJECT_BUILTIN */ - -#if defined(DUK_USE_OBJECT_BUILTIN) -DUK_INTERNAL duk_ret_t duk_bi_object_prototype_value_of(duk_hthread *thr) { - /* For lightfuncs and plain buffers, returns Object() coerced. */ - (void) duk_push_this_coercible_to_object(thr); - return 1; -} -#endif /* DUK_USE_OBJECT_BUILTIN */ - -#if defined(DUK_USE_OBJECT_BUILTIN) -DUK_INTERNAL duk_ret_t duk_bi_object_prototype_is_prototype_of(duk_hthread *thr) { - duk_hobject *h_v; - duk_hobject *h_obj; - - DUK_ASSERT_TOP(thr, 1); - - h_v = duk_get_hobject(thr, 0); - if (!h_v) { - duk_push_false(thr); /* XXX: tail call: return duk_push_false(thr) */ - return 1; - } - - h_obj = duk_push_this_coercible_to_object(thr); - DUK_ASSERT(h_obj != NULL); - - /* E5.1 Section 15.2.4.6, step 3.a, lookup proto once before compare. - * Prototype loops should cause an error to be thrown. - */ - duk_push_boolean( - thr, - duk_hobject_prototype_chain_contains(thr, DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_v), h_obj, 0 /*ignore_loop*/)); - return 1; -} -#endif /* DUK_USE_OBJECT_BUILTIN */ - -#if defined(DUK_USE_OBJECT_BUILTIN) -DUK_INTERNAL duk_ret_t duk_bi_object_prototype_has_own_property(duk_hthread *thr) { - return (duk_ret_t) duk_hobject_object_ownprop_helper(thr, 0 /*required_desc_flags*/); -} -#endif /* DUK_USE_OBJECT_BUILTIN */ - -#if defined(DUK_USE_OBJECT_BUILTIN) -DUK_INTERNAL duk_ret_t duk_bi_object_prototype_property_is_enumerable(duk_hthread *thr) { - return (duk_ret_t) duk_hobject_object_ownprop_helper(thr, DUK_PROPDESC_FLAG_ENUMERABLE /*required_desc_flags*/); -} -#endif /* DUK_USE_OBJECT_BUILTIN */ - -#if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN) -/* Shared helper to implement Object.getPrototypeOf, - * Object.prototype.__proto__ getter, and Reflect.getPrototypeOf. - * - * http://www.ecma-international.org/ecma-262/6.0/index.html#sec-get-object.prototype.__proto__ - */ -DUK_INTERNAL duk_ret_t duk_bi_object_getprototype_shared(duk_hthread *thr) { - /* - * magic = 0: __proto__ getter - * magic = 1: Object.getPrototypeOf() - * magic = 2: Reflect.getPrototypeOf() - */ - - duk_hobject *h; - duk_hobject *proto; - duk_tval *tv; - duk_int_t magic; - - magic = duk_get_current_magic(thr); - - if (magic == 0) { - DUK_ASSERT_TOP(thr, 0); - duk_push_this_coercible_to_object(thr); - } - DUK_ASSERT(duk_get_top(thr) >= 1); - if (magic < 2) { - /* ES2015 Section 19.1.2.9, step 1 */ - duk_to_object(thr, 0); - } - tv = DUK_GET_TVAL_POSIDX(thr, 0); - - switch (DUK_TVAL_GET_TAG(tv)) { - case DUK_TAG_BUFFER: - proto = thr->builtins[DUK_BIDX_UINT8ARRAY_PROTOTYPE]; - break; - case DUK_TAG_LIGHTFUNC: - proto = thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]; - break; - case DUK_TAG_OBJECT: - h = DUK_TVAL_GET_OBJECT(tv); - proto = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h); - break; - default: - /* This implicitly handles CheckObjectCoercible() caused - * TypeError. - */ - DUK_DCERROR_TYPE_INVALID_ARGS(thr); - } - if (proto != NULL) { - duk_push_hobject(thr, proto); - } else { - duk_push_null(thr); - } - return 1; -} -#endif /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */ - -#if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN) -/* Shared helper to implement ES2015 Object.setPrototypeOf, - * Object.prototype.__proto__ setter, and Reflect.setPrototypeOf. - * - * http://www.ecma-international.org/ecma-262/6.0/index.html#sec-get-object.prototype.__proto__ - * http://www.ecma-international.org/ecma-262/6.0/index.html#sec-object.setprototypeof - */ -DUK_INTERNAL duk_ret_t duk_bi_object_setprototype_shared(duk_hthread *thr) { - /* - * magic = 0: __proto__ setter - * magic = 1: Object.setPrototypeOf() - * magic = 2: Reflect.setPrototypeOf() - */ - - duk_hobject *h_obj; - duk_hobject *h_new_proto; - duk_hobject *h_curr; - duk_ret_t ret_success = 1; /* retval for success path */ - duk_uint_t mask; - duk_int_t magic; - - /* Preliminaries for __proto__ and setPrototypeOf (E6 19.1.2.18 steps 1-4). */ - magic = duk_get_current_magic(thr); - if (magic == 0) { - duk_push_this_check_object_coercible(thr); - duk_insert(thr, 0); - if (!duk_check_type_mask(thr, 1, DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_OBJECT)) { - return 0; - } - - /* __proto__ setter returns 'undefined' on success unlike the - * setPrototypeOf() call which returns the target object. - */ - ret_success = 0; - } else { - if (magic == 1) { - duk_require_object_coercible(thr, 0); - } else { - duk_require_hobject_accept_mask(thr, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER); - } - duk_require_type_mask(thr, 1, DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_OBJECT); - } - - h_new_proto = duk_get_hobject(thr, 1); - /* h_new_proto may be NULL */ - - mask = duk_get_type_mask(thr, 0); - if (mask & (DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER)) { - duk_hobject *curr_proto; - curr_proto = - thr->builtins[(mask & DUK_TYPE_MASK_LIGHTFUNC) ? DUK_BIDX_FUNCTION_PROTOTYPE : DUK_BIDX_UINT8ARRAY_PROTOTYPE]; - if (h_new_proto == curr_proto) { - goto skip; - } - goto fail_nonextensible; - } - h_obj = duk_get_hobject(thr, 0); - if (h_obj == NULL) { - goto skip; - } - DUK_ASSERT(h_obj != NULL); - - /* [[SetPrototypeOf]] standard behavior, E6 9.1.2. */ - /* TODO: implement Proxy object support here */ - - if (h_new_proto == DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_obj)) { - goto skip; - } - if (!DUK_HOBJECT_HAS_EXTENSIBLE(h_obj)) { - goto fail_nonextensible; - } - for (h_curr = h_new_proto; h_curr != NULL; h_curr = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_curr)) { - /* Loop prevention. */ - if (h_curr == h_obj) { - goto fail_loop; - } - } - DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h_obj, h_new_proto); - /* fall thru */ - -skip: - duk_set_top(thr, 1); - if (magic == 2) { - duk_push_true(thr); - } - return ret_success; - -fail_nonextensible: -fail_loop: - if (magic != 2) { - DUK_DCERROR_TYPE_INVALID_ARGS(thr); - } else { - duk_push_false(thr); - return 1; - } -} -#endif /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */ - -#if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN) -DUK_INTERNAL duk_ret_t duk_bi_object_constructor_define_property(duk_hthread *thr) { - /* - * magic = 0: Object.defineProperty() - * magic = 1: Reflect.defineProperty() - */ - - duk_hobject *obj; - duk_hstring *key; - duk_hobject *get; - duk_hobject *set; - duk_idx_t idx_value; - duk_uint_t defprop_flags; - duk_small_uint_t magic; - duk_bool_t throw_flag; - duk_bool_t ret; - - DUK_ASSERT(thr != NULL); - - DUK_DDD(DUK_DDDPRINT("Object.defineProperty(): ctx=%p obj=%!T key=%!T desc=%!T", - (void *) thr, - (duk_tval *) duk_get_tval(thr, 0), - (duk_tval *) duk_get_tval(thr, 1), - (duk_tval *) duk_get_tval(thr, 2))); - - /* [ obj key desc ] */ - - magic = (duk_small_uint_t) duk_get_current_magic(thr); - - /* Lightfuncs are currently supported by coercing to a temporary - * Function object; changes will be allowed (the coerced value is - * extensible) but will be lost. Same for plain buffers. - */ - obj = duk_require_hobject_promote_mask(thr, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER); - DUK_ASSERT(obj != NULL); - key = duk_to_property_key_hstring(thr, 1); - (void) duk_require_hobject(thr, 2); - - DUK_ASSERT(obj != NULL); - DUK_ASSERT(key != NULL); - DUK_ASSERT(duk_get_hobject(thr, 2) != NULL); - - /* - * Validate and convert argument property descriptor (an ECMAScript - * object) into a set of defprop_flags and possibly property value, - * getter, and/or setter values on the value stack. - * - * Lightfunc set/get values are coerced to full Functions. - */ - - duk_hobject_prepare_property_descriptor(thr, 2 /*idx_desc*/, &defprop_flags, &idx_value, &get, &set); - - /* - * Use Object.defineProperty() helper for the actual operation. - */ - - DUK_ASSERT(magic == 0U || magic == 1U); - throw_flag = magic ^ 1U; - ret = duk_hobject_define_property_helper(thr, defprop_flags, obj, key, idx_value, get, set, throw_flag); - - /* Ignore the normalize/validate helper outputs on the value stack, - * they're popped automatically. - */ - - if (magic == 0U) { - /* Object.defineProperty(): return target object. */ - duk_push_hobject(thr, obj); - } else { - /* Reflect.defineProperty(): return success/fail. */ - duk_push_boolean(thr, ret); - } - return 1; -} -#endif /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */ - -#if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN) -DUK_INTERNAL duk_ret_t duk_bi_object_constructor_get_own_property_descriptor(duk_hthread *thr) { - DUK_ASSERT_TOP(thr, 2); - - /* ES2015 Section 19.1.2.6, step 1 */ - if (duk_get_current_magic(thr) == 0) { - duk_to_object(thr, 0); - } - - /* [ obj key ] */ - - duk_hobject_object_get_own_property_descriptor(thr, -2); - return 1; -} -#endif /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */ - -#if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN) -DUK_INTERNAL duk_ret_t duk_bi_object_constructor_is_extensible(duk_hthread *thr) { - /* - * magic = 0: Object.isExtensible() - * magic = 1: Reflect.isExtensible() - */ - - duk_hobject *h; - - if (duk_get_current_magic(thr) == 0) { - h = duk_get_hobject(thr, 0); - } else { - /* Reflect.isExtensible(): throw if non-object, but we accept lightfuncs - * and plain buffers here because they pretend to be objects. - */ - h = duk_require_hobject_accept_mask(thr, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER); - } - - duk_push_boolean(thr, (h != NULL) && DUK_HOBJECT_HAS_EXTENSIBLE(h)); - return 1; -} -#endif /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */ - -#if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN) -/* Shared helper for various key/symbol listings, magic: - * 0=Object.keys() - * 1=Object.getOwnPropertyNames(), - * 2=Object.getOwnPropertySymbols(), - * 3=Reflect.ownKeys() - */ -DUK_LOCAL const duk_small_uint_t duk__object_keys_enum_flags[4] = { - /* Object.keys() */ - DUK_ENUM_OWN_PROPERTIES_ONLY | DUK_ENUM_NO_PROXY_BEHAVIOR, - - /* Object.getOwnPropertyNames() */ - DUK_ENUM_INCLUDE_NONENUMERABLE | DUK_ENUM_OWN_PROPERTIES_ONLY | DUK_ENUM_NO_PROXY_BEHAVIOR, - - /* Object.getOwnPropertySymbols() */ - DUK_ENUM_INCLUDE_SYMBOLS | DUK_ENUM_OWN_PROPERTIES_ONLY | DUK_ENUM_EXCLUDE_STRINGS | DUK_ENUM_INCLUDE_NONENUMERABLE | - DUK_ENUM_NO_PROXY_BEHAVIOR, - - /* Reflect.ownKeys() */ - DUK_ENUM_INCLUDE_SYMBOLS | DUK_ENUM_OWN_PROPERTIES_ONLY | DUK_ENUM_INCLUDE_NONENUMERABLE | DUK_ENUM_NO_PROXY_BEHAVIOR -}; - -DUK_INTERNAL duk_ret_t duk_bi_object_constructor_keys_shared(duk_hthread *thr) { - duk_hobject *obj; -#if defined(DUK_USE_ES6_PROXY) - duk_hobject *h_proxy_target; - duk_hobject *h_proxy_handler; - duk_hobject *h_trap_result; -#endif - duk_small_uint_t enum_flags; - duk_int_t magic; - - DUK_ASSERT_TOP(thr, 1); - - magic = duk_get_current_magic(thr); - if (magic == 3) { - /* ES2015 Section 26.1.11 requires a TypeError for non-objects. Lightfuncs - * and plain buffers pretend to be objects, so accept those too. - */ - obj = duk_require_hobject_promote_mask(thr, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER); - } else { - /* ES2015: ToObject coerce. */ - obj = duk_to_hobject(thr, 0); - } - DUK_ASSERT(obj != NULL); - DUK_UNREF(obj); - - /* XXX: proxy chains */ - -#if defined(DUK_USE_ES6_PROXY) - /* XXX: better sharing of code between proxy target call sites */ - if (DUK_LIKELY(!duk_hobject_proxy_check(obj, &h_proxy_target, &h_proxy_handler))) { - goto skip_proxy; - } - - duk_push_hobject(thr, h_proxy_handler); - if (!duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_OWN_KEYS)) { - /* Careful with reachability here: don't pop 'obj' before pushing - * proxy target. - */ - DUK_DDD(DUK_DDDPRINT("no ownKeys trap, get keys of target instead")); - duk_pop_2(thr); - duk_push_hobject(thr, h_proxy_target); - duk_replace(thr, 0); - DUK_ASSERT_TOP(thr, 1); - goto skip_proxy; - } - - /* [ obj handler trap ] */ - duk_insert(thr, -2); - duk_push_hobject(thr, h_proxy_target); /* -> [ obj trap handler target ] */ - duk_call_method(thr, 1 /*nargs*/); /* -> [ obj trap_result ] */ - h_trap_result = duk_require_hobject(thr, -1); - DUK_UNREF(h_trap_result); - - magic = duk_get_current_magic(thr); - DUK_ASSERT(magic >= 0 && magic < (duk_int_t) (sizeof(duk__object_keys_enum_flags) / sizeof(duk_small_uint_t))); - enum_flags = duk__object_keys_enum_flags[magic]; - - duk_proxy_ownkeys_postprocess(thr, h_proxy_target, enum_flags); - return 1; - -skip_proxy: -#endif /* DUK_USE_ES6_PROXY */ - - DUK_ASSERT_TOP(thr, 1); - magic = duk_get_current_magic(thr); - DUK_ASSERT(magic >= 0 && magic < (duk_int_t) (sizeof(duk__object_keys_enum_flags) / sizeof(duk_small_uint_t))); - enum_flags = duk__object_keys_enum_flags[magic]; - return duk_hobject_get_enumerated_keys(thr, enum_flags); -} -#endif /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */ - -#if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN) -DUK_INTERNAL duk_ret_t duk_bi_object_constructor_prevent_extensions(duk_hthread *thr) { - /* - * magic = 0: Object.preventExtensions() - * magic = 1: Reflect.preventExtensions() - */ - - duk_hobject *h; - duk_uint_t mask; - duk_int_t magic; - - magic = duk_get_current_magic(thr); - - /* Silent success for lightfuncs and plain buffers always. */ - mask = DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER; - - /* Object.preventExtensions() silent success for non-object. */ - if (magic == 0) { - mask |= DUK_TYPE_MASK_UNDEFINED | DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_BOOLEAN | DUK_TYPE_MASK_NUMBER | - DUK_TYPE_MASK_STRING | DUK_TYPE_MASK_POINTER; - } - - if (duk_check_type_mask(thr, 0, mask)) { - /* Not an object, already non-extensible so always success. */ - goto done; - } - h = duk_require_hobject(thr, 0); - DUK_ASSERT(h != NULL); - - DUK_HOBJECT_CLEAR_EXTENSIBLE(h); - - /* A non-extensible object cannot gain any more properties, - * so this is a good time to compact. - */ - duk_hobject_compact_props(thr, h); - -done: - if (magic == 1) { - duk_push_true(thr); - } - return 1; -} -#endif /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */ - -/* - * __defineGetter__, __defineSetter__, __lookupGetter__, __lookupSetter__ - */ - -#if defined(DUK_USE_ES8) -DUK_INTERNAL duk_ret_t duk_bi_object_prototype_defineaccessor(duk_hthread *thr) { - duk_push_this(thr); - duk_insert(thr, 0); - duk_to_object(thr, 0); - duk_require_callable(thr, 2); - - /* [ ToObject(this) key getter/setter ] */ - - /* ToPropertyKey() coercion is not needed, duk_def_prop() does it. */ - duk_def_prop(thr, - 0, - DUK_DEFPROP_SET_ENUMERABLE | DUK_DEFPROP_SET_CONFIGURABLE | - (duk_get_current_magic(thr) ? DUK_DEFPROP_HAVE_SETTER : DUK_DEFPROP_HAVE_GETTER)); - return 0; -} -DUK_INTERNAL duk_ret_t duk_bi_object_prototype_lookupaccessor(duk_hthread *thr) { - duk_uint_t sanity; - - duk_push_this(thr); - duk_to_object(thr, -1); - - /* XXX: Prototype walk (with sanity) should be a core property - * operation, could add a flag to e.g. duk_get_prop_desc(). - */ - - /* ToPropertyKey() coercion is not needed, duk_get_prop_desc() does it. */ - sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY; - while (!duk_is_undefined(thr, -1)) { - /* [ key obj ] */ - duk_dup(thr, 0); - duk_get_prop_desc(thr, 1, 0 /*flags*/); - if (!duk_is_undefined(thr, -1)) { - duk_get_prop_stridx(thr, -1, (duk_get_current_magic(thr) != 0 ? DUK_STRIDX_SET : DUK_STRIDX_GET)); - return 1; - } - duk_pop(thr); - - if (DUK_UNLIKELY(sanity-- == 0)) { - DUK_ERROR_RANGE(thr, DUK_STR_PROTOTYPE_CHAIN_LIMIT); - DUK_WO_NORETURN(return 0;); - } - - duk_get_prototype(thr, -1); - duk_remove(thr, -2); - } - return 1; -} -#endif /* DUK_USE_ES8 */ -/* - * High resolution time API (performance.now() et al) - * - * API specification: https://encoding.spec.whatwg.org/#ap://www.w3.org/TR/hr-time/ - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_PERFORMANCE_BUILTIN) -DUK_INTERNAL duk_ret_t duk_bi_performance_now(duk_hthread *thr) { - /* From API spec: - * The DOMHighResTimeStamp type is used to store a time value in - * milliseconds, measured relative from the time origin, global - * monotonic clock, or a time value that represents a duration - * between two DOMHighResTimeStamp's. - */ - duk_push_number(thr, duk_time_get_monotonic_time(thr)); - return 1; -} - -#if 0 /* Missing until semantics decided. */ -DUK_INTERNAL duk_ret_t duk_bi_performance_timeorigin_getter(duk_hthread *thr) { - /* No decision yet how to handle timeOrigins, e.g. should one be - * initialized per heap, or per global object set. See - * https://www.w3.org/TR/hr-time/#time-origin. - */ - duk_push_uint(thr, 0); - return 1; -} -#endif /* 0 */ -#endif /* DUK_USE_PERFORMANCE_BUILTIN */ -/* - * Pointer built-ins - */ - -/* #include duk_internal.h -> already included */ - -/* - * Constructor - */ - -DUK_INTERNAL duk_ret_t duk_bi_pointer_constructor(duk_hthread *thr) { - /* XXX: this behavior is quite useless now; it would be nice to be able - * to create pointer values from e.g. numbers or strings. Numbers are - * problematic on 64-bit platforms though. Hex encoded strings? - */ - if (duk_get_top(thr) == 0) { - duk_push_pointer(thr, NULL); - } else { - duk_to_pointer(thr, 0); - } - DUK_ASSERT(duk_is_pointer(thr, 0)); - duk_set_top(thr, 1); - - if (duk_is_constructor_call(thr)) { - (void) duk_push_object_helper(thr, - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_FASTREFS | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_POINTER), - DUK_BIDX_POINTER_PROTOTYPE); - - /* Pointer object internal value is immutable. */ - duk_dup_0(thr); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE); - } - /* Note: unbalanced stack on purpose */ - - return 1; -} - -/* - * toString(), valueOf() - */ - -DUK_INTERNAL duk_ret_t duk_bi_pointer_prototype_tostring_shared(duk_hthread *thr) { - duk_tval *tv; - duk_small_int_t to_string = duk_get_current_magic(thr); - - duk_push_this(thr); - tv = duk_require_tval(thr, -1); - DUK_ASSERT(tv != NULL); - - if (DUK_TVAL_IS_POINTER(tv)) { - /* nop */ - } else if (DUK_TVAL_IS_OBJECT(tv)) { - duk_hobject *h = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - - /* Must be a "pointer object", i.e. class "Pointer" */ - if (DUK_HOBJECT_GET_CLASS_NUMBER(h) != DUK_HOBJECT_CLASS_POINTER) { - goto type_error; - } - - duk_xget_owndataprop_stridx_short(thr, -1, DUK_STRIDX_INT_VALUE); - } else { - goto type_error; - } - - if (to_string) { - duk_to_string(thr, -1); - } - return 1; - -type_error: - DUK_DCERROR_TYPE_INVALID_ARGS(thr); -} -/* - * Promise built-in - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_PROMISE_BUILTIN) - -DUK_INTERNAL duk_ret_t duk_bi_promise_constructor(duk_hthread *thr) { - DUK_ERROR_TYPE(thr, "unimplemented"); - DUK_WO_NORETURN(return 0;); -} - -DUK_INTERNAL duk_ret_t duk_bi_promise_all(duk_hthread *thr) { - DUK_ERROR_TYPE(thr, "unimplemented"); - DUK_WO_NORETURN(return 0;); -} - -DUK_INTERNAL duk_ret_t duk_bi_promise_race(duk_hthread *thr) { - DUK_ERROR_TYPE(thr, "unimplemented"); - DUK_WO_NORETURN(return 0;); -} - -DUK_INTERNAL duk_ret_t duk_bi_promise_reject(duk_hthread *thr) { - DUK_ERROR_TYPE(thr, "unimplemented"); - DUK_WO_NORETURN(return 0;); -} - -DUK_INTERNAL duk_ret_t duk_bi_promise_resolve(duk_hthread *thr) { - DUK_ERROR_TYPE(thr, "unimplemented"); - DUK_WO_NORETURN(return 0;); -} - -DUK_INTERNAL duk_ret_t duk_bi_promise_catch(duk_hthread *thr) { - DUK_ERROR_TYPE(thr, "unimplemented"); - DUK_WO_NORETURN(return 0;); -} - -DUK_INTERNAL duk_ret_t duk_bi_promise_then(duk_hthread *thr) { - DUK_ERROR_TYPE(thr, "unimplemented"); - DUK_WO_NORETURN(return 0;); -} - -#endif /* DUK_USE_PROMISE_BUILTIN */ -/* - * Proxy built-in (ES2015) - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_ES6_PROXY) -/* Post-process a Proxy ownKeys() result at stack top. Push a cleaned up - * array of valid result keys (strings or symbols). TypeError for invalid - * values. Flags are shared with duk_enum(). - */ -DUK_INTERNAL void duk_proxy_ownkeys_postprocess(duk_hthread *thr, duk_hobject *h_proxy_target, duk_uint_t flags) { - duk_uarridx_t i, len, idx; - duk_propdesc desc; - - DUK_CTX_ASSERT_VALID(thr); - DUK_ASSERT(h_proxy_target != NULL); - - len = (duk_uarridx_t) duk_get_length(thr, -1); - idx = 0; - duk_push_array(thr); - /* XXX: preallocated dense array, fill in directly */ - for (i = 0; i < len; i++) { - duk_hstring *h; - - /* [ obj trap_result res_arr ] */ - (void) duk_get_prop_index(thr, -2, i); - h = duk_get_hstring(thr, -1); - if (h == NULL) { - DUK_ERROR_TYPE_INVALID_TRAP_RESULT(thr); - DUK_WO_NORETURN(return;); - } - - if (!(flags & DUK_ENUM_INCLUDE_NONENUMERABLE)) { - /* No support for 'getOwnPropertyDescriptor' trap yet, - * so check enumerability always from target object - * descriptor. - */ - if (duk_hobject_get_own_propdesc(thr, h_proxy_target, duk_known_hstring(thr, -1), &desc, 0 /*flags*/)) { - if ((desc.flags & DUK_PROPDESC_FLAG_ENUMERABLE) == 0) { - DUK_DDD(DUK_DDDPRINT("ignore non-enumerable property: %!T", duk_get_tval(thr, -1))); - goto skip_key; - } - } else { - DUK_DDD(DUK_DDDPRINT("ignore non-existent property: %!T", duk_get_tval(thr, -1))); - goto skip_key; - } - } - if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) { - if (!(flags & DUK_ENUM_INCLUDE_SYMBOLS)) { - DUK_DDD(DUK_DDDPRINT("ignore symbol property: %!T", duk_get_tval(thr, -1))); - goto skip_key; - } - if (DUK_HSTRING_HAS_HIDDEN(h) && !(flags & DUK_ENUM_INCLUDE_HIDDEN)) { - DUK_DDD(DUK_DDDPRINT("ignore hidden symbol property: %!T", duk_get_tval(thr, -1))); - goto skip_key; - } - } else { - if (flags & DUK_ENUM_EXCLUDE_STRINGS) { - DUK_DDD(DUK_DDDPRINT("ignore string property: %!T", duk_get_tval(thr, -1))); - goto skip_key; - } - } - - /* [ obj trap_result res_arr propname ] */ - duk_push_uarridx(thr, idx++); - duk_insert(thr, -2); - duk_def_prop(thr, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_WEC); - continue; - - skip_key: - duk_pop(thr); - continue; - } - - /* XXX: Missing trap result validation for non-configurable target keys - * (must be present), for non-extensible target all target keys must be - * present and no extra keys can be present. - * http://www.ecma-international.org/ecma-262/6.0/#sec-proxy-object-internal-methods-and-internal-slots-ownpropertykeys - */ - - /* XXX: The key enumerability check should trigger the "getOwnPropertyDescriptor" - * trap which has not yet been implemented. In the absence of such a trap, - * the enumerability should be checked from the target object; this is - * handled above. - */ -} -#endif /* DUK_USE_ES6_PROXY */ - -#if defined(DUK_USE_ES6_PROXY) -DUK_INTERNAL duk_ret_t duk_bi_proxy_constructor(duk_hthread *thr) { - DUK_ASSERT_TOP(thr, 2); /* [ target handler ] */ - - duk_require_constructor_call(thr); - duk_push_proxy(thr, 0 /*flags*/); /* [ target handler ] -> [ proxy ] */ - return 1; /* replacement */ -} -#endif /* DUK_USE_ES6_PROXY */ -/* - * 'Reflect' built-in (ES2016 Section 26.1) - * http://www.ecma-international.org/ecma-262/7.0/#sec-reflect-object - * - * Many Reflect built-in functions are provided by shared helpers in - * duk_bi_object.c or duk_bi_function.c. - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_REFLECT_BUILTIN) -DUK_INTERNAL duk_ret_t duk_bi_reflect_object_delete_property(duk_hthread *thr) { - duk_tval *tv_obj; - duk_tval *tv_key; - duk_bool_t ret; - - DUK_ASSERT_TOP(thr, 2); - (void) duk_require_hobject(thr, 0); - (void) duk_to_string(thr, 1); - - /* [ target key ] */ - - DUK_ASSERT(thr != NULL); - tv_obj = DUK_GET_TVAL_POSIDX(thr, 0); - tv_key = DUK_GET_TVAL_POSIDX(thr, 1); - ret = duk_hobject_delprop(thr, tv_obj, tv_key, 0 /*throw_flag*/); - duk_push_boolean(thr, ret); - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_reflect_object_get(duk_hthread *thr) { - duk_tval *tv_obj; - duk_tval *tv_key; - duk_idx_t nargs; - - DUK_ASSERT(thr != NULL); - nargs = duk_get_top_require_min(thr, 2 /*min_top*/); - (void) duk_require_hobject(thr, 0); - (void) duk_to_string(thr, 1); - if (nargs >= 3 && !duk_strict_equals(thr, 0, 2)) { - /* XXX: [[Get]] receiver currently unsupported */ - DUK_ERROR_UNSUPPORTED(thr); - DUK_WO_NORETURN(return 0;); - } - - /* [ target key receiver? ...? ] */ - - tv_obj = DUK_GET_TVAL_POSIDX(thr, 0); - tv_key = DUK_GET_TVAL_POSIDX(thr, 1); - (void) duk_hobject_getprop(thr, tv_obj, tv_key); /* This could also be a duk_get_prop(). */ - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_reflect_object_has(duk_hthread *thr) { - duk_tval *tv_obj; - duk_tval *tv_key; - duk_bool_t ret; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT_TOP(thr, 2); - (void) duk_require_hobject(thr, 0); - (void) duk_to_string(thr, 1); - - /* [ target key ] */ - - tv_obj = DUK_GET_TVAL_POSIDX(thr, 0); - tv_key = DUK_GET_TVAL_POSIDX(thr, 1); - ret = duk_hobject_hasprop(thr, tv_obj, tv_key); - duk_push_boolean(thr, ret); - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_reflect_object_set(duk_hthread *thr) { - duk_tval *tv_obj; - duk_tval *tv_key; - duk_tval *tv_val; - duk_idx_t nargs; - duk_bool_t ret; - - DUK_ASSERT(thr != NULL); - nargs = duk_get_top_require_min(thr, 3 /*min_top*/); - (void) duk_require_hobject(thr, 0); - (void) duk_to_string(thr, 1); - if (nargs >= 4 && !duk_strict_equals(thr, 0, 3)) { - /* XXX: [[Set]] receiver currently unsupported */ - DUK_ERROR_UNSUPPORTED(thr); - DUK_WO_NORETURN(return 0;); - } - - /* [ target key value receiver? ...? ] */ - - tv_obj = DUK_GET_TVAL_POSIDX(thr, 0); - tv_key = DUK_GET_TVAL_POSIDX(thr, 1); - tv_val = DUK_GET_TVAL_POSIDX(thr, 2); - ret = duk_hobject_putprop(thr, tv_obj, tv_key, tv_val, 0 /*throw_flag*/); - duk_push_boolean(thr, ret); - return 1; -} -#endif /* DUK_USE_REFLECT_BUILTIN */ -/* - * RegExp built-ins - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_REGEXP_SUPPORT) - -DUK_LOCAL void duk__get_this_regexp(duk_hthread *thr) { - duk_hobject *h; - - duk_push_this(thr); - h = duk_require_hobject_with_class(thr, -1, DUK_HOBJECT_CLASS_REGEXP); - DUK_ASSERT(h != NULL); - DUK_UNREF(h); - duk_insert(thr, 0); /* prepend regexp to valstack 0 index */ -} - -/* XXX: much to improve (code size) */ -DUK_INTERNAL duk_ret_t duk_bi_regexp_constructor(duk_hthread *thr) { - duk_hobject *h_pattern; - - DUK_ASSERT_TOP(thr, 2); - h_pattern = duk_get_hobject(thr, 0); - - if (!duk_is_constructor_call(thr) && h_pattern != NULL && - DUK_HOBJECT_GET_CLASS_NUMBER(h_pattern) == DUK_HOBJECT_CLASS_REGEXP && duk_is_undefined(thr, 1)) { - /* Called as a function, pattern has [[Class]] "RegExp" and - * flags is undefined -> return object as is. - */ - /* XXX: ES2015 has a NewTarget SameValue() check which is not - * yet implemented. - */ - duk_dup_0(thr); - return 1; - } - - /* Else functionality is identical for function call and constructor - * call. - */ - - if (h_pattern != NULL && DUK_HOBJECT_GET_CLASS_NUMBER(h_pattern) == DUK_HOBJECT_CLASS_REGEXP) { - duk_get_prop_stridx_short(thr, 0, DUK_STRIDX_SOURCE); - if (duk_is_undefined(thr, 1)) { - /* In ES5 one would need to read the flags individually; - * in ES2015 just read .flags. - */ - duk_get_prop_stridx(thr, 0, DUK_STRIDX_FLAGS); - } else { - /* In ES2015 allowed; overrides argument RegExp flags. */ - duk_dup_1(thr); - } - } else { - if (duk_is_undefined(thr, 0)) { - duk_push_hstring_empty(thr); - } else { - duk_dup_0(thr); - duk_to_string(thr, -1); /* Rejects Symbols. */ - } - if (duk_is_undefined(thr, 1)) { - duk_push_hstring_empty(thr); - } else { - duk_dup_1(thr); - duk_to_string(thr, -1); /* Rejects Symbols. */ - } - - /* [ ... pattern flags ] */ - } - - DUK_DDD(DUK_DDDPRINT("RegExp constructor/function call, pattern=%!T, flags=%!T", - (duk_tval *) duk_get_tval(thr, -2), - (duk_tval *) duk_get_tval(thr, -1))); - - /* [ ... pattern flags ] (both uncoerced) */ - - duk_to_string(thr, -2); - duk_to_string(thr, -1); - duk_regexp_compile(thr); - - /* [ ... bytecode escaped_source ] */ - - duk_regexp_create_instance(thr); - - /* [ ... RegExp ] */ - - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_exec(duk_hthread *thr) { - duk__get_this_regexp(thr); - - /* [ regexp input ] */ - - duk_regexp_match(thr); - - /* [ result ] */ - - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_test(duk_hthread *thr) { - duk__get_this_regexp(thr); - - /* [ regexp input ] */ - - /* result object is created and discarded; wasteful but saves code space */ - duk_regexp_match(thr); - - /* [ result ] */ - - duk_push_boolean(thr, (duk_is_null(thr, -1) ? 0 : 1)); - - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_tostring(duk_hthread *thr) { - /* This must be generic in ES2015 and later. */ - DUK_ASSERT_TOP(thr, 0); - duk_push_this(thr); - duk_push_literal(thr, "/"); - duk_get_prop_stridx(thr, 0, DUK_STRIDX_SOURCE); - duk_dup_m2(thr); /* another "/" */ - duk_get_prop_stridx(thr, 0, DUK_STRIDX_FLAGS); - duk_concat(thr, 4); - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_flags(duk_hthread *thr) { - /* .flags is ES2015 but present even when ES2015 bindings are - * disabled because the constructor relies on it. - */ - duk_uint8_t buf[8]; /* enough for all flags + NUL */ - duk_uint8_t *p = buf; - - /* .flags is generic and works on any object. */ - duk_push_this(thr); - (void) duk_require_hobject(thr, -1); - if (duk_get_prop_stridx_boolean(thr, 0, DUK_STRIDX_GLOBAL, NULL)) { - *p++ = DUK_ASC_LC_G; - } - if (duk_get_prop_stridx_boolean(thr, 0, DUK_STRIDX_IGNORE_CASE, NULL)) { - *p++ = DUK_ASC_LC_I; - } - if (duk_get_prop_stridx_boolean(thr, 0, DUK_STRIDX_MULTILINE, NULL)) { - *p++ = DUK_ASC_LC_M; - } - /* .unicode: to be added */ - /* .sticky: to be added */ - *p++ = DUK_ASC_NUL; - DUK_ASSERT((duk_size_t) (p - buf) <= sizeof(buf)); - - duk_push_string(thr, (const char *) buf); - return 1; -} - -/* Shared helper for providing .source, .global, .multiline, etc getters. */ -DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_shared_getter(duk_hthread *thr) { - duk_hstring *h_bc; - duk_small_uint_t re_flags; - duk_hobject *h; - duk_int_t magic; - - DUK_ASSERT_TOP(thr, 0); - - duk_push_this(thr); - h = duk_require_hobject(thr, -1); - magic = duk_get_current_magic(thr); - - if (DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_REGEXP) { - duk_xget_owndataprop_stridx_short(thr, 0, DUK_STRIDX_INT_SOURCE); - duk_xget_owndataprop_stridx_short(thr, 0, DUK_STRIDX_INT_BYTECODE); - h_bc = duk_require_hstring(thr, -1); - re_flags = (duk_small_uint_t) DUK_HSTRING_GET_DATA(h_bc)[0]; /* Safe even if h_bc length is 0 (= NUL) */ - duk_pop(thr); - } else if (h == thr->builtins[DUK_BIDX_REGEXP_PROTOTYPE]) { - /* In ES2015 and ES2016 a TypeError would be thrown here. - * However, this had real world issues so ES2017 draft - * allows RegExp.prototype specifically, returning '(?:)' - * for .source and undefined for all flags. - */ - if (magic != 16 /* .source */) { - return 0; - } - duk_push_literal(thr, "(?:)"); /* .source handled by switch-case */ - re_flags = 0; - } else { - DUK_DCERROR_TYPE_INVALID_ARGS(thr); - } - - /* [ regexp source ] */ - - switch (magic) { - case 0: { /* global */ - duk_push_boolean(thr, (re_flags & DUK_RE_FLAG_GLOBAL)); - break; - } - case 1: { /* ignoreCase */ - duk_push_boolean(thr, (re_flags & DUK_RE_FLAG_IGNORE_CASE)); - break; - } - case 2: { /* multiline */ - duk_push_boolean(thr, (re_flags & DUK_RE_FLAG_MULTILINE)); - break; - } -#if 0 - /* Don't provide until implemented to avoid interfering with feature - * detection in user code. - */ - case 3: /* sticky */ - case 4: { /* unicode */ - duk_push_false(thr); - break; - } -#endif - default: { /* source */ - /* leave 'source' on top */ - break; - } - } - - return 1; -} - -#endif /* DUK_USE_REGEXP_SUPPORT */ -/* - * String built-ins - * - * Most String built-ins must only accept strings (or String objects). - * Symbols, represented internally as strings, must be generally rejected. - * The duk_push_this_coercible_to_string() helper does this automatically. - */ - -/* XXX: There are several limitations in the current implementation for - * strings with >= 0x80000000UL characters. In some cases one would need - * to be able to represent the range [-0xffffffff,0xffffffff] and so on. - * Generally character and byte length are assumed to fit into signed 32 - * bits (< 0x80000000UL). Places with issues are not marked explicitly - * below in all cases, look for signed type usage (duk_int_t etc) for - * offsets/lengths. - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_STRING_BUILTIN) - -/* - * Helpers - */ - -DUK_LOCAL duk_hstring *duk__str_tostring_notregexp(duk_hthread *thr, duk_idx_t idx) { - duk_hstring *h; - - if (duk_get_class_number(thr, idx) == DUK_HOBJECT_CLASS_REGEXP) { - DUK_ERROR_TYPE_INVALID_ARGS(thr); - DUK_WO_NORETURN(return NULL;); - } - h = duk_to_hstring(thr, idx); - DUK_ASSERT(h != NULL); - - return h; -} - -DUK_LOCAL duk_int_t -duk__str_search_shared(duk_hthread *thr, duk_hstring *h_this, duk_hstring *h_search, duk_int_t start_cpos, duk_bool_t backwards) { - duk_int_t cpos; - duk_int_t bpos; - const duk_uint8_t *p_start, *p_end, *p; - const duk_uint8_t *q_start; - duk_int_t q_blen; - duk_uint8_t firstbyte; - duk_uint8_t t; - - cpos = start_cpos; - - /* Empty searchstring always matches; cpos must be clamped here. - * (If q_blen were < 0 due to clamped coercion, it would also be - * caught here.) - */ - q_start = DUK_HSTRING_GET_DATA(h_search); - q_blen = (duk_int_t) DUK_HSTRING_GET_BYTELEN(h_search); - if (q_blen <= 0) { - return cpos; - } - DUK_ASSERT(q_blen > 0); - - bpos = (duk_int_t) duk_heap_strcache_offset_char2byte(thr, h_this, (duk_uint32_t) cpos); - - p_start = DUK_HSTRING_GET_DATA(h_this); - p_end = p_start + DUK_HSTRING_GET_BYTELEN(h_this); - p = p_start + bpos; - - /* This loop is optimized for size. For speed, there should be - * two separate loops, and we should ensure that memcmp() can be - * used without an extra "will searchstring fit" check. Doing - * the preconditioning for 'p' and 'p_end' is easy but cpos - * must be updated if 'p' is wound back (backward scanning). - */ - - firstbyte = q_start[0]; /* leading byte of match string */ - while (p <= p_end && p >= p_start) { - t = *p; - - /* For ECMAScript strings, this check can only match for - * initial UTF-8 bytes (not continuation bytes). For other - * strings all bets are off. - */ - - if ((t == firstbyte) && ((duk_size_t) (p_end - p) >= (duk_size_t) q_blen)) { - DUK_ASSERT(q_blen > 0); - if (duk_memcmp((const void *) p, (const void *) q_start, (size_t) q_blen) == 0) { - return cpos; - } - } - - /* track cpos while scanning */ - if (backwards) { - /* when going backwards, we decrement cpos 'early'; - * 'p' may point to a continuation byte of the char - * at offset 'cpos', but that's OK because we'll - * backtrack all the way to the initial byte. - */ - if ((t & 0xc0) != 0x80) { - cpos--; - } - p--; - } else { - if ((t & 0xc0) != 0x80) { - cpos++; - } - p++; - } - } - - /* Not found. Empty string case is handled specially above. */ - return -1; -} - -/* - * Constructor - */ - -DUK_INTERNAL duk_ret_t duk_bi_string_constructor(duk_hthread *thr) { - duk_hstring *h; - duk_uint_t flags; - - /* String constructor needs to distinguish between an argument not given at all - * vs. given as 'undefined'. We're a vararg function to handle this properly. - */ - - /* XXX: copy current activation flags to thr, including current magic, - * is_constructor_call etc. This takes a few bytes in duk_hthread but - * makes call sites smaller (there are >30 is_constructor_call and get - * current magic call sites. - */ - - if (duk_get_top(thr) == 0) { - duk_push_hstring_empty(thr); - } else { - h = duk_to_hstring_acceptsymbol(thr, 0); - if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h) && !duk_is_constructor_call(thr))) { - duk_push_symbol_descriptive_string(thr, h); - duk_replace(thr, 0); - } - } - duk_to_string(thr, 0); /* catches symbol argument for constructor call */ - DUK_ASSERT(duk_is_string(thr, 0)); - duk_set_top(thr, 1); /* Top may be 1 or larger. */ - - if (duk_is_constructor_call(thr)) { - /* String object internal value is immutable */ - flags = DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_STRING); - duk_push_object_helper(thr, flags, DUK_BIDX_STRING_PROTOTYPE); - duk_dup_0(thr); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE); - } - /* Note: unbalanced stack on purpose */ - - return 1; -} - -DUK_LOCAL duk_ret_t duk__construct_from_codepoints(duk_hthread *thr, duk_bool_t nonbmp) { - duk_bufwriter_ctx bw_alloc; - duk_bufwriter_ctx *bw; - duk_idx_t i, n; - duk_ucodepoint_t cp; - - /* XXX: It would be nice to build the string directly but ToUint16() - * coercion is needed so a generic helper would not be very - * helpful (perhaps coerce the value stack first here and then - * build a string from a duk_tval number sequence in one go?). - */ - - n = duk_get_top(thr); - - bw = &bw_alloc; - DUK_BW_INIT_PUSHBUF(thr, bw, (duk_size_t) n); /* initial estimate for ASCII only codepoints */ - - for (i = 0; i < n; i++) { - /* XXX: could improve bufwriter handling to write multiple codepoints - * with one ensure call but the relative benefit would be quite small. - */ - - if (nonbmp) { - /* ES2015 requires that (1) SameValue(cp, ToInteger(cp)) and - * (2) cp >= 0 and cp <= 0x10ffff. This check does not - * implement the steps exactly but the outcome should be - * the same. - */ - duk_int32_t i32 = 0; - if (!duk_is_whole_get_int32(duk_to_number(thr, i), &i32) || i32 < 0 || i32 > 0x10ffffL) { - DUK_DCERROR_RANGE_INVALID_ARGS(thr); - } - DUK_ASSERT(i32 >= 0 && i32 <= 0x10ffffL); - cp = (duk_ucodepoint_t) i32; - DUK_BW_WRITE_ENSURE_CESU8(thr, bw, cp); - } else { -#if defined(DUK_USE_NONSTD_STRING_FROMCHARCODE_32BIT) - /* ToUint16() coercion is mandatory in the E5.1 specification, but - * this non-compliant behavior makes more sense because we support - * non-BMP codepoints. Don't use CESU-8 because that'd create - * surrogate pairs. - */ - cp = (duk_ucodepoint_t) duk_to_uint32(thr, i); - DUK_BW_WRITE_ENSURE_XUTF8(thr, bw, cp); -#else - cp = (duk_ucodepoint_t) duk_to_uint16(thr, i); - DUK_ASSERT(cp >= 0 && cp <= 0x10ffffL); - DUK_BW_WRITE_ENSURE_CESU8(thr, bw, cp); -#endif - } - } - - DUK_BW_COMPACT(thr, bw); - (void) duk_buffer_to_string(thr, -1); /* Safe, extended UTF-8 or CESU-8 encoded. */ - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_string_constructor_from_char_code(duk_hthread *thr) { - return duk__construct_from_codepoints(thr, 0 /*nonbmp*/); -} - -#if defined(DUK_USE_ES6) -DUK_INTERNAL duk_ret_t duk_bi_string_constructor_from_code_point(duk_hthread *thr) { - return duk__construct_from_codepoints(thr, 1 /*nonbmp*/); -} -#endif - -/* - * toString(), valueOf() - */ - -DUK_INTERNAL duk_ret_t duk_bi_string_prototype_to_string(duk_hthread *thr) { - duk_tval *tv; - - duk_push_this(thr); - tv = duk_require_tval(thr, -1); - DUK_ASSERT(tv != NULL); - - if (DUK_TVAL_IS_STRING(tv)) { - /* return as is */ - } else if (DUK_TVAL_IS_OBJECT(tv)) { - duk_hobject *h = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - - /* Must be a "string object", i.e. class "String" */ - if (DUK_HOBJECT_GET_CLASS_NUMBER(h) != DUK_HOBJECT_CLASS_STRING) { - goto type_error; - } - - duk_xget_owndataprop_stridx_short(thr, -1, DUK_STRIDX_INT_VALUE); - DUK_ASSERT(duk_is_string(thr, -1)); - } else { - goto type_error; - } - - (void) duk_require_hstring_notsymbol(thr, -1); /* Reject symbols (and wrapped symbols). */ - return 1; - -type_error: - DUK_DCERROR_TYPE_INVALID_ARGS(thr); -} - -/* - * Character and charcode access - */ - -DUK_INTERNAL duk_ret_t duk_bi_string_prototype_char_at(duk_hthread *thr) { - duk_hstring *h; - duk_int_t pos; - - /* XXX: faster implementation */ - - h = duk_push_this_coercible_to_string(thr); - DUK_ASSERT(h != NULL); - - pos = duk_to_int(thr, 0); - - if (sizeof(duk_size_t) >= sizeof(duk_uint_t)) { - /* Cast to duk_size_t works in this case: - * - If pos < 0, (duk_size_t) pos will always be - * >= max_charlen, and result will be the empty string - * (see duk_substring()). - * - If pos >= 0, pos + 1 cannot wrap. - */ - DUK_ASSERT((duk_size_t) DUK_INT_MIN >= DUK_HSTRING_MAX_BYTELEN); - DUK_ASSERT((duk_size_t) DUK_INT_MAX + 1U > (duk_size_t) DUK_INT_MAX); - duk_substring(thr, -1, (duk_size_t) pos, (duk_size_t) pos + 1U); - } else { - /* If size_t is smaller than int, explicit bounds checks - * are needed because an int may wrap multiple times. - */ - if (DUK_UNLIKELY(pos < 0 || (duk_uint_t) pos >= (duk_uint_t) DUK_HSTRING_GET_CHARLEN(h))) { - duk_push_hstring_empty(thr); - } else { - duk_substring(thr, -1, (duk_size_t) pos, (duk_size_t) pos + 1U); - } - } - - return 1; -} - -/* Magic: 0=charCodeAt, 1=codePointAt */ -DUK_INTERNAL duk_ret_t duk_bi_string_prototype_char_code_at(duk_hthread *thr) { - duk_int_t pos; - duk_hstring *h; - duk_bool_t clamped; - duk_uint32_t cp; - duk_int_t magic; - - /* XXX: faster implementation */ - - DUK_DDD(DUK_DDDPRINT("arg=%!T", (duk_tval *) duk_get_tval(thr, 0))); - - h = duk_push_this_coercible_to_string(thr); - DUK_ASSERT(h != NULL); - - pos = duk_to_int_clamped_raw(thr, - 0 /*index*/, - 0 /*min(incl)*/, - (duk_int_t) DUK_HSTRING_GET_CHARLEN(h) - 1 /*max(incl)*/, - &clamped /*out_clamped*/); -#if defined(DUK_USE_ES6) - magic = duk_get_current_magic(thr); -#else - DUK_ASSERT(duk_get_current_magic(thr) == 0); - magic = 0; -#endif - if (clamped) { - /* For out-of-bounds indices .charCodeAt() returns NaN and - * .codePointAt() returns undefined. - */ - if (magic != 0) { - return 0; - } - duk_push_nan(thr); - } else { - DUK_ASSERT(pos >= 0); - cp = (duk_uint32_t) duk_hstring_char_code_at_raw(thr, h, (duk_uint_t) pos, (duk_bool_t) magic /*surrogate_aware*/); - duk_push_u32(thr, cp); - } - return 1; -} - -/* - * substring(), substr(), slice() - */ - -/* XXX: any chance of merging these three similar but still slightly - * different algorithms so that footprint would be reduced? - */ - -DUK_INTERNAL duk_ret_t duk_bi_string_prototype_substring(duk_hthread *thr) { - duk_hstring *h; - duk_int_t start_pos, end_pos; - duk_int_t len; - - h = duk_push_this_coercible_to_string(thr); - DUK_ASSERT(h != NULL); - len = (duk_int_t) DUK_HSTRING_GET_CHARLEN(h); - - /* [ start end str ] */ - - start_pos = duk_to_int_clamped(thr, 0, 0, len); - if (duk_is_undefined(thr, 1)) { - end_pos = len; - } else { - end_pos = duk_to_int_clamped(thr, 1, 0, len); - } - DUK_ASSERT(start_pos >= 0 && start_pos <= len); - DUK_ASSERT(end_pos >= 0 && end_pos <= len); - - if (start_pos > end_pos) { - duk_int_t tmp = start_pos; - start_pos = end_pos; - end_pos = tmp; - } - - DUK_ASSERT(end_pos >= start_pos); - - duk_substring(thr, -1, (duk_size_t) start_pos, (duk_size_t) end_pos); - return 1; -} - -#if defined(DUK_USE_SECTION_B) -DUK_INTERNAL duk_ret_t duk_bi_string_prototype_substr(duk_hthread *thr) { - duk_hstring *h; - duk_int_t start_pos, end_pos; - duk_int_t len; - - /* Unlike non-obsolete String calls, substr() algorithm in E5.1 - * specification will happily coerce undefined and null to strings - * ("undefined" and "null"). - */ - duk_push_this(thr); - h = duk_to_hstring_m1(thr); /* Reject Symbols. */ - DUK_ASSERT(h != NULL); - len = (duk_int_t) DUK_HSTRING_GET_CHARLEN(h); - - /* [ start length str ] */ - - /* The implementation for computing of start_pos and end_pos differs - * from the standard algorithm, but is intended to result in the exactly - * same behavior. This is not always obvious. - */ - - /* combines steps 2 and 5; -len ensures max() not needed for step 5 */ - start_pos = duk_to_int_clamped(thr, 0, -len, len); - if (start_pos < 0) { - start_pos = len + start_pos; - } - DUK_ASSERT(start_pos >= 0 && start_pos <= len); - - /* combines steps 3, 6; step 7 is not needed */ - if (duk_is_undefined(thr, 1)) { - end_pos = len; - } else { - DUK_ASSERT(start_pos <= len); - end_pos = start_pos + duk_to_int_clamped(thr, 1, 0, len - start_pos); - } - DUK_ASSERT(start_pos >= 0 && start_pos <= len); - DUK_ASSERT(end_pos >= 0 && end_pos <= len); - DUK_ASSERT(end_pos >= start_pos); - - duk_substring(thr, -1, (duk_size_t) start_pos, (duk_size_t) end_pos); - return 1; -} -#endif /* DUK_USE_SECTION_B */ - -DUK_INTERNAL duk_ret_t duk_bi_string_prototype_slice(duk_hthread *thr) { - duk_hstring *h; - duk_int_t start_pos, end_pos; - duk_int_t len; - - h = duk_push_this_coercible_to_string(thr); - DUK_ASSERT(h != NULL); - len = (duk_int_t) DUK_HSTRING_GET_CHARLEN(h); - - /* [ start end str ] */ - - start_pos = duk_to_int_clamped(thr, 0, -len, len); - if (start_pos < 0) { - start_pos = len + start_pos; - } - if (duk_is_undefined(thr, 1)) { - end_pos = len; - } else { - end_pos = duk_to_int_clamped(thr, 1, -len, len); - if (end_pos < 0) { - end_pos = len + end_pos; - } - } - DUK_ASSERT(start_pos >= 0 && start_pos <= len); - DUK_ASSERT(end_pos >= 0 && end_pos <= len); - - if (end_pos < start_pos) { - end_pos = start_pos; - } - - DUK_ASSERT(end_pos >= start_pos); - - duk_substring(thr, -1, (duk_size_t) start_pos, (duk_size_t) end_pos); - return 1; -} - -/* - * Case conversion - */ - -DUK_INTERNAL duk_ret_t duk_bi_string_prototype_caseconv_shared(duk_hthread *thr) { - duk_small_int_t uppercase = duk_get_current_magic(thr); - - (void) duk_push_this_coercible_to_string(thr); - duk_unicode_case_convert_string(thr, (duk_bool_t) uppercase); - return 1; -} - -/* - * indexOf() and lastIndexOf() - */ - -DUK_INTERNAL duk_ret_t duk_bi_string_prototype_indexof_shared(duk_hthread *thr) { - duk_hstring *h_this; - duk_hstring *h_search; - duk_int_t clen_this; - duk_int_t cpos; - duk_small_uint_t is_lastindexof = (duk_small_uint_t) duk_get_current_magic(thr); /* 0=indexOf, 1=lastIndexOf */ - - h_this = duk_push_this_coercible_to_string(thr); - DUK_ASSERT(h_this != NULL); - clen_this = (duk_int_t) DUK_HSTRING_GET_CHARLEN(h_this); - - h_search = duk_to_hstring(thr, 0); - DUK_ASSERT(h_search != NULL); - - duk_to_number(thr, 1); - if (duk_is_nan(thr, 1) && is_lastindexof) { - /* indexOf: NaN should cause pos to be zero. - * lastIndexOf: NaN should cause pos to be +Infinity - * (and later be clamped to len). - */ - cpos = clen_this; - } else { - cpos = duk_to_int_clamped(thr, 1, 0, clen_this); - } - - cpos = duk__str_search_shared(thr, h_this, h_search, cpos, is_lastindexof /*backwards*/); - duk_push_int(thr, cpos); - return 1; -} - -/* - * replace() - */ - -/* XXX: the current implementation works but is quite clunky; it compiles - * to almost 1,4kB of x86 code so it needs to be simplified (better approach, - * shared helpers, etc). Some ideas for refactoring: - * - * - a primitive to convert a string into a regexp matcher (reduces matching - * code at the cost of making matching much slower) - * - use replace() as a basic helper for match() and split(), which are both - * much simpler - * - API call to get_prop and to_boolean - */ - -DUK_INTERNAL duk_ret_t duk_bi_string_prototype_replace(duk_hthread *thr) { - duk_hstring *h_input; - duk_hstring *h_match; - duk_hstring *h_search; - duk_hobject *h_re; - duk_bufwriter_ctx bw_alloc; - duk_bufwriter_ctx *bw; -#if defined(DUK_USE_REGEXP_SUPPORT) - duk_bool_t is_regexp; - duk_bool_t is_global; -#endif - duk_bool_t is_repl_func; - duk_uint32_t match_start_coff, match_start_boff; -#if defined(DUK_USE_REGEXP_SUPPORT) - duk_int_t match_caps; -#endif - duk_uint32_t prev_match_end_boff; - const duk_uint8_t *r_start, *r_end, *r; /* repl string scan */ - duk_size_t tmp_sz; - - DUK_ASSERT_TOP(thr, 2); - h_input = duk_push_this_coercible_to_string(thr); - DUK_ASSERT(h_input != NULL); - - bw = &bw_alloc; - DUK_BW_INIT_PUSHBUF(thr, bw, DUK_HSTRING_GET_BYTELEN(h_input)); /* input size is good output starting point */ - - DUK_ASSERT_TOP(thr, 4); - - /* stack[0] = search value - * stack[1] = replace value - * stack[2] = input string - * stack[3] = result buffer - */ - - h_re = duk_get_hobject_with_class(thr, 0, DUK_HOBJECT_CLASS_REGEXP); - if (h_re) { -#if defined(DUK_USE_REGEXP_SUPPORT) - is_regexp = 1; - is_global = duk_get_prop_stridx_boolean(thr, 0, DUK_STRIDX_GLOBAL, NULL); - - if (is_global) { - /* start match from beginning */ - duk_push_int(thr, 0); - duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LAST_INDEX); - } -#else /* DUK_USE_REGEXP_SUPPORT */ - DUK_DCERROR_UNSUPPORTED(thr); -#endif /* DUK_USE_REGEXP_SUPPORT */ - } else { - duk_to_string(thr, 0); /* rejects symbols */ -#if defined(DUK_USE_REGEXP_SUPPORT) - is_regexp = 0; - is_global = 0; -#endif - } - - if (duk_is_function(thr, 1)) { - is_repl_func = 1; - r_start = NULL; - r_end = NULL; - } else { - duk_hstring *h_repl; - - is_repl_func = 0; - h_repl = duk_to_hstring(thr, 1); /* reject symbols */ - DUK_ASSERT(h_repl != NULL); - r_start = DUK_HSTRING_GET_DATA(h_repl); - r_end = r_start + DUK_HSTRING_GET_BYTELEN(h_repl); - } - - prev_match_end_boff = 0; - - for (;;) { - /* - * If matching with a regexp: - * - non-global RegExp: lastIndex not touched on a match, zeroed - * on a non-match - * - global RegExp: on match, lastIndex will be updated by regexp - * executor to point to next char after the matching part (so that - * characters in the matching part are not matched again) - * - * If matching with a string: - * - always non-global match, find first occurrence - * - * We need: - * - The character offset of start-of-match for the replacer function - * - The byte offsets for start-of-match and end-of-match to implement - * the replacement values $&, $`, and $', and to copy non-matching - * input string portions (including header and trailer) verbatim. - * - * NOTE: the E5.1 specification is a bit vague how the RegExp should - * behave in the replacement process; e.g. is matching done first for - * all matches (in the global RegExp case) before any replacer calls - * are made? See: test-bi-string-proto-replace.js for discussion. - */ - - DUK_ASSERT_TOP(thr, 4); - -#if defined(DUK_USE_REGEXP_SUPPORT) - if (is_regexp) { - duk_dup_0(thr); - duk_dup_2(thr); - duk_regexp_match(thr); /* [ ... regexp input ] -> [ res_obj ] */ - if (!duk_is_object(thr, -1)) { - duk_pop(thr); - break; - } - - duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_INDEX); - DUK_ASSERT(duk_is_number(thr, -1)); - match_start_coff = duk_get_uint(thr, -1); - duk_pop(thr); - - duk_get_prop_index(thr, -1, 0); - DUK_ASSERT(duk_is_string(thr, -1)); - h_match = duk_known_hstring(thr, -1); - duk_pop(thr); /* h_match is borrowed, remains reachable through match_obj */ - - if (DUK_HSTRING_GET_BYTELEN(h_match) == 0) { - /* This should be equivalent to match() algorithm step 8.f.iii.2: - * detect an empty match and allow it, but don't allow it twice. - */ - duk_uint32_t last_index; - - duk_get_prop_stridx_short(thr, 0, DUK_STRIDX_LAST_INDEX); - last_index = (duk_uint32_t) duk_get_uint(thr, -1); - DUK_DDD(DUK_DDDPRINT("empty match, bump lastIndex: %ld -> %ld", - (long) last_index, - (long) (last_index + 1))); - duk_pop(thr); - duk_push_uint(thr, (duk_uint_t) (last_index + 1)); - duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LAST_INDEX); - } - - DUK_ASSERT(duk_get_length(thr, -1) <= DUK_INT_MAX); /* string limits */ - match_caps = (duk_int_t) duk_get_length(thr, -1); - } else { -#else /* DUK_USE_REGEXP_SUPPORT */ - { /* unconditionally */ -#endif /* DUK_USE_REGEXP_SUPPORT */ - const duk_uint8_t *p_start, *p_end, *p; /* input string scan */ - const duk_uint8_t *q_start; /* match string */ - duk_size_t p_blen; - duk_size_t q_blen; - -#if defined(DUK_USE_REGEXP_SUPPORT) - DUK_ASSERT(!is_global); /* single match always */ -#endif - - p_start = DUK_HSTRING_GET_DATA(h_input); - p_end = p_start + DUK_HSTRING_GET_BYTELEN(h_input); - p_blen = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h_input); - p = p_start; - - h_search = duk_known_hstring(thr, 0); - q_start = DUK_HSTRING_GET_DATA(h_search); - q_blen = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h_search); - - if (q_blen > p_blen) { - break; /* no match */ - } - - p_end -= q_blen; /* ensure full memcmp() fits in while */ - DUK_ASSERT(p_end >= p); - - match_start_coff = 0; - - while (p <= p_end) { - DUK_ASSERT(p + q_blen <= DUK_HSTRING_GET_DATA(h_input) + DUK_HSTRING_GET_BYTELEN(h_input)); - if (duk_memcmp((const void *) p, (const void *) q_start, (size_t) q_blen) == 0) { - duk_dup_0(thr); - h_match = duk_known_hstring(thr, -1); -#if defined(DUK_USE_REGEXP_SUPPORT) - match_caps = 0; -#endif - goto found; - } - - /* track utf-8 non-continuation bytes */ - if ((p[0] & 0xc0) != 0x80) { - match_start_coff++; - } - p++; - } - - /* not found */ - break; - } - found: - - /* stack[0] = search value - * stack[1] = replace value - * stack[2] = input string - * stack[3] = result buffer - * stack[4] = regexp match OR match string - */ - - match_start_boff = (duk_uint32_t) duk_heap_strcache_offset_char2byte(thr, h_input, match_start_coff); - - tmp_sz = (duk_size_t) (match_start_boff - prev_match_end_boff); - DUK_BW_WRITE_ENSURE_BYTES(thr, bw, DUK_HSTRING_GET_DATA(h_input) + prev_match_end_boff, tmp_sz); - - prev_match_end_boff = match_start_boff + DUK_HSTRING_GET_BYTELEN(h_match); - - if (is_repl_func) { - duk_idx_t idx_args; - duk_hstring *h_repl; - - /* regexp res_obj is at index 4 */ - - duk_dup_1(thr); - idx_args = duk_get_top(thr); - -#if defined(DUK_USE_REGEXP_SUPPORT) - if (is_regexp) { - duk_int_t idx; - duk_require_stack(thr, match_caps + 2); - for (idx = 0; idx < match_caps; idx++) { - /* match followed by capture(s) */ - duk_get_prop_index(thr, 4, (duk_uarridx_t) idx); - } - } else { -#else /* DUK_USE_REGEXP_SUPPORT */ - { /* unconditionally */ -#endif /* DUK_USE_REGEXP_SUPPORT */ - /* match == search string, by definition */ - duk_dup_0(thr); - } - duk_push_uint(thr, (duk_uint_t) match_start_coff); - duk_dup_2(thr); - - /* [ ... replacer match [captures] match_char_offset input ] */ - - duk_call(thr, duk_get_top(thr) - idx_args); - h_repl = duk_to_hstring_m1(thr); /* -> [ ... repl_value ] */ - DUK_ASSERT(h_repl != NULL); - - DUK_BW_WRITE_ENSURE_HSTRING(thr, bw, h_repl); - - duk_pop(thr); /* repl_value */ - } else { - r = r_start; - - while (r < r_end) { - duk_int_t ch1; - duk_int_t ch2; -#if defined(DUK_USE_REGEXP_SUPPORT) - duk_int_t ch3; -#endif - duk_size_t left; - - ch1 = *r++; - if (ch1 != DUK_ASC_DOLLAR) { - goto repl_write; - } - DUK_ASSERT(r <= r_end); - left = (duk_size_t) (r_end - r); - - if (left <= 0) { - goto repl_write; - } - - ch2 = r[0]; - switch (ch2) { - case DUK_ASC_DOLLAR: { - ch1 = (1 << 8) + DUK_ASC_DOLLAR; - goto repl_write; - } - case DUK_ASC_AMP: { - DUK_BW_WRITE_ENSURE_HSTRING(thr, bw, h_match); - r++; - continue; - } - case DUK_ASC_GRAVE: { - tmp_sz = (duk_size_t) match_start_boff; - DUK_BW_WRITE_ENSURE_BYTES(thr, bw, DUK_HSTRING_GET_DATA(h_input), tmp_sz); - r++; - continue; - } - case DUK_ASC_SINGLEQUOTE: { - duk_uint32_t match_end_boff; - - /* Use match charlen instead of bytelen, just in case the input and - * match codepoint encodings would have different lengths. - */ - /* XXX: charlen computed here, and also in char2byte helper. */ - match_end_boff = (duk_uint32_t) duk_heap_strcache_offset_char2byte( - thr, - h_input, - match_start_coff + (duk_uint_fast32_t) DUK_HSTRING_GET_CHARLEN(h_match)); - - tmp_sz = (duk_size_t) (DUK_HSTRING_GET_BYTELEN(h_input) - match_end_boff); - DUK_BW_WRITE_ENSURE_BYTES(thr, bw, DUK_HSTRING_GET_DATA(h_input) + match_end_boff, tmp_sz); - r++; - continue; - } - default: { -#if defined(DUK_USE_REGEXP_SUPPORT) - duk_int_t capnum, captmp, capadv; - /* XXX: optional check, match_caps is zero if no regexp, - * so dollar will be interpreted literally anyway. - */ - - if (!is_regexp) { - goto repl_write; - } - - if (!(ch2 >= DUK_ASC_0 && ch2 <= DUK_ASC_9)) { - goto repl_write; - } - capnum = ch2 - DUK_ASC_0; - capadv = 1; - - if (left >= 2) { - ch3 = r[1]; - if (ch3 >= DUK_ASC_0 && ch3 <= DUK_ASC_9) { - captmp = capnum * 10 + (ch3 - DUK_ASC_0); - if (captmp < match_caps) { - capnum = captmp; - capadv = 2; - } - } - } - - if (capnum > 0 && capnum < match_caps) { - DUK_ASSERT(is_regexp != 0); /* match_caps == 0 without regexps */ - - /* regexp res_obj is at offset 4 */ - duk_get_prop_index(thr, 4, (duk_uarridx_t) capnum); - if (duk_is_string(thr, -1)) { - duk_hstring *h_tmp_str; - - h_tmp_str = duk_known_hstring(thr, -1); - - DUK_BW_WRITE_ENSURE_HSTRING(thr, bw, h_tmp_str); - } else { - /* undefined -> skip (replaced with empty) */ - } - duk_pop(thr); - r += capadv; - continue; - } else { - goto repl_write; - } -#else /* DUK_USE_REGEXP_SUPPORT */ - goto repl_write; /* unconditionally */ -#endif /* DUK_USE_REGEXP_SUPPORT */ - } /* default case */ - } /* switch (ch2) */ - - repl_write: - /* ch1 = (r_increment << 8) + byte */ - - DUK_BW_WRITE_ENSURE_U8(thr, bw, (duk_uint8_t) (ch1 & 0xff)); - r += ch1 >> 8; - } /* while repl */ - } /* if (is_repl_func) */ - - duk_pop(thr); /* pop regexp res_obj or match string */ - -#if defined(DUK_USE_REGEXP_SUPPORT) - if (!is_global) { -#else - { /* unconditionally; is_global==0 */ -#endif - break; - } - } - - /* trailer */ - tmp_sz = (duk_size_t) (DUK_HSTRING_GET_BYTELEN(h_input) - prev_match_end_boff); - DUK_BW_WRITE_ENSURE_BYTES(thr, bw, DUK_HSTRING_GET_DATA(h_input) + prev_match_end_boff, tmp_sz); - - DUK_ASSERT_TOP(thr, 4); - DUK_BW_COMPACT(thr, bw); - (void) duk_buffer_to_string(thr, -1); /* Safe if inputs are safe. */ - return 1; -} - -/* - * split() - */ - -/* XXX: very messy now, but works; clean up, remove unused variables (nomimally - * used so compiler doesn't complain). - */ - -DUK_INTERNAL duk_ret_t duk_bi_string_prototype_split(duk_hthread *thr) { - duk_hstring *h_input; - duk_hstring *h_sep; - duk_uint32_t limit; - duk_uint32_t arr_idx; -#if defined(DUK_USE_REGEXP_SUPPORT) - duk_bool_t is_regexp; -#endif - duk_bool_t matched; /* set to 1 if any match exists (needed for empty input special case) */ - duk_uint32_t prev_match_end_coff, prev_match_end_boff; - duk_uint32_t match_start_boff, match_start_coff; - duk_uint32_t match_end_boff, match_end_coff; - - h_input = duk_push_this_coercible_to_string(thr); - DUK_ASSERT(h_input != NULL); - - duk_push_array(thr); - - if (duk_is_undefined(thr, 1)) { - limit = 0xffffffffUL; - } else { - limit = duk_to_uint32(thr, 1); - } - - if (limit == 0) { - return 1; - } - - /* If the separator is a RegExp, make a "clone" of it. The specification - * algorithm calls [[Match]] directly for specific indices; we emulate this - * by tweaking lastIndex and using a "force global" variant of duk_regexp_match() - * which will use global-style matching even when the RegExp itself is non-global. - */ - - if (duk_is_undefined(thr, 0)) { - /* The spec algorithm first does "R = ToString(separator)" before checking - * whether separator is undefined. Since this is side effect free, we can - * skip the ToString() here. - */ - duk_dup_2(thr); - duk_put_prop_index(thr, 3, 0); - return 1; - } else if (duk_get_hobject_with_class(thr, 0, DUK_HOBJECT_CLASS_REGEXP) != NULL) { -#if defined(DUK_USE_REGEXP_SUPPORT) - duk_push_hobject_bidx(thr, DUK_BIDX_REGEXP_CONSTRUCTOR); - duk_dup_0(thr); - duk_new(thr, 1); /* [ ... RegExp val ] -> [ ... res ] */ - duk_replace(thr, 0); - /* lastIndex is initialized to zero by new RegExp() */ - is_regexp = 1; -#else - DUK_DCERROR_UNSUPPORTED(thr); -#endif - } else { - duk_to_string(thr, 0); -#if defined(DUK_USE_REGEXP_SUPPORT) - is_regexp = 0; -#endif - } - - /* stack[0] = separator (string or regexp) - * stack[1] = limit - * stack[2] = input string - * stack[3] = result array - */ - - prev_match_end_boff = 0; - prev_match_end_coff = 0; - arr_idx = 0; - matched = 0; - - for (;;) { - /* - * The specification uses RegExp [[Match]] to attempt match at specific - * offsets. We don't have such a primitive, so we use an actual RegExp - * and tweak lastIndex. Since the RegExp may be non-global, we use a - * special variant which forces global-like behavior for matching. - */ - - DUK_ASSERT_TOP(thr, 4); - -#if defined(DUK_USE_REGEXP_SUPPORT) - if (is_regexp) { - duk_dup_0(thr); - duk_dup_2(thr); - duk_regexp_match_force_global(thr); /* [ ... regexp input ] -> [ res_obj ] */ - if (!duk_is_object(thr, -1)) { - duk_pop(thr); - break; - } - matched = 1; - - duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_INDEX); - DUK_ASSERT(duk_is_number(thr, -1)); - match_start_coff = duk_get_uint(thr, -1); - match_start_boff = (duk_uint32_t) duk_heap_strcache_offset_char2byte(thr, h_input, match_start_coff); - duk_pop(thr); - - if (match_start_coff == DUK_HSTRING_GET_CHARLEN(h_input)) { - /* don't allow an empty match at the end of the string */ - duk_pop(thr); - break; - } - - duk_get_prop_stridx_short(thr, 0, DUK_STRIDX_LAST_INDEX); - DUK_ASSERT(duk_is_number(thr, -1)); - match_end_coff = duk_get_uint(thr, -1); - match_end_boff = (duk_uint32_t) duk_heap_strcache_offset_char2byte(thr, h_input, match_end_coff); - duk_pop(thr); - - /* empty match -> bump and continue */ - if (prev_match_end_boff == match_end_boff) { - duk_push_uint(thr, (duk_uint_t) (match_end_coff + 1)); - duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LAST_INDEX); - duk_pop(thr); - continue; - } - } else { -#else /* DUK_USE_REGEXP_SUPPORT */ - { /* unconditionally */ -#endif /* DUK_USE_REGEXP_SUPPORT */ - const duk_uint8_t *p_start, *p_end, *p; /* input string scan */ - const duk_uint8_t *q_start; /* match string */ - duk_size_t q_blen, q_clen; - - p_start = DUK_HSTRING_GET_DATA(h_input); - p_end = p_start + DUK_HSTRING_GET_BYTELEN(h_input); - p = p_start + prev_match_end_boff; - - h_sep = duk_known_hstring(thr, 0); /* symbol already rejected above */ - q_start = DUK_HSTRING_GET_DATA(h_sep); - q_blen = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h_sep); - q_clen = (duk_size_t) DUK_HSTRING_GET_CHARLEN(h_sep); - - p_end -= q_blen; /* ensure full memcmp() fits in while */ - - match_start_coff = prev_match_end_coff; - - if (q_blen == 0) { - /* Handle empty separator case: it will always match, and always - * triggers the check in step 13.c.iii initially. Note that we - * must skip to either end of string or start of first codepoint, - * skipping over any continuation bytes! - * - * Don't allow an empty string to match at the end of the input. - */ - - matched = 1; /* empty separator can always match */ - - match_start_coff++; - p++; - while (p < p_end) { - if ((p[0] & 0xc0) != 0x80) { - goto found; - } - p++; - } - goto not_found; - } - - DUK_ASSERT(q_blen > 0 && q_clen > 0); - while (p <= p_end) { - DUK_ASSERT(p + q_blen <= DUK_HSTRING_GET_DATA(h_input) + DUK_HSTRING_GET_BYTELEN(h_input)); - DUK_ASSERT(q_blen > 0); /* no issues with empty memcmp() */ - if (duk_memcmp((const void *) p, (const void *) q_start, (size_t) q_blen) == 0) { - /* never an empty match, so step 13.c.iii can't be triggered */ - goto found; - } - - /* track utf-8 non-continuation bytes */ - if ((p[0] & 0xc0) != 0x80) { - match_start_coff++; - } - p++; - } - - not_found: - /* not found */ - break; - - found: - matched = 1; - match_start_boff = (duk_uint32_t) (p - p_start); - match_end_coff = (duk_uint32_t) (match_start_coff + q_clen); /* constrained by string length */ - match_end_boff = (duk_uint32_t) (match_start_boff + q_blen); /* ditto */ - - /* empty match (may happen with empty separator) -> bump and continue */ - if (prev_match_end_boff == match_end_boff) { - prev_match_end_boff++; - prev_match_end_coff++; - continue; - } - } /* if (is_regexp) */ - - /* stack[0] = separator (string or regexp) - * stack[1] = limit - * stack[2] = input string - * stack[3] = result array - * stack[4] = regexp res_obj (if is_regexp) - */ - - DUK_DDD(DUK_DDDPRINT("split; match_start b=%ld,c=%ld, match_end b=%ld,c=%ld, prev_end b=%ld,c=%ld", - (long) match_start_boff, - (long) match_start_coff, - (long) match_end_boff, - (long) match_end_coff, - (long) prev_match_end_boff, - (long) prev_match_end_coff)); - - duk_push_lstring(thr, - (const char *) (DUK_HSTRING_GET_DATA(h_input) + prev_match_end_boff), - (duk_size_t) (match_start_boff - prev_match_end_boff)); - duk_put_prop_index(thr, 3, arr_idx); - arr_idx++; - if (arr_idx >= limit) { - goto hit_limit; - } - -#if defined(DUK_USE_REGEXP_SUPPORT) - if (is_regexp) { - duk_size_t i, len; - - len = duk_get_length(thr, 4); - for (i = 1; i < len; i++) { - DUK_ASSERT(i <= DUK_UARRIDX_MAX); /* cannot have >4G captures */ - duk_get_prop_index(thr, 4, (duk_uarridx_t) i); - duk_put_prop_index(thr, 3, arr_idx); - arr_idx++; - if (arr_idx >= limit) { - goto hit_limit; - } - } - - duk_pop(thr); - /* lastIndex already set up for next match */ - } else { -#else /* DUK_USE_REGEXP_SUPPORT */ - { - /* unconditionally */ -#endif /* DUK_USE_REGEXP_SUPPORT */ - /* no action */ - } - - prev_match_end_boff = match_end_boff; - prev_match_end_coff = match_end_coff; - continue; - } /* for */ - - /* Combined step 11 (empty string special case) and 14-15. */ - - DUK_DDD(DUK_DDDPRINT("split trailer; prev_end b=%ld,c=%ld", (long) prev_match_end_boff, (long) prev_match_end_coff)); - - if (DUK_HSTRING_GET_BYTELEN(h_input) > 0 || !matched) { - /* Add trailer if: - * a) non-empty input - * b) empty input and no (zero size) match found (step 11) - */ - - duk_push_lstring(thr, - (const char *) DUK_HSTRING_GET_DATA(h_input) + prev_match_end_boff, - (duk_size_t) (DUK_HSTRING_GET_BYTELEN(h_input) - prev_match_end_boff)); - duk_put_prop_index(thr, 3, arr_idx); - /* No arr_idx update or limit check */ - } - - return 1; - -hit_limit: -#if defined(DUK_USE_REGEXP_SUPPORT) - if (is_regexp) { - duk_pop(thr); - } -#endif - - return 1; -} - -/* - * Various - */ - -#if defined(DUK_USE_REGEXP_SUPPORT) -DUK_LOCAL void duk__to_regexp_helper(duk_hthread *thr, duk_idx_t idx, duk_bool_t force_new) { - duk_hobject *h; - - /* Shared helper for match() steps 3-4, search() steps 3-4. */ - - DUK_ASSERT(idx >= 0); - - if (force_new) { - goto do_new; - } - - h = duk_get_hobject_with_class(thr, idx, DUK_HOBJECT_CLASS_REGEXP); - if (!h) { - goto do_new; - } - return; - -do_new: - duk_push_hobject_bidx(thr, DUK_BIDX_REGEXP_CONSTRUCTOR); - duk_dup(thr, idx); - duk_new(thr, 1); /* [ ... RegExp val ] -> [ ... res ] */ - duk_replace(thr, idx); -} -#endif /* DUK_USE_REGEXP_SUPPORT */ - -#if defined(DUK_USE_REGEXP_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_string_prototype_search(duk_hthread *thr) { - /* Easiest way to implement the search required by the specification - * is to do a RegExp test() with lastIndex forced to zero. To avoid - * side effects on the argument, "clone" the RegExp if a RegExp was - * given as input. - * - * The global flag of the RegExp should be ignored; setting lastIndex - * to zero (which happens when "cloning" the RegExp) should have an - * equivalent effect. - */ - - DUK_ASSERT_TOP(thr, 1); - (void) duk_push_this_coercible_to_string(thr); /* at index 1 */ - duk__to_regexp_helper(thr, 0 /*index*/, 1 /*force_new*/); - - /* stack[0] = regexp - * stack[1] = string - */ - - /* Avoid using RegExp.prototype methods, as they're writable and - * configurable and may have been changed. - */ - - duk_dup_0(thr); - duk_dup_1(thr); /* [ ... re_obj input ] */ - duk_regexp_match(thr); /* -> [ ... res_obj ] */ - - if (!duk_is_object(thr, -1)) { - duk_push_int(thr, -1); - return 1; - } - - duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_INDEX); - DUK_ASSERT(duk_is_number(thr, -1)); - return 1; -} -#endif /* DUK_USE_REGEXP_SUPPORT */ - -#if defined(DUK_USE_REGEXP_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_string_prototype_match(duk_hthread *thr) { - duk_bool_t global; - duk_int_t prev_last_index; - duk_int_t this_index; - duk_int_t arr_idx; - - DUK_ASSERT_TOP(thr, 1); - (void) duk_push_this_coercible_to_string(thr); - duk__to_regexp_helper(thr, 0 /*index*/, 0 /*force_new*/); - global = duk_get_prop_stridx_boolean(thr, 0, DUK_STRIDX_GLOBAL, NULL); - DUK_ASSERT_TOP(thr, 2); - - /* stack[0] = regexp - * stack[1] = string - */ - - if (!global) { - duk_regexp_match(thr); /* -> [ res_obj ] */ - return 1; /* return 'res_obj' */ - } - - /* Global case is more complex. */ - - /* [ regexp string ] */ - - duk_push_int(thr, 0); - duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LAST_INDEX); - duk_push_array(thr); - - /* [ regexp string res_arr ] */ - - prev_last_index = 0; - arr_idx = 0; - - for (;;) { - DUK_ASSERT_TOP(thr, 3); - - duk_dup_0(thr); - duk_dup_1(thr); - duk_regexp_match(thr); /* -> [ ... regexp string ] -> [ ... res_obj ] */ - - if (!duk_is_object(thr, -1)) { - duk_pop(thr); - break; - } - - duk_get_prop_stridx_short(thr, 0, DUK_STRIDX_LAST_INDEX); - DUK_ASSERT(duk_is_number(thr, -1)); - this_index = duk_get_int(thr, -1); - duk_pop(thr); - - if (this_index == prev_last_index) { - this_index++; - duk_push_int(thr, this_index); - duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LAST_INDEX); - } - prev_last_index = this_index; - - duk_get_prop_index(thr, -1, 0); /* match string */ - duk_put_prop_index(thr, 2, (duk_uarridx_t) arr_idx); - arr_idx++; - duk_pop(thr); /* res_obj */ - } - - if (arr_idx == 0) { - duk_push_null(thr); - } - - return 1; /* return 'res_arr' or 'null' */ -} -#endif /* DUK_USE_REGEXP_SUPPORT */ - -DUK_INTERNAL duk_ret_t duk_bi_string_prototype_concat(duk_hthread *thr) { - /* duk_concat() coerces arguments with ToString() in correct order */ - (void) duk_push_this_coercible_to_string(thr); - duk_insert(thr, 0); /* this is relatively expensive */ - duk_concat(thr, duk_get_top(thr)); - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_string_prototype_trim(duk_hthread *thr) { - DUK_ASSERT_TOP(thr, 0); - (void) duk_push_this_coercible_to_string(thr); - duk_trim(thr, 0); - DUK_ASSERT_TOP(thr, 1); - return 1; -} - -#if defined(DUK_USE_ES6) -DUK_INTERNAL duk_ret_t duk_bi_string_prototype_repeat(duk_hthread *thr) { - duk_hstring *h_input; - duk_size_t input_blen; - duk_size_t result_len; - duk_int_t count_signed; - duk_uint_t count; - const duk_uint8_t *src; - duk_uint8_t *buf; - duk_uint8_t *p; - duk_double_t d; -#if !defined(DUK_USE_PREFER_SIZE) - duk_size_t copy_size; - duk_uint8_t *p_end; -#endif - - DUK_ASSERT_TOP(thr, 1); - h_input = duk_push_this_coercible_to_string(thr); - DUK_ASSERT(h_input != NULL); - input_blen = DUK_HSTRING_GET_BYTELEN(h_input); - - /* Count is ToNumber() coerced; +Infinity must be always rejected - * (even if input string is zero length), as well as negative values - * and -Infinity. -Infinity doesn't require an explicit check - * because duk_get_int() clamps it to DUK_INT_MIN which gets rejected - * as a negative value (regardless of input string length). - */ - d = duk_to_number(thr, 0); - if (duk_double_is_posinf(d)) { - goto fail_range; - } - count_signed = duk_get_int(thr, 0); - if (count_signed < 0) { - goto fail_range; - } - count = (duk_uint_t) count_signed; - - /* Overflow check for result length. */ - result_len = count * input_blen; - if (count != 0 && result_len / count != input_blen) { - goto fail_range; - } - - /* Temporary fixed buffer, later converted to string. */ - buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, result_len); - DUK_ASSERT(buf != NULL); - src = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_input); - DUK_ASSERT(src != NULL); - -#if defined(DUK_USE_PREFER_SIZE) - p = buf; - while (count-- > 0) { - duk_memcpy((void *) p, (const void *) src, input_blen); /* copy size may be zero, but pointers are valid */ - p += input_blen; - } -#else /* DUK_USE_PREFER_SIZE */ - /* Take advantage of already copied pieces to speed up the process - * especially for small repeated strings. - */ - p = buf; - p_end = p + result_len; - copy_size = input_blen; - for (;;) { - duk_size_t remain = (duk_size_t) (p_end - p); - DUK_DDD(DUK_DDDPRINT("remain=%ld, copy_size=%ld, input_blen=%ld, result_len=%ld", - (long) remain, - (long) copy_size, - (long) input_blen, - (long) result_len)); - if (remain <= copy_size) { - /* If result_len is zero, this case is taken and does - * a zero size copy (with valid pointers). - */ - duk_memcpy((void *) p, (const void *) src, remain); - break; - } else { - duk_memcpy((void *) p, (const void *) src, copy_size); - p += copy_size; - } - - src = (const duk_uint8_t *) buf; /* Use buf as source for larger copies. */ - copy_size = (duk_size_t) (p - buf); - } -#endif /* DUK_USE_PREFER_SIZE */ - - /* XXX: It would be useful to be able to create a duk_hstring with - * a certain byte size whose data area wasn't initialized and which - * wasn't in the string table yet. This would allow a string to be - * constructed directly without a buffer temporary and when it was - * finished, it could be injected into the string table. Currently - * this isn't possible because duk_hstrings are only tracked by the - * intern table (they are not in heap_allocated). - */ - - duk_buffer_to_string(thr, -1); /* Safe if input is safe. */ - return 1; - -fail_range: - DUK_DCERROR_RANGE_INVALID_ARGS(thr); -} -#endif /* DUK_USE_ES6 */ - -DUK_INTERNAL duk_ret_t duk_bi_string_prototype_locale_compare(duk_hthread *thr) { - duk_hstring *h1; - duk_hstring *h2; - duk_size_t h1_len, h2_len, prefix_len; - duk_small_int_t ret = 0; - duk_small_int_t rc; - - /* The current implementation of localeCompare() is simply a codepoint - * by codepoint comparison, implemented with a simple string compare - * because UTF-8 should preserve codepoint ordering (assuming valid - * shortest UTF-8 encoding). - * - * The specification requires that the return value must be related - * to the sort order: e.g. negative means that 'this' comes before - * 'that' in sort order. We assume an ascending sort order. - */ - - /* XXX: could share code with duk_js_ops.c, duk_js_compare_helper */ - - h1 = duk_push_this_coercible_to_string(thr); - DUK_ASSERT(h1 != NULL); - - h2 = duk_to_hstring(thr, 0); - DUK_ASSERT(h2 != NULL); - - h1_len = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h1); - h2_len = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h2); - prefix_len = (h1_len <= h2_len ? h1_len : h2_len); - - rc = (duk_small_int_t) duk_memcmp((const void *) DUK_HSTRING_GET_DATA(h1), - (const void *) DUK_HSTRING_GET_DATA(h2), - (size_t) prefix_len); - - if (rc < 0) { - ret = -1; - goto done; - } else if (rc > 0) { - ret = 1; - goto done; - } - - /* prefix matches, lengths matter now */ - if (h1_len > h2_len) { - ret = 1; - goto done; - } else if (h1_len == h2_len) { - DUK_ASSERT(ret == 0); - goto done; - } - ret = -1; - goto done; - -done: - duk_push_int(thr, (duk_int_t) ret); - return 1; -} - -#if defined(DUK_USE_ES6) -DUK_INTERNAL duk_ret_t duk_bi_string_prototype_startswith_endswith(duk_hthread *thr) { - duk_int_t magic; - duk_hstring *h_target; - duk_size_t blen_target; - duk_hstring *h_search; - duk_size_t blen_search; - duk_int_t off; - duk_bool_t result = 0; - duk_size_t blen_left; - - /* Because string byte lengths are in [0,DUK_INT_MAX] it's safe to - * subtract two string lengths without overflow. - */ - DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= DUK_INT_MAX); - - h_target = duk_push_this_coercible_to_string(thr); - DUK_ASSERT(h_target != NULL); - - h_search = duk__str_tostring_notregexp(thr, 0); - DUK_ASSERT(h_search != NULL); - - magic = duk_get_current_magic(thr); - - /* Careful to avoid pointer overflows in the matching logic. */ - - blen_target = DUK_HSTRING_GET_BYTELEN(h_target); - blen_search = DUK_HSTRING_GET_BYTELEN(h_search); - -#if 0 - /* If search string is longer than the target string, we can - * never match. Could check explicitly, but should be handled - * correctly below. - */ - if (blen_search > blen_target) { - goto finish; - } -#endif - - off = 0; - if (duk_is_undefined(thr, 1)) { - if (magic) { - off = (duk_int_t) blen_target - (duk_int_t) blen_search; - } else { - DUK_ASSERT(off == 0); - } - } else { - duk_int_t len; - duk_int_t pos; - - DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= DUK_INT_MAX); - len = (duk_int_t) DUK_HSTRING_GET_CHARLEN(h_target); - pos = duk_to_int_clamped(thr, 1, 0, len); - DUK_ASSERT(pos >= 0 && pos <= len); - - off = (duk_int_t) duk_heap_strcache_offset_char2byte(thr, h_target, (duk_uint_fast32_t) pos); - if (magic) { - off -= (duk_int_t) blen_search; - } - } - if (off < 0 || off > (duk_int_t) blen_target) { - goto finish; - } - - /* The main comparison can be done using a memcmp() rather than - * doing codepoint comparisons: for CESU-8 strings there is a - * canonical representation for every codepoint. But we do need - * to deal with the char/byte offset translation to find the - * comparison range. - */ - - DUK_ASSERT(off >= 0); - DUK_ASSERT((duk_size_t) off <= blen_target); - blen_left = blen_target - (duk_size_t) off; - if (blen_left >= blen_search) { - const duk_uint8_t *p_cmp_start = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_target) + off; - const duk_uint8_t *p_search = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_search); - if (duk_memcmp_unsafe((const void *) p_cmp_start, (const void *) p_search, (size_t) blen_search) == 0) { - result = 1; - } - } - -finish: - duk_push_boolean(thr, result); - return 1; -} -#endif /* DUK_USE_ES6 */ - -#if defined(DUK_USE_ES6) -DUK_INTERNAL duk_ret_t duk_bi_string_prototype_includes(duk_hthread *thr) { - duk_hstring *h; - duk_hstring *h_search; - duk_int_t len; - duk_int_t pos; - - h = duk_push_this_coercible_to_string(thr); - DUK_ASSERT(h != NULL); - - h_search = duk__str_tostring_notregexp(thr, 0); - DUK_ASSERT(h_search != NULL); - - len = (duk_int_t) DUK_HSTRING_GET_CHARLEN(h); - pos = duk_to_int_clamped(thr, 1, 0, len); - DUK_ASSERT(pos >= 0 && pos <= len); - - pos = duk__str_search_shared(thr, h, h_search, pos, 0 /*backwards*/); - duk_push_boolean(thr, pos >= 0); - return 1; -} -#endif /* DUK_USE_ES6 */ -#endif /* DUK_USE_STRING_BUILTIN */ -/* - * Symbol built-in - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_SYMBOL_BUILTIN) - -/* - * Constructor - */ - -DUK_INTERNAL duk_ret_t duk_bi_symbol_constructor_shared(duk_hthread *thr) { - const duk_uint8_t *desc; - duk_size_t len; - duk_uint8_t *buf; - duk_uint8_t *p; - duk_int_t magic; - - magic = duk_get_current_magic(thr); - if (duk_is_undefined(thr, 0) && (magic == 0)) { - /* Symbol() accepts undefined and empty string, but they are - * treated differently. - */ - desc = NULL; - len = 0; - } else { - /* Symbol.for() coerces undefined to 'undefined' */ - desc = (const duk_uint8_t *) duk_to_lstring(thr, 0, &len); - } - - /* Maximum symbol data length: - * +1 initial byte (0x80 or 0x81) - * +len description - * +1 0xff after description, before unique suffix - * +17 autogenerated unique suffix: 'ffffffff-ffffffff' is longest - * +1 0xff after unique suffix for symbols with undefined description - */ - buf = (duk_uint8_t *) duk_push_fixed_buffer(thr, 1 + len + 1 + 17 + 1); - DUK_ASSERT(buf != NULL); - p = buf + 1; - DUK_ASSERT(desc != NULL || len == 0); /* may be NULL if len is 0 */ - duk_memcpy_unsafe((void *) p, (const void *) desc, len); - p += len; - if (magic == 0) { - /* Symbol(): create unique symbol. Use two 32-bit values - * to avoid dependency on 64-bit types and 64-bit integer - * formatting (at least for now). - */ - if (++thr->heap->sym_counter[0] == 0) { - thr->heap->sym_counter[1]++; - } - p += DUK_SPRINTF((char *) p, - "\xFF" - "%lx-%lx", - (unsigned long) thr->heap->sym_counter[1], - (unsigned long) thr->heap->sym_counter[0]); - if (desc == NULL) { - /* Special case for 'undefined' description, trailing - * 0xff distinguishes from empty string description, - * but needs minimal special case handling elsewhere. - */ - *p++ = 0xff; - } - buf[0] = 0x81; - } else { - /* Symbol.for(): create a global symbol */ - buf[0] = 0x80; - } - - duk_push_lstring(thr, (const char *) buf, (duk_size_t) (p - buf)); - DUK_DDD(DUK_DDDPRINT("created symbol: %!T", duk_get_tval(thr, -1))); - return 1; -} - -DUK_LOCAL duk_hstring *duk__auto_unbox_symbol(duk_hthread *thr, duk_tval *tv_arg) { - duk_tval *tv; - duk_hobject *h_obj; - duk_hstring *h_str; - - DUK_ASSERT(tv_arg != NULL); - - /* XXX: add internal helper: duk_auto_unbox_tval(thr, tv, mask); */ - /* XXX: add internal helper: duk_auto_unbox(thr, tv, idx); */ - - tv = tv_arg; - if (DUK_TVAL_IS_OBJECT(tv)) { - h_obj = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h_obj != NULL); - if (DUK_HOBJECT_GET_CLASS_NUMBER(h_obj) == DUK_HOBJECT_CLASS_SYMBOL) { - tv = duk_hobject_get_internal_value_tval_ptr(thr->heap, h_obj); - if (tv == NULL) { - return NULL; - } - } else { - return NULL; - } - } - - if (!DUK_TVAL_IS_STRING(tv)) { - return NULL; - } - h_str = DUK_TVAL_GET_STRING(tv); - DUK_ASSERT(h_str != NULL); - - /* Here symbol is more expected than not. */ - if (DUK_UNLIKELY(!DUK_HSTRING_HAS_SYMBOL(h_str))) { - return NULL; - } - - return h_str; -} - -DUK_INTERNAL duk_ret_t duk_bi_symbol_tostring_shared(duk_hthread *thr) { - duk_hstring *h_str; - - h_str = duk__auto_unbox_symbol(thr, DUK_HTHREAD_THIS_PTR(thr)); - if (h_str == NULL) { - return DUK_RET_TYPE_ERROR; - } - - if (duk_get_current_magic(thr) == 0) { - /* .toString() */ - duk_push_symbol_descriptive_string(thr, h_str); - } else { - /* .valueOf() */ - duk_push_hstring(thr, h_str); - } - return 1; -} - -DUK_INTERNAL duk_ret_t duk_bi_symbol_key_for(duk_hthread *thr) { - duk_hstring *h; - const duk_uint8_t *p; - - /* Argument must be a symbol but not checked here. The initial byte - * check will catch non-symbol strings. - */ - h = duk_require_hstring(thr, 0); - DUK_ASSERT(h != NULL); - - p = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h); - DUK_ASSERT(p != NULL); - - /* Even for zero length strings there's at least one NUL byte so - * we can safely check the initial byte. - */ - if (p[0] == 0x80) { - /* Global symbol, return its key (bytes just after the initial byte). */ - duk_push_lstring(thr, (const char *) (p + 1), (duk_size_t) (DUK_HSTRING_GET_BYTELEN(h) - 1)); - return 1; - } else if (p[0] == 0x81 || p[0] == 0x82 || p[0] == 0xff) { - /* Local symbol or hidden symbol, return undefined. */ - return 0; - } - - /* Covers normal strings and unknown initial bytes. */ - return DUK_RET_TYPE_ERROR; -} - -DUK_INTERNAL duk_ret_t duk_bi_symbol_toprimitive(duk_hthread *thr) { - duk_hstring *h_str; - - h_str = duk__auto_unbox_symbol(thr, DUK_HTHREAD_THIS_PTR(thr)); - if (h_str == NULL) { - return DUK_RET_TYPE_ERROR; - } - duk_push_hstring(thr, h_str); - return 1; -} - -#endif /* DUK_USE_SYMBOL_BUILTIN */ -/* - * Thread builtins - */ - -/* #include duk_internal.h -> already included */ - -/* - * Constructor - */ - -#if defined(DUK_USE_COROUTINE_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_thread_constructor(duk_hthread *thr) { - duk_hthread *new_thr; - duk_hobject *func; - - /* Check that the argument is callable; this is not 100% because we - * don't allow native functions to be a thread's initial function. - * Resume will reject such functions in any case. - */ - /* XXX: need a duk_require_func_promote_lfunc() */ - func = duk_require_hobject_promote_lfunc(thr, 0); - DUK_ASSERT(func != NULL); - duk_require_callable(thr, 0); - - duk_push_thread(thr); - new_thr = (duk_hthread *) duk_known_hobject(thr, -1); - new_thr->state = DUK_HTHREAD_STATE_INACTIVE; - - /* push initial function call to new thread stack; this is - * picked up by resume(). - */ - duk_push_hobject(new_thr, func); - - return 1; /* return thread */ -} -#endif - -/* - * Resume a thread. - * - * The thread must be in resumable state, either (a) new thread which hasn't - * yet started, or (b) a thread which has previously yielded. This method - * must be called from an ECMAScript function. - * - * Args: - * - thread - * - value - * - isError (defaults to false) - * - * Note: yield and resume handling is currently asymmetric. - */ - -#if defined(DUK_USE_COROUTINE_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_thread_resume(duk_hthread *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_hthread *thr_resume; - duk_hobject *caller_func; - duk_small_uint_t is_error; - - DUK_DDD(DUK_DDDPRINT("Duktape.Thread.resume(): thread=%!T, value=%!T, is_error=%!T", - (duk_tval *) duk_get_tval(thr, 0), - (duk_tval *) duk_get_tval(thr, 1), - (duk_tval *) duk_get_tval(thr, 2))); - - DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING); - DUK_ASSERT(thr->heap->curr_thread == thr); - - thr_resume = duk_require_hthread(thr, 0); - DUK_ASSERT(duk_get_top(thr) == 3); - is_error = (duk_small_uint_t) duk_to_boolean_top_pop(thr); - DUK_ASSERT(duk_get_top(thr) == 2); - - /* [ thread value ] */ - - /* - * Thread state and calling context checks - */ - - if (thr->callstack_top < 2) { - DUK_DD(DUK_DDPRINT( - "resume state invalid: callstack should contain at least 2 entries (caller and Duktape.Thread.resume)")); - goto state_error; - } - DUK_ASSERT(thr->callstack_curr != NULL); - DUK_ASSERT(thr->callstack_curr->parent != NULL); - DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL); /* us */ - DUK_ASSERT(DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr))); - DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr->parent) != NULL); /* caller */ - - caller_func = DUK_ACT_GET_FUNC(thr->callstack_curr->parent); - if (!DUK_HOBJECT_IS_COMPFUNC(caller_func)) { - DUK_DD(DUK_DDPRINT("resume state invalid: caller must be ECMAScript code")); - goto state_error; - } - - /* Note: there is no requirement that: 'thr->callstack_preventcount == 1' - * like for yield. - */ - - if (thr_resume->state != DUK_HTHREAD_STATE_INACTIVE && thr_resume->state != DUK_HTHREAD_STATE_YIELDED) { - DUK_DD(DUK_DDPRINT("resume state invalid: target thread must be INACTIVE or YIELDED")); - goto state_error; - } - - DUK_ASSERT(thr_resume->state == DUK_HTHREAD_STATE_INACTIVE || thr_resume->state == DUK_HTHREAD_STATE_YIELDED); - - /* Further state-dependent pre-checks */ - - if (thr_resume->state == DUK_HTHREAD_STATE_YIELDED) { - /* no pre-checks now, assume a previous yield() has left things in - * tip-top shape (longjmp handler will assert for these). - */ - } else { - duk_hobject *h_fun; - - DUK_ASSERT(thr_resume->state == DUK_HTHREAD_STATE_INACTIVE); - - /* The initial function must be an ECMAScript function (but - * can be bound). We must make sure of that before we longjmp - * because an error in the RESUME handler call processing will - * not be handled very cleanly. - */ - if ((thr_resume->callstack_top != 0) || (thr_resume->valstack_top - thr_resume->valstack != 1)) { - goto state_error; - } - - duk_push_tval(thr, DUK_GET_TVAL_NEGIDX(thr_resume, -1)); - duk_resolve_nonbound_function(thr); - h_fun = duk_require_hobject(thr, -1); /* reject lightfuncs on purpose */ - if (!DUK_HOBJECT_IS_CALLABLE(h_fun) || !DUK_HOBJECT_IS_COMPFUNC(h_fun)) { - goto state_error; - } - duk_pop(thr); - } - -#if 0 - /* This check would prevent a heap destruction time finalizer from - * launching a coroutine, which would ensure that during finalization - * 'thr' would always equal heap_thread. Normal runtime finalizers - * run with ms_running == 0, i.e. outside mark-and-sweep. See GH-2030. - */ - if (thr->heap->ms_running) { - DUK_D(DUK_DPRINT("refuse Duktape.Thread.resume() when ms_running != 0")); - goto state_error; - } -#endif - - /* - * The error object has been augmented with a traceback and other - * info from its creation point -- usually another thread. The - * error handler is called here right before throwing, but it also - * runs in the resumer's thread. It might be nice to get a traceback - * from the resumee but this is not the case now. - */ - -#if defined(DUK_USE_AUGMENT_ERROR_THROW) - if (is_error) { - DUK_ASSERT_TOP(thr, 2); /* value (error) is at stack top */ - duk_err_augment_error_throw(thr); /* in resumer's context */ - } -#endif - -#if defined(DUK_USE_DEBUG) - if (is_error) { - DUK_DDD(DUK_DDDPRINT("RESUME ERROR: thread=%!T, value=%!T", - (duk_tval *) duk_get_tval(thr, 0), - (duk_tval *) duk_get_tval(thr, 1))); - } else if (thr_resume->state == DUK_HTHREAD_STATE_YIELDED) { - DUK_DDD(DUK_DDDPRINT("RESUME NORMAL: thread=%!T, value=%!T", - (duk_tval *) duk_get_tval(thr, 0), - (duk_tval *) duk_get_tval(thr, 1))); - } else { - DUK_DDD(DUK_DDDPRINT("RESUME INITIAL: thread=%!T, value=%!T", - (duk_tval *) duk_get_tval(thr, 0), - (duk_tval *) duk_get_tval(thr, 1))); - } -#endif - - thr->heap->lj.type = DUK_LJ_TYPE_RESUME; - - /* lj value2: thread */ - DUK_ASSERT(thr->valstack_bottom < thr->valstack_top); - DUK_TVAL_SET_TVAL_UPDREF(thr, &thr->heap->lj.value2, &thr->valstack_bottom[0]); /* side effects */ - - /* lj value1: value */ - DUK_ASSERT(thr->valstack_bottom + 1 < thr->valstack_top); - DUK_TVAL_SET_TVAL_UPDREF(thr, &thr->heap->lj.value1, &thr->valstack_bottom[1]); /* side effects */ - DUK_TVAL_CHKFAST_INPLACE_SLOW(&thr->heap->lj.value1); - - thr->heap->lj.iserror = is_error; - - DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL); /* call is from executor, so we know we have a jmpbuf */ - duk_err_longjmp(thr); /* execution resumes in bytecode executor */ - DUK_UNREACHABLE(); - /* Never here, fall through to error (from compiler point of view). */ - -state_error: - DUK_DCERROR_TYPE_INVALID_STATE(thr); -} -#endif - -/* - * Yield the current thread. - * - * The thread must be in yieldable state: it must have a resumer, and there - * must not be any yield-preventing calls (native calls and constructor calls, - * currently) in the thread's call stack (otherwise a resume would not be - * possible later). This method must be called from an ECMAScript function. - * - * Args: - * - value - * - isError (defaults to false) - * - * Note: yield and resume handling is currently asymmetric. - */ - -#if defined(DUK_USE_COROUTINE_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_thread_yield(duk_hthread *thr) { - duk_hobject *caller_func; - duk_small_uint_t is_error; - - DUK_DDD(DUK_DDDPRINT("Duktape.Thread.yield(): value=%!T, is_error=%!T", - (duk_tval *) duk_get_tval(thr, 0), - (duk_tval *) duk_get_tval(thr, 1))); - - DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING); - DUK_ASSERT(thr->heap->curr_thread == thr); - - DUK_ASSERT(duk_get_top(thr) == 2); - is_error = (duk_small_uint_t) duk_to_boolean_top_pop(thr); - DUK_ASSERT(duk_get_top(thr) == 1); - - /* [ value ] */ - - /* - * Thread state and calling context checks - */ - - if (!thr->resumer) { - DUK_DD(DUK_DDPRINT("yield state invalid: current thread must have a resumer")); - goto state_error; - } - DUK_ASSERT(thr->resumer->state == DUK_HTHREAD_STATE_RESUMED); - - if (thr->callstack_top < 2) { - DUK_DD(DUK_DDPRINT( - "yield state invalid: callstack should contain at least 2 entries (caller and Duktape.Thread.yield)")); - goto state_error; - } - DUK_ASSERT(thr->callstack_curr != NULL); - DUK_ASSERT(thr->callstack_curr->parent != NULL); - DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL); /* us */ - DUK_ASSERT(DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr))); - DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr->parent) != NULL); /* caller */ - - caller_func = DUK_ACT_GET_FUNC(thr->callstack_curr->parent); - if (!DUK_HOBJECT_IS_COMPFUNC(caller_func)) { - DUK_DD(DUK_DDPRINT("yield state invalid: caller must be ECMAScript code")); - goto state_error; - } - - DUK_ASSERT(thr->callstack_preventcount >= 1); /* should never be zero, because we (Duktape.Thread.yield) are on the stack */ - if (thr->callstack_preventcount != 1) { - /* Note: the only yield-preventing call is Duktape.Thread.yield(), hence check for 1, not 0 */ - DUK_DD(DUK_DDPRINT("yield state invalid: there must be no yield-preventing calls in current thread callstack " - "(preventcount is %ld)", - (long) thr->callstack_preventcount)); - goto state_error; - } - - /* - * The error object has been augmented with a traceback and other - * info from its creation point -- usually the current thread. - * The error handler, however, is called right before throwing - * and runs in the yielder's thread. - */ - -#if defined(DUK_USE_AUGMENT_ERROR_THROW) - if (is_error) { - DUK_ASSERT_TOP(thr, 1); /* value (error) is at stack top */ - duk_err_augment_error_throw(thr); /* in yielder's context */ - } -#endif - -#if defined(DUK_USE_DEBUG) - if (is_error) { - DUK_DDD(DUK_DDDPRINT("YIELD ERROR: value=%!T", (duk_tval *) duk_get_tval(thr, 0))); - } else { - DUK_DDD(DUK_DDDPRINT("YIELD NORMAL: value=%!T", (duk_tval *) duk_get_tval(thr, 0))); - } -#endif - - /* - * Process yield - * - * After longjmp(), processing continues in bytecode executor longjmp - * handler, which will e.g. update thr->resumer to NULL. - */ - - thr->heap->lj.type = DUK_LJ_TYPE_YIELD; - - /* lj value1: value */ - DUK_ASSERT(thr->valstack_bottom < thr->valstack_top); - DUK_TVAL_SET_TVAL_UPDREF(thr, &thr->heap->lj.value1, &thr->valstack_bottom[0]); /* side effects */ - DUK_TVAL_CHKFAST_INPLACE_SLOW(&thr->heap->lj.value1); - - thr->heap->lj.iserror = is_error; - - DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL); /* call is from executor, so we know we have a jmpbuf */ - duk_err_longjmp(thr); /* execution resumes in bytecode executor */ - DUK_UNREACHABLE(); - /* Never here, fall through to error (from compiler point of view). */ - -state_error: - DUK_DCERROR_TYPE_INVALID_STATE(thr); -} -#endif - -#if defined(DUK_USE_COROUTINE_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_thread_current(duk_hthread *thr) { - duk_push_current_thread(thr); - return 1; -} -#endif -/* - * Type error thrower, E5 Section 13.2.3. - */ - -/* #include duk_internal.h -> already included */ - -DUK_INTERNAL duk_ret_t duk_bi_type_error_thrower(duk_hthread *thr) { - DUK_DCERROR_TYPE_INVALID_ARGS(thr); -} -/* - * Fixed buffer helper useful for debugging, requires no allocation - * which is critical for debugging. - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_DEBUG) - -DUK_INTERNAL void duk_fb_put_bytes(duk_fixedbuffer *fb, const duk_uint8_t *buffer, duk_size_t length) { - duk_size_t avail; - duk_size_t copylen; - - avail = (fb->offset >= fb->length ? (duk_size_t) 0 : (duk_size_t) (fb->length - fb->offset)); - if (length > avail) { - copylen = avail; - fb->truncated = 1; - } else { - copylen = length; - } - duk_memcpy_unsafe(fb->buffer + fb->offset, buffer, copylen); - fb->offset += copylen; -} - -DUK_INTERNAL void duk_fb_put_byte(duk_fixedbuffer *fb, duk_uint8_t x) { - duk_fb_put_bytes(fb, (const duk_uint8_t *) &x, 1); -} - -DUK_INTERNAL void duk_fb_put_cstring(duk_fixedbuffer *fb, const char *x) { - duk_fb_put_bytes(fb, (const duk_uint8_t *) x, (duk_size_t) DUK_STRLEN(x)); -} - -DUK_INTERNAL void duk_fb_sprintf(duk_fixedbuffer *fb, const char *fmt, ...) { - duk_size_t avail; - va_list ap; - - va_start(ap, fmt); - avail = (fb->offset >= fb->length ? (duk_size_t) 0 : (duk_size_t) (fb->length - fb->offset)); - if (avail > 0) { - duk_int_t res = (duk_int_t) DUK_VSNPRINTF((char *) (fb->buffer + fb->offset), avail, fmt, ap); - if (res < 0) { - /* error */ - } else if ((duk_size_t) res >= avail) { - /* (maybe) truncated */ - fb->offset += avail; - if ((duk_size_t) res > avail) { - /* actual chars dropped (not just NUL term) */ - fb->truncated = 1; - } - } else { - /* normal */ - fb->offset += (duk_size_t) res; - } - } - va_end(ap); -} - -DUK_INTERNAL void duk_fb_put_funcptr(duk_fixedbuffer *fb, duk_uint8_t *fptr, duk_size_t fptr_size) { - char buf[64 + 1]; - duk_debug_format_funcptr(buf, sizeof(buf), fptr, fptr_size); - buf[sizeof(buf) - 1] = (char) 0; - duk_fb_put_cstring(fb, buf); -} - -DUK_INTERNAL duk_bool_t duk_fb_is_full(duk_fixedbuffer *fb) { - return (fb->offset >= fb->length); -} - -#endif /* DUK_USE_DEBUG */ -/* - * Custom formatter for debug printing, allowing Duktape specific data - * structures (such as tagged values and heap objects) to be printed with - * a nice format string. Because debug printing should not affect execution - * state, formatting here must be independent of execution (see implications - * below) and must not allocate memory. - * - * Custom format tags begin with a '%!' to safely distinguish them from - * standard format tags. The following conversions are supported: - * - * %!T tagged value (duk_tval *) - * %!O heap object (duk_heaphdr *) - * %!I decoded bytecode instruction - * %!X bytecode instruction opcode name (arg is long) - * %!C catcher (duk_catcher *) - * %!A activation (duk_activation *) - * - * Everything is serialized in a JSON-like manner. The default depth is one - * level, internal prototype is not followed, and internal properties are not - * serialized. The following modifiers change this behavior: - * - * @ print pointers - * # print binary representations (where applicable) - * d deep traversal of own properties (not prototype) - * p follow prototype chain (useless without 'd') - * i include internal properties (other than prototype) - * x hexdump buffers - * h heavy formatting - * - * For instance, the following serializes objects recursively, but does not - * follow the prototype chain nor print internal properties: "%!dO". - * - * Notes: - * - * * Standard snprintf return value semantics seem to vary. This - * implementation returns the number of bytes it actually wrote - * (excluding the null terminator). If retval == buffer size, - * output was truncated (except for corner cases). - * - * * Output format is intentionally different from ECMAScript - * formatting requirements, as formatting here serves debugging - * of internals. - * - * * Depth checking (and updating) is done in each type printer - * separately, to allow them to call each other freely. - * - * * Some pathological structures might take ages to print (e.g. - * self recursion with 100 properties pointing to the object - * itself). To guard against these, each printer also checks - * whether the output buffer is full; if so, early exit. - * - * * Reference loops are detected using a loop stack. - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_DEBUG) - -/* #include stdio.h -> already included */ -/* #include stdarg.h -> already included */ -#include - -/* list of conversion specifiers that terminate a format tag; - * this is unfortunately guesswork. - */ -#define DUK__ALLOWED_STANDARD_SPECIFIERS "diouxXeEfFgGaAcsCSpnm" - -/* maximum length of standard format tag that we support */ -#define DUK__MAX_FORMAT_TAG_LENGTH 32 - -/* heapobj recursion depth when deep printing is selected */ -#define DUK__DEEP_DEPTH_LIMIT 8 - -/* maximum recursion depth for loop detection stacks */ -#define DUK__LOOP_STACK_DEPTH 256 - -/* must match bytecode defines now; build autogenerate? */ -DUK_LOCAL const char * const duk__bc_optab[256] = { - "LDREG", "STREG", "JUMP", "LDCONST", "LDINT", "LDINTX", "LDTHIS", "LDUNDEF", - "LDNULL", "LDTRUE", "LDFALSE", "GETVAR", "BNOT", "LNOT", "UNM", "UNP", - "EQ_RR", "EQ_CR", "EQ_RC", "EQ_CC", "NEQ_RR", "NEQ_CR", "NEQ_RC", "NEQ_CC", - "SEQ_RR", "SEQ_CR", "SEQ_RC", "SEQ_CC", "SNEQ_RR", "SNEQ_CR", "SNEQ_RC", "SNEQ_CC", - - "GT_RR", "GT_CR", "GT_RC", "GT_CC", "GE_RR", "GE_CR", "GE_RC", "GE_CC", - "LT_RR", "LT_CR", "LT_RC", "LT_CC", "LE_RR", "LE_CR", "LE_RC", "LE_CC", - "IFTRUE_R", "IFTRUE_C", "IFFALSE_R", "IFFALSE_C", "ADD_RR", "ADD_CR", "ADD_RC", "ADD_CC", - "SUB_RR", "SUB_CR", "SUB_RC", "SUB_CC", "MUL_RR", "MUL_CR", "MUL_RC", "MUL_CC", - - "DIV_RR", "DIV_CR", "DIV_RC", "DIV_CC", "MOD_RR", "MOD_CR", "MOD_RC", "MOD_CC", - "EXP_RR", "EXP_CR", "EXP_RC", "EXP_CC", "BAND_RR", "BAND_CR", "BAND_RC", "BAND_CC", - "BOR_RR", "BOR_CR", "BOR_RC", "BOR_CC", "BXOR_RR", "BXOR_CR", "BXOR_RC", "BXOR_CC", - "BASL_RR", "BASL_CR", "BASL_RC", "BASL_CC", "BLSR_RR", "BLSR_CR", "BLSR_RC", "BLSR_CC", - - "BASR_RR", "BASR_CR", "BASR_RC", "BASR_CC", "INSTOF_RR", "INSTOF_CR", "INSTOF_RC", "INSTOF_CC", - "IN_RR", "IN_CR", "IN_RC", "IN_CC", "GETPROP_RR", "GETPROP_CR", "GETPROP_RC", "GETPROP_CC", - "PUTPROP_RR", "PUTPROP_CR", "PUTPROP_RC", "PUTPROP_CC", "DELPROP_RR", "DELPROP_CR", "DELPROP_RC", "DELPROP_CC", - "PREINCR", "PREDECR", "POSTINCR", "POSTDECR", "PREINCV", "PREDECV", "POSTINCV", "POSTDECV", - - "PREINCP_RR", "PREINCP_CR", "PREINCP_RC", "PREINCP_CC", "PREDECP_RR", "PREDECP_CR", "PREDECP_RC", "PREDECP_CC", - "POSTINCP_RR", "POSTINCP_CR", "POSTINCP_RC", "POSTINCP_CC", "POSTDECP_RR", "POSTDECP_CR", "POSTDECP_RC", "POSTDECP_CC", - "DECLVAR_RR", "DECLVAR_CR", "DECLVAR_RC", "DECLVAR_CC", "REGEXP_RR", "REGEXP_RC", "REGEXP_CR", "REGEXP_CC", - "CLOSURE", "TYPEOF", "TYPEOFID", "PUTVAR", "DELVAR", "RETREG", "RETUNDEF", "RETCONST", - - "RETCONSTN", "LABEL", "ENDLABEL", "BREAK", "CONTINUE", "TRYCATCH", "ENDTRY", "ENDCATCH", - "ENDFIN", "THROW", "INVLHS", "CSREG", "CSVAR_RR", "CSVAR_CR", "CSVAR_RC", "CSVAR_CC", - "CALL0", "CALL1", "CALL2", "CALL3", "CALL4", "CALL5", "CALL6", "CALL7", - "CALL8", "CALL9", "CALL10", "CALL11", "CALL12", "CALL13", "CALL14", "CALL15", - - "NEWOBJ", "NEWARR", "MPUTOBJ", "MPUTOBJI", "INITSET", "INITGET", "MPUTARR", "MPUTARRI", - "SETALEN", "INITENUM", "NEXTENUM", "NEWTARGET", "DEBUGGER", "NOP", "INVALID", "UNUSED207", - "GETPROPC_RR", "GETPROPC_CR", "GETPROPC_RC", "GETPROPC_CC", "UNUSED212", "UNUSED213", "UNUSED214", "UNUSED215", - "UNUSED216", "UNUSED217", "UNUSED218", "UNUSED219", "UNUSED220", "UNUSED221", "UNUSED222", "UNUSED223", - - "UNUSED224", "UNUSED225", "UNUSED226", "UNUSED227", "UNUSED228", "UNUSED229", "UNUSED230", "UNUSED231", - "UNUSED232", "UNUSED233", "UNUSED234", "UNUSED235", "UNUSED236", "UNUSED237", "UNUSED238", "UNUSED239", - "UNUSED240", "UNUSED241", "UNUSED242", "UNUSED243", "UNUSED244", "UNUSED245", "UNUSED246", "UNUSED247", - "UNUSED248", "UNUSED249", "UNUSED250", "UNUSED251", "UNUSED252", "UNUSED253", "UNUSED254", "UNUSED255" -}; - -typedef struct duk__dprint_state duk__dprint_state; -struct duk__dprint_state { - duk_fixedbuffer *fb; - - /* loop_stack_index could be perhaps be replaced by 'depth', but it's nice - * to not couple these two mechanisms unnecessarily. - */ - duk_hobject *loop_stack[DUK__LOOP_STACK_DEPTH]; - duk_int_t loop_stack_index; - duk_int_t loop_stack_limit; - - duk_int_t depth; - duk_int_t depth_limit; - - duk_bool_t pointer; - duk_bool_t heavy; - duk_bool_t binary; - duk_bool_t follow_proto; - duk_bool_t internal; - duk_bool_t hexdump; -}; - -/* helpers */ -DUK_LOCAL_DECL void duk__print_hstring(duk__dprint_state *st, duk_hstring *k, duk_bool_t quotes); -DUK_LOCAL_DECL void duk__print_hobject(duk__dprint_state *st, duk_hobject *h); -DUK_LOCAL_DECL void duk__print_hbuffer(duk__dprint_state *st, duk_hbuffer *h); -DUK_LOCAL_DECL void duk__print_tval(duk__dprint_state *st, duk_tval *tv); -DUK_LOCAL_DECL void duk__print_instr(duk__dprint_state *st, duk_instr_t ins); -DUK_LOCAL_DECL void duk__print_heaphdr(duk__dprint_state *st, duk_heaphdr *h); -DUK_LOCAL_DECL void duk__print_shared_heaphdr(duk__dprint_state *st, duk_heaphdr *h); -DUK_LOCAL_DECL void duk__print_shared_heaphdr_string(duk__dprint_state *st, duk_heaphdr_string *h); - -DUK_LOCAL void duk__print_shared_heaphdr(duk__dprint_state *st, duk_heaphdr *h) { - duk_fixedbuffer *fb = st->fb; - - if (st->heavy) { - duk_fb_sprintf(fb, "(%p)", (void *) h); - } - - if (!h) { - return; - } - - if (st->binary) { - duk_size_t i; - duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_LBRACKET); - for (i = 0; i < (duk_size_t) sizeof(*h); i++) { - duk_fb_sprintf(fb, "%02lx", (unsigned long) ((duk_uint8_t *) h)[i]); - } - duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_RBRACKET); - } - -#if defined(DUK_USE_REFERENCE_COUNTING) /* currently implicitly also DUK_USE_DOUBLE_LINKED_HEAP */ - if (st->heavy) { - duk_fb_sprintf(fb, - "[h_next=%p,h_prev=%p,h_refcount=%lu,h_flags=%08lx,type=%ld," - "reachable=%ld,temproot=%ld,finalizable=%ld,finalized=%ld]", - (void *) DUK_HEAPHDR_GET_NEXT(NULL, h), - (void *) DUK_HEAPHDR_GET_PREV(NULL, h), - (unsigned long) DUK_HEAPHDR_GET_REFCOUNT(h), - (unsigned long) DUK_HEAPHDR_GET_FLAGS(h), - (long) DUK_HEAPHDR_GET_TYPE(h), - (long) (DUK_HEAPHDR_HAS_REACHABLE(h) ? 1 : 0), - (long) (DUK_HEAPHDR_HAS_TEMPROOT(h) ? 1 : 0), - (long) (DUK_HEAPHDR_HAS_FINALIZABLE(h) ? 1 : 0), - (long) (DUK_HEAPHDR_HAS_FINALIZED(h) ? 1 : 0)); - } -#else - if (st->heavy) { - duk_fb_sprintf(fb, - "[h_next=%p,h_flags=%08lx,type=%ld,reachable=%ld,temproot=%ld,finalizable=%ld,finalized=%ld]", - (void *) DUK_HEAPHDR_GET_NEXT(NULL, h), - (unsigned long) DUK_HEAPHDR_GET_FLAGS(h), - (long) DUK_HEAPHDR_GET_TYPE(h), - (long) (DUK_HEAPHDR_HAS_REACHABLE(h) ? 1 : 0), - (long) (DUK_HEAPHDR_HAS_TEMPROOT(h) ? 1 : 0), - (long) (DUK_HEAPHDR_HAS_FINALIZABLE(h) ? 1 : 0), - (long) (DUK_HEAPHDR_HAS_FINALIZED(h) ? 1 : 0)); - } -#endif -} - -DUK_LOCAL void duk__print_shared_heaphdr_string(duk__dprint_state *st, duk_heaphdr_string *h) { - duk_fixedbuffer *fb = st->fb; - - if (st->heavy) { - duk_fb_sprintf(fb, "(%p)", (void *) h); - } - - if (!h) { - return; - } - - if (st->binary) { - duk_size_t i; - duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_LBRACKET); - for (i = 0; i < (duk_size_t) sizeof(*h); i++) { - duk_fb_sprintf(fb, "%02lx", (unsigned long) ((duk_uint8_t *) h)[i]); - } - duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_RBRACKET); - } - -#if defined(DUK_USE_REFERENCE_COUNTING) - if (st->heavy) { - duk_fb_sprintf(fb, - "[h_refcount=%lu,h_flags=%08lx,type=%ld,reachable=%ld,temproot=%ld,finalizable=%ld,finalized=%ld]", - (unsigned long) DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h), - (unsigned long) DUK_HEAPHDR_GET_FLAGS((duk_heaphdr *) h), - (long) DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) h), - (long) (DUK_HEAPHDR_HAS_REACHABLE((duk_heaphdr *) h) ? 1 : 0), - (long) (DUK_HEAPHDR_HAS_TEMPROOT((duk_heaphdr *) h) ? 1 : 0), - (long) (DUK_HEAPHDR_HAS_FINALIZABLE((duk_heaphdr *) h) ? 1 : 0), - (long) (DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr *) h) ? 1 : 0)); - } -#else - if (st->heavy) { - duk_fb_sprintf(fb, - "[h_flags=%08lx,type=%ld,reachable=%ld,temproot=%ld,finalizable=%ld,finalized=%ld]", - (unsigned long) DUK_HEAPHDR_GET_FLAGS((duk_heaphdr *) h), - (long) DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) h), - (long) (DUK_HEAPHDR_HAS_REACHABLE((duk_heaphdr *) h) ? 1 : 0), - (long) (DUK_HEAPHDR_HAS_TEMPROOT((duk_heaphdr *) h) ? 1 : 0), - (long) (DUK_HEAPHDR_HAS_FINALIZABLE((duk_heaphdr *) h) ? 1 : 0), - (long) (DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr *) h) ? 1 : 0)); - } -#endif -} - -DUK_LOCAL void duk__print_hstring(duk__dprint_state *st, duk_hstring *h, duk_bool_t quotes) { - duk_fixedbuffer *fb = st->fb; - const duk_uint8_t *p; - const duk_uint8_t *p_end; - - /* terminal type: no depth check */ - - if (duk_fb_is_full(fb)) { - return; - } - - duk__print_shared_heaphdr_string(st, &h->hdr); - - if (!h) { - duk_fb_put_cstring(fb, "NULL"); - return; - } - - p = DUK_HSTRING_GET_DATA(h); - p_end = p + DUK_HSTRING_GET_BYTELEN(h); - - if (p_end > p && p[0] == DUK_ASC_UNDERSCORE) { - /* If property key begins with underscore, encode it with - * forced quotes (e.g. "_Foo") to distinguish it from encoded - * internal properties (e.g. \x82Bar -> _Bar). - */ - quotes = 1; - } - - if (quotes) { - duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_DOUBLEQUOTE); - } - while (p < p_end) { - duk_uint8_t ch = *p++; - - /* two special escapes: '\' and '"', other printables as is */ - if (ch == '\\') { - duk_fb_sprintf(fb, "\\\\"); - } else if (ch == '"') { - duk_fb_sprintf(fb, "\\\""); - } else if (ch >= 0x20 && ch <= 0x7e) { - duk_fb_put_byte(fb, ch); - } else if (ch == 0x82 && !quotes) { - /* encode \x82Bar as _Bar if no quotes are - * applied, this is for readable internal keys. - */ - duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_UNDERSCORE); - } else { - duk_fb_sprintf(fb, "\\x%02lx", (unsigned long) ch); - } - } - if (quotes) { - duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_DOUBLEQUOTE); - } -#if defined(DUK_USE_REFERENCE_COUNTING) - /* XXX: limit to quoted strings only, to save keys from being cluttered? */ - duk_fb_sprintf(fb, "/%lu", (unsigned long) DUK_HEAPHDR_GET_REFCOUNT(&h->hdr)); -#endif -} - -#define DUK__COMMA() \ - do { \ - if (first) { \ - first = 0; \ - } else { \ - duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_COMMA); \ - } \ - } while (0) - -DUK_LOCAL void duk__print_hobject(duk__dprint_state *st, duk_hobject *h) { - duk_fixedbuffer *fb = st->fb; - duk_uint_fast32_t i; - duk_tval *tv; - duk_hstring *key; - duk_bool_t first = 1; - const char *brace1 = "{"; - const char *brace2 = "}"; - duk_bool_t pushed_loopstack = 0; - - if (duk_fb_is_full(fb)) { - return; - } - - duk__print_shared_heaphdr(st, &h->hdr); - - if (h && DUK_HOBJECT_HAS_ARRAY_PART(h)) { - brace1 = "["; - brace2 = "]"; - } - - if (!h) { - duk_fb_put_cstring(fb, "NULL"); - goto finished; - } - - if (st->depth >= st->depth_limit) { - const char *subtype = "generic"; - - if (DUK_HOBJECT_IS_COMPFUNC(h)) { - subtype = "compfunc"; - } else if (DUK_HOBJECT_IS_NATFUNC(h)) { - subtype = "natfunc"; - } else if (DUK_HOBJECT_IS_THREAD(h)) { - subtype = "thread"; - } else if (DUK_HOBJECT_IS_BUFOBJ(h)) { - subtype = "bufobj"; - } else if (DUK_HOBJECT_IS_ARRAY(h)) { - subtype = "array"; - } - duk_fb_sprintf(fb, "%sobject/%s %p%s", (const char *) brace1, subtype, (void *) h, (const char *) brace2); - return; - } - - for (i = 0; i < (duk_uint_fast32_t) st->loop_stack_index; i++) { - if (st->loop_stack[i] == h) { - duk_fb_sprintf(fb, "%sLOOP:%p%s", (const char *) brace1, (void *) h, (const char *) brace2); - return; - } - } - - /* after this, return paths should 'goto finished' for decrement */ - st->depth++; - - if (st->loop_stack_index >= st->loop_stack_limit) { - duk_fb_sprintf(fb, "%sOUT-OF-LOOP-STACK%s", (const char *) brace1, (const char *) brace2); - goto finished; - } - st->loop_stack[st->loop_stack_index++] = h; - pushed_loopstack = 1; - - /* - * Notation: double underscore used for internal properties which are not - * stored in the property allocation (e.g. '__valstack'). - */ - - duk_fb_put_cstring(fb, brace1); - - if (DUK_HOBJECT_GET_PROPS(NULL, h)) { - duk_uint32_t a_limit; - - a_limit = DUK_HOBJECT_GET_ASIZE(h); - if (st->internal) { - /* dump all allocated entries, unused entries print as 'unused', - * note that these may extend beyond current 'length' and look - * a bit funny. - */ - } else { - /* leave out trailing 'unused' elements */ - while (a_limit > 0) { - tv = DUK_HOBJECT_A_GET_VALUE_PTR(NULL, h, a_limit - 1); - if (!DUK_TVAL_IS_UNUSED(tv)) { - break; - } - a_limit--; - } - } - - for (i = 0; i < a_limit; i++) { - tv = DUK_HOBJECT_A_GET_VALUE_PTR(NULL, h, i); - DUK__COMMA(); - duk__print_tval(st, tv); - } - for (i = 0; i < DUK_HOBJECT_GET_ENEXT(h); i++) { - key = DUK_HOBJECT_E_GET_KEY(NULL, h, i); - if (!key) { - continue; - } - if (!st->internal && DUK_HSTRING_HAS_HIDDEN(key)) { - continue; - } - DUK__COMMA(); - duk__print_hstring(st, key, 0); - duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_COLON); - if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(NULL, h, i)) { - duk_fb_sprintf(fb, - "[get:%p,set:%p]", - (void *) DUK_HOBJECT_E_GET_VALUE(NULL, h, i).a.get, - (void *) DUK_HOBJECT_E_GET_VALUE(NULL, h, i).a.set); - } else { - tv = &DUK_HOBJECT_E_GET_VALUE(NULL, h, i).v; - duk__print_tval(st, tv); - } - if (st->heavy) { - duk_fb_sprintf(fb, "<%02lx>", (unsigned long) DUK_HOBJECT_E_GET_FLAGS(NULL, h, i)); - } - } - } - if (st->internal) { - if (DUK_HOBJECT_IS_ARRAY(h)) { - DUK__COMMA(); - duk_fb_sprintf(fb, "__array:true"); - } - if (DUK_HOBJECT_HAS_EXTENSIBLE(h)) { - DUK__COMMA(); - duk_fb_sprintf(fb, "__extensible:true"); - } - if (DUK_HOBJECT_HAS_CONSTRUCTABLE(h)) { - DUK__COMMA(); - duk_fb_sprintf(fb, "__constructable:true"); - } - if (DUK_HOBJECT_HAS_BOUNDFUNC(h)) { - DUK__COMMA(); - duk_fb_sprintf(fb, "__boundfunc:true"); - } - if (DUK_HOBJECT_HAS_COMPFUNC(h)) { - DUK__COMMA(); - duk_fb_sprintf(fb, "__compfunc:true"); - } - if (DUK_HOBJECT_HAS_NATFUNC(h)) { - DUK__COMMA(); - duk_fb_sprintf(fb, "__natfunc:true"); - } - if (DUK_HOBJECT_HAS_BUFOBJ(h)) { - DUK__COMMA(); - duk_fb_sprintf(fb, "__bufobj:true"); - } - if (DUK_HOBJECT_IS_THREAD(h)) { - DUK__COMMA(); - duk_fb_sprintf(fb, "__thread:true"); - } - if (DUK_HOBJECT_HAS_ARRAY_PART(h)) { - DUK__COMMA(); - duk_fb_sprintf(fb, "__array_part:true"); - } - if (DUK_HOBJECT_HAS_STRICT(h)) { - DUK__COMMA(); - duk_fb_sprintf(fb, "__strict:true"); - } - if (DUK_HOBJECT_HAS_NOTAIL(h)) { - DUK__COMMA(); - duk_fb_sprintf(fb, "__notail:true"); - } - if (DUK_HOBJECT_HAS_NEWENV(h)) { - DUK__COMMA(); - duk_fb_sprintf(fb, "__newenv:true"); - } - if (DUK_HOBJECT_HAS_NAMEBINDING(h)) { - DUK__COMMA(); - duk_fb_sprintf(fb, "__namebinding:true"); - } - if (DUK_HOBJECT_HAS_CREATEARGS(h)) { - DUK__COMMA(); - duk_fb_sprintf(fb, "__createargs:true"); - } - if (DUK_HOBJECT_HAS_EXOTIC_ARRAY(h)) { - DUK__COMMA(); - duk_fb_sprintf(fb, "__exotic_array:true"); - } - if (DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(h)) { - DUK__COMMA(); - duk_fb_sprintf(fb, "__exotic_stringobj:true"); - } - if (DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(h)) { - DUK__COMMA(); - duk_fb_sprintf(fb, "__exotic_arguments:true"); - } - if (DUK_HOBJECT_IS_BUFOBJ(h)) { - DUK__COMMA(); - duk_fb_sprintf(fb, "__exotic_bufobj:true"); - } - if (DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(h)) { - DUK__COMMA(); - duk_fb_sprintf(fb, "__exotic_proxyobj:true"); - } - } - - if (st->internal && DUK_HOBJECT_IS_ARRAY(h)) { - duk_harray *a = (duk_harray *) h; - DUK__COMMA(); - duk_fb_sprintf(fb, "__length:%ld", (long) a->length); - DUK__COMMA(); - duk_fb_sprintf(fb, "__length_nonwritable:%ld", (long) a->length_nonwritable); - } else if (st->internal && DUK_HOBJECT_IS_COMPFUNC(h)) { - duk_hcompfunc *f = (duk_hcompfunc *) h; - DUK__COMMA(); - duk_fb_put_cstring(fb, "__data:"); - duk__print_hbuffer(st, (duk_hbuffer *) DUK_HCOMPFUNC_GET_DATA(NULL, f)); - DUK__COMMA(); - duk_fb_put_cstring(fb, "__lexenv:"); - duk__print_hobject(st, DUK_HCOMPFUNC_GET_LEXENV(NULL, f)); - DUK__COMMA(); - duk_fb_put_cstring(fb, "__varenv:"); - duk__print_hobject(st, DUK_HCOMPFUNC_GET_VARENV(NULL, f)); - DUK__COMMA(); - duk_fb_sprintf(fb, "__nregs:%ld", (long) f->nregs); - DUK__COMMA(); - duk_fb_sprintf(fb, "__nargs:%ld", (long) f->nargs); -#if defined(DUK_USE_DEBUGGER_SUPPORT) - DUK__COMMA(); - duk_fb_sprintf(fb, "__start_line:%ld", (long) f->start_line); - DUK__COMMA(); - duk_fb_sprintf(fb, "__end_line:%ld", (long) f->end_line); -#endif - DUK__COMMA(); - duk_fb_put_cstring(fb, "__data:"); - duk__print_hbuffer(st, (duk_hbuffer *) DUK_HCOMPFUNC_GET_DATA(NULL, f)); - } else if (st->internal && DUK_HOBJECT_IS_NATFUNC(h)) { - duk_hnatfunc *f = (duk_hnatfunc *) h; - DUK__COMMA(); - duk_fb_sprintf(fb, "__func:"); - duk_fb_put_funcptr(fb, (duk_uint8_t *) &f->func, sizeof(f->func)); - DUK__COMMA(); - duk_fb_sprintf(fb, "__nargs:%ld", (long) f->nargs); - DUK__COMMA(); - duk_fb_sprintf(fb, "__magic:%ld", (long) f->magic); - } else if (st->internal && DUK_HOBJECT_IS_DECENV(h)) { - duk_hdecenv *e = (duk_hdecenv *) h; - DUK__COMMA(); - duk_fb_sprintf(fb, "__thread:"); - duk__print_hobject(st, (duk_hobject *) e->thread); - DUK__COMMA(); - duk_fb_sprintf(fb, "__varmap:"); - duk__print_hobject(st, (duk_hobject *) e->varmap); - DUK__COMMA(); - duk_fb_sprintf(fb, "__regbase_byteoff:%ld", (long) e->regbase_byteoff); - } else if (st->internal && DUK_HOBJECT_IS_OBJENV(h)) { - duk_hobjenv *e = (duk_hobjenv *) h; - DUK__COMMA(); - duk_fb_sprintf(fb, "__target:"); - duk__print_hobject(st, (duk_hobject *) e->target); - DUK__COMMA(); - duk_fb_sprintf(fb, "__has_this:%ld", (long) e->has_this); -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) - } else if (st->internal && DUK_HOBJECT_IS_BUFOBJ(h)) { - duk_hbufobj *b = (duk_hbufobj *) h; - DUK__COMMA(); - duk_fb_sprintf(fb, "__buf:"); - duk__print_hbuffer(st, (duk_hbuffer *) b->buf); - DUK__COMMA(); - duk_fb_sprintf(fb, "__buf_prop:"); - duk__print_hobject(st, (duk_hobject *) b->buf_prop); - DUK__COMMA(); - duk_fb_sprintf(fb, "__offset:%ld", (long) b->offset); - DUK__COMMA(); - duk_fb_sprintf(fb, "__length:%ld", (long) b->length); - DUK__COMMA(); - duk_fb_sprintf(fb, "__shift:%ld", (long) b->shift); - DUK__COMMA(); - duk_fb_sprintf(fb, "__elemtype:%ld", (long) b->elem_type); -#endif - } else if (st->internal && DUK_HOBJECT_IS_PROXY(h)) { - duk_hproxy *p = (duk_hproxy *) h; - DUK__COMMA(); - duk_fb_sprintf(fb, "__target:"); - duk__print_hobject(st, p->target); - DUK__COMMA(); - duk_fb_sprintf(fb, "__handler:"); - duk__print_hobject(st, p->handler); - } else if (st->internal && DUK_HOBJECT_IS_THREAD(h)) { - duk_hthread *t = (duk_hthread *) h; - DUK__COMMA(); - duk_fb_sprintf(fb, "__ptr_curr_pc:%p", (void *) t->ptr_curr_pc); - DUK__COMMA(); - duk_fb_sprintf(fb, "__heap:%p", (void *) t->heap); - DUK__COMMA(); - duk_fb_sprintf(fb, "__strict:%ld", (long) t->strict); - DUK__COMMA(); - duk_fb_sprintf(fb, "__state:%ld", (long) t->state); - DUK__COMMA(); - duk_fb_sprintf(fb, "__unused1:%ld", (long) t->unused1); - DUK__COMMA(); - duk_fb_sprintf(fb, "__unused2:%ld", (long) t->unused2); - DUK__COMMA(); - duk_fb_sprintf(fb, "__valstack:%p", (void *) t->valstack); - DUK__COMMA(); - duk_fb_sprintf(fb, "__valstack_end:%p/%ld", (void *) t->valstack_end, (long) (t->valstack_end - t->valstack)); - DUK__COMMA(); - duk_fb_sprintf(fb, - "__valstack_alloc_end:%p/%ld", - (void *) t->valstack_alloc_end, - (long) (t->valstack_alloc_end - t->valstack)); - DUK__COMMA(); - duk_fb_sprintf(fb, - "__valstack_bottom:%p/%ld", - (void *) t->valstack_bottom, - (long) (t->valstack_bottom - t->valstack)); - DUK__COMMA(); - duk_fb_sprintf(fb, "__valstack_top:%p/%ld", (void *) t->valstack_top, (long) (t->valstack_top - t->valstack)); - DUK__COMMA(); - duk_fb_sprintf(fb, "__callstack_curr:%p", (void *) t->callstack_curr); - DUK__COMMA(); - duk_fb_sprintf(fb, "__callstack_top:%ld", (long) t->callstack_top); - DUK__COMMA(); - duk_fb_sprintf(fb, "__callstack_preventcount:%ld", (long) t->callstack_preventcount); - DUK__COMMA(); - duk_fb_sprintf(fb, "__resumer:"); - duk__print_hobject(st, (duk_hobject *) t->resumer); - DUK__COMMA(); - duk_fb_sprintf(fb, "__compile_ctx:%p", (void *) t->compile_ctx); -#if defined(DUK_USE_INTERRUPT_COUNTER) - DUK__COMMA(); - duk_fb_sprintf(fb, "__interrupt_counter:%ld", (long) t->interrupt_counter); - DUK__COMMA(); - duk_fb_sprintf(fb, "__interrupt_init:%ld", (long) t->interrupt_init); -#endif - - /* XXX: print built-ins array? */ - } -#if defined(DUK_USE_REFERENCE_COUNTING) - if (st->internal) { - DUK__COMMA(); - duk_fb_sprintf(fb, "__refcount:%lu", (unsigned long) DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h)); - } -#endif - if (st->internal) { - DUK__COMMA(); - duk_fb_sprintf(fb, "__class:%ld", (long) DUK_HOBJECT_GET_CLASS_NUMBER(h)); - } - - DUK__COMMA(); - duk_fb_sprintf(fb, "__heapptr:%p", (void *) h); /* own pointer */ - - /* prototype should be last, for readability */ - if (DUK_HOBJECT_GET_PROTOTYPE(NULL, h)) { - if (st->follow_proto) { - DUK__COMMA(); - duk_fb_put_cstring(fb, "__prototype:"); - duk__print_hobject(st, DUK_HOBJECT_GET_PROTOTYPE(NULL, h)); - } else { - DUK__COMMA(); - duk_fb_sprintf(fb, "__prototype:%p", (void *) DUK_HOBJECT_GET_PROTOTYPE(NULL, h)); - } - } - - duk_fb_put_cstring(fb, brace2); - -#if defined(DUK_USE_HOBJECT_HASH_PART) - if (st->heavy && DUK_HOBJECT_GET_HSIZE(h) > 0) { - duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_LANGLE); - for (i = 0; i < DUK_HOBJECT_GET_HSIZE(h); i++) { - duk_uint_t h_idx = DUK_HOBJECT_H_GET_INDEX(NULL, h, i); - if (i > 0) { - duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_COMMA); - } - if (h_idx == DUK_HOBJECT_HASHIDX_UNUSED) { - duk_fb_sprintf(fb, "u"); - } else if (h_idx == DUK_HOBJECT_HASHIDX_DELETED) { - duk_fb_sprintf(fb, "d"); - } else { - duk_fb_sprintf(fb, "%ld", (long) h_idx); - } - } - duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_RANGLE); - } -#endif - -finished: - st->depth--; - if (pushed_loopstack) { - st->loop_stack_index--; - st->loop_stack[st->loop_stack_index] = NULL; - } -} - -DUK_LOCAL void duk__print_hbuffer(duk__dprint_state *st, duk_hbuffer *h) { - duk_fixedbuffer *fb = st->fb; - duk_size_t i, n; - duk_uint8_t *p; - - if (duk_fb_is_full(fb)) { - return; - } - - /* terminal type: no depth check */ - - if (!h) { - duk_fb_put_cstring(fb, "NULL"); - return; - } - - if (DUK_HBUFFER_HAS_DYNAMIC(h)) { - if (DUK_HBUFFER_HAS_EXTERNAL(h)) { - duk_hbuffer_external *g = (duk_hbuffer_external *) h; - duk_fb_sprintf(fb, - "buffer:external:%p:%ld", - (void *) DUK_HBUFFER_EXTERNAL_GET_DATA_PTR(NULL, g), - (long) DUK_HBUFFER_EXTERNAL_GET_SIZE(g)); - } else { - duk_hbuffer_dynamic *g = (duk_hbuffer_dynamic *) h; - duk_fb_sprintf(fb, - "buffer:dynamic:%p:%ld", - (void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(NULL, g), - (long) DUK_HBUFFER_DYNAMIC_GET_SIZE(g)); - } - } else { - duk_fb_sprintf(fb, "buffer:fixed:%ld", (long) DUK_HBUFFER_GET_SIZE(h)); - } - -#if defined(DUK_USE_REFERENCE_COUNTING) - duk_fb_sprintf(fb, "/%lu", (unsigned long) DUK_HEAPHDR_GET_REFCOUNT(&h->hdr)); -#endif - - if (st->hexdump) { - duk_fb_sprintf(fb, "=["); - n = DUK_HBUFFER_GET_SIZE(h); - p = (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(NULL, h); - for (i = 0; i < n; i++) { - duk_fb_sprintf(fb, "%02lx", (unsigned long) p[i]); - } - duk_fb_sprintf(fb, "]"); - } -} - -DUK_LOCAL void duk__print_heaphdr(duk__dprint_state *st, duk_heaphdr *h) { - duk_fixedbuffer *fb = st->fb; - - if (duk_fb_is_full(fb)) { - return; - } - - if (!h) { - duk_fb_put_cstring(fb, "NULL"); - return; - } - - switch (DUK_HEAPHDR_GET_TYPE(h)) { - case DUK_HTYPE_STRING: - duk__print_hstring(st, (duk_hstring *) h, 1); - break; - case DUK_HTYPE_OBJECT: - duk__print_hobject(st, (duk_hobject *) h); - break; - case DUK_HTYPE_BUFFER: - duk__print_hbuffer(st, (duk_hbuffer *) h); - break; - default: - duk_fb_sprintf(fb, "[unknown htype %ld]", (long) DUK_HEAPHDR_GET_TYPE(h)); - break; - } -} - -DUK_LOCAL void duk__print_tval(duk__dprint_state *st, duk_tval *tv) { - duk_fixedbuffer *fb = st->fb; - - if (duk_fb_is_full(fb)) { - return; - } - - /* depth check is done when printing an actual type */ - - if (st->heavy) { - duk_fb_sprintf(fb, "(%p)", (void *) tv); - } - - if (!tv) { - duk_fb_put_cstring(fb, "NULL"); - return; - } - - if (st->binary) { - duk_size_t i; - duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_LBRACKET); - for (i = 0; i < (duk_size_t) sizeof(*tv); i++) { - duk_fb_sprintf(fb, "%02lx", (unsigned long) ((duk_uint8_t *) tv)[i]); - } - duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_RBRACKET); - } - - if (st->heavy) { - duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_LANGLE); - } - switch (DUK_TVAL_GET_TAG(tv)) { - case DUK_TAG_UNDEFINED: { - duk_fb_put_cstring(fb, "undefined"); - break; - } - case DUK_TAG_UNUSED: { - duk_fb_put_cstring(fb, "unused"); - break; - } - case DUK_TAG_NULL: { - duk_fb_put_cstring(fb, "null"); - break; - } - case DUK_TAG_BOOLEAN: { - duk_fb_put_cstring(fb, DUK_TVAL_GET_BOOLEAN(tv) ? "true" : "false"); - break; - } - case DUK_TAG_STRING: { - /* Note: string is a terminal heap object, so no depth check here */ - duk__print_hstring(st, DUK_TVAL_GET_STRING(tv), 1); - break; - } - case DUK_TAG_OBJECT: { - duk__print_hobject(st, DUK_TVAL_GET_OBJECT(tv)); - break; - } - case DUK_TAG_BUFFER: { - duk__print_hbuffer(st, DUK_TVAL_GET_BUFFER(tv)); - break; - } - case DUK_TAG_POINTER: { - duk_fb_sprintf(fb, "pointer:%p", (void *) DUK_TVAL_GET_POINTER(tv)); - break; - } - case DUK_TAG_LIGHTFUNC: { - duk_c_function func; - duk_small_uint_t lf_flags; - - DUK_TVAL_GET_LIGHTFUNC(tv, func, lf_flags); - duk_fb_sprintf(fb, "lightfunc:"); - duk_fb_put_funcptr(fb, (duk_uint8_t *) &func, sizeof(func)); - duk_fb_sprintf(fb, ":%04lx", (long) lf_flags); - break; - } -#if defined(DUK_USE_FASTINT) - case DUK_TAG_FASTINT: - DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv)); - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); - duk_fb_sprintf(fb, "%.18g_F", (double) DUK_TVAL_GET_NUMBER(tv)); - break; -#endif - default: { - /* IEEE double is approximately 16 decimal digits; print a couple extra */ - DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv)); - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); - duk_fb_sprintf(fb, "%.18g", (double) DUK_TVAL_GET_NUMBER(tv)); - break; - } - } - if (st->heavy) { - duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_RANGLE); - } -} - -DUK_LOCAL void duk__print_instr(duk__dprint_state *st, duk_instr_t ins) { - duk_fixedbuffer *fb = st->fb; - duk_small_int_t op; - const char *op_name; - - op = (duk_small_int_t) DUK_DEC_OP(ins); - op_name = duk__bc_optab[op]; - - /* XXX: option to fix opcode length so it lines up nicely */ - - if (op == DUK_OP_JUMP) { - duk_int_t diff1 = (duk_int_t) (DUK_DEC_ABC(ins) - DUK_BC_JUMP_BIAS); /* from next pc */ - duk_int_t diff2 = diff1 + 1; /* from curr pc */ - - duk_fb_sprintf(fb, - "%s %ld (to pc%c%ld)", - (const char *) op_name, - (long) diff1, - (int) (diff2 >= 0 ? '+' : '-'), /* char format: use int */ - (long) (diff2 >= 0 ? diff2 : -diff2)); - } else { - duk_fb_sprintf(fb, - "%s %ld, %ld, %ld", - (const char *) op_name, - (long) DUK_DEC_A(ins), - (long) DUK_DEC_B(ins), - (long) DUK_DEC_C(ins)); - } -} - -DUK_LOCAL void duk__print_opcode(duk__dprint_state *st, duk_small_int_t opcode) { - duk_fixedbuffer *fb = st->fb; - - if (opcode < DUK_BC_OP_MIN || opcode > DUK_BC_OP_MAX) { - duk_fb_sprintf(fb, "?(%ld)", (long) opcode); - } else { - duk_fb_sprintf(fb, "%s", (const char *) duk__bc_optab[opcode]); - } -} - -DUK_LOCAL void duk__print_catcher(duk__dprint_state *st, duk_catcher *cat) { - duk_fixedbuffer *fb = st->fb; - - if (duk_fb_is_full(fb)) { - return; - } - - if (!cat) { - duk_fb_put_cstring(fb, "NULL"); - return; - } - - duk_fb_sprintf(fb, - "[catcher ptr=%p parent=%p varname=%p pc_base=%p, idx_base=%ld, flags=0x%08lx]", - (void *) cat, - (void *) cat->parent, - (void *) cat->h_varname, - (void *) cat->pc_base, - (long) cat->idx_base, - (unsigned long) cat->flags); -} - -DUK_LOCAL void duk__print_activation(duk__dprint_state *st, duk_activation *act) { - duk_fixedbuffer *fb = st->fb; - - if (duk_fb_is_full(fb)) { - return; - } - - if (!act) { - duk_fb_put_cstring(fb, "NULL"); - return; - } - - /* prev_caller: conditional, omitted on purpose, it's rarely used. */ - /* prev_line: conditional, omitted on purpose (but would be nice). */ - duk_fb_sprintf(fb, - "[activation ptr=%p tv_func= func=%p parent=%p var_env=%p lex_env=%p cat=%p curr_pc=%p " - "bottom_byteoff=%ld retval_byteoff=%ld reserve_byteoff=%ld flags=%ld]", - (void *) act, - (void *) act->func, - (void *) act->parent, - (void *) act->var_env, - (void *) act->lex_env, - (void *) act->cat, - (void *) act->curr_pc, - (long) act->bottom_byteoff, - (long) act->retval_byteoff, - (long) act->reserve_byteoff, - (long) act->flags); -} - -DUK_INTERNAL duk_int_t duk_debug_vsnprintf(char *str, duk_size_t size, const char *format, va_list ap) { - duk_fixedbuffer fb; - const char *p = format; - const char *p_end = p + DUK_STRLEN(format); - duk_int_t retval; - - duk_memzero(&fb, sizeof(fb)); - fb.buffer = (duk_uint8_t *) str; - fb.length = size; - fb.offset = 0; - fb.truncated = 0; - - while (p < p_end) { - char ch = *p++; - const char *p_begfmt = NULL; - duk_bool_t got_exclamation = 0; - duk_bool_t got_long = 0; /* %lf, %ld etc */ - duk__dprint_state st; - - if (ch != DUK_ASC_PERCENT) { - duk_fb_put_byte(&fb, (duk_uint8_t) ch); - continue; - } - - /* - * Format tag parsing. Since we don't understand all the - * possible format tags allowed, we just scan for a terminating - * specifier and keep track of relevant modifiers that we do - * understand. See man 3 printf. - */ - - duk_memzero(&st, sizeof(st)); - st.fb = &fb; - st.depth = 0; - st.depth_limit = 1; - st.loop_stack_index = 0; - st.loop_stack_limit = DUK__LOOP_STACK_DEPTH; - - p_begfmt = p - 1; - while (p < p_end) { - ch = *p++; - - if (ch == DUK_ASC_STAR) { - /* unsupported: would consume multiple args */ - goto format_error; - } else if (ch == DUK_ASC_PERCENT) { - duk_fb_put_byte(&fb, (duk_uint8_t) DUK_ASC_PERCENT); - break; - } else if (ch == DUK_ASC_EXCLAMATION) { - got_exclamation = 1; - } else if (!got_exclamation && ch == DUK_ASC_LC_L) { - got_long = 1; - } else if (got_exclamation && ch == DUK_ASC_LC_D) { - st.depth_limit = DUK__DEEP_DEPTH_LIMIT; - } else if (got_exclamation && ch == DUK_ASC_LC_P) { - st.follow_proto = 1; - } else if (got_exclamation && ch == DUK_ASC_LC_I) { - st.internal = 1; - } else if (got_exclamation && ch == DUK_ASC_LC_X) { - st.hexdump = 1; - } else if (got_exclamation && ch == DUK_ASC_LC_H) { - st.heavy = 1; - } else if (got_exclamation && ch == DUK_ASC_ATSIGN) { - st.pointer = 1; - } else if (got_exclamation && ch == DUK_ASC_HASH) { - st.binary = 1; - } else if (got_exclamation && ch == DUK_ASC_UC_T) { - duk_tval *t = va_arg(ap, duk_tval *); - if (st.pointer && !st.heavy) { - duk_fb_sprintf(&fb, "(%p)", (void *) t); - } - duk__print_tval(&st, t); - break; - } else if (got_exclamation && ch == DUK_ASC_UC_O) { - duk_heaphdr *t = va_arg(ap, duk_heaphdr *); - if (st.pointer && !st.heavy) { - duk_fb_sprintf(&fb, "(%p)", (void *) t); - } - duk__print_heaphdr(&st, t); - break; - } else if (got_exclamation && ch == DUK_ASC_UC_I) { - duk_instr_t t = va_arg(ap, duk_instr_t); - duk__print_instr(&st, t); - break; - } else if (got_exclamation && ch == DUK_ASC_UC_X) { - long t = va_arg(ap, long); - duk__print_opcode(&st, (duk_small_int_t) t); - break; - } else if (got_exclamation && ch == DUK_ASC_UC_C) { - duk_catcher *t = va_arg(ap, duk_catcher *); - duk__print_catcher(&st, t); - break; - } else if (got_exclamation && ch == DUK_ASC_UC_A) { - duk_activation *t = va_arg(ap, duk_activation *); - duk__print_activation(&st, t); - break; - } else if (!got_exclamation && strchr(DUK__ALLOWED_STANDARD_SPECIFIERS, (int) ch)) { - char fmtbuf[DUK__MAX_FORMAT_TAG_LENGTH]; - duk_size_t fmtlen; - - DUK_ASSERT(p >= p_begfmt); - fmtlen = (duk_size_t) (p - p_begfmt); - if (fmtlen >= sizeof(fmtbuf)) { - /* format is too large, abort */ - goto format_error; - } - duk_memzero(fmtbuf, sizeof(fmtbuf)); - duk_memcpy(fmtbuf, p_begfmt, fmtlen); - - /* assume exactly 1 arg, which is why '*' is forbidden; arg size still - * depends on type though. - */ - - if (ch == DUK_ASC_LC_F || ch == DUK_ASC_LC_G || ch == DUK_ASC_LC_E) { - /* %f and %lf both consume a 'long' */ - double arg = va_arg(ap, double); - duk_fb_sprintf(&fb, fmtbuf, arg); - } else if (ch == DUK_ASC_LC_D && got_long) { - /* %ld */ - long arg = va_arg(ap, long); - duk_fb_sprintf(&fb, fmtbuf, arg); - } else if (ch == DUK_ASC_LC_D) { - /* %d; only 16 bits are guaranteed */ - int arg = va_arg(ap, int); - duk_fb_sprintf(&fb, fmtbuf, arg); - } else if (ch == DUK_ASC_LC_U && got_long) { - /* %lu */ - unsigned long arg = va_arg(ap, unsigned long); - duk_fb_sprintf(&fb, fmtbuf, arg); - } else if (ch == DUK_ASC_LC_U) { - /* %u; only 16 bits are guaranteed */ - unsigned int arg = va_arg(ap, unsigned int); - duk_fb_sprintf(&fb, fmtbuf, arg); - } else if (ch == DUK_ASC_LC_X && got_long) { - /* %lx */ - unsigned long arg = va_arg(ap, unsigned long); - duk_fb_sprintf(&fb, fmtbuf, arg); - } else if (ch == DUK_ASC_LC_X) { - /* %x; only 16 bits are guaranteed */ - unsigned int arg = va_arg(ap, unsigned int); - duk_fb_sprintf(&fb, fmtbuf, arg); - } else if (ch == DUK_ASC_LC_S) { - /* %s */ - const char *arg = va_arg(ap, const char *); - if (arg == NULL) { - /* '%s' and NULL is not portable, so special case - * it for debug printing. - */ - duk_fb_sprintf(&fb, "NULL"); - } else { - duk_fb_sprintf(&fb, fmtbuf, arg); - } - } else if (ch == DUK_ASC_LC_P) { - /* %p */ - void *arg = va_arg(ap, void *); - if (arg == NULL) { - /* '%p' and NULL is portable, but special case it - * anyway to get a standard NULL marker in logs. - */ - duk_fb_sprintf(&fb, "NULL"); - } else { - duk_fb_sprintf(&fb, fmtbuf, arg); - } - } else if (ch == DUK_ASC_LC_C) { - /* '%c', passed concretely as int */ - int arg = va_arg(ap, int); - duk_fb_sprintf(&fb, fmtbuf, arg); - } else { - /* Should not happen. */ - duk_fb_sprintf(&fb, "INVALID-FORMAT(%s)", (const char *) fmtbuf); - } - break; - } else { - /* ignore */ - } - } - } - goto done; - -format_error: - duk_fb_put_cstring(&fb, "FMTERR"); - /* fall through */ - -done: - retval = (duk_int_t) fb.offset; - duk_fb_put_byte(&fb, (duk_uint8_t) 0); - - /* return total chars written excluding terminator */ - return retval; -} - -#if 0 /*unused*/ -DUK_INTERNAL duk_int_t duk_debug_snprintf(char *str, duk_size_t size, const char *format, ...) { - duk_int_t retval; - va_list ap; - va_start(ap, format); - retval = duk_debug_vsnprintf(str, size, format, ap); - va_end(ap); - return retval; -} -#endif - -/* Formatting function pointers is tricky: there is no standard pointer for - * function pointers and the size of a function pointer may depend on the - * specific pointer type. This helper formats a function pointer based on - * its memory layout to get something useful on most platforms. - */ -DUK_INTERNAL void duk_debug_format_funcptr(char *buf, duk_size_t buf_size, duk_uint8_t *fptr, duk_size_t fptr_size) { - duk_size_t i; - duk_uint8_t *p = (duk_uint8_t *) buf; - duk_uint8_t *p_end = (duk_uint8_t *) (buf + buf_size - 1); - - DUK_ASSERT(buf != NULL); - duk_memzero(buf, buf_size); - - for (i = 0; i < fptr_size; i++) { - duk_int_t left = (duk_int_t) (p_end - p); - duk_uint8_t ch; - if (left <= 0) { - break; - } - - /* Quite approximate but should be useful for little and big endian. */ -#if defined(DUK_USE_INTEGER_BE) - ch = fptr[i]; -#else - ch = fptr[fptr_size - 1 - i]; -#endif - p += DUK_SNPRINTF((char *) p, (duk_size_t) left, "%02lx", (unsigned long) ch); - } -} - -#endif /* DUK_USE_DEBUG */ - -/* automatic undefs */ -#undef DUK__ALLOWED_STANDARD_SPECIFIERS -#undef DUK__COMMA -#undef DUK__DEEP_DEPTH_LIMIT -#undef DUK__LOOP_STACK_DEPTH -#undef DUK__MAX_FORMAT_TAG_LENGTH -/* - * Duktape debugger - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_DEBUGGER_SUPPORT) - -/* - * Assert helpers - */ - -#if defined(DUK_USE_ASSERTIONS) -#define DUK__DBG_TPORT_ENTER() \ - do { \ - DUK_ASSERT(heap->dbg_calling_transport == 0); \ - heap->dbg_calling_transport = 1; \ - } while (0) -#define DUK__DBG_TPORT_EXIT() \ - do { \ - DUK_ASSERT(heap->dbg_calling_transport == 1); \ - heap->dbg_calling_transport = 0; \ - } while (0) -#else -#define DUK__DBG_TPORT_ENTER() \ - do { \ - } while (0) -#define DUK__DBG_TPORT_EXIT() \ - do { \ - } while (0) -#endif - -/* - * Helper structs - */ - -typedef union { - void *p; - duk_uint_t b[1]; - /* Use b[] to access the size of the union, which is strictly not - * correct. Can't use fixed size unless there's feature detection - * for pointer byte size. - */ -} duk__ptr_union; - -/* - * Detach handling - */ - -#define DUK__SET_CONN_BROKEN(thr, reason) \ - do { \ - /* For now shared handler is fine. */ \ - duk__debug_do_detach1((thr)->heap, (reason)); \ - } while (0) - -DUK_LOCAL void duk__debug_do_detach1(duk_heap *heap, duk_int_t reason) { - /* Can be called multiple times with no harm. Mark the transport - * bad (dbg_read_cb == NULL) and clear state except for the detached - * callback and the udata field. The detached callback is delayed - * to the message loop so that it can be called between messages; - * this avoids corner cases related to immediate debugger reattach - * inside the detached callback. - */ - - if (heap->dbg_detaching) { - DUK_D(DUK_DPRINT("debugger already detaching, ignore detach1")); - return; - } - - DUK_D(DUK_DPRINT("debugger transport detaching, marking transport broken")); - - heap->dbg_detaching = 1; /* prevent multiple in-progress detaches */ - - if (heap->dbg_write_cb != NULL) { - duk_hthread *thr; - - thr = heap->heap_thread; - DUK_ASSERT(thr != NULL); - - duk_debug_write_notify(thr, DUK_DBG_CMD_DETACHING); - duk_debug_write_int(thr, reason); - duk_debug_write_eom(thr); - } - - heap->dbg_read_cb = NULL; - heap->dbg_write_cb = NULL; - heap->dbg_peek_cb = NULL; - heap->dbg_read_flush_cb = NULL; - heap->dbg_write_flush_cb = NULL; - heap->dbg_request_cb = NULL; - /* heap->dbg_detached_cb: keep */ - /* heap->dbg_udata: keep */ - /* heap->dbg_processing: keep on purpose to avoid debugger re-entry in detaching state */ - heap->dbg_state_dirty = 0; - heap->dbg_force_restart = 0; - heap->dbg_pause_flags = 0; - heap->dbg_pause_act = NULL; - heap->dbg_pause_startline = 0; - heap->dbg_have_next_byte = 0; - duk_debug_clear_paused(heap); /* XXX: some overlap with field inits above */ - heap->dbg_state_dirty = 0; /* XXX: clear_paused sets dirty; rework? */ - - /* Ensure there are no stale active breakpoint pointers. - * Breakpoint list is currently kept - we could empty it - * here but we'd need to handle refcounts correctly, and - * we'd need a 'thr' reference for that. - * - * XXX: clear breakpoint on either attach or detach? - */ - heap->dbg_breakpoints_active[0] = (duk_breakpoint *) NULL; -} - -DUK_LOCAL void duk__debug_do_detach2(duk_heap *heap) { - duk_debug_detached_function detached_cb; - void *detached_udata; - duk_hthread *thr; - - thr = heap->heap_thread; - if (thr == NULL) { - DUK_ASSERT(heap->dbg_detached_cb == NULL); - return; - } - - /* Safe to call multiple times. */ - - detached_cb = heap->dbg_detached_cb; - detached_udata = heap->dbg_udata; - heap->dbg_detached_cb = NULL; - heap->dbg_udata = NULL; - - if (detached_cb) { - /* Careful here: state must be wiped before the call - * so that we can cleanly handle a re-attach from - * inside the callback. - */ - DUK_D(DUK_DPRINT("detached during message loop, delayed call to detached_cb")); - detached_cb(thr, detached_udata); - } - - heap->dbg_detaching = 0; -} - -DUK_INTERNAL void duk_debug_do_detach(duk_heap *heap) { - duk__debug_do_detach1(heap, 0); - duk__debug_do_detach2(heap); -} - -/* Called on a read/write error: NULL all callbacks except the detached - * callback so that we never accidentally call them after a read/write - * error has been indicated. This is especially important for the transport - * I/O callbacks to fulfill guaranteed callback semantics. - */ -DUK_LOCAL void duk__debug_null_most_callbacks(duk_hthread *thr) { - duk_heap *heap; - - DUK_ASSERT(thr != NULL); - - heap = thr->heap; - DUK_D(DUK_DPRINT("transport read/write error, NULL all callbacks expected detached")); - heap->dbg_read_cb = NULL; - heap->dbg_write_cb = NULL; /* this is especially critical to avoid another write call in detach1() */ - heap->dbg_peek_cb = NULL; - heap->dbg_read_flush_cb = NULL; - heap->dbg_write_flush_cb = NULL; - heap->dbg_request_cb = NULL; - /* keep heap->dbg_detached_cb */ -} - -/* - * Pause handling - */ - -DUK_LOCAL void duk__debug_set_pause_state(duk_hthread *thr, duk_heap *heap, duk_small_uint_t pause_flags) { - duk_uint_fast32_t line; - - line = duk_debug_curr_line(thr); - if (line == 0) { - /* No line info for current function. */ - duk_small_uint_t updated_flags; - - updated_flags = pause_flags & ~(DUK_PAUSE_FLAG_LINE_CHANGE); - DUK_D(DUK_DPRINT("no line info for current activation, disable line-based pause flags: 0x%08lx -> 0x%08lx", - (long) pause_flags, - (long) updated_flags)); - pause_flags = updated_flags; - } - - heap->dbg_pause_flags = pause_flags; - heap->dbg_pause_act = thr->callstack_curr; - heap->dbg_pause_startline = (duk_uint32_t) line; - heap->dbg_state_dirty = 1; - - DUK_D(DUK_DPRINT("set state for automatic pause triggers, flags=0x%08lx, act=%p, startline=%ld", - (long) heap->dbg_pause_flags, - (void *) heap->dbg_pause_act, - (long) heap->dbg_pause_startline)); -} - -/* - * Debug connection peek and flush primitives - */ - -DUK_INTERNAL duk_bool_t duk_debug_read_peek(duk_hthread *thr) { - duk_heap *heap; - duk_bool_t ret; - - DUK_ASSERT(thr != NULL); - heap = thr->heap; - DUK_ASSERT(heap != NULL); - - if (heap->dbg_read_cb == NULL) { - DUK_D(DUK_DPRINT("attempt to peek in detached state, return zero (= no data)")); - return 0; - } - if (heap->dbg_peek_cb == NULL) { - DUK_DD(DUK_DDPRINT("no peek callback, return zero (= no data)")); - return 0; - } - - DUK__DBG_TPORT_ENTER(); - ret = (duk_bool_t) (heap->dbg_peek_cb(heap->dbg_udata) > 0); - DUK__DBG_TPORT_EXIT(); - return ret; -} - -DUK_INTERNAL void duk_debug_read_flush(duk_hthread *thr) { - duk_heap *heap; - - DUK_ASSERT(thr != NULL); - heap = thr->heap; - DUK_ASSERT(heap != NULL); - - if (heap->dbg_read_cb == NULL) { - DUK_D(DUK_DPRINT("attempt to read flush in detached state, ignore")); - return; - } - if (heap->dbg_read_flush_cb == NULL) { - DUK_DD(DUK_DDPRINT("no read flush callback, ignore")); - return; - } - - DUK__DBG_TPORT_ENTER(); - heap->dbg_read_flush_cb(heap->dbg_udata); - DUK__DBG_TPORT_EXIT(); -} - -DUK_INTERNAL void duk_debug_write_flush(duk_hthread *thr) { - duk_heap *heap; - - DUK_ASSERT(thr != NULL); - heap = thr->heap; - DUK_ASSERT(heap != NULL); - - if (heap->dbg_read_cb == NULL) { - DUK_D(DUK_DPRINT("attempt to write flush in detached state, ignore")); - return; - } - if (heap->dbg_write_flush_cb == NULL) { - DUK_DD(DUK_DDPRINT("no write flush callback, ignore")); - return; - } - - DUK__DBG_TPORT_ENTER(); - heap->dbg_write_flush_cb(heap->dbg_udata); - DUK__DBG_TPORT_EXIT(); -} - -/* - * Debug connection skip primitives - */ - -/* Skip fully. */ -DUK_INTERNAL void duk_debug_skip_bytes(duk_hthread *thr, duk_size_t length) { - duk_uint8_t dummy[64]; - duk_size_t now; - - DUK_ASSERT(thr != NULL); - - while (length > 0) { - now = (length > sizeof(dummy) ? sizeof(dummy) : length); - duk_debug_read_bytes(thr, dummy, now); - length -= now; - } -} - -DUK_INTERNAL void duk_debug_skip_byte(duk_hthread *thr) { - DUK_ASSERT(thr != NULL); - - (void) duk_debug_read_byte(thr); -} - -/* - * Debug connection read primitives - */ - -/* Peek ahead in the stream one byte. */ -DUK_INTERNAL uint8_t duk_debug_peek_byte(duk_hthread *thr) { - /* It is important not to call this if the last byte read was an EOM. - * Reading ahead in this scenario would cause unnecessary blocking if - * another message is not available. - */ - - duk_uint8_t x; - - x = duk_debug_read_byte(thr); - thr->heap->dbg_have_next_byte = 1; - thr->heap->dbg_next_byte = x; - return x; -} - -/* Read fully. */ -DUK_INTERNAL void duk_debug_read_bytes(duk_hthread *thr, duk_uint8_t *data, duk_size_t length) { - duk_heap *heap; - duk_uint8_t *p; - duk_size_t left; - duk_size_t got; - - DUK_ASSERT(thr != NULL); - heap = thr->heap; - DUK_ASSERT(heap != NULL); - DUK_ASSERT(data != NULL); - - if (heap->dbg_read_cb == NULL) { - DUK_D(DUK_DPRINT("attempt to read %ld bytes in detached state, return zero data", (long) length)); - goto fail; - } - - /* NOTE: length may be zero */ - p = data; - if (length >= 1 && heap->dbg_have_next_byte) { - heap->dbg_have_next_byte = 0; - *p++ = heap->dbg_next_byte; - } - for (;;) { - left = (duk_size_t) ((data + length) - p); - if (left == 0) { - break; - } - DUK_ASSERT(heap->dbg_read_cb != NULL); - DUK_ASSERT(left >= 1); -#if defined(DUK_USE_DEBUGGER_TRANSPORT_TORTURE) - left = 1; -#endif - DUK__DBG_TPORT_ENTER(); - got = heap->dbg_read_cb(heap->dbg_udata, (char *) p, left); - DUK__DBG_TPORT_EXIT(); - - if (got == 0 || got > left) { - DUK_D(DUK_DPRINT("connection error during read, return zero data")); - duk__debug_null_most_callbacks(thr); /* avoid calling write callback in detach1() */ - DUK__SET_CONN_BROKEN(thr, 1); - goto fail; - } - p += got; - } - return; - -fail: - duk_memzero((void *) data, (size_t) length); -} - -DUK_INTERNAL duk_uint8_t duk_debug_read_byte(duk_hthread *thr) { - duk_uint8_t x; - - x = 0; /* just in case callback is broken and won't write 'x' */ - duk_debug_read_bytes(thr, &x, 1); - return x; -} - -DUK_LOCAL duk_uint32_t duk__debug_read_uint32_raw(duk_hthread *thr) { - duk_uint8_t buf[4]; - - DUK_ASSERT(thr != NULL); - - duk_debug_read_bytes(thr, buf, 4); - return ((duk_uint32_t) buf[0] << 24) | ((duk_uint32_t) buf[1] << 16) | ((duk_uint32_t) buf[2] << 8) | (duk_uint32_t) buf[3]; -} - -DUK_LOCAL duk_int32_t duk__debug_read_int32_raw(duk_hthread *thr) { - return (duk_int32_t) duk__debug_read_uint32_raw(thr); -} - -DUK_LOCAL duk_uint16_t duk__debug_read_uint16_raw(duk_hthread *thr) { - duk_uint8_t buf[2]; - - DUK_ASSERT(thr != NULL); - - duk_debug_read_bytes(thr, buf, 2); - return ((duk_uint16_t) buf[0] << 8) | (duk_uint16_t) buf[1]; -} - -DUK_INTERNAL duk_int32_t duk_debug_read_int(duk_hthread *thr) { - duk_small_uint_t x; - duk_small_uint_t t; - - DUK_ASSERT(thr != NULL); - - x = duk_debug_read_byte(thr); - if (x >= 0xc0) { - t = duk_debug_read_byte(thr); - return (duk_int32_t) (((x - 0xc0) << 8) + t); - } else if (x >= 0x80) { - return (duk_int32_t) (x - 0x80); - } else if (x == DUK_DBG_IB_INT4) { - return (duk_int32_t) duk__debug_read_uint32_raw(thr); - } - - DUK_D(DUK_DPRINT("debug connection error: failed to decode int")); - DUK__SET_CONN_BROKEN(thr, 1); - return 0; -} - -DUK_LOCAL duk_hstring *duk__debug_read_hstring_raw(duk_hthread *thr, duk_uint32_t len) { - duk_uint8_t buf[31]; - duk_uint8_t *p; - - if (len <= sizeof(buf)) { - duk_debug_read_bytes(thr, buf, (duk_size_t) len); - duk_push_lstring(thr, (const char *) buf, (duk_size_t) len); - } else { - p = (duk_uint8_t *) duk_push_fixed_buffer(thr, (duk_size_t) len); /* zero for paranoia */ - DUK_ASSERT(p != NULL); - duk_debug_read_bytes(thr, p, (duk_size_t) len); - (void) duk_buffer_to_string(thr, -1); /* Safety relies on debug client, which is OK. */ - } - - return duk_require_hstring(thr, -1); -} - -DUK_INTERNAL duk_hstring *duk_debug_read_hstring(duk_hthread *thr) { - duk_small_uint_t x; - duk_uint32_t len; - - DUK_ASSERT(thr != NULL); - - x = duk_debug_read_byte(thr); - if (x >= 0x60 && x <= 0x7f) { - /* For short strings, use a fixed temp buffer. */ - len = (duk_uint32_t) (x - 0x60); - } else if (x == DUK_DBG_IB_STR2) { - len = (duk_uint32_t) duk__debug_read_uint16_raw(thr); - } else if (x == DUK_DBG_IB_STR4) { - len = (duk_uint32_t) duk__debug_read_uint32_raw(thr); - } else { - goto fail; - } - - return duk__debug_read_hstring_raw(thr, len); - -fail: - DUK_D(DUK_DPRINT("debug connection error: failed to decode int")); - DUK__SET_CONN_BROKEN(thr, 1); - duk_push_hstring_empty(thr); /* always push some string */ - return duk_require_hstring(thr, -1); -} - -DUK_LOCAL duk_hbuffer *duk__debug_read_hbuffer_raw(duk_hthread *thr, duk_uint32_t len) { - duk_uint8_t *p; - - p = (duk_uint8_t *) duk_push_fixed_buffer(thr, (duk_size_t) len); /* zero for paranoia */ - DUK_ASSERT(p != NULL); - duk_debug_read_bytes(thr, p, (duk_size_t) len); - - return duk_require_hbuffer(thr, -1); -} - -DUK_LOCAL void *duk__debug_read_pointer_raw(duk_hthread *thr) { - duk_small_uint_t x; - duk__ptr_union pu; - - DUK_ASSERT(thr != NULL); - - x = duk_debug_read_byte(thr); - if (x != sizeof(pu)) { - goto fail; - } - duk_debug_read_bytes(thr, (duk_uint8_t *) &pu.p, sizeof(pu)); -#if defined(DUK_USE_INTEGER_LE) - duk_byteswap_bytes((duk_uint8_t *) pu.b, sizeof(pu)); -#endif - return (void *) pu.p; - -fail: - DUK_D(DUK_DPRINT("debug connection error: failed to decode pointer")); - DUK__SET_CONN_BROKEN(thr, 1); - return (void *) NULL; -} - -DUK_LOCAL duk_double_t duk__debug_read_double_raw(duk_hthread *thr) { - duk_double_union du; - - DUK_ASSERT(sizeof(du.uc) == 8); - duk_debug_read_bytes(thr, (duk_uint8_t *) du.uc, sizeof(du.uc)); - DUK_DBLUNION_DOUBLE_NTOH(&du); - return du.d; -} - -#if 0 -DUK_INTERNAL duk_heaphdr *duk_debug_read_heapptr(duk_hthread *thr) { - duk_small_uint_t x; - - DUK_ASSERT(thr != NULL); - - x = duk_debug_read_byte(thr); - if (x != DUK_DBG_IB_HEAPPTR) { - goto fail; - } - - return (duk_heaphdr *) duk__debug_read_pointer_raw(thr); - - fail: - DUK_D(DUK_DPRINT("debug connection error: failed to decode heapptr")); - DUK__SET_CONN_BROKEN(thr, 1); - return NULL; -} -#endif - -DUK_INTERNAL duk_heaphdr *duk_debug_read_any_ptr(duk_hthread *thr) { - duk_small_uint_t x; - - DUK_ASSERT(thr != NULL); - - x = duk_debug_read_byte(thr); - switch (x) { - case DUK_DBG_IB_OBJECT: - case DUK_DBG_IB_POINTER: - case DUK_DBG_IB_HEAPPTR: - /* Accept any pointer-like value; for 'object' dvalue, read - * and ignore the class number. - */ - if (x == DUK_DBG_IB_OBJECT) { - duk_debug_skip_byte(thr); - } - break; - default: - goto fail; - } - - return (duk_heaphdr *) duk__debug_read_pointer_raw(thr); - -fail: - DUK_D(DUK_DPRINT("debug connection error: failed to decode any pointer (object, pointer, heapptr)")); - DUK__SET_CONN_BROKEN(thr, 1); - return NULL; -} - -DUK_INTERNAL duk_tval *duk_debug_read_tval(duk_hthread *thr) { - duk_uint8_t x; - duk_uint_t t; - duk_uint32_t len; - - DUK_ASSERT(thr != NULL); - - x = duk_debug_read_byte(thr); - - if (x >= 0xc0) { - t = (duk_uint_t) (x - 0xc0); - t = (t << 8) + duk_debug_read_byte(thr); - duk_push_uint(thr, (duk_uint_t) t); - goto return_ptr; - } - if (x >= 0x80) { - duk_push_uint(thr, (duk_uint_t) (x - 0x80)); - goto return_ptr; - } - if (x >= 0x60) { - len = (duk_uint32_t) (x - 0x60); - duk__debug_read_hstring_raw(thr, len); - goto return_ptr; - } - - switch (x) { - case DUK_DBG_IB_INT4: { - duk_int32_t i = duk__debug_read_int32_raw(thr); - duk_push_i32(thr, i); - break; - } - case DUK_DBG_IB_STR4: { - len = duk__debug_read_uint32_raw(thr); - duk__debug_read_hstring_raw(thr, len); - break; - } - case DUK_DBG_IB_STR2: { - len = duk__debug_read_uint16_raw(thr); - duk__debug_read_hstring_raw(thr, len); - break; - } - case DUK_DBG_IB_BUF4: { - len = duk__debug_read_uint32_raw(thr); - duk__debug_read_hbuffer_raw(thr, len); - break; - } - case DUK_DBG_IB_BUF2: { - len = duk__debug_read_uint16_raw(thr); - duk__debug_read_hbuffer_raw(thr, len); - break; - } - case DUK_DBG_IB_UNDEFINED: { - duk_push_undefined(thr); - break; - } - case DUK_DBG_IB_NULL: { - duk_push_null(thr); - break; - } - case DUK_DBG_IB_TRUE: { - duk_push_true(thr); - break; - } - case DUK_DBG_IB_FALSE: { - duk_push_false(thr); - break; - } - case DUK_DBG_IB_NUMBER: { - duk_double_t d; - d = duk__debug_read_double_raw(thr); - duk_push_number(thr, d); - break; - } - case DUK_DBG_IB_OBJECT: { - duk_heaphdr *h; - duk_debug_skip_byte(thr); - h = (duk_heaphdr *) duk__debug_read_pointer_raw(thr); - duk_push_heapptr(thr, (void *) h); - break; - } - case DUK_DBG_IB_POINTER: { - void *ptr; - ptr = duk__debug_read_pointer_raw(thr); - duk_push_pointer(thr, ptr); - break; - } - case DUK_DBG_IB_LIGHTFUNC: { - /* XXX: Not needed for now, so not implemented. Note that - * function pointers may have different size/layout than - * a void pointer. - */ - DUK_D(DUK_DPRINT("reading lightfunc values unimplemented")); - goto fail; - } - case DUK_DBG_IB_HEAPPTR: { - duk_heaphdr *h; - h = (duk_heaphdr *) duk__debug_read_pointer_raw(thr); - duk_push_heapptr(thr, (void *) h); - break; - } - case DUK_DBG_IB_UNUSED: /* unused: not accepted in inbound messages */ - default: - goto fail; - } - -return_ptr: - return DUK_GET_TVAL_NEGIDX(thr, -1); - -fail: - DUK_D(DUK_DPRINT("debug connection error: failed to decode tval")); - DUK__SET_CONN_BROKEN(thr, 1); - return NULL; -} - -/* - * Debug connection write primitives - */ - -/* Write fully. */ -DUK_INTERNAL void duk_debug_write_bytes(duk_hthread *thr, const duk_uint8_t *data, duk_size_t length) { - duk_heap *heap; - const duk_uint8_t *p; - duk_size_t left; - duk_size_t got; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(length == 0 || data != NULL); - heap = thr->heap; - DUK_ASSERT(heap != NULL); - - if (heap->dbg_write_cb == NULL) { - DUK_D(DUK_DPRINT("attempt to write %ld bytes in detached state, ignore", (long) length)); - return; - } - if (length == 0) { - /* Avoid doing an actual write callback with length == 0, - * because that's reserved for a write flush. - */ - return; - } - DUK_ASSERT(data != NULL); - - p = data; - for (;;) { - left = (duk_size_t) ((data + length) - p); - if (left == 0) { - break; - } - DUK_ASSERT(heap->dbg_write_cb != NULL); - DUK_ASSERT(left >= 1); -#if defined(DUK_USE_DEBUGGER_TRANSPORT_TORTURE) - left = 1; -#endif - DUK__DBG_TPORT_ENTER(); - got = heap->dbg_write_cb(heap->dbg_udata, (const char *) p, left); - DUK__DBG_TPORT_EXIT(); - - if (got == 0 || got > left) { - duk__debug_null_most_callbacks(thr); /* avoid calling write callback in detach1() */ - DUK_D(DUK_DPRINT("connection error during write")); - DUK__SET_CONN_BROKEN(thr, 1); - return; - } - p += got; - } -} - -DUK_INTERNAL void duk_debug_write_byte(duk_hthread *thr, duk_uint8_t x) { - duk_debug_write_bytes(thr, (const duk_uint8_t *) &x, 1); -} - -DUK_INTERNAL void duk_debug_write_unused(duk_hthread *thr) { - duk_debug_write_byte(thr, DUK_DBG_IB_UNUSED); -} - -DUK_INTERNAL void duk_debug_write_undefined(duk_hthread *thr) { - duk_debug_write_byte(thr, DUK_DBG_IB_UNDEFINED); -} - -#if defined(DUK_USE_DEBUGGER_INSPECT) -DUK_INTERNAL void duk_debug_write_null(duk_hthread *thr) { - duk_debug_write_byte(thr, DUK_DBG_IB_NULL); -} -#endif - -DUK_INTERNAL void duk_debug_write_boolean(duk_hthread *thr, duk_uint_t val) { - duk_debug_write_byte(thr, val ? DUK_DBG_IB_TRUE : DUK_DBG_IB_FALSE); -} - -/* Write signed 32-bit integer. */ -DUK_INTERNAL void duk_debug_write_int(duk_hthread *thr, duk_int32_t x) { - duk_uint8_t buf[5]; - duk_size_t len; - - DUK_ASSERT(thr != NULL); - - if (x >= 0 && x <= 0x3fL) { - buf[0] = (duk_uint8_t) (0x80 + x); - len = 1; - } else if (x >= 0 && x <= 0x3fffL) { - buf[0] = (duk_uint8_t) (0xc0 + (x >> 8)); - buf[1] = (duk_uint8_t) (x & 0xff); - len = 2; - } else { - /* Signed integers always map to 4 bytes now. */ - buf[0] = (duk_uint8_t) DUK_DBG_IB_INT4; - buf[1] = (duk_uint8_t) ((x >> 24) & 0xff); - buf[2] = (duk_uint8_t) ((x >> 16) & 0xff); - buf[3] = (duk_uint8_t) ((x >> 8) & 0xff); - buf[4] = (duk_uint8_t) (x & 0xff); - len = 5; - } - duk_debug_write_bytes(thr, buf, len); -} - -/* Write unsigned 32-bit integer. */ -DUK_INTERNAL void duk_debug_write_uint(duk_hthread *thr, duk_uint32_t x) { - /* The debugger protocol doesn't support a plain integer encoding for - * the full 32-bit unsigned range (only 32-bit signed). For now, - * unsigned 32-bit values simply written as signed ones. This is not - * a concrete issue except for 32-bit heaphdr fields. Proper solutions - * would be to (a) write such integers as IEEE doubles or (b) add an - * unsigned 32-bit dvalue. - */ - if (x >= 0x80000000UL) { - DUK_D(DUK_DPRINT("writing unsigned integer 0x%08lx as signed integer", (long) x)); - } - duk_debug_write_int(thr, (duk_int32_t) x); -} - -DUK_INTERNAL void duk_debug_write_strbuf(duk_hthread *thr, const char *data, duk_size_t length, duk_uint8_t marker_base) { - duk_uint8_t buf[5]; - duk_size_t buflen; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(length == 0 || data != NULL); - - if (length <= 0x1fUL && marker_base == DUK_DBG_IB_STR4) { - /* For strings, special form for short lengths. */ - buf[0] = (duk_uint8_t) (0x60 + length); - buflen = 1; - } else if (length <= 0xffffUL) { - buf[0] = (duk_uint8_t) (marker_base + 1); - buf[1] = (duk_uint8_t) (length >> 8); - buf[2] = (duk_uint8_t) (length & 0xff); - buflen = 3; - } else { - buf[0] = (duk_uint8_t) marker_base; - buf[1] = (duk_uint8_t) (length >> 24); - buf[2] = (duk_uint8_t) ((length >> 16) & 0xff); - buf[3] = (duk_uint8_t) ((length >> 8) & 0xff); - buf[4] = (duk_uint8_t) (length & 0xff); - buflen = 5; - } - - duk_debug_write_bytes(thr, (const duk_uint8_t *) buf, buflen); - duk_debug_write_bytes(thr, (const duk_uint8_t *) data, length); -} - -DUK_INTERNAL void duk_debug_write_string(duk_hthread *thr, const char *data, duk_size_t length) { - duk_debug_write_strbuf(thr, data, length, DUK_DBG_IB_STR4); -} - -DUK_INTERNAL void duk_debug_write_cstring(duk_hthread *thr, const char *data) { - DUK_ASSERT(thr != NULL); - - duk_debug_write_string(thr, data, data ? DUK_STRLEN(data) : 0); -} - -DUK_INTERNAL void duk_debug_write_hstring(duk_hthread *thr, duk_hstring *h) { - DUK_ASSERT(thr != NULL); - - /* XXX: differentiate null pointer from empty string? */ - duk_debug_write_string(thr, - (h != NULL ? (const char *) DUK_HSTRING_GET_DATA(h) : NULL), - (h != NULL ? (duk_size_t) DUK_HSTRING_GET_BYTELEN(h) : 0)); -} - -DUK_LOCAL void duk__debug_write_hstring_safe_top(duk_hthread *thr) { - duk_debug_write_hstring(thr, duk_safe_to_hstring(thr, -1)); -} - -DUK_INTERNAL void duk_debug_write_buffer(duk_hthread *thr, const char *data, duk_size_t length) { - duk_debug_write_strbuf(thr, data, length, DUK_DBG_IB_BUF4); -} - -DUK_INTERNAL void duk_debug_write_hbuffer(duk_hthread *thr, duk_hbuffer *h) { - DUK_ASSERT(thr != NULL); - - duk_debug_write_buffer(thr, - (h != NULL ? (const char *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h) : NULL), - (h != NULL ? (duk_size_t) DUK_HBUFFER_GET_SIZE(h) : 0)); -} - -DUK_LOCAL void duk__debug_write_pointer_raw(duk_hthread *thr, void *ptr, duk_uint8_t ibyte) { - duk_uint8_t buf[2]; - duk__ptr_union pu; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(sizeof(ptr) >= 1 && sizeof(ptr) <= 16); - /* ptr may be NULL */ - - buf[0] = ibyte; - buf[1] = sizeof(pu); - duk_debug_write_bytes(thr, buf, 2); - pu.p = (void *) ptr; -#if defined(DUK_USE_INTEGER_LE) - duk_byteswap_bytes((duk_uint8_t *) pu.b, sizeof(pu)); -#endif - duk_debug_write_bytes(thr, (const duk_uint8_t *) &pu.p, (duk_size_t) sizeof(pu)); -} - -DUK_INTERNAL void duk_debug_write_pointer(duk_hthread *thr, void *ptr) { - duk__debug_write_pointer_raw(thr, ptr, DUK_DBG_IB_POINTER); -} - -#if defined(DUK_USE_DEBUGGER_DUMPHEAP) || defined(DUK_USE_DEBUGGER_INSPECT) -DUK_INTERNAL void duk_debug_write_heapptr(duk_hthread *thr, duk_heaphdr *h) { - duk__debug_write_pointer_raw(thr, (void *) h, DUK_DBG_IB_HEAPPTR); -} -#endif /* DUK_USE_DEBUGGER_DUMPHEAP || DUK_USE_DEBUGGER_INSPECT */ - -DUK_INTERNAL void duk_debug_write_hobject(duk_hthread *thr, duk_hobject *obj) { - duk_uint8_t buf[3]; - duk__ptr_union pu; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(sizeof(obj) >= 1 && sizeof(obj) <= 16); - DUK_ASSERT(obj != NULL); - - buf[0] = DUK_DBG_IB_OBJECT; - buf[1] = (duk_uint8_t) DUK_HOBJECT_GET_CLASS_NUMBER(obj); - buf[2] = sizeof(pu); - duk_debug_write_bytes(thr, buf, 3); - pu.p = (void *) obj; -#if defined(DUK_USE_INTEGER_LE) - duk_byteswap_bytes((duk_uint8_t *) pu.b, sizeof(pu)); -#endif - duk_debug_write_bytes(thr, (const duk_uint8_t *) &pu.p, (duk_size_t) sizeof(pu)); -} - -DUK_INTERNAL void duk_debug_write_tval(duk_hthread *thr, duk_tval *tv) { - duk_c_function lf_func; - duk_small_uint_t lf_flags; - duk_uint8_t buf[4]; - duk_double_union du1; - duk_double_union du2; - duk_int32_t i32; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(tv != NULL); - - switch (DUK_TVAL_GET_TAG(tv)) { - case DUK_TAG_UNDEFINED: - duk_debug_write_byte(thr, DUK_DBG_IB_UNDEFINED); - break; - case DUK_TAG_UNUSED: - duk_debug_write_byte(thr, DUK_DBG_IB_UNUSED); - break; - case DUK_TAG_NULL: - duk_debug_write_byte(thr, DUK_DBG_IB_NULL); - break; - case DUK_TAG_BOOLEAN: - DUK_ASSERT(DUK_TVAL_GET_BOOLEAN(tv) == 0 || DUK_TVAL_GET_BOOLEAN(tv) == 1); - duk_debug_write_boolean(thr, DUK_TVAL_GET_BOOLEAN(tv)); - break; - case DUK_TAG_POINTER: - duk_debug_write_pointer(thr, (void *) DUK_TVAL_GET_POINTER(tv)); - break; - case DUK_TAG_LIGHTFUNC: - DUK_TVAL_GET_LIGHTFUNC(tv, lf_func, lf_flags); - buf[0] = DUK_DBG_IB_LIGHTFUNC; - buf[1] = (duk_uint8_t) (lf_flags >> 8); - buf[2] = (duk_uint8_t) (lf_flags & 0xff); - buf[3] = sizeof(lf_func); - duk_debug_write_bytes(thr, buf, 4); - duk_debug_write_bytes(thr, (const duk_uint8_t *) &lf_func, sizeof(lf_func)); - break; - case DUK_TAG_STRING: - duk_debug_write_hstring(thr, DUK_TVAL_GET_STRING(tv)); - break; - case DUK_TAG_OBJECT: - duk_debug_write_hobject(thr, DUK_TVAL_GET_OBJECT(tv)); - break; - case DUK_TAG_BUFFER: - duk_debug_write_hbuffer(thr, DUK_TVAL_GET_BUFFER(tv)); - break; -#if defined(DUK_USE_FASTINT) - case DUK_TAG_FASTINT: -#endif - default: - /* Numbers are normalized to big (network) endian. We can - * (but are not required) to use integer dvalues when there's - * no loss of precision. - * - * XXX: share check with other code; this check is slow but - * reliable and doesn't require careful exponent/mantissa - * mask tricks as in the fastint downgrade code. - */ - DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv)); - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); - du1.d = DUK_TVAL_GET_NUMBER(tv); - i32 = (duk_int32_t) du1.d; - du2.d = (duk_double_t) i32; - - DUK_DD(DUK_DDPRINT("i32=%ld du1=%02x%02x%02x%02x%02x%02x%02x%02x " - "du2=%02x%02x%02x%02x%02x%02x%02x%02x", - (long) i32, - (unsigned int) du1.uc[0], - (unsigned int) du1.uc[1], - (unsigned int) du1.uc[2], - (unsigned int) du1.uc[3], - (unsigned int) du1.uc[4], - (unsigned int) du1.uc[5], - (unsigned int) du1.uc[6], - (unsigned int) du1.uc[7], - (unsigned int) du2.uc[0], - (unsigned int) du2.uc[1], - (unsigned int) du2.uc[2], - (unsigned int) du2.uc[3], - (unsigned int) du2.uc[4], - (unsigned int) du2.uc[5], - (unsigned int) du2.uc[6], - (unsigned int) du2.uc[7])); - - if (duk_memcmp((const void *) du1.uc, (const void *) du2.uc, sizeof(du1.uc)) == 0) { - duk_debug_write_int(thr, i32); - } else { - DUK_DBLUNION_DOUBLE_HTON(&du1); - duk_debug_write_byte(thr, DUK_DBG_IB_NUMBER); - duk_debug_write_bytes(thr, (const duk_uint8_t *) du1.uc, sizeof(du1.uc)); - } - } -} - -#if defined(DUK_USE_DEBUGGER_DUMPHEAP) -/* Variant for writing duk_tvals so that any heap allocated values are - * written out as tagged heap pointers. - */ -DUK_LOCAL void duk__debug_write_tval_heapptr(duk_hthread *thr, duk_tval *tv) { - if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) { - duk_heaphdr *h = DUK_TVAL_GET_HEAPHDR(tv); - duk_debug_write_heapptr(thr, h); - } else { - duk_debug_write_tval(thr, tv); - } -} -#endif /* DUK_USE_DEBUGGER_DUMPHEAP */ - -/* - * Debug connection message write helpers - */ - -#if 0 /* unused */ -DUK_INTERNAL void duk_debug_write_request(duk_hthread *thr, duk_small_uint_t command) { - duk_debug_write_byte(thr, DUK_DBG_IB_REQUEST); - duk_debug_write_int(thr, command); -} -#endif - -DUK_INTERNAL void duk_debug_write_reply(duk_hthread *thr) { - duk_debug_write_byte(thr, DUK_DBG_IB_REPLY); -} - -DUK_INTERNAL void duk_debug_write_error_eom(duk_hthread *thr, duk_small_uint_t err_code, const char *msg) { - /* Allow NULL 'msg' */ - duk_debug_write_byte(thr, DUK_DBG_IB_ERROR); - duk_debug_write_int(thr, (duk_int32_t) err_code); - duk_debug_write_cstring(thr, msg); - duk_debug_write_eom(thr); -} - -DUK_INTERNAL void duk_debug_write_notify(duk_hthread *thr, duk_small_uint_t command) { - duk_debug_write_byte(thr, DUK_DBG_IB_NOTIFY); - duk_debug_write_int(thr, (duk_int32_t) command); -} - -DUK_INTERNAL void duk_debug_write_eom(duk_hthread *thr) { - duk_debug_write_byte(thr, DUK_DBG_IB_EOM); - - /* As an initial implementation, write flush after every EOM (and the - * version identifier). A better implementation would flush only when - * Duktape is finished processing messages so that a flush only happens - * after all outbound messages are finished on that occasion. - */ - duk_debug_write_flush(thr); -} - -/* - * Status message and helpers - */ - -DUK_INTERNAL duk_uint_fast32_t duk_debug_curr_line(duk_hthread *thr) { - duk_activation *act; - duk_uint_fast32_t line; - duk_uint_fast32_t pc; - - act = thr->callstack_curr; - if (act == NULL) { - return 0; - } - - /* We're conceptually between two opcodes; act->pc indicates the next - * instruction to be executed. This is usually the correct pc/line to - * indicate in Status. (For the 'debugger' statement this now reports - * the pc/line after the debugger statement because the debugger opcode - * has already been executed.) - */ - - pc = duk_hthread_get_act_curr_pc(thr, act); - - /* XXX: this should be optimized to be a raw query and avoid valstack - * operations if possible. - */ - duk_push_tval(thr, &act->tv_func); - line = duk_hobject_pc2line_query(thr, -1, pc); - duk_pop(thr); - return line; -} - -DUK_INTERNAL void duk_debug_send_status(duk_hthread *thr) { - duk_activation *act; - - duk_debug_write_notify(thr, DUK_DBG_CMD_STATUS); - duk_debug_write_int(thr, (DUK_HEAP_HAS_DEBUGGER_PAUSED(thr->heap) ? 1 : 0)); - - act = thr->callstack_curr; - if (act == NULL) { - duk_debug_write_undefined(thr); - duk_debug_write_undefined(thr); - duk_debug_write_int(thr, 0); - duk_debug_write_int(thr, 0); - } else { - duk_push_tval(thr, &act->tv_func); - duk_get_prop_literal(thr, -1, "fileName"); - duk__debug_write_hstring_safe_top(thr); - duk_get_prop_literal(thr, -2, "name"); - duk__debug_write_hstring_safe_top(thr); - duk_pop_3(thr); - /* Report next pc/line to be executed. */ - duk_debug_write_uint(thr, (duk_uint32_t) duk_debug_curr_line(thr)); - duk_debug_write_uint(thr, (duk_uint32_t) duk_hthread_get_act_curr_pc(thr, act)); - } - - duk_debug_write_eom(thr); -} - -#if defined(DUK_USE_DEBUGGER_THROW_NOTIFY) -DUK_INTERNAL void duk_debug_send_throw(duk_hthread *thr, duk_bool_t fatal) { - /* - * NFY EOM - */ - - duk_activation *act; - duk_uint32_t pc; - - DUK_ASSERT(thr->valstack_top > thr->valstack); /* At least: ... [err] */ - - duk_debug_write_notify(thr, DUK_DBG_CMD_THROW); - duk_debug_write_int(thr, (duk_int32_t) fatal); - - /* Report thrown value to client coerced to string */ - duk_dup_top(thr); - duk__debug_write_hstring_safe_top(thr); - duk_pop(thr); - - if (duk_is_error(thr, -1)) { - /* Error instance, use augmented error data directly */ - duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_FILE_NAME); - duk__debug_write_hstring_safe_top(thr); - duk_get_prop_stridx_short(thr, -2, DUK_STRIDX_LINE_NUMBER); - duk_debug_write_uint(thr, duk_get_uint(thr, -1)); - duk_pop_2(thr); - } else { - /* For anything other than an Error instance, we calculate the - * error location directly from the current activation if one - * exists. - */ - act = thr->callstack_curr; - if (act != NULL) { - duk_push_tval(thr, &act->tv_func); - duk_get_prop_literal(thr, -1, "fileName"); - duk__debug_write_hstring_safe_top(thr); - pc = (duk_uint32_t) duk_hthread_get_act_prev_pc(thr, act); - duk_debug_write_uint(thr, (duk_uint32_t) duk_hobject_pc2line_query(thr, -2, pc)); - duk_pop_2(thr); - } else { - /* Can happen if duk_throw() is called on an empty - * callstack. - */ - duk_debug_write_cstring(thr, ""); - duk_debug_write_uint(thr, 0); - } - } - - duk_debug_write_eom(thr); -} -#endif /* DUK_USE_DEBUGGER_THROW_NOTIFY */ - -/* - * Debug message processing - */ - -/* Skip dvalue. */ -DUK_LOCAL duk_bool_t duk__debug_skip_dvalue(duk_hthread *thr) { - duk_uint8_t x; - duk_uint32_t len; - - x = duk_debug_read_byte(thr); - - if (x >= 0xc0) { - duk_debug_skip_byte(thr); - return 0; - } - if (x >= 0x80) { - return 0; - } - if (x >= 0x60) { - duk_debug_skip_bytes(thr, (duk_size_t) (x - 0x60)); - return 0; - } - switch (x) { - case DUK_DBG_IB_EOM: - return 1; /* Return 1: got EOM */ - case DUK_DBG_IB_REQUEST: - case DUK_DBG_IB_REPLY: - case DUK_DBG_IB_ERROR: - case DUK_DBG_IB_NOTIFY: - break; - case DUK_DBG_IB_INT4: - (void) duk__debug_read_uint32_raw(thr); - break; - case DUK_DBG_IB_STR4: - case DUK_DBG_IB_BUF4: - len = duk__debug_read_uint32_raw(thr); - duk_debug_skip_bytes(thr, len); - break; - case DUK_DBG_IB_STR2: - case DUK_DBG_IB_BUF2: - len = duk__debug_read_uint16_raw(thr); - duk_debug_skip_bytes(thr, len); - break; - case DUK_DBG_IB_UNUSED: - case DUK_DBG_IB_UNDEFINED: - case DUK_DBG_IB_NULL: - case DUK_DBG_IB_TRUE: - case DUK_DBG_IB_FALSE: - break; - case DUK_DBG_IB_NUMBER: - duk_debug_skip_bytes(thr, 8); - break; - case DUK_DBG_IB_OBJECT: - duk_debug_skip_byte(thr); - len = duk_debug_read_byte(thr); - duk_debug_skip_bytes(thr, len); - break; - case DUK_DBG_IB_POINTER: - case DUK_DBG_IB_HEAPPTR: - len = duk_debug_read_byte(thr); - duk_debug_skip_bytes(thr, len); - break; - case DUK_DBG_IB_LIGHTFUNC: - duk_debug_skip_bytes(thr, 2); - len = duk_debug_read_byte(thr); - duk_debug_skip_bytes(thr, len); - break; - default: - goto fail; - } - - return 0; - -fail: - DUK__SET_CONN_BROKEN(thr, 1); - return 1; /* Pretend like we got EOM */ -} - -/* Skip dvalues to EOM. */ -DUK_LOCAL void duk__debug_skip_to_eom(duk_hthread *thr) { - for (;;) { - if (duk__debug_skip_dvalue(thr)) { - break; - } - } -} - -/* Read and validate a call stack index. If index is invalid, write out an - * error message and return zero. - */ -DUK_LOCAL duk_int32_t duk__debug_read_validate_csindex(duk_hthread *thr) { - duk_int32_t level; - level = duk_debug_read_int(thr); - if (level >= 0 || -level > (duk_int32_t) thr->callstack_top) { - duk_debug_write_error_eom(thr, DUK_DBG_ERR_NOTFOUND, "invalid callstack index"); - return 0; /* zero indicates failure */ - } - return level; -} - -/* Read a call stack index and lookup the corresponding duk_activation. - * If index is invalid, write out an error message and return NULL. - */ -DUK_LOCAL duk_activation *duk__debug_read_level_get_activation(duk_hthread *thr) { - duk_activation *act; - duk_int32_t level; - - level = duk_debug_read_int(thr); - act = duk_hthread_get_activation_for_level(thr, level); - if (act == NULL) { - duk_debug_write_error_eom(thr, DUK_DBG_ERR_NOTFOUND, "invalid callstack index"); - } - return act; -} - -/* - * Simple commands - */ - -DUK_LOCAL void duk__debug_handle_basic_info(duk_hthread *thr, duk_heap *heap) { - DUK_UNREF(heap); - DUK_D(DUK_DPRINT("debug command Version")); - - duk_debug_write_reply(thr); - duk_debug_write_int(thr, DUK_VERSION); - duk_debug_write_cstring(thr, DUK_GIT_DESCRIBE); - duk_debug_write_cstring(thr, DUK_USE_TARGET_INFO); -#if defined(DUK_USE_DOUBLE_LE) - duk_debug_write_int(thr, 1); -#elif defined(DUK_USE_DOUBLE_ME) - duk_debug_write_int(thr, 2); -#elif defined(DUK_USE_DOUBLE_BE) - duk_debug_write_int(thr, 3); -#else - duk_debug_write_int(thr, 0); -#endif - duk_debug_write_int(thr, (duk_int_t) sizeof(void *)); - duk_debug_write_eom(thr); -} - -DUK_LOCAL void duk__debug_handle_trigger_status(duk_hthread *thr, duk_heap *heap) { - DUK_UNREF(heap); - DUK_D(DUK_DPRINT("debug command TriggerStatus")); - - duk_debug_write_reply(thr); - duk_debug_write_eom(thr); - heap->dbg_state_dirty = 1; -} - -DUK_LOCAL void duk__debug_handle_pause(duk_hthread *thr, duk_heap *heap) { - DUK_D(DUK_DPRINT("debug command Pause")); - duk_debug_set_paused(heap); - duk_debug_write_reply(thr); - duk_debug_write_eom(thr); -} - -DUK_LOCAL void duk__debug_handle_resume(duk_hthread *thr, duk_heap *heap) { - duk_small_uint_t pause_flags; - - DUK_D(DUK_DPRINT("debug command Resume")); - - duk_debug_clear_paused(heap); - - pause_flags = 0; -#if 0 /* manual testing */ - pause_flags |= DUK_PAUSE_FLAG_ONE_OPCODE; - pause_flags |= DUK_PAUSE_FLAG_CAUGHT_ERROR; - pause_flags |= DUK_PAUSE_FLAG_UNCAUGHT_ERROR; -#endif -#if defined(DUK_USE_DEBUGGER_PAUSE_UNCAUGHT) - pause_flags |= DUK_PAUSE_FLAG_UNCAUGHT_ERROR; -#endif - - duk__debug_set_pause_state(thr, heap, pause_flags); - - duk_debug_write_reply(thr); - duk_debug_write_eom(thr); -} - -DUK_LOCAL void duk__debug_handle_step(duk_hthread *thr, duk_heap *heap, duk_int32_t cmd) { - duk_small_uint_t pause_flags; - - DUK_D(DUK_DPRINT("debug command StepInto/StepOver/StepOut: %d", (int) cmd)); - - if (cmd == DUK_DBG_CMD_STEPINTO) { - pause_flags = DUK_PAUSE_FLAG_LINE_CHANGE | DUK_PAUSE_FLAG_FUNC_ENTRY | DUK_PAUSE_FLAG_FUNC_EXIT; - } else if (cmd == DUK_DBG_CMD_STEPOVER) { - pause_flags = DUK_PAUSE_FLAG_LINE_CHANGE | DUK_PAUSE_FLAG_FUNC_EXIT; - } else { - DUK_ASSERT(cmd == DUK_DBG_CMD_STEPOUT); - pause_flags = DUK_PAUSE_FLAG_FUNC_EXIT; - } -#if defined(DUK_USE_DEBUGGER_PAUSE_UNCAUGHT) - pause_flags |= DUK_PAUSE_FLAG_UNCAUGHT_ERROR; -#endif - - /* If current activation doesn't have line information, line-based - * pause flags are automatically disabled. As a result, e.g. - * StepInto will then pause on (native) function entry or exit. - */ - duk_debug_clear_paused(heap); - duk__debug_set_pause_state(thr, heap, pause_flags); - - duk_debug_write_reply(thr); - duk_debug_write_eom(thr); -} - -DUK_LOCAL void duk__debug_handle_list_break(duk_hthread *thr, duk_heap *heap) { - duk_small_int_t i; - - DUK_D(DUK_DPRINT("debug command ListBreak")); - duk_debug_write_reply(thr); - for (i = 0; i < (duk_small_int_t) heap->dbg_breakpoint_count; i++) { - duk_debug_write_hstring(thr, heap->dbg_breakpoints[i].filename); - duk_debug_write_uint(thr, (duk_uint32_t) heap->dbg_breakpoints[i].line); - } - duk_debug_write_eom(thr); -} - -DUK_LOCAL void duk__debug_handle_add_break(duk_hthread *thr, duk_heap *heap) { - duk_hstring *filename; - duk_uint32_t linenumber; - duk_small_int_t idx; - - DUK_UNREF(heap); - - filename = duk_debug_read_hstring(thr); - linenumber = (duk_uint32_t) duk_debug_read_int(thr); - DUK_D(DUK_DPRINT("debug command AddBreak: %!O:%ld", (duk_hobject *) filename, (long) linenumber)); - idx = duk_debug_add_breakpoint(thr, filename, linenumber); - if (idx >= 0) { - duk_debug_write_reply(thr); - duk_debug_write_int(thr, (duk_int32_t) idx); - duk_debug_write_eom(thr); - } else { - duk_debug_write_error_eom(thr, DUK_DBG_ERR_TOOMANY, "no space for breakpoint"); - } -} - -DUK_LOCAL void duk__debug_handle_del_break(duk_hthread *thr, duk_heap *heap) { - duk_small_uint_t idx; - - DUK_UNREF(heap); - - DUK_D(DUK_DPRINT("debug command DelBreak")); - idx = (duk_small_uint_t) duk_debug_read_int(thr); - if (duk_debug_remove_breakpoint(thr, idx)) { - duk_debug_write_reply(thr); - duk_debug_write_eom(thr); - } else { - duk_debug_write_error_eom(thr, DUK_DBG_ERR_NOTFOUND, "invalid breakpoint index"); - } -} - -DUK_LOCAL void duk__debug_handle_get_var(duk_hthread *thr, duk_heap *heap) { - duk_activation *act; - duk_hstring *str; - duk_bool_t rc; - - DUK_UNREF(heap); - DUK_D(DUK_DPRINT("debug command GetVar")); - - act = duk__debug_read_level_get_activation(thr); - if (act == NULL) { - return; - } - str = duk_debug_read_hstring(thr); /* push to stack */ - DUK_ASSERT(str != NULL); - - rc = duk_js_getvar_activation(thr, act, str, 0); - - duk_debug_write_reply(thr); - if (rc) { - duk_debug_write_int(thr, 1); - DUK_ASSERT(duk_get_tval(thr, -2) != NULL); - duk_debug_write_tval(thr, duk_get_tval(thr, -2)); - } else { - duk_debug_write_int(thr, 0); - duk_debug_write_unused(thr); - } - duk_debug_write_eom(thr); -} - -DUK_LOCAL void duk__debug_handle_put_var(duk_hthread *thr, duk_heap *heap) { - duk_activation *act; - duk_hstring *str; - duk_tval *tv; - - DUK_UNREF(heap); - DUK_D(DUK_DPRINT("debug command PutVar")); - - act = duk__debug_read_level_get_activation(thr); - if (act == NULL) { - return; - } - str = duk_debug_read_hstring(thr); /* push to stack */ - DUK_ASSERT(str != NULL); - tv = duk_debug_read_tval(thr); - if (tv == NULL) { - /* detached */ - return; - } - - duk_js_putvar_activation(thr, act, str, tv, 0); - - /* XXX: Current putvar implementation doesn't have a success flag, - * add one and send to debug client? - */ - duk_debug_write_reply(thr); - duk_debug_write_eom(thr); -} - -DUK_LOCAL void duk__debug_handle_get_call_stack(duk_hthread *thr, duk_heap *heap) { - duk_hthread *curr_thr = thr; - duk_activation *curr_act; - duk_uint_fast32_t pc; - duk_uint_fast32_t line; - - DUK_ASSERT(thr != NULL); - DUK_UNREF(heap); - - duk_debug_write_reply(thr); - while (curr_thr != NULL) { - for (curr_act = curr_thr->callstack_curr; curr_act != NULL; curr_act = curr_act->parent) { - /* PC/line semantics here are: - * - For callstack top we're conceptually between two - * opcodes and current PC indicates next line to - * execute, so report that (matches Status). - * - For other activations we're conceptually still - * executing the instruction at PC-1, so report that - * (matches error stacktrace behavior). - * - See: https://github.com/svaarala/duktape/issues/281 - */ - - /* XXX: optimize to use direct reads, i.e. avoid - * value stack operations. - */ - duk_push_tval(thr, &curr_act->tv_func); - duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_FILE_NAME); - duk__debug_write_hstring_safe_top(thr); - duk_get_prop_stridx_short(thr, -2, DUK_STRIDX_NAME); - duk__debug_write_hstring_safe_top(thr); - pc = duk_hthread_get_act_curr_pc(thr, curr_act); - if (curr_act != curr_thr->callstack_curr && pc > 0) { - pc--; - } - line = duk_hobject_pc2line_query(thr, -3, pc); - duk_debug_write_uint(thr, (duk_uint32_t) line); - duk_debug_write_uint(thr, (duk_uint32_t) pc); - duk_pop_3(thr); - } - curr_thr = curr_thr->resumer; - } - /* SCANBUILD: warning about 'thr' potentially being NULL here, - * warning is incorrect because thr != NULL always here. - */ - duk_debug_write_eom(thr); -} - -DUK_LOCAL void duk__debug_handle_get_locals(duk_hthread *thr, duk_heap *heap) { - duk_activation *act; - duk_hstring *varname; - - DUK_UNREF(heap); - - act = duk__debug_read_level_get_activation(thr); - if (act == NULL) { - return; - } - - duk_debug_write_reply(thr); - - /* XXX: several nice-to-have improvements here: - * - Use direct reads avoiding value stack operations - * - Avoid triggering getters, indicate getter values to debug client - * - If side effects are possible, add error catching - */ - - if (DUK_TVAL_IS_OBJECT(&act->tv_func)) { - duk_hobject *h_func = DUK_TVAL_GET_OBJECT(&act->tv_func); - duk_hobject *h_varmap; - - h_varmap = duk_hobject_get_varmap(thr, h_func); - if (h_varmap != NULL) { - duk_push_hobject(thr, h_varmap); - duk_enum(thr, -1, 0 /*enum_flags*/); - while (duk_next(thr, -1 /*enum_index*/, 0 /*get_value*/)) { - varname = duk_known_hstring(thr, -1); - - duk_js_getvar_activation(thr, act, varname, 0 /*throw_flag*/); - /* [ ... func varmap enum key value this ] */ - duk_debug_write_hstring(thr, duk_get_hstring(thr, -3)); - duk_debug_write_tval(thr, duk_get_tval(thr, -2)); - duk_pop_3(thr); /* -> [ ... func varmap enum ] */ - } - } else { - DUK_D(DUK_DPRINT("varmap missing in GetLocals, ignore")); - } - } else { - DUK_D(DUK_DPRINT("varmap is not an object in GetLocals, ignore")); - } - - duk_debug_write_eom(thr); -} - -DUK_LOCAL void duk__debug_handle_eval(duk_hthread *thr, duk_heap *heap) { - duk_small_uint_t call_flags; - duk_int_t call_ret; - duk_small_int_t eval_err; - duk_bool_t direct_eval; - duk_int32_t level; - duk_idx_t idx_func; - - DUK_UNREF(heap); - - DUK_D(DUK_DPRINT("debug command Eval")); - - /* The eval code is executed within the lexical environment of a specified - * activation. For now, use global object eval() function, with the eval - * considered a 'direct call to eval'. - * - * Callstack index for debug commands only affects scope -- the callstack - * as seen by, e.g. Duktape.act() will be the same regardless. - */ - - /* nargs == 2 so we can pass a callstack index to eval(). */ - idx_func = duk_get_top(thr); - duk_push_c_function(thr, duk_bi_global_object_eval, 2 /*nargs*/); - duk_push_undefined(thr); /* 'this' binding shouldn't matter here */ - - /* Read callstack index, if non-null. */ - if (duk_debug_peek_byte(thr) == DUK_DBG_IB_NULL) { - direct_eval = 0; - level = -1; /* Not needed, but silences warning. */ - (void) duk_debug_read_byte(thr); - } else { - direct_eval = 1; - level = duk__debug_read_validate_csindex(thr); - if (level == 0) { - return; - } - } - - DUK_ASSERT(!direct_eval || (level < 0 && -level <= (duk_int32_t) thr->callstack_top)); - - (void) duk_debug_read_hstring(thr); - if (direct_eval) { - duk_push_int(thr, level - 1); /* compensate for eval() call */ - } - - /* [ ... eval "eval" eval_input level? ] */ - - call_flags = 0; - if (direct_eval) { - duk_activation *act; - duk_hobject *fun; - - act = duk_hthread_get_activation_for_level(thr, level); - if (act != NULL) { - fun = DUK_ACT_GET_FUNC(act); - if (fun != NULL && DUK_HOBJECT_IS_COMPFUNC(fun)) { - /* Direct eval requires that there's a current - * activation and it is an ECMAScript function. - * When Eval is executed from e.g. cooperate API - * call we'll need to do an indirect eval instead. - */ - call_flags |= DUK_CALL_FLAG_DIRECT_EVAL; - } - } - } - - call_ret = duk_pcall_method_flags(thr, duk_get_top(thr) - (idx_func + 2), call_flags); - - if (call_ret == DUK_EXEC_SUCCESS) { - eval_err = 0; - /* Use result value as is. */ - } else { - /* For errors a string coerced result is most informative - * right now, as the debug client doesn't have the capability - * to traverse the error object. - */ - eval_err = 1; - duk_safe_to_string(thr, -1); - } - - /* [ ... result ] */ - - duk_debug_write_reply(thr); - duk_debug_write_int(thr, (duk_int32_t) eval_err); - DUK_ASSERT(duk_get_tval(thr, -1) != NULL); - duk_debug_write_tval(thr, duk_get_tval(thr, -1)); - duk_debug_write_eom(thr); -} - -DUK_LOCAL void duk__debug_handle_detach(duk_hthread *thr, duk_heap *heap) { - DUK_UNREF(heap); - DUK_D(DUK_DPRINT("debug command Detach")); - - duk_debug_write_reply(thr); - duk_debug_write_eom(thr); - - DUK_D(DUK_DPRINT("debug connection detached, mark broken")); - DUK__SET_CONN_BROKEN(thr, 0); /* not an error */ -} - -DUK_LOCAL void duk__debug_handle_apprequest(duk_hthread *thr, duk_heap *heap) { - duk_idx_t old_top; - - DUK_D(DUK_DPRINT("debug command AppRequest")); - - old_top = duk_get_top(thr); /* save stack top */ - - if (heap->dbg_request_cb != NULL) { - duk_idx_t nrets; - duk_idx_t nvalues = 0; - duk_idx_t top, idx; - - /* Read tvals from the message and push them onto the valstack, - * then call the request callback to process the request. - */ - while (duk_debug_peek_byte(thr) != DUK_DBG_IB_EOM) { - duk_tval *tv; - if (!duk_check_stack(thr, 1)) { - DUK_D(DUK_DPRINT("failed to allocate space for request dvalue(s)")); - goto fail; - } - tv = duk_debug_read_tval(thr); /* push to stack */ - if (tv == NULL) { - /* detached */ - return; - } - nvalues++; - } - DUK_ASSERT(duk_get_top(thr) == old_top + nvalues); - - /* Request callback should push values for reply to client onto valstack */ - DUK_D(DUK_DPRINT("calling into AppRequest request_cb with nvalues=%ld, old_top=%ld, top=%ld", - (long) nvalues, - (long) old_top, - (long) duk_get_top(thr))); - nrets = heap->dbg_request_cb(thr, heap->dbg_udata, nvalues); - DUK_D(DUK_DPRINT("returned from AppRequest request_cb; nvalues=%ld -> nrets=%ld, old_top=%ld, top=%ld", - (long) nvalues, - (long) nrets, - (long) old_top, - (long) duk_get_top(thr))); - if (nrets >= 0) { - DUK_ASSERT(duk_get_top(thr) >= old_top + nrets); - if (duk_get_top(thr) < old_top + nrets) { - DUK_D(DUK_DPRINT("AppRequest callback doesn't match value stack configuration, " - "top=%ld < old_top=%ld + nrets=%ld; " - "this might mean it's unsafe to continue!", - (long) duk_get_top(thr), - (long) old_top, - (long) nrets)); - goto fail; - } - - /* Reply with tvals pushed by request callback */ - duk_debug_write_byte(thr, DUK_DBG_IB_REPLY); - top = duk_get_top(thr); - for (idx = top - nrets; idx < top; idx++) { - duk_debug_write_tval(thr, DUK_GET_TVAL_POSIDX(thr, idx)); - } - duk_debug_write_eom(thr); - } else { - DUK_ASSERT(duk_get_top(thr) >= old_top + 1); - if (duk_get_top(thr) < old_top + 1) { - DUK_D(DUK_DPRINT("request callback return value doesn't match value stack configuration")); - goto fail; - } - duk_debug_write_error_eom(thr, DUK_DBG_ERR_APPLICATION, duk_get_string(thr, -1)); - } - - duk_set_top(thr, old_top); /* restore stack top */ - } else { - DUK_D(DUK_DPRINT("no request callback, treat AppRequest as unsupported")); - duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNSUPPORTED, "AppRequest unsupported by target"); - } - - return; - -fail: - duk_set_top(thr, old_top); /* restore stack top */ - DUK__SET_CONN_BROKEN(thr, 1); -} - -/* - * DumpHeap command - */ - -#if defined(DUK_USE_DEBUGGER_DUMPHEAP) -/* XXX: this has some overlap with object inspection; remove this and make - * DumpHeap return lists of heapptrs instead? - */ -DUK_LOCAL void duk__debug_dump_heaphdr(duk_hthread *thr, duk_heap *heap, duk_heaphdr *hdr) { - DUK_UNREF(heap); - - duk_debug_write_heapptr(thr, hdr); - duk_debug_write_uint(thr, (duk_uint32_t) DUK_HEAPHDR_GET_TYPE(hdr)); - duk_debug_write_uint(thr, (duk_uint32_t) DUK_HEAPHDR_GET_FLAGS_RAW(hdr)); -#if defined(DUK_USE_REFERENCE_COUNTING) - duk_debug_write_uint(thr, (duk_uint32_t) DUK_HEAPHDR_GET_REFCOUNT(hdr)); -#else - duk_debug_write_int(thr, (duk_int32_t) -1); -#endif - - switch (DUK_HEAPHDR_GET_TYPE(hdr)) { - case DUK_HTYPE_STRING: { - duk_hstring *h = (duk_hstring *) hdr; - - duk_debug_write_uint(thr, (duk_uint32_t) DUK_HSTRING_GET_BYTELEN(h)); - duk_debug_write_uint(thr, (duk_uint32_t) DUK_HSTRING_GET_CHARLEN(h)); - duk_debug_write_uint(thr, (duk_uint32_t) DUK_HSTRING_GET_HASH(h)); - duk_debug_write_hstring(thr, h); - break; - } - case DUK_HTYPE_OBJECT: { - duk_hobject *h = (duk_hobject *) hdr; - duk_hstring *k; - duk_uint_fast32_t i; - - duk_debug_write_uint(thr, (duk_uint32_t) DUK_HOBJECT_GET_CLASS_NUMBER(h)); - duk_debug_write_heapptr(thr, (duk_heaphdr *) DUK_HOBJECT_GET_PROTOTYPE(heap, h)); - duk_debug_write_uint(thr, (duk_uint32_t) DUK_HOBJECT_GET_ESIZE(h)); - duk_debug_write_uint(thr, (duk_uint32_t) DUK_HOBJECT_GET_ENEXT(h)); - duk_debug_write_uint(thr, (duk_uint32_t) DUK_HOBJECT_GET_ASIZE(h)); - duk_debug_write_uint(thr, (duk_uint32_t) DUK_HOBJECT_GET_HSIZE(h)); - - for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(h); i++) { - duk_debug_write_uint(thr, (duk_uint32_t) DUK_HOBJECT_E_GET_FLAGS(heap, h, i)); - k = DUK_HOBJECT_E_GET_KEY(heap, h, i); - duk_debug_write_heapptr(thr, (duk_heaphdr *) k); - if (k == NULL) { - duk_debug_write_int(thr, 0); /* isAccessor */ - duk_debug_write_unused(thr); - continue; - } - if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(heap, h, i)) { - duk_debug_write_int(thr, 1); /* isAccessor */ - duk_debug_write_heapptr(thr, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_PTR(heap, h, i)->a.get); - duk_debug_write_heapptr(thr, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_PTR(heap, h, i)->a.set); - } else { - duk_debug_write_int(thr, 0); /* isAccessor */ - - duk__debug_write_tval_heapptr(thr, &DUK_HOBJECT_E_GET_VALUE_PTR(heap, h, i)->v); - } - } - - for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(h); i++) { - /* Note: array dump will include elements beyond - * 'length'. - */ - duk__debug_write_tval_heapptr(thr, DUK_HOBJECT_A_GET_VALUE_PTR(heap, h, i)); - } - break; - } - case DUK_HTYPE_BUFFER: { - duk_hbuffer *h = (duk_hbuffer *) hdr; - - duk_debug_write_uint(thr, (duk_uint32_t) DUK_HBUFFER_GET_SIZE(h)); - duk_debug_write_buffer(thr, (const char *) DUK_HBUFFER_GET_DATA_PTR(heap, h), (duk_size_t) DUK_HBUFFER_GET_SIZE(h)); - break; - } - default: { - DUK_D(DUK_DPRINT("invalid htype: %d", (int) DUK_HEAPHDR_GET_TYPE(hdr))); - } - } -} - -DUK_LOCAL void duk__debug_dump_heap_allocated(duk_hthread *thr, duk_heap *heap) { - duk_heaphdr *hdr; - - hdr = heap->heap_allocated; - while (hdr != NULL) { - duk__debug_dump_heaphdr(thr, heap, hdr); - hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr); - } -} - -DUK_LOCAL void duk__debug_dump_strtab(duk_hthread *thr, duk_heap *heap) { - duk_uint32_t i; - duk_hstring *h; - - for (i = 0; i < heap->st_size; i++) { -#if defined(DUK_USE_STRTAB_PTRCOMP) - h = DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, heap->strtable16[i]); -#else - h = heap->strtable[i]; -#endif - while (h != NULL) { - duk__debug_dump_heaphdr(thr, heap, (duk_heaphdr *) h); - h = h->hdr.h_next; - } - } -} - -DUK_LOCAL void duk__debug_handle_dump_heap(duk_hthread *thr, duk_heap *heap) { - DUK_D(DUK_DPRINT("debug command DumpHeap")); - - duk_debug_write_reply(thr); - duk__debug_dump_heap_allocated(thr, heap); - duk__debug_dump_strtab(thr, heap); - duk_debug_write_eom(thr); -} -#endif /* DUK_USE_DEBUGGER_DUMPHEAP */ - -DUK_LOCAL void duk__debug_handle_get_bytecode(duk_hthread *thr, duk_heap *heap) { - duk_activation *act; - duk_hcompfunc *fun = NULL; - duk_size_t i, n; - duk_tval *tv; - duk_hobject **fn; - duk_int32_t level = -1; - duk_uint8_t ibyte; - - DUK_UNREF(heap); - - DUK_D(DUK_DPRINT("debug command GetBytecode")); - - ibyte = duk_debug_peek_byte(thr); - if (ibyte != DUK_DBG_IB_EOM) { - tv = duk_debug_read_tval(thr); - if (tv == NULL) { - /* detached */ - return; - } - if (DUK_TVAL_IS_OBJECT(tv)) { - /* tentative, checked later */ - fun = (duk_hcompfunc *) DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(fun != NULL); - } else if (DUK_TVAL_IS_NUMBER(tv)) { - level = (duk_int32_t) DUK_TVAL_GET_NUMBER(tv); - } else { - DUK_D(DUK_DPRINT("invalid argument to GetBytecode: %!T", tv)); - goto fail_args; - } - } - - if (fun == NULL) { - act = duk_hthread_get_activation_for_level(thr, level); - if (act == NULL) { - goto fail_index; - } - fun = (duk_hcompfunc *) DUK_ACT_GET_FUNC(act); - } - - if (fun == NULL || !DUK_HOBJECT_IS_COMPFUNC((duk_hobject *) fun)) { - DUK_D(DUK_DPRINT("invalid argument to GetBytecode: %!O", fun)); - goto fail_args; - } - DUK_ASSERT(fun != NULL && DUK_HOBJECT_IS_COMPFUNC((duk_hobject *) fun)); - - duk_debug_write_reply(thr); - n = DUK_HCOMPFUNC_GET_CONSTS_COUNT(heap, fun); - duk_debug_write_int(thr, (duk_int32_t) n); - tv = DUK_HCOMPFUNC_GET_CONSTS_BASE(heap, fun); - for (i = 0; i < n; i++) { - duk_debug_write_tval(thr, tv); - tv++; - } - n = DUK_HCOMPFUNC_GET_FUNCS_COUNT(heap, fun); - duk_debug_write_int(thr, (duk_int32_t) n); - fn = DUK_HCOMPFUNC_GET_FUNCS_BASE(heap, fun); - for (i = 0; i < n; i++) { - duk_debug_write_hobject(thr, *fn); - fn++; - } - duk_debug_write_string(thr, - (const char *) DUK_HCOMPFUNC_GET_CODE_BASE(heap, fun), - (duk_size_t) DUK_HCOMPFUNC_GET_CODE_SIZE(heap, fun)); - duk_debug_write_eom(thr); - return; - -fail_args: - duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNKNOWN, "invalid argument"); - return; - -fail_index: - duk_debug_write_error_eom(thr, DUK_DBG_ERR_NOTFOUND, "invalid callstack index"); - return; -} - -/* - * Object inspection commands: GetHeapObjInfo, GetObjPropDesc, - * GetObjPropDescRange - */ - -#if defined(DUK_USE_DEBUGGER_INSPECT) - -#if 0 /* pruned */ -DUK_LOCAL const char * const duk__debug_getinfo_heaphdr_keys[] = { - "reachable", - "temproot", - "finalizable", - "finalized", - "readonly" - /* NULL not needed here */ -}; -DUK_LOCAL duk_uint_t duk__debug_getinfo_heaphdr_masks[] = { - DUK_HEAPHDR_FLAG_REACHABLE, - DUK_HEAPHDR_FLAG_TEMPROOT, - DUK_HEAPHDR_FLAG_FINALIZABLE, - DUK_HEAPHDR_FLAG_FINALIZED, - DUK_HEAPHDR_FLAG_READONLY, - 0 /* terminator */ -}; -#endif -DUK_LOCAL const char * const duk__debug_getinfo_hstring_keys[] = { -#if 0 - "arridx", - "symbol", - "hidden", - "reserved_word", - "strict_reserved_word", - "eval_or_arguments", -#endif - "extdata" - /* NULL not needed here */ -}; -DUK_LOCAL duk_uint_t duk__debug_getinfo_hstring_masks[] = { -#if 0 - DUK_HSTRING_FLAG_ARRIDX, - DUK_HSTRING_FLAG_SYMBOL, - DUK_HSTRING_FLAG_HIDDEN, - DUK_HSTRING_FLAG_RESERVED_WORD, - DUK_HSTRING_FLAG_STRICT_RESERVED_WORD, - DUK_HSTRING_FLAG_EVAL_OR_ARGUMENTS, -#endif - DUK_HSTRING_FLAG_EXTDATA, - 0 /* terminator */ -}; -DUK_LOCAL const char * const duk__debug_getinfo_hobject_keys[] = { - "extensible", "constructable", "callable", "boundfunc", "compfunc", "natfunc", "bufobj", - "fastrefs", "array_part", "strict", "notail", "newenv", "namebinding", "createargs", - "have_finalizer", "exotic_array", "exotic_stringobj", "exotic_arguments", "exotic_proxyobj", "special_call" - /* NULL not needed here */ -}; -DUK_LOCAL duk_uint_t duk__debug_getinfo_hobject_masks[] = { - DUK_HOBJECT_FLAG_EXTENSIBLE, DUK_HOBJECT_FLAG_CONSTRUCTABLE, DUK_HOBJECT_FLAG_CALLABLE, - DUK_HOBJECT_FLAG_BOUNDFUNC, DUK_HOBJECT_FLAG_COMPFUNC, DUK_HOBJECT_FLAG_NATFUNC, - DUK_HOBJECT_FLAG_BUFOBJ, DUK_HOBJECT_FLAG_FASTREFS, DUK_HOBJECT_FLAG_ARRAY_PART, - DUK_HOBJECT_FLAG_STRICT, DUK_HOBJECT_FLAG_NOTAIL, DUK_HOBJECT_FLAG_NEWENV, - DUK_HOBJECT_FLAG_NAMEBINDING, DUK_HOBJECT_FLAG_CREATEARGS, DUK_HOBJECT_FLAG_HAVE_FINALIZER, - DUK_HOBJECT_FLAG_EXOTIC_ARRAY, DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ, DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS, - DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ, DUK_HOBJECT_FLAG_SPECIAL_CALL, 0 /* terminator */ -}; -DUK_LOCAL const char * const duk__debug_getinfo_hbuffer_keys[] = { - "dynamic", - "external" - /* NULL not needed here */ -}; -DUK_LOCAL duk_uint_t duk__debug_getinfo_hbuffer_masks[] = { - DUK_HBUFFER_FLAG_DYNAMIC, - DUK_HBUFFER_FLAG_EXTERNAL, - 0 /* terminator */ -}; - -DUK_LOCAL void duk__debug_getinfo_flags_key(duk_hthread *thr, const char *key) { - duk_debug_write_uint(thr, 0); - duk_debug_write_cstring(thr, key); -} - -DUK_LOCAL void duk__debug_getinfo_prop_uint(duk_hthread *thr, const char *key, duk_uint_t val) { - duk_debug_write_uint(thr, 0); - duk_debug_write_cstring(thr, key); - duk_debug_write_uint(thr, val); -} - -DUK_LOCAL void duk__debug_getinfo_prop_int(duk_hthread *thr, const char *key, duk_int_t val) { - duk_debug_write_uint(thr, 0); - duk_debug_write_cstring(thr, key); - duk_debug_write_int(thr, val); -} - -DUK_LOCAL void duk__debug_getinfo_prop_bool(duk_hthread *thr, const char *key, duk_bool_t val) { - duk_debug_write_uint(thr, 0); - duk_debug_write_cstring(thr, key); - duk_debug_write_boolean(thr, val); -} - -DUK_LOCAL void duk__debug_getinfo_bitmask(duk_hthread *thr, const char * const *keys, duk_uint_t *masks, duk_uint_t flags) { - const char *key; - duk_uint_t mask; - - for (;;) { - mask = *masks++; - if (mask == 0) { - break; - } - key = *keys++; - DUK_ASSERT(key != NULL); - - DUK_DD(DUK_DDPRINT("inspect bitmask: key=%s, mask=0x%08lx, flags=0x%08lx", - key, - (unsigned long) mask, - (unsigned long) flags)); - duk__debug_getinfo_prop_bool(thr, key, flags & mask); - } -} - -/* Inspect a property using a virtual index into a conceptual property list - * consisting of (1) all array part items from [0,a_size[ (even when above - * .length) and (2) all entry part items from [0,e_next[. Unused slots are - * indicated using dvalue 'unused'. - */ -DUK_LOCAL duk_bool_t duk__debug_getprop_index(duk_hthread *thr, duk_heap *heap, duk_hobject *h_obj, duk_uint_t idx) { - duk_uint_t a_size; - duk_tval *tv; - duk_hstring *h_key; - duk_hobject *h_getset; - duk_uint_t flags; - - DUK_UNREF(heap); - - a_size = DUK_HOBJECT_GET_ASIZE(h_obj); - if (idx < a_size) { - duk_debug_write_uint(thr, DUK_PROPDESC_FLAGS_WEC); - duk_debug_write_uint(thr, idx); - tv = DUK_HOBJECT_A_GET_VALUE_PTR(heap, h_obj, idx); - duk_debug_write_tval(thr, tv); - return 1; - } - - idx -= a_size; - if (idx >= DUK_HOBJECT_GET_ENEXT(h_obj)) { - return 0; - } - - h_key = DUK_HOBJECT_E_GET_KEY(heap, h_obj, idx); - if (h_key == NULL) { - duk_debug_write_uint(thr, 0); - duk_debug_write_null(thr); - duk_debug_write_unused(thr); - return 1; - } - - flags = DUK_HOBJECT_E_GET_FLAGS(heap, h_obj, idx); - if (DUK_HSTRING_HAS_SYMBOL(h_key)) { - flags |= DUK_DBG_PROPFLAG_SYMBOL; - } - if (DUK_HSTRING_HAS_HIDDEN(h_key)) { - flags |= DUK_DBG_PROPFLAG_HIDDEN; - } - duk_debug_write_uint(thr, flags); - duk_debug_write_hstring(thr, h_key); - if (flags & DUK_PROPDESC_FLAG_ACCESSOR) { - h_getset = DUK_HOBJECT_E_GET_VALUE_GETTER(heap, h_obj, idx); - if (h_getset) { - duk_debug_write_hobject(thr, h_getset); - } else { - duk_debug_write_null(thr); - } - h_getset = DUK_HOBJECT_E_GET_VALUE_SETTER(heap, h_obj, idx); - if (h_getset) { - duk_debug_write_hobject(thr, h_getset); - } else { - duk_debug_write_null(thr); - } - } else { - tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(heap, h_obj, idx); - duk_debug_write_tval(thr, tv); - } - return 1; -} - -DUK_LOCAL void duk__debug_handle_get_heap_obj_info(duk_hthread *thr, duk_heap *heap) { - duk_heaphdr *h; - - DUK_D(DUK_DPRINT("debug command GetHeapObjInfo")); - DUK_UNREF(heap); - - DUK_ASSERT(sizeof(duk__debug_getinfo_hstring_keys) / sizeof(const char *) == - sizeof(duk__debug_getinfo_hstring_masks) / sizeof(duk_uint_t) - 1); - DUK_ASSERT(sizeof(duk__debug_getinfo_hobject_keys) / sizeof(const char *) == - sizeof(duk__debug_getinfo_hobject_masks) / sizeof(duk_uint_t) - 1); - DUK_ASSERT(sizeof(duk__debug_getinfo_hbuffer_keys) / sizeof(const char *) == - sizeof(duk__debug_getinfo_hbuffer_masks) / sizeof(duk_uint_t) - 1); - - h = duk_debug_read_any_ptr(thr); - if (!h) { - duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNKNOWN, "invalid target"); - return; - } - - duk_debug_write_reply(thr); - - /* As with all inspection code, we rely on the debug client providing - * a valid, non-stale pointer: there's no portable way to safely - * validate the pointer here. - */ - - duk__debug_getinfo_flags_key(thr, "heapptr"); - duk_debug_write_heapptr(thr, h); - - /* XXX: comes out as signed now */ - duk__debug_getinfo_prop_uint(thr, "heaphdr_flags", (duk_uint_t) DUK_HEAPHDR_GET_FLAGS(h)); - duk__debug_getinfo_prop_uint(thr, "heaphdr_type", (duk_uint_t) DUK_HEAPHDR_GET_TYPE(h)); -#if defined(DUK_USE_REFERENCE_COUNTING) - duk__debug_getinfo_prop_uint(thr, "refcount", (duk_uint_t) DUK_HEAPHDR_GET_REFCOUNT(h)); -#endif -#if 0 /* pruned */ - duk__debug_getinfo_bitmask(thr, - duk__debug_getinfo_heaphdr_keys, - duk__debug_getinfo_heaphdr_masks, - DUK_HEAPHDR_GET_FLAGS_RAW(h)); -#endif - - switch (DUK_HEAPHDR_GET_TYPE(h)) { - case DUK_HTYPE_STRING: { - duk_hstring *h_str; - - h_str = (duk_hstring *) h; - duk__debug_getinfo_bitmask(thr, - duk__debug_getinfo_hstring_keys, - duk__debug_getinfo_hstring_masks, - DUK_HEAPHDR_GET_FLAGS_RAW(h)); - duk__debug_getinfo_prop_uint(thr, "bytelen", (duk_uint_t) DUK_HSTRING_GET_BYTELEN(h_str)); - duk__debug_getinfo_prop_uint(thr, "charlen", (duk_uint_t) DUK_HSTRING_GET_CHARLEN(h_str)); - duk__debug_getinfo_prop_uint(thr, "hash", (duk_uint_t) DUK_HSTRING_GET_HASH(h_str)); - duk__debug_getinfo_flags_key(thr, "data"); - duk_debug_write_hstring(thr, h_str); - break; - } - case DUK_HTYPE_OBJECT: { - duk_hobject *h_obj; - duk_hobject *h_proto; - - h_obj = (duk_hobject *) h; - h_proto = DUK_HOBJECT_GET_PROTOTYPE(heap, h_obj); - - /* duk_hobject specific fields. */ - duk__debug_getinfo_bitmask(thr, - duk__debug_getinfo_hobject_keys, - duk__debug_getinfo_hobject_masks, - DUK_HEAPHDR_GET_FLAGS_RAW(h)); - duk__debug_getinfo_prop_uint(thr, "class_number", DUK_HOBJECT_GET_CLASS_NUMBER(h_obj)); - duk__debug_getinfo_flags_key(thr, "class_name"); - duk_debug_write_hstring(thr, DUK_HOBJECT_GET_CLASS_STRING(heap, h_obj)); - duk__debug_getinfo_flags_key(thr, "prototype"); - if (h_proto != NULL) { - duk_debug_write_hobject(thr, h_proto); - } else { - duk_debug_write_null(thr); - } - duk__debug_getinfo_flags_key(thr, "props"); - duk_debug_write_pointer(thr, (void *) DUK_HOBJECT_GET_PROPS(heap, h_obj)); - duk__debug_getinfo_prop_uint(thr, "e_size", (duk_uint_t) DUK_HOBJECT_GET_ESIZE(h_obj)); - duk__debug_getinfo_prop_uint(thr, "e_next", (duk_uint_t) DUK_HOBJECT_GET_ENEXT(h_obj)); - duk__debug_getinfo_prop_uint(thr, "a_size", (duk_uint_t) DUK_HOBJECT_GET_ASIZE(h_obj)); - duk__debug_getinfo_prop_uint(thr, "h_size", (duk_uint_t) DUK_HOBJECT_GET_HSIZE(h_obj)); - - if (DUK_HOBJECT_IS_ARRAY(h_obj)) { - duk_harray *h_arr; - h_arr = (duk_harray *) h_obj; - - duk__debug_getinfo_prop_uint(thr, "length", (duk_uint_t) h_arr->length); - duk__debug_getinfo_prop_bool(thr, "length_nonwritable", h_arr->length_nonwritable); - } - - if (DUK_HOBJECT_IS_NATFUNC(h_obj)) { - duk_hnatfunc *h_fun; - h_fun = (duk_hnatfunc *) h_obj; - - duk__debug_getinfo_prop_int(thr, "nargs", h_fun->nargs); - duk__debug_getinfo_prop_int(thr, "magic", h_fun->magic); - duk__debug_getinfo_prop_bool(thr, "varargs", h_fun->magic == DUK_HNATFUNC_NARGS_VARARGS); - /* Native function pointer may be different from a void pointer, - * and we serialize it from memory directly now (no byte swapping etc). - */ - duk__debug_getinfo_flags_key(thr, "funcptr"); - duk_debug_write_buffer(thr, (const char *) &h_fun->func, sizeof(h_fun->func)); - } - - if (DUK_HOBJECT_IS_COMPFUNC(h_obj)) { - duk_hcompfunc *h_fun; - duk_hbuffer *h_buf; - duk_hobject *h_lexenv; - duk_hobject *h_varenv; - h_fun = (duk_hcompfunc *) h_obj; - - duk__debug_getinfo_prop_int(thr, "nregs", h_fun->nregs); - duk__debug_getinfo_prop_int(thr, "nargs", h_fun->nargs); - - duk__debug_getinfo_flags_key(thr, "lex_env"); - h_lexenv = DUK_HCOMPFUNC_GET_LEXENV(thr->heap, h_fun); - if (h_lexenv != NULL) { - duk_debug_write_hobject(thr, h_lexenv); - } else { - duk_debug_write_null(thr); - } - duk__debug_getinfo_flags_key(thr, "var_env"); - h_varenv = DUK_HCOMPFUNC_GET_VARENV(thr->heap, h_fun); - if (h_varenv != NULL) { - duk_debug_write_hobject(thr, h_varenv); - } else { - duk_debug_write_null(thr); - } - - duk__debug_getinfo_prop_uint(thr, "start_line", h_fun->start_line); - duk__debug_getinfo_prop_uint(thr, "end_line", h_fun->end_line); - h_buf = (duk_hbuffer *) DUK_HCOMPFUNC_GET_DATA(thr->heap, h_fun); - if (h_buf != NULL) { - duk__debug_getinfo_flags_key(thr, "data"); - duk_debug_write_heapptr(thr, (duk_heaphdr *) h_buf); - } - } - - if (DUK_HOBJECT_IS_BOUNDFUNC(h_obj)) { - duk_hboundfunc *h_bfun; - h_bfun = (duk_hboundfunc *) (void *) h_obj; - - duk__debug_getinfo_flags_key(thr, "target"); - duk_debug_write_tval(thr, &h_bfun->target); - duk__debug_getinfo_flags_key(thr, "this_binding"); - duk_debug_write_tval(thr, &h_bfun->this_binding); - duk__debug_getinfo_flags_key(thr, "nargs"); - duk_debug_write_int(thr, h_bfun->nargs); - /* h_bfun->args not exposed now */ - } - - if (DUK_HOBJECT_IS_THREAD(h_obj)) { - /* XXX: Currently no inspection of threads, e.g. value stack, call - * stack, catch stack, etc. - */ - duk_hthread *h_thr; - h_thr = (duk_hthread *) h_obj; - DUK_UNREF(h_thr); - } - - if (DUK_HOBJECT_IS_DECENV(h_obj)) { - duk_hdecenv *h_env; - h_env = (duk_hdecenv *) h_obj; - - duk__debug_getinfo_flags_key(thr, "thread"); - duk_debug_write_heapptr(thr, (duk_heaphdr *) (h_env->thread)); - duk__debug_getinfo_flags_key(thr, "varmap"); - duk_debug_write_heapptr(thr, (duk_heaphdr *) (h_env->varmap)); - duk__debug_getinfo_prop_uint(thr, "regbase", (duk_uint_t) h_env->regbase_byteoff); - } - - if (DUK_HOBJECT_IS_OBJENV(h_obj)) { - duk_hobjenv *h_env; - h_env = (duk_hobjenv *) h_obj; - - duk__debug_getinfo_flags_key(thr, "target"); - duk_debug_write_heapptr(thr, (duk_heaphdr *) (h_env->target)); - duk__debug_getinfo_prop_bool(thr, "has_this", h_env->has_this); - } - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) - if (DUK_HOBJECT_IS_BUFOBJ(h_obj)) { - duk_hbufobj *h_bufobj; - h_bufobj = (duk_hbufobj *) h_obj; - - duk__debug_getinfo_prop_uint(thr, "slice_offset", h_bufobj->offset); - duk__debug_getinfo_prop_uint(thr, "slice_length", h_bufobj->length); - duk__debug_getinfo_prop_uint(thr, "elem_shift", (duk_uint_t) h_bufobj->shift); - duk__debug_getinfo_prop_uint(thr, "elem_type", (duk_uint_t) h_bufobj->elem_type); - duk__debug_getinfo_prop_bool(thr, "is_typedarray", (duk_uint_t) h_bufobj->is_typedarray); - if (h_bufobj->buf != NULL) { - duk__debug_getinfo_flags_key(thr, "buffer"); - duk_debug_write_heapptr(thr, (duk_heaphdr *) h_bufobj->buf); - } - } -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - break; - } - case DUK_HTYPE_BUFFER: { - duk_hbuffer *h_buf; - - h_buf = (duk_hbuffer *) h; - duk__debug_getinfo_bitmask(thr, - duk__debug_getinfo_hbuffer_keys, - duk__debug_getinfo_hbuffer_masks, - DUK_HEAPHDR_GET_FLAGS_RAW(h)); - duk__debug_getinfo_prop_uint(thr, "size", (duk_uint_t) DUK_HBUFFER_GET_SIZE(h_buf)); - duk__debug_getinfo_flags_key(thr, "dataptr"); - duk_debug_write_pointer(thr, (void *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_buf)); - duk__debug_getinfo_flags_key(thr, "data"); - duk_debug_write_hbuffer(thr, h_buf); /* tolerates NULL h_buf */ - break; - } - default: { - /* Since we already started writing the reply, just emit nothing. */ - DUK_D(DUK_DPRINT("inspect target pointer has invalid heaphdr type")); - } - } - - duk_debug_write_eom(thr); -} - -DUK_LOCAL void duk__debug_handle_get_obj_prop_desc(duk_hthread *thr, duk_heap *heap) { - duk_heaphdr *h; - duk_hobject *h_obj; - duk_hstring *h_key; - duk_propdesc desc; - - DUK_D(DUK_DPRINT("debug command GetObjPropDesc")); - DUK_UNREF(heap); - - h = duk_debug_read_any_ptr(thr); - if (!h) { - duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNKNOWN, "invalid target"); - return; - } - h_key = duk_debug_read_hstring(thr); - if (h == NULL || DUK_HEAPHDR_GET_TYPE(h) != DUK_HTYPE_OBJECT || h_key == NULL) { - goto fail_args; - } - h_obj = (duk_hobject *) h; - - if (duk_hobject_get_own_propdesc(thr, h_obj, h_key, &desc, 0 /*flags*/)) { - duk_int_t virtual_idx; - duk_bool_t rc; - - /* To use the shared helper need the virtual index. */ - DUK_ASSERT(desc.e_idx >= 0 || desc.a_idx >= 0); - virtual_idx = (desc.a_idx >= 0 ? desc.a_idx : (duk_int_t) DUK_HOBJECT_GET_ASIZE(h_obj) + desc.e_idx); - - duk_debug_write_reply(thr); - rc = duk__debug_getprop_index(thr, heap, h_obj, (duk_uint_t) virtual_idx); - DUK_ASSERT(rc == 1); - DUK_UNREF(rc); - duk_debug_write_eom(thr); - } else { - duk_debug_write_error_eom(thr, DUK_DBG_ERR_NOTFOUND, "not found"); - } - return; - -fail_args: - duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNKNOWN, "invalid args"); -} - -DUK_LOCAL void duk__debug_handle_get_obj_prop_desc_range(duk_hthread *thr, duk_heap *heap) { - duk_heaphdr *h; - duk_hobject *h_obj; - duk_uint_t idx, idx_start, idx_end; - - DUK_D(DUK_DPRINT("debug command GetObjPropDescRange")); - DUK_UNREF(heap); - - h = duk_debug_read_any_ptr(thr); - idx_start = (duk_uint_t) duk_debug_read_int(thr); - idx_end = (duk_uint_t) duk_debug_read_int(thr); - if (h == NULL || DUK_HEAPHDR_GET_TYPE(h) != DUK_HTYPE_OBJECT) { - goto fail_args; - } - h_obj = (duk_hobject *) h; - - /* The index range space is conceptually the array part followed by the - * entry part. Unlike normal enumeration all slots are exposed here as - * is and return 'unused' if the slots are not in active use. In particular - * the array part is included for the full a_size regardless of what the - * array .length is. - */ - - duk_debug_write_reply(thr); - for (idx = idx_start; idx < idx_end; idx++) { - if (!duk__debug_getprop_index(thr, heap, h_obj, idx)) { - break; - } - } - duk_debug_write_eom(thr); - return; - -fail_args: - duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNKNOWN, "invalid args"); -} - -#endif /* DUK_USE_DEBUGGER_INSPECT */ - -/* - * Process incoming debug requests - * - * Individual request handlers can push temporaries on the value stack and - * rely on duk__debug_process_message() to restore the value stack top - * automatically. - */ - -/* Process one debug message. Automatically restore value stack top to its - * entry value, so that individual message handlers don't need exact value - * stack handling which is convenient. - */ -DUK_LOCAL void duk__debug_process_message(duk_hthread *thr) { - duk_heap *heap; - duk_uint8_t x; - duk_int32_t cmd; - duk_idx_t entry_top; - - DUK_ASSERT(thr != NULL); - heap = thr->heap; - DUK_ASSERT(heap != NULL); - - entry_top = duk_get_top(thr); - - x = duk_debug_read_byte(thr); - switch (x) { - case DUK_DBG_IB_REQUEST: { - cmd = duk_debug_read_int(thr); - switch (cmd) { - case DUK_DBG_CMD_BASICINFO: { - duk__debug_handle_basic_info(thr, heap); - break; - } - case DUK_DBG_CMD_TRIGGERSTATUS: { - duk__debug_handle_trigger_status(thr, heap); - break; - } - case DUK_DBG_CMD_PAUSE: { - duk__debug_handle_pause(thr, heap); - break; - } - case DUK_DBG_CMD_RESUME: { - duk__debug_handle_resume(thr, heap); - break; - } - case DUK_DBG_CMD_STEPINTO: - case DUK_DBG_CMD_STEPOVER: - case DUK_DBG_CMD_STEPOUT: { - duk__debug_handle_step(thr, heap, cmd); - break; - } - case DUK_DBG_CMD_LISTBREAK: { - duk__debug_handle_list_break(thr, heap); - break; - } - case DUK_DBG_CMD_ADDBREAK: { - duk__debug_handle_add_break(thr, heap); - break; - } - case DUK_DBG_CMD_DELBREAK: { - duk__debug_handle_del_break(thr, heap); - break; - } - case DUK_DBG_CMD_GETVAR: { - duk__debug_handle_get_var(thr, heap); - break; - } - case DUK_DBG_CMD_PUTVAR: { - duk__debug_handle_put_var(thr, heap); - break; - } - case DUK_DBG_CMD_GETCALLSTACK: { - duk__debug_handle_get_call_stack(thr, heap); - break; - } - case DUK_DBG_CMD_GETLOCALS: { - duk__debug_handle_get_locals(thr, heap); - break; - } - case DUK_DBG_CMD_EVAL: { - duk__debug_handle_eval(thr, heap); - break; - } - case DUK_DBG_CMD_DETACH: { - /* The actual detached_cb call is postponed to message loop so - * we don't need any special precautions here (just skip to EOM - * on the already closed connection). - */ - duk__debug_handle_detach(thr, heap); - break; - } -#if defined(DUK_USE_DEBUGGER_DUMPHEAP) - case DUK_DBG_CMD_DUMPHEAP: { - duk__debug_handle_dump_heap(thr, heap); - break; - } -#endif /* DUK_USE_DEBUGGER_DUMPHEAP */ - case DUK_DBG_CMD_GETBYTECODE: { - duk__debug_handle_get_bytecode(thr, heap); - break; - } - case DUK_DBG_CMD_APPREQUEST: { - duk__debug_handle_apprequest(thr, heap); - break; - } -#if defined(DUK_USE_DEBUGGER_INSPECT) - case DUK_DBG_CMD_GETHEAPOBJINFO: { - duk__debug_handle_get_heap_obj_info(thr, heap); - break; - } - case DUK_DBG_CMD_GETOBJPROPDESC: { - duk__debug_handle_get_obj_prop_desc(thr, heap); - break; - } - case DUK_DBG_CMD_GETOBJPROPDESCRANGE: { - duk__debug_handle_get_obj_prop_desc_range(thr, heap); - break; - } -#endif /* DUK_USE_DEBUGGER_INSPECT */ - default: { - DUK_D(DUK_DPRINT("debug command unsupported: %d", (int) cmd)); - duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNSUPPORTED, "unsupported command"); - } - } /* switch cmd */ - break; - } - case DUK_DBG_IB_REPLY: { - DUK_D(DUK_DPRINT("debug reply, skipping")); - break; - } - case DUK_DBG_IB_ERROR: { - DUK_D(DUK_DPRINT("debug error, skipping")); - break; - } - case DUK_DBG_IB_NOTIFY: { - DUK_D(DUK_DPRINT("debug notify, skipping")); - break; - } - default: { - DUK_D(DUK_DPRINT("invalid initial byte, drop connection: %d", (int) x)); - goto fail; - } - } /* switch initial byte */ - - DUK_ASSERT(duk_get_top(thr) >= entry_top); - duk_set_top(thr, entry_top); - duk__debug_skip_to_eom(thr); - return; - -fail: - DUK_ASSERT(duk_get_top(thr) >= entry_top); - duk_set_top(thr, entry_top); - DUK__SET_CONN_BROKEN(thr, 1); - return; -} - -DUK_LOCAL void duk__check_resend_status(duk_hthread *thr) { - if (thr->heap->dbg_read_cb != NULL && thr->heap->dbg_state_dirty) { - duk_debug_send_status(thr); - thr->heap->dbg_state_dirty = 0; - } -} - -DUK_INTERNAL duk_bool_t duk_debug_process_messages(duk_hthread *thr, duk_bool_t no_block) { -#if defined(DUK_USE_ASSERTIONS) - duk_idx_t entry_top; -#endif - duk_bool_t retval = 0; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); -#if defined(DUK_USE_ASSERTIONS) - entry_top = duk_get_top(thr); -#endif - - DUK_D(DUK_DPRINT("process debug messages: read_cb=%s, no_block=%ld, detaching=%ld, processing=%ld", - thr->heap->dbg_read_cb ? "not NULL" : "NULL", - (long) no_block, - (long) thr->heap->dbg_detaching, - (long) thr->heap->dbg_processing)); - DUK_DD(DUK_DDPRINT("top at entry: %ld", (long) duk_get_top(thr))); - - /* thr->heap->dbg_detaching may be != 0 if a debugger write outside - * the message loop caused a transport error and detach1() to run. - */ - DUK_ASSERT(thr->heap->dbg_detaching == 0 || thr->heap->dbg_detaching == 1); - DUK_ASSERT(thr->heap->dbg_processing == 0); - thr->heap->dbg_processing = 1; - - /* Ensure dirty state causes a Status even if never process any - * messages. This is expected by the bytecode executor when in - * the running state. - */ - duk__check_resend_status(thr); - - for (;;) { - /* Process messages until we're no longer paused or we peek - * and see there's nothing to read right now. - */ - DUK_DD(DUK_DDPRINT("top at loop top: %ld", (long) duk_get_top(thr))); - DUK_ASSERT(thr->heap->dbg_processing == 1); - - while (thr->heap->dbg_read_cb == NULL && thr->heap->dbg_detaching) { - /* Detach is pending; can be triggered from outside the - * debugger loop (e.g. Status notify write error) or by - * previous message handling. Call detached callback - * here, in a controlled state, to ensure a possible - * reattach inside the detached_cb is handled correctly. - * - * Recheck for detach in a while loop: an immediate - * reattach involves a call to duk_debugger_attach() - * which writes a debugger handshake line immediately - * inside the API call. If the transport write fails - * for that handshake, we can immediately end up in a - * "transport broken, detaching" case several times here. - * Loop back until we're either cleanly attached or - * fully detached. - * - * NOTE: Reset dbg_processing = 1 forcibly, in case we - * re-attached; duk_debugger_attach() sets dbg_processing - * to 0 at the moment. - */ - - DUK_D(DUK_DPRINT("detach pending (dbg_read_cb == NULL, dbg_detaching != 0), call detach2")); - - duk__debug_do_detach2(thr->heap); - thr->heap->dbg_processing = 1; /* may be set to 0 by duk_debugger_attach() inside callback */ - - DUK_D(DUK_DPRINT("after detach2 (and possible reattach): dbg_read_cb=%s, dbg_detaching=%ld", - thr->heap->dbg_read_cb ? "not NULL" : "NULL", - (long) thr->heap->dbg_detaching)); - } - DUK_ASSERT(thr->heap->dbg_detaching == 0); /* true even with reattach */ - DUK_ASSERT(thr->heap->dbg_processing == 1); /* even after a detach and possible reattach */ - - if (thr->heap->dbg_read_cb == NULL) { - DUK_D(DUK_DPRINT("debug connection broken (and not detaching), stop processing messages")); - break; - } - - if (!DUK_HEAP_HAS_DEBUGGER_PAUSED(thr->heap) || no_block) { - if (!duk_debug_read_peek(thr)) { - /* Note: peek cannot currently trigger a detach - * so the dbg_detaching == 0 assert outside the - * loop is correct. - */ - DUK_D(DUK_DPRINT("processing debug message, peek indicated no data, stop processing messages")); - break; - } - DUK_D(DUK_DPRINT("processing debug message, peek indicated there is data, handle it")); - } else { - DUK_D(DUK_DPRINT("paused, process debug message, blocking if necessary")); - } - - duk__check_resend_status(thr); - duk__debug_process_message(thr); - duk__check_resend_status(thr); - - retval = 1; /* processed one or more messages */ - } - - DUK_ASSERT(thr->heap->dbg_detaching == 0); - DUK_ASSERT(thr->heap->dbg_processing == 1); - thr->heap->dbg_processing = 0; - - /* As an initial implementation, read flush after exiting the message - * loop. If transport is broken, this is a no-op (with debug logs). - */ - duk_debug_read_flush(thr); /* this cannot initiate a detach */ - DUK_ASSERT(thr->heap->dbg_detaching == 0); - - DUK_DD(DUK_DDPRINT("top at exit: %ld", (long) duk_get_top(thr))); - -#if defined(DUK_USE_ASSERTIONS) - /* Easy to get wrong, so assert for it. */ - DUK_ASSERT(entry_top == duk_get_top(thr)); -#endif - - return retval; -} - -/* - * Halt execution helper - */ - -/* Halt execution and enter a debugger message loop until execution is resumed - * by the client. PC for the current activation may be temporarily decremented - * so that the "current" instruction will be shown by the client. This helper - * is callable from anywhere, also outside bytecode executor. - */ - -DUK_INTERNAL void duk_debug_halt_execution(duk_hthread *thr, duk_bool_t use_prev_pc) { - duk_activation *act; - duk_hcompfunc *fun; - duk_instr_t *old_pc = NULL; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - DUK_ASSERT(duk_debug_is_attached(thr->heap)); - DUK_ASSERT(thr->heap->dbg_processing == 0); - DUK_ASSERT(!duk_debug_is_paused(thr->heap)); - - duk_debug_set_paused(thr->heap); - - act = thr->callstack_curr; - - /* NOTE: act may be NULL if an error is thrown outside of any activation, - * which may happen in the case of, e.g. syntax errors. - */ - - /* Decrement PC if that was requested, this requires a PC sync. */ - if (act != NULL) { - duk_hthread_sync_currpc(thr); - old_pc = act->curr_pc; - fun = (duk_hcompfunc *) DUK_ACT_GET_FUNC(act); - - /* Short circuit if is safe: if act->curr_pc != NULL, 'fun' is - * guaranteed to be a non-NULL ECMAScript function. - */ - DUK_ASSERT(act->curr_pc == NULL || (fun != NULL && DUK_HOBJECT_IS_COMPFUNC((duk_hobject *) fun))); - if (use_prev_pc && act->curr_pc != NULL && act->curr_pc > DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, fun)) { - act->curr_pc--; - } - } - - /* Process debug messages until we are no longer paused. */ - - /* NOTE: This is a bit fragile. It's important to ensure that - * duk_debug_process_messages() never throws an error or - * act->curr_pc will never be reset. - */ - - thr->heap->dbg_state_dirty = 1; - while (DUK_HEAP_HAS_DEBUGGER_PAUSED(thr->heap)) { - DUK_ASSERT(duk_debug_is_attached(thr->heap)); - DUK_ASSERT(thr->heap->dbg_processing == 0); - duk_debug_process_messages(thr, 0 /*no_block*/); - } - - /* XXX: Decrementing and restoring act->curr_pc works now, but if the - * debugger message loop gains the ability to adjust the current PC - * (e.g. a forced jump) restoring the PC here will break. Another - * approach would be to use a state flag for the "decrement 1 from - * topmost activation's PC" and take it into account whenever dealing - * with PC values. - */ - if (act != NULL) { - act->curr_pc = old_pc; /* restore PC */ - } -} - -/* - * Breakpoint management - */ - -DUK_INTERNAL duk_small_int_t duk_debug_add_breakpoint(duk_hthread *thr, duk_hstring *filename, duk_uint32_t line) { - duk_heap *heap; - duk_breakpoint *b; - - /* Caller must trigger recomputation of active breakpoint list. To - * ensure stale values are not used if that doesn't happen, clear the - * active breakpoint list here. - */ - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(filename != NULL); - heap = thr->heap; - DUK_ASSERT(heap != NULL); - - if (heap->dbg_breakpoint_count >= DUK_HEAP_MAX_BREAKPOINTS) { - DUK_D(DUK_DPRINT("failed to add breakpoint for %O:%ld, all breakpoint slots used", - (duk_heaphdr *) filename, - (long) line)); - return -1; - } - heap->dbg_breakpoints_active[0] = (duk_breakpoint *) NULL; - b = heap->dbg_breakpoints + (heap->dbg_breakpoint_count++); - b->filename = filename; - b->line = line; - DUK_HSTRING_INCREF(thr, filename); - - return (duk_small_int_t) (heap->dbg_breakpoint_count - 1); /* index */ -} - -DUK_INTERNAL duk_bool_t duk_debug_remove_breakpoint(duk_hthread *thr, duk_small_uint_t breakpoint_index) { - duk_heap *heap; - duk_hstring *h; - duk_breakpoint *b; - duk_size_t move_size; - - /* Caller must trigger recomputation of active breakpoint list. To - * ensure stale values are not used if that doesn't happen, clear the - * active breakpoint list here. - */ - - DUK_ASSERT(thr != NULL); - heap = thr->heap; - DUK_ASSERT(heap != NULL); - DUK_ASSERT(duk_debug_is_attached(thr->heap)); - DUK_ASSERT_DISABLE(breakpoint_index >= 0); /* unsigned */ - - if (breakpoint_index >= heap->dbg_breakpoint_count) { - DUK_D(DUK_DPRINT("invalid breakpoint index: %ld", (long) breakpoint_index)); - return 0; - } - b = heap->dbg_breakpoints + breakpoint_index; - - h = b->filename; - DUK_ASSERT(h != NULL); - - move_size = sizeof(duk_breakpoint) * (heap->dbg_breakpoint_count - breakpoint_index - 1); - duk_memmove((void *) b, (const void *) (b + 1), (size_t) move_size); - - heap->dbg_breakpoint_count--; - heap->dbg_breakpoints_active[0] = (duk_breakpoint *) NULL; - - DUK_HSTRING_DECREF(thr, h); /* side effects */ - DUK_UNREF(h); /* w/o refcounting */ - - /* Breakpoint entries above the used area are left as garbage. */ - - return 1; -} - -/* - * Misc state management - */ - -DUK_INTERNAL duk_bool_t duk_debug_is_attached(duk_heap *heap) { - return (heap->dbg_read_cb != NULL); -} - -DUK_INTERNAL duk_bool_t duk_debug_is_paused(duk_heap *heap) { - return (DUK_HEAP_HAS_DEBUGGER_PAUSED(heap) != 0); -} - -DUK_INTERNAL void duk_debug_set_paused(duk_heap *heap) { - if (duk_debug_is_paused(heap)) { - DUK_D(DUK_DPRINT("trying to set paused state when already paused, ignoring")); - } else { - DUK_HEAP_SET_DEBUGGER_PAUSED(heap); - heap->dbg_state_dirty = 1; - duk_debug_clear_pause_state(heap); - DUK_ASSERT(heap->ms_running == 0); /* debugger can't be triggered within mark-and-sweep */ - heap->ms_running = 2; /* prevent mark-and-sweep, prevent refzero queueing */ - heap->ms_prevent_count++; - DUK_ASSERT(heap->ms_prevent_count != 0); /* Wrap. */ - DUK_ASSERT(heap->heap_thread != NULL); - } -} - -DUK_INTERNAL void duk_debug_clear_paused(duk_heap *heap) { - if (duk_debug_is_paused(heap)) { - DUK_HEAP_CLEAR_DEBUGGER_PAUSED(heap); - heap->dbg_state_dirty = 1; - duk_debug_clear_pause_state(heap); - DUK_ASSERT(heap->ms_running == 2); - DUK_ASSERT(heap->ms_prevent_count > 0); - heap->ms_prevent_count--; - heap->ms_running = 0; - DUK_ASSERT(heap->heap_thread != NULL); - } else { - DUK_D(DUK_DPRINT("trying to clear paused state when not paused, ignoring")); - } -} - -DUK_INTERNAL void duk_debug_clear_pause_state(duk_heap *heap) { - heap->dbg_pause_flags = 0; - heap->dbg_pause_act = NULL; - heap->dbg_pause_startline = 0; -} - -#else /* DUK_USE_DEBUGGER_SUPPORT */ - -/* No debugger support. */ - -#endif /* DUK_USE_DEBUGGER_SUPPORT */ - -/* automatic undefs */ -#undef DUK__DBG_TPORT_ENTER -#undef DUK__DBG_TPORT_EXIT -#undef DUK__SET_CONN_BROKEN -/* - * Augmenting errors at their creation site and their throw site. - * - * When errors are created, traceback data is added by built-in code - * and a user error handler (if defined) can process or replace the - * error. Similarly, when errors are thrown, a user error handler - * (if defined) can process or replace the error. - * - * Augmentation and other processing at error creation time is nice - * because an error is only created once, but it may be thrown and - * rethrown multiple times. User error handler registered for processing - * an error at its throw site must be careful to handle rethrowing in - * a useful manner. - * - * Error augmentation may throw an internal error (e.g. alloc error). - * - * ECMAScript allows throwing any values, so all values cannot be - * augmented. Currently, the built-in augmentation at error creation - * only augments error values which are Error instances (= have the - * built-in Error.prototype in their prototype chain) and are also - * extensible. User error handlers have no limitations in this respect. - */ - -/* #include duk_internal.h -> already included */ - -/* - * Helper for calling a user error handler. - * - * 'thr' must be the currently active thread; the error handler is called - * in its context. The valstack of 'thr' must have the error value on - * top, and will be replaced by another error value based on the return - * value of the error handler. - * - * The helper calls duk_handle_call() recursively in protected mode. - * Before that call happens, no longjmps should happen; as a consequence, - * we must assume that the valstack contains enough temporary space for - * arguments and such. - * - * While the error handler runs, any errors thrown will not trigger a - * recursive error handler call (this is implemented using a heap level - * flag which will "follow" through any coroutines resumed inside the - * error handler). If the error handler is not callable or throws an - * error, the resulting error replaces the original error (for Duktape - * internal errors, duk_error_throw.c further substitutes this error with - * a DoubleError which is not ideal). This would be easy to change and - * even signal to the caller. - * - * The user error handler is stored in 'Duktape.errCreate' or - * 'Duktape.errThrow' depending on whether we're augmenting the error at - * creation or throw time. There are several alternatives to this approach, - * see doc/error-objects.rst for discussion. - * - * Note: since further longjmp()s may occur while calling the error handler - * (for many reasons, e.g. a labeled 'break' inside the handler), the - * caller can make no assumptions on the thr->heap->lj state after the - * call (this affects especially duk_error_throw.c). This is not an issue - * as long as the caller writes to the lj state only after the error handler - * finishes. - */ - -#if defined(DUK_USE_ERRTHROW) || defined(DUK_USE_ERRCREATE) -DUK_LOCAL void duk__err_augment_user(duk_hthread *thr, duk_small_uint_t stridx_cb) { - duk_tval *tv_hnd; - duk_int_t rc; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - DUK_ASSERT_STRIDX_VALID(stridx_cb); - - if (thr->heap->augmenting_error) { - DUK_D(DUK_DPRINT("recursive call to error augmentation, ignore")); - return; - } - - /* - * Check whether or not we have an error handler. - * - * We must be careful of not triggering an error when looking up the - * property. For instance, if the property is a getter, we don't want - * to call it, only plain values are allowed. The value, if it exists, - * is not checked. If the value is not a function, a TypeError happens - * when it is called and that error replaces the original one. - */ - - DUK_ASSERT_VALSTACK_SPACE(thr, 4); /* 3 entries actually needed below */ - - /* [ ... errval ] */ - - if (thr->builtins[DUK_BIDX_DUKTAPE] == NULL) { - /* When creating built-ins, some of the built-ins may not be set - * and we want to tolerate that when throwing errors. - */ - DUK_DD(DUK_DDPRINT("error occurred when DUK_BIDX_DUKTAPE is NULL, ignoring")); - return; - } - tv_hnd = duk_hobject_find_entry_tval_ptr_stridx(thr->heap, thr->builtins[DUK_BIDX_DUKTAPE], stridx_cb); - if (tv_hnd == NULL) { - DUK_DD(DUK_DDPRINT("error handler does not exist or is not a plain value: %!T", (duk_tval *) tv_hnd)); - return; - } - DUK_DDD(DUK_DDDPRINT("error handler dump (callability not checked): %!T", (duk_tval *) tv_hnd)); - duk_push_tval(thr, tv_hnd); - - /* [ ... errval errhandler ] */ - - duk_insert(thr, -2); /* -> [ ... errhandler errval ] */ - duk_push_undefined(thr); - duk_insert(thr, -2); /* -> [ ... errhandler undefined(= this) errval ] */ - - /* [ ... errhandler undefined errval ] */ - - /* - * heap->augmenting_error prevents recursive re-entry and also causes - * call handling to use a larger (but not unbounded) call stack limit - * for the duration of error augmentation. - * - * We ignore errors now: a success return and an error value both - * replace the original error value. (This would be easy to change.) - */ - - DUK_ASSERT(thr->heap->augmenting_error == 0); - thr->heap->augmenting_error = 1; - - rc = duk_pcall_method(thr, 1); - DUK_UNREF(rc); /* no need to check now: both success and error are OK */ - - DUK_ASSERT(thr->heap->augmenting_error == 1); - thr->heap->augmenting_error = 0; - - /* [ ... errval ] */ -} -#endif /* DUK_USE_ERRTHROW || DUK_USE_ERRCREATE */ - -/* - * Add ._Tracedata to an error on the stack top. - */ - -#if defined(DUK_USE_TRACEBACKS) -DUK_LOCAL void duk__add_traceback(duk_hthread *thr, - duk_hthread *thr_callstack, - const char *c_filename, - duk_int_t c_line, - duk_small_uint_t flags) { - duk_activation *act; - duk_int_t depth; - duk_int_t arr_size; - duk_tval *tv; - duk_hstring *s; - duk_uint32_t u32; - duk_double_t d; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr_callstack != NULL); - - /* [ ... error ] */ - - /* - * The traceback format is pretty arcane in an attempt to keep it compact - * and cheap to create. It may change arbitrarily from version to version. - * It should be decoded/accessed through version specific accessors only. - * - * See doc/error-objects.rst. - */ - - DUK_DDD(DUK_DDDPRINT("adding traceback to object: %!T", (duk_tval *) duk_get_tval(thr, -1))); - - /* Preallocate array to correct size, so that we can just write out - * the _Tracedata values into the array part. - */ - act = thr->callstack_curr; - depth = DUK_USE_TRACEBACK_DEPTH; - DUK_ASSERT(thr_callstack->callstack_top <= DUK_INT_MAX); /* callstack limits */ - if (depth > (duk_int_t) thr_callstack->callstack_top) { - depth = (duk_int_t) thr_callstack->callstack_top; - } - if (depth > 0) { - if (flags & DUK_AUGMENT_FLAG_SKIP_ONE) { - DUK_ASSERT(act != NULL); - act = act->parent; - depth--; - } - } - arr_size = depth * 2; - if (thr->compile_ctx != NULL && thr->compile_ctx->h_filename != NULL) { - arr_size += 2; - } - if (c_filename) { - /* We need the C filename to be interned before getting the - * array part pointer to avoid any GC interference while the - * array part is populated. - */ - duk_push_string(thr, c_filename); - arr_size += 2; - } - - /* XXX: Uninitialized would be OK. Maybe add internal primitive to - * push bare duk_harray with size? - */ - DUK_D(DUK_DPRINT("preallocated _Tracedata to %ld items", (long) arr_size)); - tv = duk_push_harray_with_size_outptr(thr, (duk_uint32_t) arr_size); - duk_clear_prototype(thr, -1); - DUK_ASSERT(duk_is_bare_object(thr, -1)); - DUK_ASSERT(arr_size == 0 || tv != NULL); - - /* Compiler SyntaxErrors (and other errors) come first, and are - * blamed by default (not flagged "noblame"). - */ - if (thr->compile_ctx != NULL && thr->compile_ctx->h_filename != NULL) { - s = thr->compile_ctx->h_filename; - DUK_TVAL_SET_STRING(tv, s); - DUK_HSTRING_INCREF(thr, s); - tv++; - - u32 = (duk_uint32_t) thr->compile_ctx->curr_token.start_line; /* (flags<<32) + (line), flags = 0 */ - DUK_TVAL_SET_U32(tv, u32); - tv++; - } - - /* Filename/line from C macros (__FILE__, __LINE__) are added as an - * entry with a special format: (string, number). The number contains - * the line and flags. - */ - - /* [ ... error c_filename? arr ] */ - - if (c_filename) { - DUK_ASSERT(DUK_TVAL_IS_STRING(thr->valstack_top - 2)); - s = DUK_TVAL_GET_STRING(thr->valstack_top - 2); /* interned c_filename */ - DUK_ASSERT(s != NULL); - DUK_TVAL_SET_STRING(tv, s); - DUK_HSTRING_INCREF(thr, s); - tv++; - - d = ((flags & DUK_AUGMENT_FLAG_NOBLAME_FILELINE) ? - ((duk_double_t) DUK_TB_FLAG_NOBLAME_FILELINE) * DUK_DOUBLE_2TO32 : - 0.0) + - (duk_double_t) c_line; - DUK_TVAL_SET_DOUBLE(tv, d); - tv++; - } - - /* Traceback depth doesn't take into account the filename/line - * special handling above (intentional). - */ - for (; depth-- > 0; act = act->parent) { - duk_uint32_t pc; - duk_tval *tv_src; - - /* [... arr] */ - - DUK_ASSERT(act != NULL); /* depth check above, assumes book-keeping is correct */ - DUK_ASSERT_DISABLE(act->pc >= 0); /* unsigned */ - - /* Add function object. */ - tv_src = &act->tv_func; /* object (function) or lightfunc */ - DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv_src) || DUK_TVAL_IS_LIGHTFUNC(tv_src)); - DUK_TVAL_SET_TVAL(tv, tv_src); - DUK_TVAL_INCREF(thr, tv); - tv++; - - /* Add a number containing: pc, activation flags. - * - * PC points to next instruction, find offending PC. Note that - * PC == 0 for native code. - */ - pc = (duk_uint32_t) duk_hthread_get_act_prev_pc(thr_callstack, act); - DUK_ASSERT_DISABLE(pc >= 0); /* unsigned */ - DUK_ASSERT((duk_double_t) pc < DUK_DOUBLE_2TO32); /* assume PC is at most 32 bits and non-negative */ - d = ((duk_double_t) act->flags) * DUK_DOUBLE_2TO32 + (duk_double_t) pc; - DUK_TVAL_SET_DOUBLE(tv, d); - tv++; - } - -#if defined(DUK_USE_ASSERTIONS) - { - duk_harray *a; - a = (duk_harray *) duk_known_hobject(thr, -1); - DUK_ASSERT(a != NULL); - DUK_ASSERT((duk_uint32_t) (tv - DUK_HOBJECT_A_GET_BASE(thr->heap, (duk_hobject *) a)) == a->length); - DUK_ASSERT(a->length == (duk_uint32_t) arr_size); - DUK_ASSERT(duk_is_bare_object(thr, -1)); - } -#endif - - /* [ ... error c_filename? arr ] */ - - if (c_filename) { - duk_remove_m2(thr); - } - - /* [ ... error arr ] */ - - duk_xdef_prop_stridx_short_wec(thr, -2, DUK_STRIDX_INT_TRACEDATA); /* -> [ ... error ] */ -} -#endif /* DUK_USE_TRACEBACKS */ - -/* - * Add .fileName and .lineNumber to an error on the stack top. - */ - -#if defined(DUK_USE_AUGMENT_ERROR_CREATE) && !defined(DUK_USE_TRACEBACKS) -DUK_LOCAL void duk__add_fileline(duk_hthread *thr, - duk_hthread *thr_callstack, - const char *c_filename, - duk_int_t c_line, - duk_small_uint_t flags) { -#if defined(DUK_USE_ASSERTIONS) - duk_int_t entry_top; -#endif - -#if defined(DUK_USE_ASSERTIONS) - entry_top = duk_get_top(thr); -#endif - - /* - * If tracebacks are disabled, 'fileName' and 'lineNumber' are added - * as plain own properties. Since Error.prototype has accessors of - * the same name, we need to define own properties directly (cannot - * just use e.g. duk_put_prop_stridx). Existing properties are not - * overwritten in case they already exist. - */ - - if (thr->compile_ctx != NULL && thr->compile_ctx->h_filename != NULL) { - /* Compiler SyntaxError (or other error) gets the primary blame. - * Currently no flag to prevent blaming. - */ - duk_push_uint(thr, (duk_uint_t) thr->compile_ctx->curr_token.start_line); - duk_push_hstring(thr, thr->compile_ctx->h_filename); - } else if (c_filename && (flags & DUK_AUGMENT_FLAG_NOBLAME_FILELINE) == 0) { - /* C call site gets blamed next, unless flagged not to do so. - * XXX: file/line is disabled in minimal builds, so disable this - * too when appropriate. - */ - duk_push_int(thr, c_line); - duk_push_string(thr, c_filename); - } else { - /* Finally, blame the innermost callstack entry which has a - * .fileName property. - */ - duk_small_uint_t depth; - duk_uint32_t ecma_line; - duk_activation *act; - - DUK_ASSERT(thr_callstack->callstack_top <= DUK_INT_MAX); /* callstack limits */ - depth = DUK_USE_TRACEBACK_DEPTH; - if (depth > thr_callstack->callstack_top) { - depth = thr_callstack->callstack_top; - } - for (act = thr_callstack->callstack_curr; depth-- > 0; act = act->parent) { - duk_hobject *func; - duk_uint32_t pc; - - DUK_ASSERT(act != NULL); - func = DUK_ACT_GET_FUNC(act); - if (func == NULL) { - /* Lightfunc, not blamed now. */ - continue; - } - - /* PC points to next instruction, find offending PC, - * PC == 0 for native code. - */ - pc = duk_hthread_get_act_prev_pc( - thr, - act); /* thr argument only used for thr->heap, so specific thread doesn't matter */ - DUK_UNREF(pc); - DUK_ASSERT_DISABLE(pc >= 0); /* unsigned */ - DUK_ASSERT((duk_double_t) pc < DUK_DOUBLE_2TO32); /* assume PC is at most 32 bits and non-negative */ - - duk_push_hobject(thr, func); - - /* [ ... error func ] */ - - duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_FILE_NAME); - if (!duk_is_string_notsymbol(thr, -1)) { - duk_pop_2(thr); - continue; - } - - /* [ ... error func fileName ] */ - - ecma_line = 0; -#if defined(DUK_USE_PC2LINE) - if (DUK_HOBJECT_IS_COMPFUNC(func)) { - ecma_line = duk_hobject_pc2line_query(thr, -2, (duk_uint_fast32_t) pc); - } else { - /* Native function, no relevant lineNumber. */ - } -#endif /* DUK_USE_PC2LINE */ - duk_push_u32(thr, ecma_line); - - /* [ ... error func fileName lineNumber ] */ - - duk_replace(thr, -3); - - /* [ ... error lineNumber fileName ] */ - goto define_props; - } - - /* No activation matches, use undefined for both .fileName and - * .lineNumber (matches what we do with a _Tracedata based - * no-match lookup. - */ - duk_push_undefined(thr); - duk_push_undefined(thr); - } - -define_props: - /* [ ... error lineNumber fileName ] */ -#if defined(DUK_USE_ASSERTIONS) - DUK_ASSERT(duk_get_top(thr) == entry_top + 2); -#endif - duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_C | DUK_PROPDESC_FLAG_NO_OVERWRITE); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LINE_NUMBER, DUK_PROPDESC_FLAGS_C | DUK_PROPDESC_FLAG_NO_OVERWRITE); -} -#endif /* DUK_USE_AUGMENT_ERROR_CREATE && !DUK_USE_TRACEBACKS */ - -/* - * Add line number to a compiler error. - */ - -#if defined(DUK_USE_AUGMENT_ERROR_CREATE) -DUK_LOCAL void duk__add_compiler_error_line(duk_hthread *thr) { - /* Append a "(line NNN)" to the "message" property of any error - * thrown during compilation. Usually compilation errors are - * SyntaxErrors but they can also be out-of-memory errors and - * the like. - */ - - /* [ ... error ] */ - - DUK_ASSERT(duk_is_object(thr, -1)); - - if (!(thr->compile_ctx != NULL && thr->compile_ctx->h_filename != NULL)) { - return; - } - - DUK_DDD(DUK_DDDPRINT("compile error, before adding line info: %!T", (duk_tval *) duk_get_tval(thr, -1))); - - if (duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_MESSAGE)) { - duk_bool_t at_end; - - /* Best guesstimate that error occurred at end of input, token - * truncated by end of input, etc. - */ -#if 0 - at_end = (thr->compile_ctx->curr_token.start_offset + 1 >= thr->compile_ctx->lex.input_length); - at_end = (thr->compile_ctx->lex.window[0].codepoint < 0 || thr->compile_ctx->lex.window[1].codepoint < 0); -#endif - at_end = (thr->compile_ctx->lex.window[0].codepoint < 0); - - DUK_D(DUK_DPRINT("syntax error, determined at_end=%ld; curr_token.start_offset=%ld, " - "lex.input_length=%ld, window[0].codepoint=%ld, window[1].codepoint=%ld", - (long) at_end, - (long) thr->compile_ctx->curr_token.start_offset, - (long) thr->compile_ctx->lex.input_length, - (long) thr->compile_ctx->lex.window[0].codepoint, - (long) thr->compile_ctx->lex.window[1].codepoint)); - - duk_push_sprintf(thr, - " (line %ld%s)", - (long) thr->compile_ctx->curr_token.start_line, - at_end ? ", end of input" : ""); - duk_concat(thr, 2); - duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_MESSAGE); - } else { - duk_pop(thr); - } - - DUK_DDD(DUK_DDDPRINT("compile error, after adding line info: %!T", (duk_tval *) duk_get_tval(thr, -1))); -} -#endif /* DUK_USE_AUGMENT_ERROR_CREATE */ - -/* - * Augment an error being created using Duktape specific properties - * like _Tracedata or .fileName/.lineNumber. - */ - -#if defined(DUK_USE_AUGMENT_ERROR_CREATE) -DUK_LOCAL void duk__err_augment_builtin_create(duk_hthread *thr, - duk_hthread *thr_callstack, - const char *c_filename, - duk_int_t c_line, - duk_hobject *obj, - duk_small_uint_t flags) { -#if defined(DUK_USE_ASSERTIONS) - duk_int_t entry_top; -#endif - -#if defined(DUK_USE_ASSERTIONS) - entry_top = duk_get_top(thr); -#endif - DUK_ASSERT(obj != NULL); - - DUK_UNREF(obj); /* unreferenced w/o tracebacks */ - - duk__add_compiler_error_line(thr); - -#if defined(DUK_USE_TRACEBACKS) - /* If tracebacks are enabled, the '_Tracedata' property is the only - * thing we need: 'fileName' and 'lineNumber' are virtual properties - * which use '_Tracedata'. (Check _Tracedata only as own property.) - */ - if (duk_hobject_find_entry_tval_ptr_stridx(thr->heap, obj, DUK_STRIDX_INT_TRACEDATA) != NULL) { - DUK_DDD(DUK_DDDPRINT("error value already has a '_Tracedata' property, not modifying it")); - } else { - duk__add_traceback(thr, thr_callstack, c_filename, c_line, flags); - } -#else - /* Without tracebacks the concrete .fileName and .lineNumber need - * to be added directly. - */ - duk__add_fileline(thr, thr_callstack, c_filename, c_line, flags); -#endif - -#if defined(DUK_USE_ASSERTIONS) - DUK_ASSERT(duk_get_top(thr) == entry_top); -#endif -} -#endif /* DUK_USE_AUGMENT_ERROR_CREATE */ - -/* - * Augment an error at creation time with _Tracedata/fileName/lineNumber - * and allow a user error handler (if defined) to process/replace the error. - * The error to be augmented is at the stack top. - * - * thr: thread containing the error value - * thr_callstack: thread which should be used for generating callstack etc. - * c_filename: C __FILE__ related to the error - * c_line: C __LINE__ related to the error - * flags & DUK_AUGMENT_FLAG_NOBLAME_FILELINE: - * if true, don't fileName/line as error source, otherwise use traceback - * (needed because user code filename/line are reported but internal ones - * are not) - */ - -#if defined(DUK_USE_AUGMENT_ERROR_CREATE) -DUK_INTERNAL void duk_err_augment_error_create(duk_hthread *thr, - duk_hthread *thr_callstack, - const char *c_filename, - duk_int_t c_line, - duk_small_uint_t flags) { - duk_hobject *obj; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr_callstack != NULL); - - /* [ ... error ] */ - - /* - * Criteria for augmenting: - * - * - augmentation enabled in build (naturally) - * - error value internal prototype chain contains the built-in - * Error prototype object (i.e. 'val instanceof Error') - * - * Additional criteria for built-in augmenting: - * - * - error value is an extensible object - */ - - obj = duk_get_hobject(thr, -1); - if (!obj) { - DUK_DDD(DUK_DDDPRINT("value is not an object, skip both built-in and user augment")); - return; - } - if (!duk_hobject_prototype_chain_contains(thr, obj, thr->builtins[DUK_BIDX_ERROR_PROTOTYPE], 1 /*ignore_loop*/)) { - /* If the value has a prototype loop, it's critical not to - * throw here. Instead, assume the value is not to be - * augmented. - */ - DUK_DDD(DUK_DDDPRINT("value is not an error instance, skip both built-in and user augment")); - return; - } - if (DUK_HOBJECT_HAS_EXTENSIBLE(obj)) { - DUK_DDD(DUK_DDDPRINT("error meets criteria, built-in augment")); - duk__err_augment_builtin_create(thr, thr_callstack, c_filename, c_line, obj, flags); - } else { - DUK_DDD(DUK_DDDPRINT("error does not meet criteria, no built-in augment")); - } - - /* [ ... error ] */ - -#if defined(DUK_USE_ERRCREATE) - duk__err_augment_user(thr, DUK_STRIDX_ERR_CREATE); -#endif -} -#endif /* DUK_USE_AUGMENT_ERROR_CREATE */ - -/* - * Augment an error at throw time; allow a user error handler (if defined) - * to process/replace the error. The error to be augmented is at the - * stack top. - */ - -#if defined(DUK_USE_AUGMENT_ERROR_THROW) -DUK_INTERNAL void duk_err_augment_error_throw(duk_hthread *thr) { -#if defined(DUK_USE_ERRTHROW) - duk__err_augment_user(thr, DUK_STRIDX_ERR_THROW); -#endif /* DUK_USE_ERRTHROW */ -} -#endif /* DUK_USE_AUGMENT_ERROR_THROW */ -/* - * Do a longjmp call, calling the fatal error handler if no - * catchpoint exists. - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_PREFER_SIZE) -DUK_NORETURN(DUK_LOCAL_DECL void duk__uncaught_minimal(duk_hthread *thr)); -DUK_LOCAL void duk__uncaught_minimal(duk_hthread *thr) { - (void) duk_fatal(thr, "uncaught error"); - DUK_WO_NORETURN(return;); -} -#endif - -#if 0 -DUK_NORETURN(DUK_LOCAL_DECL void duk__uncaught_readable(duk_hthread *thr)); -DUK_LOCAL void duk__uncaught_readable(duk_hthread *thr) { - const char *summary; - char buf[DUK_USE_FATAL_MAXLEN]; - - summary = duk_push_string_tval_readable(thr, &thr->heap->lj.value1); - DUK_SNPRINTF(buf, sizeof(buf), "uncaught: %s", summary); - buf[sizeof(buf) - 1] = (char) 0; - (void) duk_fatal(thr, (const char *) buf); - DUK_WO_NORETURN(return;); -} -#endif - -#if !defined(DUK_USE_PREFER_SIZE) -DUK_NORETURN(DUK_LOCAL_DECL void duk__uncaught_error_aware(duk_hthread *thr)); -DUK_LOCAL void duk__uncaught_error_aware(duk_hthread *thr) { - const char *summary; - char buf[DUK_USE_FATAL_MAXLEN]; - - summary = duk_push_string_tval_readable_error(thr, &thr->heap->lj.value1); - DUK_ASSERT(summary != NULL); - DUK_SNPRINTF(buf, sizeof(buf), "uncaught: %s", summary); - buf[sizeof(buf) - 1] = (char) 0; - (void) duk_fatal(thr, (const char *) buf); - DUK_WO_NORETURN(return;); -} -#endif - -DUK_INTERNAL void duk_err_longjmp(duk_hthread *thr) { - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - - DUK_DD(DUK_DDPRINT("longjmp error: type=%d iserror=%d value1=%!T value2=%!T", - (int) thr->heap->lj.type, - (int) thr->heap->lj.iserror, - &thr->heap->lj.value1, - &thr->heap->lj.value2)); - - /* Prevent finalizer execution during error handling. All error - * handling sites will process pending finalizers once error handling - * is complete and we're ready for the side effects. Does not prevent - * refzero freeing or mark-and-sweep during error handling. - * - * NOTE: when we come here some calling code may have used DECREF - * NORZ macros without an explicit DUK_REFZERO_CHECK_xxx() call. - * We don't want to do it here because it would just check for - * pending finalizers and we prevent that explicitly. Instead, - * the error catcher will run the finalizers once error handling - * is complete. - */ - - DUK_ASSERT_LJSTATE_SET(thr->heap); - - thr->heap->pf_prevent_count++; - DUK_ASSERT(thr->heap->pf_prevent_count != 0); /* Wrap. */ - -#if defined(DUK_USE_ASSERTIONS) - /* XXX: set this immediately when longjmp state is set */ - DUK_ASSERT(thr->heap->error_not_allowed == 0); /* Detect error within critical section. */ - thr->heap->error_not_allowed = 1; -#endif - - DUK_DD(DUK_DDPRINT("about to longjmp, pf_prevent_count=%ld", (long) thr->heap->pf_prevent_count)); - - /* If we don't have a jmpbuf_ptr, there is little we can do except - * cause a fatal error. The caller's expectation is that we never - * return. - */ - if (!thr->heap->lj.jmpbuf_ptr) { - DUK_D(DUK_DPRINT("uncaught error: type=%d iserror=%d value1=%!T value2=%!T", - (int) thr->heap->lj.type, - (int) thr->heap->lj.iserror, - &thr->heap->lj.value1, - &thr->heap->lj.value2)); - -#if defined(DUK_USE_PREFER_SIZE) - duk__uncaught_minimal(thr); -#else - duk__uncaught_error_aware(thr); -#endif - DUK_UNREACHABLE(); - } - -#if defined(DUK_USE_CPP_EXCEPTIONS) - throw duk_internal_exception(); /* dummy */ -#else - DUK_LONGJMP(thr->heap->lj.jmpbuf_ptr->jb); -#endif - - DUK_UNREACHABLE(); -} -/* - * Error helpers - */ - -/* #include duk_internal.h -> already included */ - -/* - * Helper to walk the thread chain and see if there is an active error - * catcher. Protected calls or finally blocks aren't considered catching. - */ - -#if defined(DUK_USE_DEBUGGER_SUPPORT) -DUK_LOCAL duk_bool_t duk__have_active_catcher(duk_hthread *thr) { - /* As noted above, a protected API call won't be counted as a - * catcher. This is usually convenient, e.g. in the case of a top- - * level duk_pcall(), but may not always be desirable. Perhaps add - * an argument to treat them as catchers? - */ - - duk_activation *act; - duk_catcher *cat; - - DUK_ASSERT(thr != NULL); - - for (; thr != NULL; thr = thr->resumer) { - for (act = thr->callstack_curr; act != NULL; act = act->parent) { - for (cat = act->cat; cat != NULL; cat = cat->parent) { - if (DUK_CAT_HAS_CATCH_ENABLED(cat)) { - return 1; /* all we need to know */ - } - } - } - } - return 0; -} -#endif /* DUK_USE_DEBUGGER_SUPPORT */ - -/* - * Get prototype object for an integer error code. - */ - -DUK_INTERNAL duk_hobject *duk_error_prototype_from_code(duk_hthread *thr, duk_errcode_t code) { - switch (code) { - case DUK_ERR_EVAL_ERROR: - return thr->builtins[DUK_BIDX_EVAL_ERROR_PROTOTYPE]; - case DUK_ERR_RANGE_ERROR: - return thr->builtins[DUK_BIDX_RANGE_ERROR_PROTOTYPE]; - case DUK_ERR_REFERENCE_ERROR: - return thr->builtins[DUK_BIDX_REFERENCE_ERROR_PROTOTYPE]; - case DUK_ERR_SYNTAX_ERROR: - return thr->builtins[DUK_BIDX_SYNTAX_ERROR_PROTOTYPE]; - case DUK_ERR_TYPE_ERROR: - return thr->builtins[DUK_BIDX_TYPE_ERROR_PROTOTYPE]; - case DUK_ERR_URI_ERROR: - return thr->builtins[DUK_BIDX_URI_ERROR_PROTOTYPE]; - case DUK_ERR_ERROR: - default: - return thr->builtins[DUK_BIDX_ERROR_PROTOTYPE]; - } -} - -/* - * Helper for debugger throw notify and pause-on-uncaught integration. - */ - -#if defined(DUK_USE_DEBUGGER_SUPPORT) -DUK_INTERNAL void duk_err_check_debugger_integration(duk_hthread *thr) { - duk_bool_t uncaught; - duk_tval *tv_obj; - - /* If something is thrown with the debugger attached and nobody will - * catch it, execution is paused before the longjmp, turning over - * control to the debug client. This allows local state to be examined - * before the stack is unwound. Errors are not intercepted when debug - * message loop is active (e.g. for Eval). - */ - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - - /* XXX: Allow customizing the pause and notify behavior at runtime - * using debugger runtime flags. For now the behavior is fixed using - * config options. - */ - - if (!duk_debug_is_attached(thr->heap) || thr->heap->dbg_processing || thr->heap->lj.type != DUK_LJ_TYPE_THROW || - thr->heap->creating_error) { - DUK_D(DUK_DPRINT("skip debugger error integration; not attached, debugger processing, not THROW, or error thrown " - "while creating error")); - return; - } - - /* Don't intercept a DoubleError, we may have caused the initial double - * fault and attempting to intercept it will cause us to be called - * recursively and exhaust the C stack. (This should no longer happen - * for the initial throw because DoubleError path doesn't do a debugger - * integration check, but it might happen for rethrows.) - */ - tv_obj = &thr->heap->lj.value1; - if (DUK_TVAL_IS_OBJECT(tv_obj) && DUK_TVAL_GET_OBJECT(tv_obj) == thr->builtins[DUK_BIDX_DOUBLE_ERROR]) { - DUK_D(DUK_DPRINT("built-in DoubleError instance (re)thrown, not intercepting")); - return; - } - - uncaught = !duk__have_active_catcher(thr); - - /* Debugger code expects the value at stack top. This also serves - * as a backup: we need to store/restore the longjmp state because - * when the debugger is paused Eval commands may be executed and - * they can arbitrarily clobber the longjmp state. - */ - duk_push_tval(thr, tv_obj); - - /* Store and reset longjmp state. */ - DUK_ASSERT_LJSTATE_SET(thr->heap); - DUK_TVAL_DECREF_NORZ(thr, tv_obj); - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value2)); /* Always for THROW type. */ - DUK_TVAL_SET_UNDEFINED(tv_obj); - thr->heap->lj.type = DUK_LJ_TYPE_UNKNOWN; - DUK_ASSERT_LJSTATE_UNSET(thr->heap); - -#if defined(DUK_USE_DEBUGGER_THROW_NOTIFY) - /* Report it to the debug client */ - DUK_D(DUK_DPRINT("throw with debugger attached, report to client")); - duk_debug_send_throw(thr, uncaught); -#endif - - if (uncaught) { - if (thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_UNCAUGHT_ERROR) { - DUK_D(DUK_DPRINT("PAUSE TRIGGERED by uncaught error")); - duk_debug_halt_execution(thr, 1 /*use_prev_pc*/); - } - } else { - if (thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_CAUGHT_ERROR) { - DUK_D(DUK_DPRINT("PAUSE TRIGGERED by caught error")); - duk_debug_halt_execution(thr, 1 /*use_prev_pc*/); - } - } - - /* Restore longjmp state. */ - DUK_ASSERT_LJSTATE_UNSET(thr->heap); - thr->heap->lj.type = DUK_LJ_TYPE_THROW; - tv_obj = DUK_GET_TVAL_NEGIDX(thr, -1); - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value1)); - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value2)); - DUK_TVAL_SET_TVAL(&thr->heap->lj.value1, tv_obj); - DUK_TVAL_INCREF(thr, tv_obj); - DUK_ASSERT_LJSTATE_SET(thr->heap); - - duk_pop(thr); -} -#endif /* DUK_USE_DEBUGGER_SUPPORT */ - -/* - * Helpers for setting up heap longjmp state. - */ - -DUK_INTERNAL void duk_err_setup_ljstate1(duk_hthread *thr, duk_small_uint_t lj_type, duk_tval *tv_val) { - duk_heap *heap; - - DUK_ASSERT(thr != NULL); - heap = thr->heap; - DUK_ASSERT(heap != NULL); - DUK_ASSERT(tv_val != NULL); - - DUK_ASSERT_LJSTATE_UNSET(heap); - - heap->lj.type = lj_type; - DUK_TVAL_SET_TVAL(&heap->lj.value1, tv_val); - DUK_TVAL_INCREF(thr, tv_val); - - DUK_ASSERT_LJSTATE_SET(heap); -} -/* - * Create and throw an ECMAScript error object based on a code and a message. - * - * Used when we throw errors internally. ECMAScript generated error objects - * are created by ECMAScript code, and the throwing is handled by the bytecode - * executor. - */ - -/* #include duk_internal.h -> already included */ - -/* - * Create and throw an error (originating from Duktape internally) - * - * Push an error object on top of the stack, possibly throw augmenting - * the error, and finally longjmp. - * - * If an error occurs while we're dealing with the current error, we might - * enter an infinite recursion loop. This is prevented by detecting a - * "double fault" through the heap->creating_error flag; the recursion - * then stops at the second level. - */ - -#if defined(DUK_USE_VERBOSE_ERRORS) -DUK_INTERNAL void duk_err_create_and_throw(duk_hthread *thr, - duk_errcode_t code, - const char *msg, - const char *filename, - duk_int_t line) { -#else -DUK_INTERNAL void duk_err_create_and_throw(duk_hthread *thr, duk_errcode_t code) { -#endif -#if defined(DUK_USE_VERBOSE_ERRORS) - DUK_DD(DUK_DDPRINT("duk_err_create_and_throw(): code=%ld, msg=%s, filename=%s, line=%ld", - (long) code, - (const char *) msg, - (const char *) filename, - (long) line)); -#else - DUK_DD(DUK_DDPRINT("duk_err_create_and_throw(): code=%ld", (long) code)); -#endif - - DUK_ASSERT(thr != NULL); - - /* Even though nested call is possible because we throw an error when - * trying to create an error, the potential errors must happen before - * the longjmp state is configured. - */ - DUK_ASSERT_LJSTATE_UNSET(thr->heap); - - /* Sync so that augmentation sees up-to-date activations, NULL - * thr->ptr_curr_pc so that it's not used if side effects occur - * in augmentation or longjmp handling. - */ - duk_hthread_sync_and_null_currpc(thr); - - /* - * Create and push an error object onto the top of stack. - * The error is potentially augmented before throwing. - * - * If a "double error" occurs, use a fixed error instance - * to avoid further trouble. - */ - - if (thr->heap->creating_error) { - duk_tval tv_val; - duk_hobject *h_err; - - thr->heap->creating_error = 0; - - h_err = thr->builtins[DUK_BIDX_DOUBLE_ERROR]; - if (h_err != NULL) { - DUK_D(DUK_DPRINT("double fault detected -> use built-in fixed 'double error' instance")); - DUK_TVAL_SET_OBJECT(&tv_val, h_err); - } else { - DUK_D(DUK_DPRINT("double fault detected; there is no built-in fixed 'double error' instance " - "-> use the error code as a number")); - DUK_TVAL_SET_I32(&tv_val, (duk_int32_t) code); - } - - duk_err_setup_ljstate1(thr, DUK_LJ_TYPE_THROW, &tv_val); - - /* No augmentation to avoid any allocations or side effects. */ - } else { - /* Prevent infinite recursion. Extra call stack and C - * recursion headroom (see GH-191) is added for augmentation. - * That is now signalled by heap->augmenting error and taken - * into account in call handling without an explicit limit bump. - */ - thr->heap->creating_error = 1; - - duk_require_stack(thr, 1); - - /* XXX: usually unnecessary '%s' formatting here, but cannot - * use 'msg' as a format string directly. - */ -#if defined(DUK_USE_VERBOSE_ERRORS) - duk_push_error_object_raw(thr, code | DUK_ERRCODE_FLAG_NOBLAME_FILELINE, filename, line, "%s", (const char *) msg); -#else - duk_push_error_object_raw(thr, code | DUK_ERRCODE_FLAG_NOBLAME_FILELINE, NULL, 0, NULL); -#endif - - /* Note that an alloc error may happen during error augmentation. - * This may happen both when the original error is an alloc error - * and when it's something else. Because any error in augmentation - * must be handled correctly anyway, there's no special check for - * avoiding it for alloc errors (this differs from Duktape 1.x). - */ -#if defined(DUK_USE_AUGMENT_ERROR_THROW) - DUK_DDD(DUK_DDDPRINT("THROW ERROR (INTERNAL): %!iT (before throw augment)", (duk_tval *) duk_get_tval(thr, -1))); - duk_err_augment_error_throw(thr); -#endif - - duk_err_setup_ljstate1(thr, DUK_LJ_TYPE_THROW, DUK_GET_TVAL_NEGIDX(thr, -1)); - thr->heap->creating_error = 0; - - /* Error is now created and we assume no errors can occur any - * more. Check for debugger Throw integration only when the - * error is complete. If we enter debugger message loop, - * creating_error must be 0 so that errors can be thrown in - * the paused state, e.g. in Eval commands. - */ -#if defined(DUK_USE_DEBUGGER_SUPPORT) - duk_err_check_debugger_integration(thr); -#endif - } - - /* - * Finally, longjmp - */ - - DUK_DDD(DUK_DDDPRINT("THROW ERROR (INTERNAL): %!iT, %!iT (after throw augment)", - (duk_tval *) &thr->heap->lj.value1, - (duk_tval *) &thr->heap->lj.value2)); - - duk_err_longjmp(thr); - DUK_UNREACHABLE(); -} - -/* - * Helper for C function call negative return values. - */ - -DUK_INTERNAL void duk_error_throw_from_negative_rc(duk_hthread *thr, duk_ret_t rc) { - DUK_ASSERT(thr != NULL); - DUK_ASSERT(rc < 0); - - /* - * The __FILE__ and __LINE__ information is intentionally not used in the - * creation of the error object, as it isn't useful in the tracedata. The - * tracedata still contains the function which returned the negative return - * code, and having the file/line of this function isn't very useful. - * - * The error messages for DUK_RET_xxx shorthand are intentionally very - * minimal: they're only really useful for low memory targets. - */ - - duk_error_raw(thr, -rc, NULL, 0, "error (rc %ld)", (long) rc); - DUK_WO_NORETURN(return;); -} -/* - * duk_hbuffer allocation and freeing. - */ - -/* #include duk_internal.h -> already included */ - -/* Allocate a new duk_hbuffer of a certain type and return a pointer to it - * (NULL on error). Write buffer data pointer to 'out_bufdata' (only if - * allocation successful). - */ -DUK_INTERNAL duk_hbuffer *duk_hbuffer_alloc(duk_heap *heap, duk_size_t size, duk_small_uint_t flags, void **out_bufdata) { - duk_hbuffer *res = NULL; - duk_size_t header_size; - duk_size_t alloc_size; - - DUK_ASSERT(heap != NULL); - DUK_ASSERT(out_bufdata != NULL); - - DUK_DDD(DUK_DDDPRINT("allocate hbuffer")); - - /* Size sanity check. Should not be necessary because caller is - * required to check this, but we don't want to cause a segfault - * if the size wraps either in duk_size_t computation or when - * storing the size in a 16-bit field. - */ - if (size > DUK_HBUFFER_MAX_BYTELEN) { - DUK_D(DUK_DPRINT("hbuffer alloc failed: size too large: %ld", (long) size)); - return NULL; /* no need to write 'out_bufdata' */ - } - - if (flags & DUK_BUF_FLAG_EXTERNAL) { - header_size = sizeof(duk_hbuffer_external); - alloc_size = sizeof(duk_hbuffer_external); - } else if (flags & DUK_BUF_FLAG_DYNAMIC) { - header_size = sizeof(duk_hbuffer_dynamic); - alloc_size = sizeof(duk_hbuffer_dynamic); - } else { - header_size = sizeof(duk_hbuffer_fixed); - alloc_size = sizeof(duk_hbuffer_fixed) + size; - DUK_ASSERT(alloc_size >= sizeof(duk_hbuffer_fixed)); /* no wrapping */ - } - - res = (duk_hbuffer *) DUK_ALLOC(heap, alloc_size); - if (DUK_UNLIKELY(res == NULL)) { - goto alloc_error; - } - - /* zero everything unless requested not to do so */ -#if defined(DUK_USE_ZERO_BUFFER_DATA) - duk_memzero((void *) res, (flags & DUK_BUF_FLAG_NOZERO) ? header_size : alloc_size); -#else - duk_memzero((void *) res, header_size); -#endif - - if (flags & DUK_BUF_FLAG_EXTERNAL) { - duk_hbuffer_external *h; - h = (duk_hbuffer_external *) res; - DUK_UNREF(h); - *out_bufdata = NULL; -#if defined(DUK_USE_EXPLICIT_NULL_INIT) -#if defined(DUK_USE_HEAPPTR16) -/* the compressed pointer is zeroed which maps to NULL, so nothing to do. */ -#else - DUK_HBUFFER_EXTERNAL_SET_DATA_PTR(heap, h, NULL); -#endif -#endif - DUK_ASSERT(DUK_HBUFFER_EXTERNAL_GET_DATA_PTR(heap, h) == NULL); - } else if (flags & DUK_BUF_FLAG_DYNAMIC) { - duk_hbuffer_dynamic *h = (duk_hbuffer_dynamic *) res; - void *ptr; - - if (size > 0) { - DUK_ASSERT(!(flags & DUK_BUF_FLAG_EXTERNAL)); /* alloc external with size zero */ - DUK_DDD(DUK_DDDPRINT("dynamic buffer with nonzero size, alloc actual buffer")); -#if defined(DUK_USE_ZERO_BUFFER_DATA) - ptr = DUK_ALLOC_ZEROED(heap, size); -#else - ptr = DUK_ALLOC(heap, size); -#endif - if (DUK_UNLIKELY(ptr == NULL)) { - /* Because size > 0, NULL check is correct */ - goto alloc_error; - } - *out_bufdata = ptr; - - DUK_HBUFFER_DYNAMIC_SET_DATA_PTR(heap, h, ptr); - } else { - *out_bufdata = NULL; -#if defined(DUK_USE_EXPLICIT_NULL_INIT) -#if defined(DUK_USE_HEAPPTR16) -/* the compressed pointer is zeroed which maps to NULL, so nothing to do. */ -#else - DUK_HBUFFER_DYNAMIC_SET_DATA_PTR(heap, h, NULL); -#endif -#endif - DUK_ASSERT(DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(heap, h) == NULL); - } - } else { - *out_bufdata = (void *) ((duk_hbuffer_fixed *) (void *) res + 1); - } - - DUK_HBUFFER_SET_SIZE(res, size); - - DUK_HEAPHDR_SET_TYPE(&res->hdr, DUK_HTYPE_BUFFER); - if (flags & DUK_BUF_FLAG_DYNAMIC) { - DUK_HBUFFER_SET_DYNAMIC(res); - if (flags & DUK_BUF_FLAG_EXTERNAL) { - DUK_HBUFFER_SET_EXTERNAL(res); - } - } else { - DUK_ASSERT(!(flags & DUK_BUF_FLAG_EXTERNAL)); - } - DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap, &res->hdr); - - DUK_DDD(DUK_DDDPRINT("allocated hbuffer: %p", (void *) res)); - return res; - -alloc_error: - DUK_DD(DUK_DDPRINT("hbuffer allocation failed")); - - DUK_FREE(heap, res); - return NULL; /* no need to write 'out_bufdata' */ -} - -/* For indirect allocs. */ - -DUK_INTERNAL void *duk_hbuffer_get_dynalloc_ptr(duk_heap *heap, void *ud) { - duk_hbuffer_dynamic *buf = (duk_hbuffer_dynamic *) ud; - DUK_UNREF(heap); - return (void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(heap, buf); -} -/* - * duk_hbuffer assertion helpers - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_ASSERTIONS) - -DUK_INTERNAL void duk_hbuffer_assert_valid(duk_hbuffer *h) { - DUK_ASSERT(h != NULL); -} - -#endif /* DUK_USE_ASSERTIONS */ -/* - * duk_hbuffer operations such as resizing and inserting/appending data to - * a dynamic buffer. - */ - -/* #include duk_internal.h -> already included */ - -/* - * Resizing - */ - -DUK_INTERNAL void duk_hbuffer_resize(duk_hthread *thr, duk_hbuffer_dynamic *buf, duk_size_t new_size) { - void *res; - duk_size_t prev_size; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(buf != NULL); - DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf)); - DUK_ASSERT(!DUK_HBUFFER_HAS_EXTERNAL(buf)); - - /* - * Maximum size check - */ - - if (new_size > DUK_HBUFFER_MAX_BYTELEN) { - DUK_ERROR_RANGE(thr, "buffer too long"); - DUK_WO_NORETURN(return;); - } - - /* - * Note: use indirect realloc variant just in case mark-and-sweep - * (finalizers) might resize this same buffer during garbage - * collection. - */ - - res = DUK_REALLOC_INDIRECT(thr->heap, duk_hbuffer_get_dynalloc_ptr, (void *) buf, new_size); - if (DUK_LIKELY(res != NULL || new_size == 0)) { - /* 'res' may be NULL if new allocation size is 0. */ - - DUK_DDD(DUK_DDDPRINT("resized dynamic buffer %p:%ld -> %p:%ld", - (void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, buf), - (long) DUK_HBUFFER_DYNAMIC_GET_SIZE(buf), - (void *) res, - (long) new_size)); - - /* - * The entire allocated buffer area, regardless of actual used - * size, is kept zeroed in resizes for simplicity. If the buffer - * is grown, zero the new part. - */ - - prev_size = DUK_HBUFFER_DYNAMIC_GET_SIZE(buf); - if (new_size > prev_size) { - DUK_ASSERT(new_size - prev_size > 0); -#if defined(DUK_USE_ZERO_BUFFER_DATA) - duk_memzero((void *) ((char *) res + prev_size), (duk_size_t) (new_size - prev_size)); -#endif - } - - DUK_HBUFFER_DYNAMIC_SET_SIZE(buf, new_size); - DUK_HBUFFER_DYNAMIC_SET_DATA_PTR(thr->heap, buf, res); - } else { - DUK_ERROR_ALLOC_FAILED(thr); - DUK_WO_NORETURN(return;); - } - - DUK_ASSERT(res != NULL || new_size == 0); -} - -DUK_INTERNAL void duk_hbuffer_reset(duk_hthread *thr, duk_hbuffer_dynamic *buf) { - DUK_ASSERT(thr != NULL); - DUK_ASSERT(buf != NULL); - DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf)); - DUK_ASSERT(!DUK_HBUFFER_HAS_EXTERNAL(buf)); - - duk_hbuffer_resize(thr, buf, 0); -} -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_uint_t duk_hbufobj_clamp_bytelength(duk_hbufobj *h_bufobj, duk_uint_t len) { - duk_uint_t buf_size; - duk_uint_t buf_avail; - - DUK_ASSERT(h_bufobj != NULL); - DUK_ASSERT(h_bufobj->buf != NULL); - - buf_size = (duk_uint_t) DUK_HBUFFER_GET_SIZE(h_bufobj->buf); - if (h_bufobj->offset > buf_size) { - /* Slice starting point is beyond current length. */ - return 0; - } - buf_avail = buf_size - h_bufobj->offset; - - return buf_avail >= len ? len : buf_avail; -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ -/* - * duk_heap allocation and freeing. - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_ROM_STRINGS) -/* Fixed seed value used with ROM strings. */ -#define DUK__FIXED_HASH_SEED 0xabcd1234 -#endif - -/* - * Free a heap object. - * - * Free heap object and its internal (non-heap) pointers. Assumes that - * caller has removed the object from heap allocated list or the string - * intern table, and any weak references (which strings may have) have - * been already dealt with. - */ - -DUK_INTERNAL void duk_free_hobject(duk_heap *heap, duk_hobject *h) { - DUK_ASSERT(heap != NULL); - DUK_ASSERT(h != NULL); - - DUK_FREE(heap, DUK_HOBJECT_GET_PROPS(heap, h)); - - if (DUK_HOBJECT_IS_COMPFUNC(h)) { - duk_hcompfunc *f = (duk_hcompfunc *) h; - DUK_UNREF(f); - /* Currently nothing to free; 'data' is a heap object */ - } else if (DUK_HOBJECT_IS_NATFUNC(h)) { - duk_hnatfunc *f = (duk_hnatfunc *) h; - DUK_UNREF(f); - /* Currently nothing to free */ - } else if (DUK_HOBJECT_IS_THREAD(h)) { - duk_hthread *t = (duk_hthread *) h; - duk_activation *act; - - DUK_FREE(heap, t->valstack); - - /* Don't free h->resumer because it exists in the heap. - * Callstack entries also contain function pointers which - * are not freed for the same reason. They are decref - * finalized and the targets are freed if necessary based - * on their refcount (or reachability). - */ - for (act = t->callstack_curr; act != NULL;) { - duk_activation *act_next; - duk_catcher *cat; - - for (cat = act->cat; cat != NULL;) { - duk_catcher *cat_next; - - cat_next = cat->parent; - DUK_FREE(heap, (void *) cat); - cat = cat_next; - } - - act_next = act->parent; - DUK_FREE(heap, (void *) act); - act = act_next; - } - - /* XXX: with 'caller' property the callstack would need - * to be unwound to update the 'caller' properties of - * functions in the callstack. - */ - } else if (DUK_HOBJECT_IS_BOUNDFUNC(h)) { - duk_hboundfunc *f = (duk_hboundfunc *) (void *) h; - - DUK_FREE(heap, f->args); - } - - DUK_FREE(heap, (void *) h); -} - -DUK_INTERNAL void duk_free_hbuffer(duk_heap *heap, duk_hbuffer *h) { - DUK_ASSERT(heap != NULL); - DUK_ASSERT(h != NULL); - - if (DUK_HBUFFER_HAS_DYNAMIC(h) && !DUK_HBUFFER_HAS_EXTERNAL(h)) { - duk_hbuffer_dynamic *g = (duk_hbuffer_dynamic *) h; - DUK_DDD(DUK_DDDPRINT("free dynamic buffer %p", (void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(heap, g))); - DUK_FREE(heap, DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(heap, g)); - } - DUK_FREE(heap, (void *) h); -} - -DUK_INTERNAL void duk_free_hstring(duk_heap *heap, duk_hstring *h) { - DUK_ASSERT(heap != NULL); - DUK_ASSERT(h != NULL); - - DUK_UNREF(heap); - DUK_UNREF(h); - -#if defined(DUK_USE_HSTRING_EXTDATA) && defined(DUK_USE_EXTSTR_FREE) - if (DUK_HSTRING_HAS_EXTDATA(h)) { - DUK_DDD( - DUK_DDDPRINT("free extstr: hstring %!O, extdata: %p", h, DUK_HSTRING_GET_EXTDATA((duk_hstring_external *) h))); - DUK_USE_EXTSTR_FREE(heap->heap_udata, (const void *) DUK_HSTRING_GET_EXTDATA((duk_hstring_external *) h)); - } -#endif - DUK_FREE(heap, (void *) h); -} - -DUK_INTERNAL void duk_heap_free_heaphdr_raw(duk_heap *heap, duk_heaphdr *hdr) { - DUK_ASSERT(heap); - DUK_ASSERT(hdr); - - DUK_DDD(DUK_DDDPRINT("free heaphdr %p, htype %ld", (void *) hdr, (long) DUK_HEAPHDR_GET_TYPE(hdr))); - - switch (DUK_HEAPHDR_GET_TYPE(hdr)) { - case DUK_HTYPE_STRING: - duk_free_hstring(heap, (duk_hstring *) hdr); - break; - case DUK_HTYPE_OBJECT: - duk_free_hobject(heap, (duk_hobject *) hdr); - break; - default: - DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(hdr) == DUK_HTYPE_BUFFER); - duk_free_hbuffer(heap, (duk_hbuffer *) hdr); - } -} - -/* - * Free the heap. - * - * Frees heap-related non-heap-tracked allocations such as the - * string intern table; then frees the heap allocated objects; - * and finally frees the heap structure itself. Reference counts - * and GC markers are ignored (and not updated) in this process, - * and finalizers won't be called. - * - * The heap pointer and heap object pointers must not be used - * after this call. - */ - -#if defined(DUK_USE_CACHE_ACTIVATION) -DUK_LOCAL duk_size_t duk__heap_free_activation_freelist(duk_heap *heap) { - duk_activation *act; - duk_activation *act_next; - duk_size_t count_act = 0; - - for (act = heap->activation_free; act != NULL;) { - act_next = act->parent; - DUK_FREE(heap, (void *) act); - act = act_next; -#if defined(DUK_USE_DEBUG) - count_act++; -#endif - } - heap->activation_free = NULL; /* needed when called from mark-and-sweep */ - return count_act; -} -#endif /* DUK_USE_CACHE_ACTIVATION */ - -#if defined(DUK_USE_CACHE_CATCHER) -DUK_LOCAL duk_size_t duk__heap_free_catcher_freelist(duk_heap *heap) { - duk_catcher *cat; - duk_catcher *cat_next; - duk_size_t count_cat = 0; - - for (cat = heap->catcher_free; cat != NULL;) { - cat_next = cat->parent; - DUK_FREE(heap, (void *) cat); - cat = cat_next; -#if defined(DUK_USE_DEBUG) - count_cat++; -#endif - } - heap->catcher_free = NULL; /* needed when called from mark-and-sweep */ - - return count_cat; -} -#endif /* DUK_USE_CACHE_CATCHER */ - -DUK_INTERNAL void duk_heap_free_freelists(duk_heap *heap) { - duk_size_t count_act = 0; - duk_size_t count_cat = 0; - -#if defined(DUK_USE_CACHE_ACTIVATION) - count_act = duk__heap_free_activation_freelist(heap); -#endif -#if defined(DUK_USE_CACHE_CATCHER) - count_cat = duk__heap_free_catcher_freelist(heap); -#endif - DUK_UNREF(heap); - DUK_UNREF(count_act); - DUK_UNREF(count_cat); - - DUK_D( - DUK_DPRINT("freed %ld activation freelist entries, %ld catcher freelist entries", (long) count_act, (long) count_cat)); -} - -DUK_LOCAL void duk__free_allocated(duk_heap *heap) { - duk_heaphdr *curr; - duk_heaphdr *next; - - curr = heap->heap_allocated; - while (curr) { - /* We don't log or warn about freeing zero refcount objects - * because they may happen with finalizer processing. - */ - - DUK_DDD(DUK_DDDPRINT("FINALFREE (allocated): %!iO", (duk_heaphdr *) curr)); - next = DUK_HEAPHDR_GET_NEXT(heap, curr); - duk_heap_free_heaphdr_raw(heap, curr); - curr = next; - } -} - -#if defined(DUK_USE_FINALIZER_SUPPORT) -DUK_LOCAL void duk__free_finalize_list(duk_heap *heap) { - duk_heaphdr *curr; - duk_heaphdr *next; - - curr = heap->finalize_list; - while (curr) { - DUK_DDD(DUK_DDDPRINT("FINALFREE (finalize_list): %!iO", (duk_heaphdr *) curr)); - next = DUK_HEAPHDR_GET_NEXT(heap, curr); - duk_heap_free_heaphdr_raw(heap, curr); - curr = next; - } -} -#endif /* DUK_USE_FINALIZER_SUPPORT */ - -DUK_LOCAL void duk__free_stringtable(duk_heap *heap) { - /* strings are only tracked by stringtable */ - duk_heap_strtable_free(heap); -} - -#if defined(DUK_USE_FINALIZER_SUPPORT) -DUK_LOCAL void duk__free_run_finalizers(duk_heap *heap) { - duk_heaphdr *curr; - duk_uint_t round_no; - duk_size_t count_all; - duk_size_t count_finalized; - duk_size_t curr_limit; - - DUK_ASSERT(heap != NULL); - -#if defined(DUK_USE_REFERENCE_COUNTING) - DUK_ASSERT(heap->refzero_list == NULL); /* refzero not running -> must be empty */ -#endif - DUK_ASSERT(heap->finalize_list == NULL); /* mark-and-sweep last pass */ - - if (heap->heap_thread == NULL) { - /* May happen when heap allocation fails right off. There - * cannot be any finalizable objects in this case. - */ - DUK_D(DUK_DPRINT("no heap_thread in heap destruct, assume no finalizable objects")); - return; - } - - /* Prevent finalize_list processing and mark-and-sweep entirely. - * Setting ms_running != 0 also prevents refzero handling from moving - * objects away from the heap_allocated list. The flag name is a bit - * misleading here. - * - * Use a distinct value for ms_running here (== 2) so that assertions - * can detect this situation separate from the normal runtime - * mark-and-sweep case. This allows better assertions (GH-2030). - */ - DUK_ASSERT(heap->pf_prevent_count == 0); - DUK_ASSERT(heap->ms_running == 0); - DUK_ASSERT(heap->ms_prevent_count == 0); - heap->pf_prevent_count = 1; - heap->ms_running = 2; /* Use distinguishable value. */ - heap->ms_prevent_count = 1; /* Bump, because mark-and-sweep assumes it's bumped when ms_running is set. */ - - curr_limit = 0; /* suppress warning, not used */ - for (round_no = 0;; round_no++) { - curr = heap->heap_allocated; - count_all = 0; - count_finalized = 0; - while (curr) { - count_all++; - if (DUK_HEAPHDR_IS_OBJECT(curr)) { - /* Only objects in heap_allocated may have finalizers. Check that - * the object itself has a _Finalizer property (own or inherited) - * so that we don't execute finalizers for e.g. Proxy objects. - */ - DUK_ASSERT(curr != NULL); - - if (DUK_HOBJECT_HAS_FINALIZER_FAST(heap, (duk_hobject *) curr)) { - if (!DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr *) curr)) { - DUK_ASSERT( - DUK_HEAP_HAS_FINALIZER_NORESCUE(heap)); /* maps to finalizer 2nd argument */ - duk_heap_run_finalizer(heap, (duk_hobject *) curr); - count_finalized++; - } - } - } - curr = DUK_HEAPHDR_GET_NEXT(heap, curr); - } - - /* Each round of finalizer execution may spawn new finalizable objects - * which is normal behavior for some applications. Allow multiple - * rounds of finalization, but use a shrinking limit based on the - * first round to detect the case where a runaway finalizer creates - * an unbounded amount of new finalizable objects. Finalizer rescue - * is not supported: the semantics are unclear because most of the - * objects being finalized here are already reachable. The finalizer - * is given a boolean to indicate that rescue is not possible. - * - * See discussion in: https://github.com/svaarala/duktape/pull/473 - */ - - if (round_no == 0) { - /* Cannot wrap: each object is at least 8 bytes so count is - * at most 1/8 of that. - */ - curr_limit = count_all * 2; - } else { - curr_limit = (curr_limit * 3) / 4; /* Decrease by 25% every round */ - } - DUK_D(DUK_DPRINT("finalizer round %ld complete, %ld objects, tried to execute %ld finalizers, current limit is %ld", - (long) round_no, - (long) count_all, - (long) count_finalized, - (long) curr_limit)); - - if (count_finalized == 0) { - DUK_D(DUK_DPRINT("no more finalizable objects, forced finalization finished")); - break; - } - if (count_finalized >= curr_limit) { - DUK_D(DUK_DPRINT("finalizer count above limit, potentially runaway finalizer; skip remaining finalizers")); - break; - } - } - - DUK_ASSERT(heap->ms_running == 2); - DUK_ASSERT(heap->pf_prevent_count == 1); - heap->ms_running = 0; - heap->pf_prevent_count = 0; -} -#endif /* DUK_USE_FINALIZER_SUPPORT */ - -DUK_INTERNAL void duk_heap_free(duk_heap *heap) { - DUK_D(DUK_DPRINT("free heap: %p", (void *) heap)); - -#if defined(DUK_USE_DEBUG) - duk_heap_strtable_dump(heap); -#endif - -#if defined(DUK_USE_DEBUGGER_SUPPORT) - /* Detach a debugger if attached (can be called multiple times) - * safely. - */ - /* XXX: Add a flag to reject an attempt to re-attach? Otherwise - * the detached callback may immediately reattach. - */ - duk_debug_do_detach(heap); -#endif - - /* Execute finalizers before freeing the heap, even for reachable - * objects. This gives finalizers the chance to free any native - * resources like file handles, allocations made outside Duktape, - * etc. This is quite tricky to get right, so that all finalizer - * guarantees are honored. - * - * Run mark-and-sweep a few times just in case (unreachable object - * finalizers run already here). The last round must rescue objects - * from the previous round without running any more finalizers. This - * ensures rescued objects get their FINALIZED flag cleared so that - * their finalizer is called once more in forced finalization to - * satisfy finalizer guarantees. However, we don't want to run any - * more finalizers because that'd required one more loop, and so on. - * - * XXX: this perhaps requires an execution time limit. - */ - DUK_D(DUK_DPRINT("execute finalizers before freeing heap")); - DUK_ASSERT(heap->pf_skip_finalizers == 0); - DUK_D(DUK_DPRINT("forced gc #1 in heap destruction")); - duk_heap_mark_and_sweep(heap, 0); - DUK_D(DUK_DPRINT("forced gc #2 in heap destruction")); - duk_heap_mark_and_sweep(heap, 0); - DUK_D(DUK_DPRINT("forced gc #3 in heap destruction (don't run finalizers)")); - heap->pf_skip_finalizers = 1; - duk_heap_mark_and_sweep(heap, 0); /* Skip finalizers; queue finalizable objects to heap_allocated. */ - - /* There are never objects in refzero_list at this point, or at any - * point beyond a DECREF (even a DECREF_NORZ). Since Duktape 2.1 - * refzero_list processing is side effect free, so it is always - * processed to completion by a DECREF initially triggering a zero - * refcount. - */ -#if defined(DUK_USE_REFERENCE_COUNTING) - DUK_ASSERT(heap->refzero_list == NULL); /* Always processed to completion inline. */ -#endif -#if defined(DUK_USE_FINALIZER_SUPPORT) - DUK_ASSERT(heap->finalize_list == NULL); /* Last mark-and-sweep with skip_finalizers. */ -#endif - -#if defined(DUK_USE_FINALIZER_SUPPORT) - DUK_D(DUK_DPRINT("run finalizers for remaining finalizable objects")); - DUK_HEAP_SET_FINALIZER_NORESCUE(heap); /* Rescue no longer supported. */ - duk__free_run_finalizers(heap); -#endif /* DUK_USE_FINALIZER_SUPPORT */ - - /* Note: heap->heap_thread, heap->curr_thread, and heap->heap_object - * are on the heap allocated list. - */ - - DUK_D(DUK_DPRINT("freeing temporary freelists")); - duk_heap_free_freelists(heap); - - DUK_D(DUK_DPRINT("freeing heap_allocated of heap: %p", (void *) heap)); - duk__free_allocated(heap); - -#if defined(DUK_USE_REFERENCE_COUNTING) - DUK_ASSERT(heap->refzero_list == NULL); /* Always processed to completion inline. */ -#endif - -#if defined(DUK_USE_FINALIZER_SUPPORT) - DUK_D(DUK_DPRINT("freeing finalize_list of heap: %p", (void *) heap)); - duk__free_finalize_list(heap); -#endif - - DUK_D(DUK_DPRINT("freeing string table of heap: %p", (void *) heap)); - duk__free_stringtable(heap); - - DUK_D(DUK_DPRINT("freeing heap structure: %p", (void *) heap)); - heap->free_func(heap->heap_udata, heap); -} - -/* - * Allocate a heap. - * - * String table is initialized with built-in strings from genbuiltins.py, - * either by dynamically creating the strings or by referring to ROM strings. - */ - -#if defined(DUK_USE_ROM_STRINGS) -DUK_LOCAL duk_bool_t duk__init_heap_strings(duk_heap *heap) { -#if defined(DUK_USE_ASSERTIONS) - duk_small_uint_t i; -#endif - - DUK_UNREF(heap); - - /* With ROM-based strings, heap->strs[] and thr->strs[] are omitted - * so nothing to initialize for strs[]. - */ - -#if defined(DUK_USE_ASSERTIONS) - for (i = 0; i < sizeof(duk_rom_strings_lookup) / sizeof(const duk_hstring *); i++) { - const duk_hstring *h; - duk_uint32_t hash; - - h = duk_rom_strings_lookup[i]; - while (h != NULL) { - hash = duk_heap_hashstring(heap, (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h), DUK_HSTRING_GET_BYTELEN(h)); - DUK_DD(DUK_DDPRINT("duk_rom_strings_lookup[%d] -> hash 0x%08lx, computed 0x%08lx", - (int) i, - (unsigned long) DUK_HSTRING_GET_HASH(h), - (unsigned long) hash)); - DUK_ASSERT(hash == (duk_uint32_t) DUK_HSTRING_GET_HASH(h)); - - h = (const duk_hstring *) h->hdr.h_next; - } - } -#endif - return 1; -} -#else /* DUK_USE_ROM_STRINGS */ - -DUK_LOCAL duk_bool_t duk__init_heap_strings(duk_heap *heap) { - duk_bitdecoder_ctx bd_ctx; - duk_bitdecoder_ctx *bd = &bd_ctx; /* convenience */ - duk_small_uint_t i; - - duk_memzero(&bd_ctx, sizeof(bd_ctx)); - bd->data = (const duk_uint8_t *) duk_strings_data; - bd->length = (duk_size_t) DUK_STRDATA_DATA_LENGTH; - - for (i = 0; i < DUK_HEAP_NUM_STRINGS; i++) { - duk_uint8_t tmp[DUK_STRDATA_MAX_STRLEN]; - duk_small_uint_t len; - duk_hstring *h; - - len = duk_bd_decode_bitpacked_string(bd, tmp); - - /* No need to length check string: it will never exceed even - * the 16-bit length maximum. - */ - DUK_ASSERT(len <= 0xffffUL); - DUK_DDD(DUK_DDDPRINT("intern built-in string %ld", (long) i)); - h = duk_heap_strtable_intern(heap, tmp, len); - if (!h) { - goto failed; - } - DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) h)); - - /* Special flags checks. Since these strings are always - * reachable and a string cannot appear twice in the string - * table, there's no need to check/set these flags elsewhere. - * The 'internal' flag is set by string intern code. - */ - if (i == DUK_STRIDX_EVAL || i == DUK_STRIDX_LC_ARGUMENTS) { - DUK_HSTRING_SET_EVAL_OR_ARGUMENTS(h); - } - if (i >= DUK_STRIDX_START_RESERVED && i < DUK_STRIDX_END_RESERVED) { - DUK_HSTRING_SET_RESERVED_WORD(h); - if (i >= DUK_STRIDX_START_STRICT_RESERVED) { - DUK_HSTRING_SET_STRICT_RESERVED_WORD(h); - } - } - - DUK_DDD(DUK_DDDPRINT("interned: %!O", (duk_heaphdr *) h)); - - /* XXX: The incref macro takes a thread pointer but doesn't - * use it right now. - */ - DUK_HSTRING_INCREF(_never_referenced_, h); - -#if defined(DUK_USE_HEAPPTR16) - heap->strs16[i] = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) h); -#else - heap->strs[i] = h; -#endif - } - - return 1; - -failed: - return 0; -} -#endif /* DUK_USE_ROM_STRINGS */ - -DUK_LOCAL duk_bool_t duk__init_heap_thread(duk_heap *heap) { - duk_hthread *thr; - - DUK_D(DUK_DPRINT("heap init: alloc heap thread")); - thr = duk_hthread_alloc_unchecked(heap, DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_THREAD)); - if (thr == NULL) { - DUK_D(DUK_DPRINT("failed to alloc heap_thread")); - return 0; - } - thr->state = DUK_HTHREAD_STATE_INACTIVE; -#if defined(DUK_USE_ROM_STRINGS) - /* No strs[] pointer. */ -#else /* DUK_USE_ROM_STRINGS */ -#if defined(DUK_USE_HEAPPTR16) - thr->strs16 = heap->strs16; -#else - thr->strs = heap->strs; -#endif -#endif /* DUK_USE_ROM_STRINGS */ - - heap->heap_thread = thr; - DUK_HTHREAD_INCREF(thr, thr); /* Note: first argument not really used */ - - /* 'thr' is now reachable */ - - DUK_D(DUK_DPRINT("heap init: init heap thread stacks")); - if (!duk_hthread_init_stacks(heap, thr)) { - return 0; - } - - /* XXX: this may now fail, and is not handled correctly */ - duk_hthread_create_builtin_objects(thr); - - /* default prototype */ - DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) thr, thr->builtins[DUK_BIDX_THREAD_PROTOTYPE]); - - return 1; -} - -#if defined(DUK_USE_DEBUG) -#define DUK__DUMPSZ(t) \ - do { \ - DUK_D(DUK_DPRINT("" #t "=%ld", (long) sizeof(t))); \ - } while (0) - -/* These is not 100% because format would need to be non-portable "long long". - * Also print out as doubles to catch cases where the "long" type is not wide - * enough; the limits will then not be printed accurately but the magnitude - * will be correct. - */ -#define DUK__DUMPLM_SIGNED_RAW(t, a, b) \ - do { \ - DUK_D(DUK_DPRINT(t "=[%ld,%ld]=[%lf,%lf]", (long) (a), (long) (b), (double) (a), (double) (b))); \ - } while (0) -#define DUK__DUMPLM_UNSIGNED_RAW(t, a, b) \ - do { \ - DUK_D(DUK_DPRINT(t "=[%lu,%lu]=[%lf,%lf]", (unsigned long) (a), (unsigned long) (b), (double) (a), (double) (b))); \ - } while (0) -#define DUK__DUMPLM_SIGNED(t) \ - do { \ - DUK__DUMPLM_SIGNED_RAW("DUK_" #t "_{MIN,MAX}", DUK_##t##_MIN, DUK_##t##_MAX); \ - } while (0) -#define DUK__DUMPLM_UNSIGNED(t) \ - do { \ - DUK__DUMPLM_UNSIGNED_RAW("DUK_" #t "_{MIN,MAX}", DUK_##t##_MIN, DUK_##t##_MAX); \ - } while (0) - -DUK_LOCAL void duk__dump_type_sizes(void) { - DUK_D(DUK_DPRINT("sizeof()")); - - /* basic platform types */ - DUK__DUMPSZ(char); - DUK__DUMPSZ(short); - DUK__DUMPSZ(int); - DUK__DUMPSZ(long); - DUK__DUMPSZ(double); - DUK__DUMPSZ(void *); - DUK__DUMPSZ(size_t); - - /* basic types from duk_features.h */ - DUK__DUMPSZ(duk_uint8_t); - DUK__DUMPSZ(duk_int8_t); - DUK__DUMPSZ(duk_uint16_t); - DUK__DUMPSZ(duk_int16_t); - DUK__DUMPSZ(duk_uint32_t); - DUK__DUMPSZ(duk_int32_t); - DUK__DUMPSZ(duk_uint64_t); - DUK__DUMPSZ(duk_int64_t); - DUK__DUMPSZ(duk_uint_least8_t); - DUK__DUMPSZ(duk_int_least8_t); - DUK__DUMPSZ(duk_uint_least16_t); - DUK__DUMPSZ(duk_int_least16_t); - DUK__DUMPSZ(duk_uint_least32_t); - DUK__DUMPSZ(duk_int_least32_t); -#if defined(DUK_USE_64BIT_OPS) - DUK__DUMPSZ(duk_uint_least64_t); - DUK__DUMPSZ(duk_int_least64_t); -#endif - DUK__DUMPSZ(duk_uint_fast8_t); - DUK__DUMPSZ(duk_int_fast8_t); - DUK__DUMPSZ(duk_uint_fast16_t); - DUK__DUMPSZ(duk_int_fast16_t); - DUK__DUMPSZ(duk_uint_fast32_t); - DUK__DUMPSZ(duk_int_fast32_t); -#if defined(DUK_USE_64BIT_OPS) - DUK__DUMPSZ(duk_uint_fast64_t); - DUK__DUMPSZ(duk_int_fast64_t); -#endif - DUK__DUMPSZ(duk_uintptr_t); - DUK__DUMPSZ(duk_intptr_t); - DUK__DUMPSZ(duk_uintmax_t); - DUK__DUMPSZ(duk_intmax_t); - DUK__DUMPSZ(duk_double_t); - - /* important chosen base types */ - DUK__DUMPSZ(duk_int_t); - DUK__DUMPSZ(duk_uint_t); - DUK__DUMPSZ(duk_int_fast_t); - DUK__DUMPSZ(duk_uint_fast_t); - DUK__DUMPSZ(duk_small_int_t); - DUK__DUMPSZ(duk_small_uint_t); - DUK__DUMPSZ(duk_small_int_fast_t); - DUK__DUMPSZ(duk_small_uint_fast_t); - - /* some derived types */ - DUK__DUMPSZ(duk_codepoint_t); - DUK__DUMPSZ(duk_ucodepoint_t); - DUK__DUMPSZ(duk_idx_t); - DUK__DUMPSZ(duk_errcode_t); - DUK__DUMPSZ(duk_uarridx_t); - - /* tval */ - DUK__DUMPSZ(duk_double_union); - DUK__DUMPSZ(duk_tval); - - /* structs from duk_forwdecl.h */ - DUK__DUMPSZ(duk_jmpbuf); /* just one 'int' for C++ exceptions */ - DUK__DUMPSZ(duk_heaphdr); - DUK__DUMPSZ(duk_heaphdr_string); - DUK__DUMPSZ(duk_hstring); - DUK__DUMPSZ(duk_hstring_external); - DUK__DUMPSZ(duk_hobject); - DUK__DUMPSZ(duk_harray); - DUK__DUMPSZ(duk_hcompfunc); - DUK__DUMPSZ(duk_hnatfunc); - DUK__DUMPSZ(duk_hdecenv); - DUK__DUMPSZ(duk_hobjenv); - DUK__DUMPSZ(duk_hthread); -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) - DUK__DUMPSZ(duk_hbufobj); -#endif - DUK__DUMPSZ(duk_hproxy); - DUK__DUMPSZ(duk_hbuffer); - DUK__DUMPSZ(duk_hbuffer_fixed); - DUK__DUMPSZ(duk_hbuffer_dynamic); - DUK__DUMPSZ(duk_hbuffer_external); - DUK__DUMPSZ(duk_propaccessor); - DUK__DUMPSZ(duk_propvalue); - DUK__DUMPSZ(duk_propdesc); - DUK__DUMPSZ(duk_heap); - DUK__DUMPSZ(duk_activation); - DUK__DUMPSZ(duk_catcher); - DUK__DUMPSZ(duk_strcache_entry); - DUK__DUMPSZ(duk_litcache_entry); - DUK__DUMPSZ(duk_ljstate); - DUK__DUMPSZ(duk_fixedbuffer); - DUK__DUMPSZ(duk_bitdecoder_ctx); - DUK__DUMPSZ(duk_bitencoder_ctx); - DUK__DUMPSZ(duk_token); - DUK__DUMPSZ(duk_re_token); - DUK__DUMPSZ(duk_lexer_point); - DUK__DUMPSZ(duk_lexer_ctx); - DUK__DUMPSZ(duk_compiler_instr); - DUK__DUMPSZ(duk_compiler_func); - DUK__DUMPSZ(duk_compiler_ctx); - DUK__DUMPSZ(duk_re_matcher_ctx); - DUK__DUMPSZ(duk_re_compiler_ctx); -} -DUK_LOCAL void duk__dump_type_limits(void) { - DUK_D(DUK_DPRINT("limits")); - - /* basic types */ - DUK__DUMPLM_SIGNED(INT8); - DUK__DUMPLM_UNSIGNED(UINT8); - DUK__DUMPLM_SIGNED(INT_FAST8); - DUK__DUMPLM_UNSIGNED(UINT_FAST8); - DUK__DUMPLM_SIGNED(INT_LEAST8); - DUK__DUMPLM_UNSIGNED(UINT_LEAST8); - DUK__DUMPLM_SIGNED(INT16); - DUK__DUMPLM_UNSIGNED(UINT16); - DUK__DUMPLM_SIGNED(INT_FAST16); - DUK__DUMPLM_UNSIGNED(UINT_FAST16); - DUK__DUMPLM_SIGNED(INT_LEAST16); - DUK__DUMPLM_UNSIGNED(UINT_LEAST16); - DUK__DUMPLM_SIGNED(INT32); - DUK__DUMPLM_UNSIGNED(UINT32); - DUK__DUMPLM_SIGNED(INT_FAST32); - DUK__DUMPLM_UNSIGNED(UINT_FAST32); - DUK__DUMPLM_SIGNED(INT_LEAST32); - DUK__DUMPLM_UNSIGNED(UINT_LEAST32); -#if defined(DUK_USE_64BIT_OPS) - DUK__DUMPLM_SIGNED(INT64); - DUK__DUMPLM_UNSIGNED(UINT64); - DUK__DUMPLM_SIGNED(INT_FAST64); - DUK__DUMPLM_UNSIGNED(UINT_FAST64); - DUK__DUMPLM_SIGNED(INT_LEAST64); - DUK__DUMPLM_UNSIGNED(UINT_LEAST64); -#endif - DUK__DUMPLM_SIGNED(INTPTR); - DUK__DUMPLM_UNSIGNED(UINTPTR); - DUK__DUMPLM_SIGNED(INTMAX); - DUK__DUMPLM_UNSIGNED(UINTMAX); - - /* derived types */ - DUK__DUMPLM_SIGNED(INT); - DUK__DUMPLM_UNSIGNED(UINT); - DUK__DUMPLM_SIGNED(INT_FAST); - DUK__DUMPLM_UNSIGNED(UINT_FAST); - DUK__DUMPLM_SIGNED(SMALL_INT); - DUK__DUMPLM_UNSIGNED(SMALL_UINT); - DUK__DUMPLM_SIGNED(SMALL_INT_FAST); - DUK__DUMPLM_UNSIGNED(SMALL_UINT_FAST); -} - -DUK_LOCAL void duk__dump_misc_options(void) { - DUK_D(DUK_DPRINT("DUK_VERSION: %ld", (long) DUK_VERSION)); - DUK_D(DUK_DPRINT("DUK_GIT_DESCRIBE: %s", DUK_GIT_DESCRIBE)); - DUK_D(DUK_DPRINT("OS string: %s", DUK_USE_OS_STRING)); - DUK_D(DUK_DPRINT("architecture string: %s", DUK_USE_ARCH_STRING)); - DUK_D(DUK_DPRINT("compiler string: %s", DUK_USE_COMPILER_STRING)); - DUK_D(DUK_DPRINT("debug level: %ld", (long) DUK_USE_DEBUG_LEVEL)); -#if defined(DUK_USE_PACKED_TVAL) - DUK_D(DUK_DPRINT("DUK_USE_PACKED_TVAL: yes")); -#else - DUK_D(DUK_DPRINT("DUK_USE_PACKED_TVAL: no")); -#endif -#if defined(DUK_USE_VARIADIC_MACROS) - DUK_D(DUK_DPRINT("DUK_USE_VARIADIC_MACROS: yes")); -#else - DUK_D(DUK_DPRINT("DUK_USE_VARIADIC_MACROS: no")); -#endif -#if defined(DUK_USE_INTEGER_LE) - DUK_D(DUK_DPRINT("integer endianness: little")); -#elif defined(DUK_USE_INTEGER_ME) - DUK_D(DUK_DPRINT("integer endianness: mixed")); -#elif defined(DUK_USE_INTEGER_BE) - DUK_D(DUK_DPRINT("integer endianness: big")); -#else - DUK_D(DUK_DPRINT("integer endianness: ???")); -#endif -#if defined(DUK_USE_DOUBLE_LE) - DUK_D(DUK_DPRINT("IEEE double endianness: little")); -#elif defined(DUK_USE_DOUBLE_ME) - DUK_D(DUK_DPRINT("IEEE double endianness: mixed")); -#elif defined(DUK_USE_DOUBLE_BE) - DUK_D(DUK_DPRINT("IEEE double endianness: big")); -#else - DUK_D(DUK_DPRINT("IEEE double endianness: ???")); -#endif -} -#endif /* DUK_USE_DEBUG */ - -DUK_INTERNAL -duk_heap *duk_heap_alloc(duk_alloc_function alloc_func, - duk_realloc_function realloc_func, - duk_free_function free_func, - void *heap_udata, - duk_fatal_function fatal_func) { - duk_heap *res = NULL; - duk_uint32_t st_initsize; - - DUK_D(DUK_DPRINT("allocate heap")); - - /* - * Random config sanity asserts - */ - - DUK_ASSERT(DUK_USE_STRTAB_MINSIZE >= 64); - - DUK_ASSERT((DUK_HTYPE_STRING & 0x01U) == 0); - DUK_ASSERT((DUK_HTYPE_BUFFER & 0x01U) == 0); - DUK_ASSERT((DUK_HTYPE_OBJECT & 0x01U) == 1); /* DUK_HEAPHDR_IS_OBJECT() relies ont his. */ - - /* - * Debug dump type sizes - */ - -#if defined(DUK_USE_DEBUG) - duk__dump_misc_options(); - duk__dump_type_sizes(); - duk__dump_type_limits(); -#endif - - /* - * If selftests enabled, run them as early as possible. - */ - -#if defined(DUK_USE_SELF_TESTS) - DUK_D(DUK_DPRINT("run self tests")); - if (duk_selftest_run_tests(alloc_func, realloc_func, free_func, heap_udata) > 0) { - fatal_func(heap_udata, "self test(s) failed"); - } - DUK_D(DUK_DPRINT("self tests passed")); -#endif - - /* - * Important assert-like checks that should be enabled even - * when assertions are otherwise not enabled. - */ - -#if defined(DUK_USE_EXEC_REGCONST_OPTIMIZE) - /* Can't check sizeof() using preprocessor so explicit check. - * This will be optimized away in practice; unfortunately a - * warning is generated on some compilers as a result. - */ -#if defined(DUK_USE_PACKED_TVAL) - if (sizeof(duk_tval) != 8) { -#else - if (sizeof(duk_tval) != 16) { -#endif - fatal_func(heap_udata, "sizeof(duk_tval) not 8 or 16, cannot use DUK_USE_EXEC_REGCONST_OPTIMIZE option"); - } -#endif /* DUK_USE_EXEC_REGCONST_OPTIMIZE */ - - /* - * Computed values (e.g. INFINITY) - */ - -#if defined(DUK_USE_COMPUTED_NAN) - do { - /* Workaround for some exotic platforms where NAN is missing - * and the expression (0.0 / 0.0) does NOT result in a NaN. - * Such platforms use the global 'duk_computed_nan' which must - * be initialized at runtime. Use 'volatile' to ensure that - * the compiler will actually do the computation and not try - * to do constant folding which might result in the original - * problem. - */ - volatile double dbl1 = 0.0; - volatile double dbl2 = 0.0; - duk_computed_nan = dbl1 / dbl2; - } while (0); -#endif - -#if defined(DUK_USE_COMPUTED_INFINITY) - do { - /* Similar workaround for INFINITY. */ - volatile double dbl1 = 1.0; - volatile double dbl2 = 0.0; - duk_computed_infinity = dbl1 / dbl2; - } while (0); -#endif - - /* - * Allocate heap struct - * - * Use a raw call, all macros expect the heap to be initialized - */ - -#if defined(DUK_USE_INJECT_HEAP_ALLOC_ERROR) && (DUK_USE_INJECT_HEAP_ALLOC_ERROR == 1) - goto failed; -#endif - DUK_D(DUK_DPRINT("alloc duk_heap object")); - res = (duk_heap *) alloc_func(heap_udata, sizeof(duk_heap)); - if (!res) { - goto failed; - } - - /* - * Zero the struct, and start initializing roughly in order - */ - - duk_memzero(res, sizeof(*res)); -#if defined(DUK_USE_ASSERTIONS) - res->heap_initializing = 1; -#endif - - /* explicit NULL inits */ -#if defined(DUK_USE_EXPLICIT_NULL_INIT) - res->heap_udata = NULL; - res->heap_allocated = NULL; -#if defined(DUK_USE_REFERENCE_COUNTING) - res->refzero_list = NULL; -#endif -#if defined(DUK_USE_FINALIZER_SUPPORT) - res->finalize_list = NULL; -#if defined(DUK_USE_ASSERTIONS) - res->currently_finalizing = NULL; -#endif -#endif -#if defined(DUK_USE_CACHE_ACTIVATION) - res->activation_free = NULL; -#endif -#if defined(DUK_USE_CACHE_CATCHER) - res->catcher_free = NULL; -#endif - res->heap_thread = NULL; - res->curr_thread = NULL; - res->heap_object = NULL; -#if defined(DUK_USE_STRTAB_PTRCOMP) - res->strtable16 = NULL; -#else - res->strtable = NULL; -#endif -#if defined(DUK_USE_ROM_STRINGS) - /* no res->strs[] */ -#else /* DUK_USE_ROM_STRINGS */ -#if defined(DUK_USE_HEAPPTR16) - /* res->strs16[] is zeroed and zero decodes to NULL, so no NULL inits. */ -#else - { - duk_small_uint_t i; - for (i = 0; i < DUK_HEAP_NUM_STRINGS; i++) { - res->strs[i] = NULL; - } - } -#endif -#endif /* DUK_USE_ROM_STRINGS */ -#if defined(DUK_USE_DEBUGGER_SUPPORT) - res->dbg_read_cb = NULL; - res->dbg_write_cb = NULL; - res->dbg_peek_cb = NULL; - res->dbg_read_flush_cb = NULL; - res->dbg_write_flush_cb = NULL; - res->dbg_request_cb = NULL; - res->dbg_udata = NULL; - res->dbg_pause_act = NULL; -#endif -#endif /* DUK_USE_EXPLICIT_NULL_INIT */ - - res->alloc_func = alloc_func; - res->realloc_func = realloc_func; - res->free_func = free_func; - res->heap_udata = heap_udata; - res->fatal_func = fatal_func; - - /* XXX: for now there's a pointer packing zero assumption, i.e. - * NULL <=> compressed pointer 0. If this is removed, may need - * to precompute e.g. null16 here. - */ - - /* res->ms_trigger_counter == 0 -> now causes immediate GC; which is OK */ - - /* Prevent mark-and-sweep and finalizer execution until heap is completely - * initialized. - */ - DUK_ASSERT(res->ms_prevent_count == 0); - DUK_ASSERT(res->pf_prevent_count == 0); - res->ms_prevent_count = 1; - res->pf_prevent_count = 1; - DUK_ASSERT(res->ms_running == 0); - - res->call_recursion_depth = 0; - res->call_recursion_limit = DUK_USE_NATIVE_CALL_RECLIMIT; - - /* XXX: use the pointer as a seed for now: mix in time at least */ - - /* The casts through duk_uintptr_t is to avoid the following GCC warning: - * - * warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] - * - * This still generates a /Wp64 warning on VS2010 when compiling for x86. - */ -#if defined(DUK_USE_ROM_STRINGS) - /* XXX: make a common DUK_USE_ option, and allow custom fixed seed? */ - DUK_D(DUK_DPRINT("using rom strings, force heap hash_seed to fixed value 0x%08lx", (long) DUK__FIXED_HASH_SEED)); - res->hash_seed = (duk_uint32_t) DUK__FIXED_HASH_SEED; -#else /* DUK_USE_ROM_STRINGS */ - res->hash_seed = (duk_uint32_t) (duk_uintptr_t) res; -#if !defined(DUK_USE_STRHASH_DENSE) - res->hash_seed ^= 5381; /* Bernstein hash init value is normally 5381; XOR it in in case pointer low bits are 0 */ -#endif -#endif /* DUK_USE_ROM_STRINGS */ - -#if defined(DUK_USE_EXPLICIT_NULL_INIT) - res->lj.jmpbuf_ptr = NULL; -#endif - DUK_ASSERT(res->lj.type == DUK_LJ_TYPE_UNKNOWN); /* zero */ - DUK_ASSERT(res->lj.iserror == 0); - DUK_TVAL_SET_UNDEFINED(&res->lj.value1); - DUK_TVAL_SET_UNDEFINED(&res->lj.value2); - - DUK_ASSERT_LJSTATE_UNSET(res); - - /* - * Init stringtable: fixed variant - */ - - st_initsize = DUK_USE_STRTAB_MINSIZE; -#if defined(DUK_USE_STRTAB_PTRCOMP) - res->strtable16 = (duk_uint16_t *) alloc_func(heap_udata, sizeof(duk_uint16_t) * st_initsize); - if (res->strtable16 == NULL) { - goto failed; - } -#else - res->strtable = (duk_hstring **) alloc_func(heap_udata, sizeof(duk_hstring *) * st_initsize); - if (res->strtable == NULL) { - goto failed; - } -#endif - res->st_size = st_initsize; - res->st_mask = st_initsize - 1; -#if (DUK_USE_STRTAB_MINSIZE != DUK_USE_STRTAB_MAXSIZE) - DUK_ASSERT(res->st_count == 0); -#endif - -#if defined(DUK_USE_STRTAB_PTRCOMP) - /* zero assumption */ - duk_memzero(res->strtable16, sizeof(duk_uint16_t) * st_initsize); -#else -#if defined(DUK_USE_EXPLICIT_NULL_INIT) - { - duk_uint32_t i; - for (i = 0; i < st_initsize; i++) { - res->strtable[i] = NULL; - } - } -#else - duk_memzero(res->strtable, sizeof(duk_hstring *) * st_initsize); -#endif /* DUK_USE_EXPLICIT_NULL_INIT */ -#endif /* DUK_USE_STRTAB_PTRCOMP */ - - /* - * Init stringcache - */ - -#if defined(DUK_USE_EXPLICIT_NULL_INIT) - { - duk_uint_t i; - for (i = 0; i < DUK_HEAP_STRCACHE_SIZE; i++) { - res->strcache[i].h = NULL; - } - } -#endif - - /* - * Init litcache - */ -#if defined(DUK_USE_LITCACHE_SIZE) - DUK_ASSERT(DUK_USE_LITCACHE_SIZE > 0); - DUK_ASSERT(DUK_IS_POWER_OF_TWO((duk_uint_t) DUK_USE_LITCACHE_SIZE)); -#if defined(DUK_USE_EXPLICIT_NULL_INIT) - { - duk_uint_t i; - for (i = 0; i < DUK_USE_LITCACHE_SIZE; i++) { - res->litcache[i].addr = NULL; - res->litcache[i].h = NULL; - } - } -#endif -#endif /* DUK_USE_LITCACHE_SIZE */ - - /* XXX: error handling is incomplete. It would be cleanest if - * there was a setjmp catchpoint, so that all init code could - * freely throw errors. If that were the case, the return code - * passing here could be removed. - */ - - /* - * Init built-in strings - */ - -#if defined(DUK_USE_INJECT_HEAP_ALLOC_ERROR) && (DUK_USE_INJECT_HEAP_ALLOC_ERROR == 2) - goto failed; -#endif - DUK_D(DUK_DPRINT("heap init: initialize heap strings")); - if (!duk__init_heap_strings(res)) { - goto failed; - } - - /* - * Init the heap thread - */ - -#if defined(DUK_USE_INJECT_HEAP_ALLOC_ERROR) && (DUK_USE_INJECT_HEAP_ALLOC_ERROR == 3) - goto failed; -#endif - DUK_D(DUK_DPRINT("heap init: initialize heap thread")); - if (!duk__init_heap_thread(res)) { - goto failed; - } - - /* - * Init the heap object - */ - -#if defined(DUK_USE_INJECT_HEAP_ALLOC_ERROR) && (DUK_USE_INJECT_HEAP_ALLOC_ERROR == 4) - goto failed; -#endif - DUK_D(DUK_DPRINT("heap init: initialize heap object")); - DUK_ASSERT(res->heap_thread != NULL); - res->heap_object = duk_hobject_alloc_unchecked(res, - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_FASTREFS | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT)); - if (res->heap_object == NULL) { - goto failed; - } - DUK_HOBJECT_INCREF(res->heap_thread, res->heap_object); - - /* - * Odds and ends depending on the heap thread - */ - -#if !defined(DUK_USE_GET_RANDOM_DOUBLE) -#if defined(DUK_USE_PREFER_SIZE) || !defined(DUK_USE_64BIT_OPS) - res->rnd_state = (duk_uint32_t) duk_time_get_ecmascript_time(res->heap_thread); - duk_util_tinyrandom_prepare_seed(res->heap_thread); -#else - res->rnd_state[0] = (duk_uint64_t) duk_time_get_ecmascript_time(res->heap_thread); - DUK_ASSERT(res->rnd_state[1] == 0); /* Not filled here, filled in by seed preparation. */ -#if 0 /* Manual test values matching misc/xoroshiro128plus_test.c. */ - res->rnd_state[0] = DUK_U64_CONSTANT(0xdeadbeef12345678); - res->rnd_state[1] = DUK_U64_CONSTANT(0xcafed00d12345678); -#endif - duk_util_tinyrandom_prepare_seed(res->heap_thread); - /* Mix in heap pointer: this ensures that if two Duktape heaps are - * created on the same millisecond, they get a different PRNG - * sequence (unless e.g. virtual memory addresses cause also the - * heap object pointer to be the same). - */ - { - duk_uint64_t tmp_u64; - tmp_u64 = 0; - duk_memcpy((void *) &tmp_u64, - (const void *) &res, - (size_t) (sizeof(void *) >= sizeof(duk_uint64_t) ? sizeof(duk_uint64_t) : sizeof(void *))); - res->rnd_state[1] ^= tmp_u64; - } - do { - duk_small_uint_t i; - for (i = 0; i < 10; i++) { - /* Throw away a few initial random numbers just in - * case. Probably unnecessary due to SplitMix64 - * preparation. - */ - (void) duk_util_tinyrandom_get_double(res->heap_thread); - } - } while (0); -#endif -#endif - - /* - * Allow finalizer and mark-and-sweep processing. - */ - - DUK_D(DUK_DPRINT("heap init: allow finalizer/mark-and-sweep processing")); - DUK_ASSERT(res->ms_prevent_count == 1); - DUK_ASSERT(res->pf_prevent_count == 1); - res->ms_prevent_count = 0; - res->pf_prevent_count = 0; - DUK_ASSERT(res->ms_running == 0); -#if defined(DUK_USE_ASSERTIONS) - res->heap_initializing = 0; -#endif - - /* - * All done. - */ - - DUK_D(DUK_DPRINT("allocated heap: %p", (void *) res)); - return res; - -failed: - DUK_D(DUK_DPRINT("heap allocation failed")); - - if (res != NULL) { - /* Assumes that allocated pointers and alloc funcs are valid - * if res exists. - */ - DUK_ASSERT(res->ms_prevent_count == 1); - DUK_ASSERT(res->pf_prevent_count == 1); - DUK_ASSERT(res->ms_running == 0); - if (res->heap_thread != NULL) { - res->ms_prevent_count = 0; - res->pf_prevent_count = 0; - } -#if defined(DUK_USE_ASSERTIONS) - res->heap_initializing = 0; -#endif - - DUK_ASSERT(res->alloc_func != NULL); - DUK_ASSERT(res->realloc_func != NULL); - DUK_ASSERT(res->free_func != NULL); - duk_heap_free(res); - } - - return NULL; -} - -/* automatic undefs */ -#undef DUK__DUMPLM_SIGNED -#undef DUK__DUMPLM_SIGNED_RAW -#undef DUK__DUMPLM_UNSIGNED -#undef DUK__DUMPLM_UNSIGNED_RAW -#undef DUK__DUMPSZ -#undef DUK__FIXED_HASH_SEED -/* - * Finalizer handling. - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_FINALIZER_SUPPORT) - -/* - * Fake torture finalizer. - */ - -#if defined(DUK_USE_FINALIZER_TORTURE) -DUK_LOCAL duk_ret_t duk__fake_global_finalizer(duk_hthread *thr) { - DUK_DD(DUK_DDPRINT("fake global torture finalizer executed")); - - /* Require a lot of stack to force a value stack grow/shrink. */ - duk_require_stack(thr, 100000); - - /* Force a reallocation with pointer change for value stack - * to maximize side effects. - */ - duk_hthread_valstack_torture_realloc(thr); - - /* Inner function call, error throw. */ - duk_eval_string_noresult(thr, - "(function dummy() {\n" - " dummy.prototype = null; /* break reference loop */\n" - " try {\n" - " throw 'fake-finalizer-dummy-error';\n" - " } catch (e) {\n" - " void e;\n" - " }\n" - "})()"); - - /* The above creates garbage (e.g. a function instance). Because - * the function/prototype reference loop is broken, it gets collected - * immediately by DECREF. If Function.prototype has a _Finalizer - * property (happens in some test cases), the garbage gets queued to - * finalize_list. This still won't cause an infinite loop because - * the torture finalizer is called once per finalize_list run and - * the garbage gets handled in the same run. (If the garbage needs - * mark-and-sweep collection, an infinite loop might ensue.) - */ - return 0; -} - -DUK_LOCAL void duk__run_global_torture_finalizer(duk_hthread *thr) { - DUK_ASSERT(thr != NULL); - - /* Avoid fake finalization when callstack limit is near. Otherwise - * a callstack limit error will be created, then refzero'ed. The - * +5 headroom is conservative. - */ - if (thr->heap->call_recursion_depth + 5 >= thr->heap->call_recursion_limit || - thr->callstack_top + 5 >= DUK_USE_CALLSTACK_LIMIT) { - DUK_D(DUK_DPRINT("skip global torture finalizer, too little headroom for call recursion or call stack size")); - return; - } - - /* Run fake finalizer. Avoid creating unnecessary garbage. */ - duk_push_c_function(thr, duk__fake_global_finalizer, 0 /*nargs*/); - (void) duk_pcall(thr, 0 /*nargs*/); - duk_pop(thr); -} -#endif /* DUK_USE_FINALIZER_TORTURE */ - -/* - * Process the finalize_list to completion. - * - * An object may be placed on finalize_list by either refcounting or - * mark-and-sweep. The refcount of objects placed by refcounting will be - * zero; the refcount of objects placed by mark-and-sweep is > 0. In both - * cases the refcount is bumped by 1 artificially so that a REFZERO event - * can never happen while an object is waiting for finalization. Without - * this bump a REFZERO could now happen because user code may call - * duk_push_heapptr() and then pop a value even when it's on finalize_list. - * - * List processing assumes refcounts are kept up-to-date at all times, so - * that once the finalizer returns, a zero refcount is a reliable reason to - * free the object immediately rather than place it back to the heap. This - * is the case because we run outside of refzero_list processing so that - * DECREF cascades are handled fully inline. - * - * For mark-and-sweep queued objects (had_zero_refcount false) the object - * may be freed immediately if its refcount is zero after the finalizer call - * (i.e. finalizer removed the reference loop for the object). If not, the - * next mark-and-sweep will collect the object unless it has become reachable - * (i.e. rescued) by that time and its refcount hasn't fallen to zero before - * that. Mark-and-sweep detects these objects because their FINALIZED flag - * is set. - * - * There's an inherent limitation for mark-and-sweep finalizer rescuing: an - * object won't get refinalized if (1) it's rescued, but (2) becomes - * unreachable before mark-and-sweep has had time to notice it. The next - * mark-and-sweep round simply doesn't have any information of whether the - * object has been unreachable the whole time or not (the only way to get - * that information would be a mark-and-sweep pass for *every finalized - * object*). This is awkward for the application because the mark-and-sweep - * round is not generally visible or under full application control. - * - * For refcount queued objects (had_zero_refcount true) the object is either - * immediately freed or rescued, and waiting for a mark-and-sweep round is not - * necessary (or desirable); FINALIZED is cleared when a rescued object is - * queued back to heap_allocated. The object is eligible for finalization - * again (either via refcounting or mark-and-sweep) immediately after being - * rescued. If a refcount finalized object is placed into an unreachable - * reference loop by its finalizer, it will get collected by mark-and-sweep - * and currently the finalizer will execute again. - * - * There's a special case where: - * - * - Mark-and-sweep queues an object to finalize_list for finalization. - * - The finalizer is executed, FINALIZED is set, and object is queued - * back to heap_allocated, waiting for a new mark-and-sweep round. - * - The object's refcount drops to zero before mark-and-sweep has a - * chance to run another round and make a rescue/free decision. - * - * This is now handled by refzero code: if an object has a finalizer but - * FINALIZED is already set, the object is freed without finalizer processing. - * The outcome is the same as if mark-and-sweep was executed at that point; - * mark-and-sweep would also free the object without another finalizer run. - * This could also be changed so that the refzero-triggered finalizer *IS* - * executed: being refzero collected implies someone has operated on the - * object so it hasn't been totally unreachable the whole time. This would - * risk a finalizer loop however. - */ - -DUK_INTERNAL void duk_heap_process_finalize_list(duk_heap *heap) { - duk_heaphdr *curr; -#if defined(DUK_USE_DEBUG) - duk_size_t count = 0; -#endif - - DUK_DDD(DUK_DDDPRINT("duk_heap_process_finalize_list: %p", (void *) heap)); - - if (heap->pf_prevent_count != 0) { - DUK_DDD(DUK_DDDPRINT("skip finalize_list processing: pf_prevent_count != 0")); - return; - } - - /* Heap alloc prevents mark-and-sweep before heap_thread is ready. */ - DUK_ASSERT(heap != NULL); - DUK_ASSERT(heap->heap_thread != NULL); - DUK_ASSERT(heap->heap_thread->valstack != NULL); -#if defined(DUK_USE_REFERENCE_COUNTING) - DUK_ASSERT(heap->refzero_list == NULL); -#endif - - DUK_ASSERT(heap->pf_prevent_count == 0); - heap->pf_prevent_count = 1; - - /* Mark-and-sweep no longer needs to be prevented when running - * finalizers: mark-and-sweep skips any rescue decisions if there - * are any objects in finalize_list when mark-and-sweep is entered. - * This protects finalized objects from incorrect rescue decisions - * caused by finalize_list being a reachability root and only - * partially processed. Freeing decisions are not postponed. - */ - - /* When finalizer torture is enabled, make a fake finalizer call with - * maximum side effects regardless of whether finalize_list is empty. - */ -#if defined(DUK_USE_FINALIZER_TORTURE) - duk__run_global_torture_finalizer(heap->heap_thread); -#endif - - /* Process finalize_list until it becomes empty. There's currently no - * protection against a finalizer always creating more garbage. - */ - while ((curr = heap->finalize_list) != NULL) { -#if defined(DUK_USE_REFERENCE_COUNTING) - duk_bool_t queue_back; -#endif - - DUK_DD(DUK_DDPRINT("processing finalize_list entry: %p -> %!iO", (void *) curr, curr)); - - DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT); /* Only objects have finalizers. */ - DUK_ASSERT(!DUK_HEAPHDR_HAS_REACHABLE(curr)); - DUK_ASSERT(!DUK_HEAPHDR_HAS_TEMPROOT(curr)); - DUK_ASSERT(DUK_HEAPHDR_HAS_FINALIZABLE( - curr)); /* All objects on finalize_list will have this flag (except object being finalized right now). */ - DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED(curr)); /* Queueing code ensures. */ - DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY(curr)); /* ROM objects never get freed (or finalized). */ - -#if defined(DUK_USE_ASSERTIONS) - DUK_ASSERT(heap->currently_finalizing == NULL); - heap->currently_finalizing = curr; -#endif - - /* Clear FINALIZABLE for object being finalized, so that - * duk_push_heapptr() can properly ignore the object. - */ - DUK_HEAPHDR_CLEAR_FINALIZABLE(curr); - - if (DUK_LIKELY(!heap->pf_skip_finalizers)) { - /* Run the finalizer, duk_heap_run_finalizer() sets - * and checks for FINALIZED to prevent the finalizer - * from executing multiple times per finalization cycle. - * (This safeguard shouldn't be actually needed anymore). - */ - -#if defined(DUK_USE_REFERENCE_COUNTING) - duk_bool_t had_zero_refcount; -#endif - - /* The object's refcount is >0 throughout so it won't be - * refzero processed prematurely. - */ -#if defined(DUK_USE_REFERENCE_COUNTING) - DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(curr) >= 1); - had_zero_refcount = (DUK_HEAPHDR_GET_REFCOUNT(curr) == 1); /* Preincremented on finalize_list insert. */ -#endif - - DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED(curr)); - duk_heap_run_finalizer(heap, (duk_hobject *) curr); /* must never longjmp */ - DUK_ASSERT(DUK_HEAPHDR_HAS_FINALIZED(curr)); - /* XXX: assert that object is still in finalize_list - * when duk_push_heapptr() allows automatic rescue. - */ - -#if defined(DUK_USE_REFERENCE_COUNTING) - DUK_DD(DUK_DDPRINT("refcount after finalizer (includes bump): %ld", (long) DUK_HEAPHDR_GET_REFCOUNT(curr))); - if (DUK_HEAPHDR_GET_REFCOUNT(curr) == 1) { /* Only artificial bump in refcount? */ -#if defined(DUK_USE_DEBUG) - if (had_zero_refcount) { - DUK_DD(DUK_DDPRINT( - "finalized object's refcount is zero -> free immediately (refcount queued)")); - } else { - DUK_DD(DUK_DDPRINT( - "finalized object's refcount is zero -> free immediately (mark-and-sweep queued)")); - } -#endif - queue_back = 0; - } else -#endif - { -#if defined(DUK_USE_REFERENCE_COUNTING) - queue_back = 1; - if (had_zero_refcount) { - /* When finalization is triggered - * by refzero and we queue the object - * back, clear FINALIZED right away - * so that the object can be refinalized - * immediately if necessary. - */ - DUK_HEAPHDR_CLEAR_FINALIZED(curr); - } -#endif - } - } else { - /* Used during heap destruction: don't actually run finalizers - * because we're heading into forced finalization. Instead, - * queue finalizable objects back to the heap_allocated list. - */ - DUK_D(DUK_DPRINT("skip finalizers flag set, queue object to heap_allocated without finalizing")); - DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED(curr)); -#if defined(DUK_USE_REFERENCE_COUNTING) - queue_back = 1; -#endif - } - - /* Dequeue object from finalize_list. Note that 'curr' may no - * longer be finalize_list head because new objects may have - * been queued to the list. As a result we can't optimize for - * the single-linked heap case and must scan the list for - * removal, typically the scan is very short however. - */ - DUK_HEAP_REMOVE_FROM_FINALIZE_LIST(heap, curr); - - /* Queue back to heap_allocated or free immediately. */ -#if defined(DUK_USE_REFERENCE_COUNTING) - if (queue_back) { - /* FINALIZED is only cleared if object originally - * queued for finalization by refcounting. For - * mark-and-sweep FINALIZED is left set, so that - * next mark-and-sweep round can make a rescue/free - * decision. - */ - DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(curr) >= 1); - DUK_HEAPHDR_PREDEC_REFCOUNT(curr); /* Remove artificial refcount bump. */ - DUK_HEAPHDR_CLEAR_FINALIZABLE(curr); - DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap, curr); - } else { - /* No need to remove the refcount bump here. */ - DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT); /* currently, always the case */ - DUK_DD(DUK_DDPRINT("refcount finalize after finalizer call: %!O", curr)); - duk_hobject_refcount_finalize_norz(heap, (duk_hobject *) curr); - duk_free_hobject(heap, (duk_hobject *) curr); - DUK_DD(DUK_DDPRINT("freed hobject after finalization: %p", (void *) curr)); - } -#else /* DUK_USE_REFERENCE_COUNTING */ - DUK_HEAPHDR_CLEAR_FINALIZABLE(curr); - DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap, curr); -#endif /* DUK_USE_REFERENCE_COUNTING */ - -#if defined(DUK_USE_DEBUG) - count++; -#endif - -#if defined(DUK_USE_ASSERTIONS) - DUK_ASSERT(heap->currently_finalizing != NULL); - heap->currently_finalizing = NULL; -#endif - } - - /* finalize_list will always be processed completely. */ - DUK_ASSERT(heap->finalize_list == NULL); - -#if 0 - /* While NORZ macros are used above, this is unnecessary because the - * only pending side effects are now finalizers, and finalize_list is - * empty. - */ - DUK_REFZERO_CHECK_SLOW(heap->heap_thread); -#endif - - /* Prevent count may be bumped while finalizers run, but should always - * be reliably unbumped by the time we get here. - */ - DUK_ASSERT(heap->pf_prevent_count == 1); - heap->pf_prevent_count = 0; - -#if defined(DUK_USE_DEBUG) - DUK_DD(DUK_DDPRINT("duk_heap_process_finalize_list: %ld finalizers called", (long) count)); -#endif -} - -/* - * Run an duk_hobject finalizer. Must never throw an uncaught error - * (but may throw caught errors). - * - * There is no return value. Any return value or error thrown by - * the finalizer is ignored (although errors are debug logged). - * - * Notes: - * - * - The finalizer thread 'top' assertions are there because it is - * critical that strict stack policy is observed (i.e. no cruft - * left on the finalizer stack). - */ - -DUK_LOCAL duk_ret_t duk__finalize_helper(duk_hthread *thr, void *udata) { - DUK_ASSERT(thr != NULL); - DUK_UNREF(udata); - - DUK_DDD(DUK_DDDPRINT("protected finalization helper running")); - - /* [... obj] */ - - /* _Finalizer property is read without checking if the value is - * callable or even exists. This is intentional, and handled - * by throwing an error which is caught by the safe call wrapper. - * - * XXX: Finalizer lookup should traverse the prototype chain (to allow - * inherited finalizers) but should not invoke accessors or proxy object - * behavior. At the moment this lookup will invoke proxy behavior, so - * caller must ensure that this function is not called if the target is - * a Proxy. - */ - duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_INT_FINALIZER); /* -> [... obj finalizer] */ - duk_dup_m2(thr); - duk_push_boolean(thr, DUK_HEAP_HAS_FINALIZER_NORESCUE(thr->heap)); - DUK_DDD(DUK_DDDPRINT("calling finalizer")); - duk_call(thr, 2); /* [ ... obj finalizer obj heapDestruct ] -> [ ... obj retval ] */ - DUK_DDD(DUK_DDDPRINT("finalizer returned successfully")); - return 0; - - /* Note: we rely on duk_safe_call() to fix up the stack for the caller, - * so we don't need to pop stuff here. There is no return value; - * caller determines rescued status based on object refcount. - */ -} - -DUK_INTERNAL void duk_heap_run_finalizer(duk_heap *heap, duk_hobject *obj) { - duk_hthread *thr; - duk_ret_t rc; -#if defined(DUK_USE_ASSERTIONS) - duk_idx_t entry_top; -#endif - - DUK_DD(DUK_DDPRINT("running duk_hobject finalizer for object: %p", (void *) obj)); - - DUK_ASSERT(heap != NULL); - DUK_ASSERT(heap->heap_thread != NULL); - thr = heap->heap_thread; - DUK_ASSERT(obj != NULL); - DUK_ASSERT_VALSTACK_SPACE(heap->heap_thread, 1); - -#if defined(DUK_USE_ASSERTIONS) - entry_top = duk_get_top(thr); -#endif - /* - * Get and call the finalizer. All of this must be wrapped - * in a protected call, because even getting the finalizer - * may trigger an error (getter may throw one, for instance). - */ - - /* ROM objects could inherit a finalizer, but they are never deemed - * unreachable by mark-and-sweep, and their refcount never falls to 0. - */ - DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)); - - /* Duktape 2.1: finalize_list never contains objects with FINALIZED - * set, so no need to check here. - */ - DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr *) obj)); -#if 0 - if (DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr *) obj)) { - DUK_D(DUK_DPRINT("object already finalized, avoid running finalizer twice: %!O", obj)); - return; - } -#endif - DUK_HEAPHDR_SET_FINALIZED((duk_heaphdr *) obj); /* ensure never re-entered until rescue cycle complete */ - -#if defined(DUK_USE_ES6_PROXY) - if (DUK_HOBJECT_IS_PROXY(obj)) { - /* This may happen if duk_set_finalizer() or Duktape.fin() is - * called for a Proxy object. In such cases the fast finalizer - * flag will be set on the Proxy, not the target, and neither - * will be finalized. - */ - DUK_D(DUK_DPRINT("object is a Proxy, skip finalizer call")); - return; - } -#endif /* DUK_USE_ES6_PROXY */ - - duk_push_hobject(thr, obj); /* this also increases refcount by one */ - rc = duk_safe_call(thr, duk__finalize_helper, NULL /*udata*/, 0 /*nargs*/, 1 /*nrets*/); /* -> [... obj retval/error] */ - DUK_ASSERT_TOP(thr, entry_top + 2); /* duk_safe_call discipline */ - - if (rc != DUK_EXEC_SUCCESS) { - /* Note: we ask for one return value from duk_safe_call to get this - * error debugging here. - */ - DUK_D(DUK_DPRINT("wrapped finalizer call failed for object %p (ignored); error: %!T", - (void *) obj, - (duk_tval *) duk_get_tval(thr, -1))); - } - duk_pop_2(thr); /* -> [...] */ - - DUK_ASSERT_TOP(thr, entry_top); -} - -#else /* DUK_USE_FINALIZER_SUPPORT */ - -/* nothing */ - -#endif /* DUK_USE_FINALIZER_SUPPORT */ -/* - * String hash computation (interning). - * - * String hashing is performance critical because a string hash is computed - * for all new strings which are candidates to be added to the string table. - * However, strings actually added to the string table go through a codepoint - * length calculation which dominates performance because it goes through - * every byte of the input string (but only for strings added). - * - * The string hash algorithm should be fast, but on the other hand provide - * good enough hashes to ensure both string table and object property table - * hash tables work reasonably well (i.e., there aren't too many collisions - * with real world inputs). Unless the hash is cryptographic, it's always - * possible to craft inputs with maximal hash collisions. - * - * NOTE: The hash algorithms must match tools/dukutil.py:duk_heap_hashstring() - * for ROM string support! - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_STRHASH_DENSE) -/* Constants for duk_hashstring(). */ -#define DUK__STRHASH_SHORTSTRING 4096L -#define DUK__STRHASH_MEDIUMSTRING (256L * 1024L) -#define DUK__STRHASH_BLOCKSIZE 256L - -DUK_INTERNAL duk_uint32_t duk_heap_hashstring(duk_heap *heap, const duk_uint8_t *str, duk_size_t len) { - duk_uint32_t hash; - - /* Use Murmurhash2 directly for short strings, and use "block skipping" - * for long strings: hash an initial part and then sample the rest of - * the string with reasonably sized chunks. An initial offset for the - * sampling is computed based on a hash of the initial part of the string; - * this is done to (usually) avoid the case where all long strings have - * certain offset ranges which are never sampled. - * - * Skip should depend on length and bound the total time to roughly - * logarithmic. With current values: - * - * 1M string => 256 * 241 = 61696 bytes (0.06M) of hashing - * 1G string => 256 * 16321 = 4178176 bytes (3.98M) of hashing - * - * XXX: It would be better to compute the skip offset more "smoothly" - * instead of having a few boundary values. - */ - - /* note: mixing len into seed improves hashing when skipping */ - duk_uint32_t str_seed = heap->hash_seed ^ ((duk_uint32_t) len); - - if (len <= DUK__STRHASH_SHORTSTRING) { - hash = duk_util_hashbytes(str, len, str_seed); - } else { - duk_size_t off; - duk_size_t skip; - - if (len <= DUK__STRHASH_MEDIUMSTRING) { - skip = (duk_size_t) (16 * DUK__STRHASH_BLOCKSIZE + DUK__STRHASH_BLOCKSIZE); - } else { - skip = (duk_size_t) (256 * DUK__STRHASH_BLOCKSIZE + DUK__STRHASH_BLOCKSIZE); - } - - hash = duk_util_hashbytes(str, (duk_size_t) DUK__STRHASH_SHORTSTRING, str_seed); - off = DUK__STRHASH_SHORTSTRING + (skip * (hash % 256)) / 256; - - /* XXX: inefficient loop */ - while (off < len) { - duk_size_t left = len - off; - duk_size_t now = (duk_size_t) (left > DUK__STRHASH_BLOCKSIZE ? DUK__STRHASH_BLOCKSIZE : left); - hash ^= duk_util_hashbytes(str + off, now, str_seed); - off += skip; - } - } - -#if defined(DUK_USE_STRHASH16) - /* Truncate to 16 bits here, so that a computed hash can be compared - * against a hash stored in a 16-bit field. - */ - hash &= 0x0000ffffUL; -#endif - return hash; -} -#else /* DUK_USE_STRHASH_DENSE */ -DUK_INTERNAL duk_uint32_t duk_heap_hashstring(duk_heap *heap, const duk_uint8_t *str, duk_size_t len) { - duk_uint32_t hash; - duk_size_t step; - duk_size_t off; - - /* Slightly modified "Bernstein hash" from: - * - * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx - * - * Modifications: string skipping and reverse direction similar to - * Lua 5.1.5, and different hash initializer. - * - * The reverse direction ensures last byte it always included in the - * hash which is a good default as changing parts of the string are - * more often in the suffix than in the prefix. - */ - - hash = heap->hash_seed ^ ((duk_uint32_t) len); /* Bernstein hash init value is normally 5381 */ - step = (len >> DUK_USE_STRHASH_SKIP_SHIFT) + 1; - for (off = len; off >= step; off -= step) { - DUK_ASSERT(off >= 1); /* off >= step, and step >= 1 */ - hash = (hash * 33) + str[off - 1]; - } - -#if defined(DUK_USE_STRHASH16) - /* Truncate to 16 bits here, so that a computed hash can be compared - * against a hash stored in a 16-bit field. - */ - hash &= 0x0000ffffUL; -#endif - return hash; -} -#endif /* DUK_USE_STRHASH_DENSE */ - -/* automatic undefs */ -#undef DUK__STRHASH_BLOCKSIZE -#undef DUK__STRHASH_MEDIUMSTRING -#undef DUK__STRHASH_SHORTSTRING -/* - * Mark-and-sweep garbage collection. - */ - -/* #include duk_internal.h -> already included */ - -DUK_LOCAL_DECL void duk__mark_heaphdr(duk_heap *heap, duk_heaphdr *h); -DUK_LOCAL_DECL void duk__mark_heaphdr_nonnull(duk_heap *heap, duk_heaphdr *h); -DUK_LOCAL_DECL void duk__mark_tval(duk_heap *heap, duk_tval *tv); -DUK_LOCAL_DECL void duk__mark_tvals(duk_heap *heap, duk_tval *tv, duk_idx_t count); - -/* - * Marking functions for heap types: mark children recursively. - */ - -DUK_LOCAL void duk__mark_hstring(duk_heap *heap, duk_hstring *h) { - DUK_UNREF(heap); - DUK_UNREF(h); - - DUK_DDD(DUK_DDDPRINT("duk__mark_hstring: %p", (void *) h)); - DUK_ASSERT(h); - DUK_HSTRING_ASSERT_VALID(h); - - /* nothing to process */ -} - -DUK_LOCAL void duk__mark_hobject(duk_heap *heap, duk_hobject *h) { - duk_uint_fast32_t i; - - DUK_DDD(DUK_DDDPRINT("duk__mark_hobject: %p", (void *) h)); - - DUK_ASSERT(h); - DUK_HOBJECT_ASSERT_VALID(h); - - /* XXX: use advancing pointers instead of index macros -> faster and smaller? */ - - for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(h); i++) { - duk_hstring *key = DUK_HOBJECT_E_GET_KEY(heap, h, i); - if (key == NULL) { - continue; - } - duk__mark_heaphdr_nonnull(heap, (duk_heaphdr *) key); - if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(heap, h, i)) { - duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_PTR(heap, h, i)->a.get); - duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_PTR(heap, h, i)->a.set); - } else { - duk__mark_tval(heap, &DUK_HOBJECT_E_GET_VALUE_PTR(heap, h, i)->v); - } - } - - for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(h); i++) { - duk__mark_tval(heap, DUK_HOBJECT_A_GET_VALUE_PTR(heap, h, i)); - } - - /* Hash part is a 'weak reference' and does not contribute. */ - - duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HOBJECT_GET_PROTOTYPE(heap, h)); - - /* Fast path for objects which don't have a subclass struct, or have a - * subclass struct but nothing that needs marking in the subclass struct. - */ - if (DUK_HOBJECT_HAS_FASTREFS(h)) { - DUK_ASSERT(DUK_HOBJECT_ALLOWS_FASTREFS(h)); - return; - } - DUK_ASSERT(DUK_HOBJECT_PROHIBITS_FASTREFS(h)); - - /* XXX: reorg, more common first */ - if (DUK_HOBJECT_IS_COMPFUNC(h)) { - duk_hcompfunc *f = (duk_hcompfunc *) h; - duk_tval *tv, *tv_end; - duk_hobject **fn, **fn_end; - - DUK_HCOMPFUNC_ASSERT_VALID(f); - - /* 'data' is reachable through every compiled function which - * contains a reference. - */ - - duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HCOMPFUNC_GET_DATA(heap, f)); - duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HCOMPFUNC_GET_LEXENV(heap, f)); - duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HCOMPFUNC_GET_VARENV(heap, f)); - - if (DUK_HCOMPFUNC_GET_DATA(heap, f) != NULL) { - tv = DUK_HCOMPFUNC_GET_CONSTS_BASE(heap, f); - tv_end = DUK_HCOMPFUNC_GET_CONSTS_END(heap, f); - while (tv < tv_end) { - duk__mark_tval(heap, tv); - tv++; - } - - fn = DUK_HCOMPFUNC_GET_FUNCS_BASE(heap, f); - fn_end = DUK_HCOMPFUNC_GET_FUNCS_END(heap, f); - while (fn < fn_end) { - duk__mark_heaphdr_nonnull(heap, (duk_heaphdr *) *fn); - fn++; - } - } else { - /* May happen in some out-of-memory corner cases. */ - DUK_D(DUK_DPRINT("duk_hcompfunc 'data' is NULL, skipping marking")); - } - } else if (DUK_HOBJECT_IS_DECENV(h)) { - duk_hdecenv *e = (duk_hdecenv *) h; - DUK_HDECENV_ASSERT_VALID(e); - duk__mark_heaphdr(heap, (duk_heaphdr *) e->thread); - duk__mark_heaphdr(heap, (duk_heaphdr *) e->varmap); - } else if (DUK_HOBJECT_IS_OBJENV(h)) { - duk_hobjenv *e = (duk_hobjenv *) h; - DUK_HOBJENV_ASSERT_VALID(e); - duk__mark_heaphdr_nonnull(heap, (duk_heaphdr *) e->target); -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) - } else if (DUK_HOBJECT_IS_BUFOBJ(h)) { - duk_hbufobj *b = (duk_hbufobj *) h; - DUK_HBUFOBJ_ASSERT_VALID(b); - duk__mark_heaphdr(heap, (duk_heaphdr *) b->buf); - duk__mark_heaphdr(heap, (duk_heaphdr *) b->buf_prop); -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - } else if (DUK_HOBJECT_IS_BOUNDFUNC(h)) { - duk_hboundfunc *f = (duk_hboundfunc *) (void *) h; - DUK_HBOUNDFUNC_ASSERT_VALID(f); - duk__mark_tval(heap, &f->target); - duk__mark_tval(heap, &f->this_binding); - duk__mark_tvals(heap, f->args, f->nargs); -#if defined(DUK_USE_ES6_PROXY) - } else if (DUK_HOBJECT_IS_PROXY(h)) { - duk_hproxy *p = (duk_hproxy *) h; - DUK_HPROXY_ASSERT_VALID(p); - duk__mark_heaphdr_nonnull(heap, (duk_heaphdr *) p->target); - duk__mark_heaphdr_nonnull(heap, (duk_heaphdr *) p->handler); -#endif /* DUK_USE_ES6_PROXY */ - } else if (DUK_HOBJECT_IS_THREAD(h)) { - duk_hthread *t = (duk_hthread *) h; - duk_activation *act; - duk_tval *tv; - - DUK_HTHREAD_ASSERT_VALID(t); - - tv = t->valstack; - while (tv < t->valstack_top) { - duk__mark_tval(heap, tv); - tv++; - } - - for (act = t->callstack_curr; act != NULL; act = act->parent) { - duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_ACT_GET_FUNC(act)); - duk__mark_heaphdr(heap, (duk_heaphdr *) act->var_env); - duk__mark_heaphdr(heap, (duk_heaphdr *) act->lex_env); -#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY) - duk__mark_heaphdr(heap, (duk_heaphdr *) act->prev_caller); -#endif -#if 0 /* nothing now */ - for (cat = act->cat; cat != NULL; cat = cat->parent) { - } -#endif - } - - duk__mark_heaphdr(heap, (duk_heaphdr *) t->resumer); - - for (i = 0; i < DUK_NUM_BUILTINS; i++) { - duk__mark_heaphdr(heap, (duk_heaphdr *) t->builtins[i]); - } - } else { - /* We may come here if the object should have a FASTREFS flag - * but it's missing for some reason. Assert for never getting - * here; however, other than performance, this is harmless. - */ - DUK_D(DUK_DPRINT("missing FASTREFS flag for: %!iO", h)); - DUK_ASSERT(0); - } -} - -/* Mark any duk_heaphdr type. Recursion tracking happens only here. */ -DUK_LOCAL void duk__mark_heaphdr(duk_heap *heap, duk_heaphdr *h) { - DUK_DDD( - DUK_DDDPRINT("duk__mark_heaphdr %p, type %ld", (void *) h, (h != NULL ? (long) DUK_HEAPHDR_GET_TYPE(h) : (long) -1))); - - /* XXX: add non-null variant? */ - if (h == NULL) { - return; - } - - DUK_HEAPHDR_ASSERT_VALID(h); - DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY(h) || DUK_HEAPHDR_HAS_REACHABLE(h)); - -#if defined(DUK_USE_ASSERTIONS) && defined(DUK_USE_REFERENCE_COUNTING) - if (!DUK_HEAPHDR_HAS_READONLY(h)) { - h->h_assert_refcount++; /* Comparison refcount: bump even if already reachable. */ - } -#endif - if (DUK_HEAPHDR_HAS_REACHABLE(h)) { - DUK_DDD(DUK_DDDPRINT("already marked reachable, skip")); - return; - } -#if defined(DUK_USE_ROM_OBJECTS) - /* READONLY objects always have REACHABLE set, so the check above - * will prevent READONLY objects from being marked here. - */ - DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY(h)); -#endif - - DUK_HEAPHDR_SET_REACHABLE(h); - - if (heap->ms_recursion_depth >= DUK_USE_MARK_AND_SWEEP_RECLIMIT) { - DUK_D(DUK_DPRINT("mark-and-sweep recursion limit reached, marking as temproot: %p", (void *) h)); - DUK_HEAP_SET_MARKANDSWEEP_RECLIMIT_REACHED(heap); - DUK_HEAPHDR_SET_TEMPROOT(h); - return; - } - - heap->ms_recursion_depth++; - DUK_ASSERT(heap->ms_recursion_depth != 0); /* Wrap. */ - - switch (DUK_HEAPHDR_GET_TYPE(h)) { - case DUK_HTYPE_STRING: - duk__mark_hstring(heap, (duk_hstring *) h); - break; - case DUK_HTYPE_OBJECT: - duk__mark_hobject(heap, (duk_hobject *) h); - break; - case DUK_HTYPE_BUFFER: - /* nothing to mark */ - break; - default: - DUK_D(DUK_DPRINT("attempt to mark heaphdr %p with invalid htype %ld", (void *) h, (long) DUK_HEAPHDR_GET_TYPE(h))); - DUK_UNREACHABLE(); - } - - DUK_ASSERT(heap->ms_recursion_depth > 0); - heap->ms_recursion_depth--; -} - -DUK_LOCAL void duk__mark_tval(duk_heap *heap, duk_tval *tv) { - DUK_DDD(DUK_DDDPRINT("duk__mark_tval %p", (void *) tv)); - if (tv == NULL) { - return; - } - DUK_TVAL_ASSERT_VALID(tv); - if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) { - duk_heaphdr *h; - h = DUK_TVAL_GET_HEAPHDR(tv); - DUK_ASSERT(h != NULL); - duk__mark_heaphdr_nonnull(heap, h); - } -} - -DUK_LOCAL void duk__mark_tvals(duk_heap *heap, duk_tval *tv, duk_idx_t count) { - DUK_ASSERT(count == 0 || tv != NULL); - - while (count-- > 0) { - DUK_TVAL_ASSERT_VALID(tv); - if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) { - duk_heaphdr *h; - h = DUK_TVAL_GET_HEAPHDR(tv); - DUK_ASSERT(h != NULL); - duk__mark_heaphdr_nonnull(heap, h); - } - tv++; - } -} - -/* Mark any duk_heaphdr type, caller guarantees a non-NULL pointer. */ -DUK_LOCAL void duk__mark_heaphdr_nonnull(duk_heap *heap, duk_heaphdr *h) { - /* For now, just call the generic handler. Change when call sites - * are changed too. - */ - duk__mark_heaphdr(heap, h); -} - -/* - * Mark the heap. - */ - -DUK_LOCAL void duk__mark_roots_heap(duk_heap *heap) { - duk_small_uint_t i; - - DUK_DD(DUK_DDPRINT("duk__mark_roots_heap: %p", (void *) heap)); - - duk__mark_heaphdr(heap, (duk_heaphdr *) heap->heap_thread); - duk__mark_heaphdr(heap, (duk_heaphdr *) heap->heap_object); - - for (i = 0; i < DUK_HEAP_NUM_STRINGS; i++) { - duk_hstring *h = DUK_HEAP_GET_STRING(heap, i); - duk__mark_heaphdr(heap, (duk_heaphdr *) h); - } - - duk__mark_tval(heap, &heap->lj.value1); - duk__mark_tval(heap, &heap->lj.value2); - -#if defined(DUK_USE_DEBUGGER_SUPPORT) - for (i = 0; i < heap->dbg_breakpoint_count; i++) { - duk__mark_heaphdr(heap, (duk_heaphdr *) heap->dbg_breakpoints[i].filename); - } -#endif -} - -/* - * Mark unreachable, finalizable objects. - * - * Such objects will be moved aside and their finalizers run later. They - * have to be treated as reachability roots for their properties etc to - * remain allocated. This marking is only done for unreachable values which - * would be swept later. - * - * Objects are first marked FINALIZABLE and only then marked as reachability - * roots; otherwise circular references might be handled inconsistently. - */ - -#if defined(DUK_USE_FINALIZER_SUPPORT) -DUK_LOCAL void duk__mark_finalizable(duk_heap *heap) { - duk_heaphdr *hdr; - duk_size_t count_finalizable = 0; - - DUK_DD(DUK_DDPRINT("duk__mark_finalizable: %p", (void *) heap)); - - DUK_ASSERT(heap->heap_thread != NULL); - - hdr = heap->heap_allocated; - while (hdr != NULL) { - /* A finalizer is looked up from the object and up its - * prototype chain (which allows inherited finalizers). - * The finalizer is checked for using a duk_hobject flag - * which is kept in sync with the presence and callability - * of a _Finalizer hidden symbol. - */ - - if (!DUK_HEAPHDR_HAS_REACHABLE(hdr) && DUK_HEAPHDR_IS_OBJECT(hdr) && !DUK_HEAPHDR_HAS_FINALIZED(hdr) && - DUK_HOBJECT_HAS_FINALIZER_FAST(heap, (duk_hobject *) hdr)) { - /* heaphdr: - * - is not reachable - * - is an object - * - is not a finalized object waiting for rescue/keep decision - * - has a finalizer - */ - - DUK_DD(DUK_DDPRINT("unreachable heap object will be " - "finalized -> mark as finalizable " - "and treat as a reachability root: %p", - (void *) hdr)); - DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY(hdr)); - DUK_HEAPHDR_SET_FINALIZABLE(hdr); - count_finalizable++; - } - - hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr); - } - - if (count_finalizable == 0) { - return; - } - - DUK_DD(DUK_DDPRINT("marked %ld heap objects as finalizable, now mark them reachable", (long) count_finalizable)); - - hdr = heap->heap_allocated; - while (hdr != NULL) { - if (DUK_HEAPHDR_HAS_FINALIZABLE(hdr)) { - duk__mark_heaphdr_nonnull(heap, hdr); - } - - hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr); - } - - /* Caller will finish the marking process if we hit a recursion limit. */ -} -#endif /* DUK_USE_FINALIZER_SUPPORT */ - -/* - * Mark objects on finalize_list. - */ - -#if defined(DUK_USE_FINALIZER_SUPPORT) -DUK_LOCAL void duk__mark_finalize_list(duk_heap *heap) { - duk_heaphdr *hdr; -#if defined(DUK_USE_DEBUG) - duk_size_t count_finalize_list = 0; -#endif - - DUK_DD(DUK_DDPRINT("duk__mark_finalize_list: %p", (void *) heap)); - - hdr = heap->finalize_list; - while (hdr != NULL) { - duk__mark_heaphdr_nonnull(heap, hdr); - hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr); -#if defined(DUK_USE_DEBUG) - count_finalize_list++; -#endif - } - -#if defined(DUK_USE_DEBUG) - if (count_finalize_list > 0) { - DUK_D(DUK_DPRINT("marked %ld objects on the finalize_list as reachable (previous finalizer run skipped)", - (long) count_finalize_list)); - } -#endif -} -#endif /* DUK_USE_FINALIZER_SUPPORT */ - -/* - * Fallback marking handler if recursion limit is reached. - * - * Iterates 'temproots' until recursion limit is no longer hit. Temproots - * can be in heap_allocated or finalize_list; refzero_list is now always - * empty for mark-and-sweep. A temproot may occur in finalize_list now if - * there are objects on the finalize_list and user code creates a reference - * from an object in heap_allocated to the object in finalize_list (which is - * now allowed), and it happened to coincide with the recursion depth limit. - * - * This is a slow scan, but guarantees that we finish with a bounded C stack. - * - * Note that nodes may have been marked as temproots before this scan begun, - * OR they may have been marked during the scan (as we process nodes - * recursively also during the scan). This is intended behavior. - */ - -#if defined(DUK_USE_DEBUG) -DUK_LOCAL void duk__handle_temproot(duk_heap *heap, duk_heaphdr *hdr, duk_size_t *count) { -#else -DUK_LOCAL void duk__handle_temproot(duk_heap *heap, duk_heaphdr *hdr) { -#endif - DUK_ASSERT(hdr != NULL); - - if (!DUK_HEAPHDR_HAS_TEMPROOT(hdr)) { - DUK_DDD(DUK_DDDPRINT("not a temp root: %p", (void *) hdr)); - return; - } - - DUK_DDD(DUK_DDDPRINT("found a temp root: %p", (void *) hdr)); - DUK_HEAPHDR_CLEAR_TEMPROOT(hdr); - DUK_HEAPHDR_CLEAR_REACHABLE(hdr); /* Done so that duk__mark_heaphdr() works correctly. */ -#if defined(DUK_USE_ASSERTIONS) && defined(DUK_USE_REFERENCE_COUNTING) - hdr->h_assert_refcount--; /* Same node visited twice. */ -#endif - duk__mark_heaphdr_nonnull(heap, hdr); - -#if defined(DUK_USE_DEBUG) - (*count)++; -#endif -} - -DUK_LOCAL void duk__mark_temproots_by_heap_scan(duk_heap *heap) { - duk_heaphdr *hdr; -#if defined(DUK_USE_DEBUG) - duk_size_t count; -#endif - - DUK_DD(DUK_DDPRINT("duk__mark_temproots_by_heap_scan: %p", (void *) heap)); - - while (DUK_HEAP_HAS_MARKANDSWEEP_RECLIMIT_REACHED(heap)) { - DUK_DD(DUK_DDPRINT("recursion limit reached, doing heap scan to continue from temproots")); - -#if defined(DUK_USE_DEBUG) - count = 0; -#endif - DUK_HEAP_CLEAR_MARKANDSWEEP_RECLIMIT_REACHED(heap); - - hdr = heap->heap_allocated; - while (hdr) { -#if defined(DUK_USE_DEBUG) - duk__handle_temproot(heap, hdr, &count); -#else - duk__handle_temproot(heap, hdr); -#endif - hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr); - } - -#if defined(DUK_USE_FINALIZER_SUPPORT) - hdr = heap->finalize_list; - while (hdr) { -#if defined(DUK_USE_DEBUG) - duk__handle_temproot(heap, hdr, &count); -#else - duk__handle_temproot(heap, hdr); -#endif - hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr); - } -#endif - -#if defined(DUK_USE_DEBUG) - DUK_DD(DUK_DDPRINT("temproot mark heap scan processed %ld temp roots", (long) count)); -#endif - } -} - -/* - * Finalize refcounts for heap elements just about to be freed. - * This must be done for all objects before freeing to avoid any - * stale pointer dereferences. - * - * Note that this must deduce the set of objects to be freed - * identically to duk__sweep_heap(). - */ - -#if defined(DUK_USE_REFERENCE_COUNTING) -DUK_LOCAL void duk__finalize_refcounts(duk_heap *heap) { - duk_heaphdr *hdr; - - DUK_ASSERT(heap->heap_thread != NULL); - - DUK_DD(DUK_DDPRINT("duk__finalize_refcounts: heap=%p", (void *) heap)); - - hdr = heap->heap_allocated; - while (hdr) { - if (!DUK_HEAPHDR_HAS_REACHABLE(hdr)) { - /* - * Unreachable object about to be swept. Finalize target refcounts - * (objects which the unreachable object points to) without doing - * refzero processing. Recursive decrefs are also prevented when - * refzero processing is disabled. - * - * Value cannot be a finalizable object, as they have been made - * temporarily reachable for this round. - */ - - DUK_DDD(DUK_DDDPRINT("unreachable object, refcount finalize before sweeping: %p", (void *) hdr)); - - /* Finalize using heap->heap_thread; DECREF has a - * suppress check for mark-and-sweep which is based - * on heap->ms_running. - */ - duk_heaphdr_refcount_finalize_norz(heap, hdr); - } - - hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr); - } -} -#endif /* DUK_USE_REFERENCE_COUNTING */ - -/* - * Clear (reachable) flags of finalize_list. - * - * We could mostly do in the sweep phase when we move objects from the - * heap into the finalize_list. However, if a finalizer run is skipped - * during a mark-and-sweep, the objects on the finalize_list will be marked - * reachable during the next mark-and-sweep. Since they're already on the - * finalize_list, no-one will be clearing their REACHABLE flag so we do it - * here. (This now overlaps with the sweep handling in a harmless way.) - */ - -#if defined(DUK_USE_FINALIZER_SUPPORT) -DUK_LOCAL void duk__clear_finalize_list_flags(duk_heap *heap) { - duk_heaphdr *hdr; - - DUK_DD(DUK_DDPRINT("duk__clear_finalize_list_flags: %p", (void *) heap)); - - hdr = heap->finalize_list; - while (hdr) { - DUK_HEAPHDR_CLEAR_REACHABLE(hdr); -#if defined(DUK_USE_ASSERTIONS) - DUK_ASSERT(DUK_HEAPHDR_HAS_FINALIZABLE(hdr) || (heap->currently_finalizing == hdr)); -#endif - /* DUK_HEAPHDR_FLAG_FINALIZED may be set. */ - DUK_ASSERT(!DUK_HEAPHDR_HAS_TEMPROOT(hdr)); - hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr); - } -} -#endif /* DUK_USE_FINALIZER_SUPPORT */ - -/* - * Sweep stringtable. - */ - -DUK_LOCAL void duk__sweep_stringtable(duk_heap *heap, duk_size_t *out_count_keep) { - duk_hstring *h; - duk_hstring *prev; - duk_uint32_t i; -#if defined(DUK_USE_DEBUG) - duk_size_t count_free = 0; -#endif - duk_size_t count_keep = 0; - - DUK_DD(DUK_DDPRINT("duk__sweep_stringtable: %p", (void *) heap)); - -#if defined(DUK_USE_STRTAB_PTRCOMP) - if (heap->strtable16 == NULL) { -#else - if (heap->strtable == NULL) { -#endif - goto done; - } - - for (i = 0; i < heap->st_size; i++) { -#if defined(DUK_USE_STRTAB_PTRCOMP) - h = DUK_USE_HEAPPTR_DEC16(heap->heap_udata, heap->strtable16[i]); -#else - h = heap->strtable[i]; -#endif - prev = NULL; - while (h != NULL) { - duk_hstring *next; - next = h->hdr.h_next; - - if (DUK_HEAPHDR_HAS_REACHABLE((duk_heaphdr *) h)) { - DUK_HEAPHDR_CLEAR_REACHABLE((duk_heaphdr *) h); - count_keep++; - prev = h; - } else { -#if defined(DUK_USE_DEBUG) - count_free++; -#endif - - /* For pinned strings the refcount has been - * bumped. We could unbump it here before - * freeing, but that's actually not necessary - * except for assertions. - */ -#if 0 - if (DUK_HSTRING_HAS_PINNED_LITERAL(h)) { - DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h) > 0U); - DUK_HSTRING_DECREF_NORZ(heap->heap_thread, h); - DUK_HSTRING_CLEAR_PINNED_LITERAL(h); - } -#endif -#if defined(DUK_USE_REFERENCE_COUNTING) - /* Non-zero refcounts should not happen for unreachable strings, - * because we refcount finalize all unreachable objects which - * should have decreased unreachable string refcounts to zero - * (even for cycles). However, pinned strings have a +1 bump. - */ - DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h) == DUK_HSTRING_HAS_PINNED_LITERAL(h) ? 1U : - 0U); -#endif - - /* Deal with weak references first. */ - duk_heap_strcache_string_remove(heap, (duk_hstring *) h); - - /* Remove the string from the string table. */ - duk_heap_strtable_unlink_prev(heap, (duk_hstring *) h, (duk_hstring *) prev); - - /* Free inner references (these exist e.g. when external - * strings are enabled) and the struct itself. - */ - duk_free_hstring(heap, (duk_hstring *) h); - - /* Don't update 'prev'; it should be last string kept. */ - } - - h = next; - } - } - -done: -#if defined(DUK_USE_DEBUG) - DUK_D(DUK_DPRINT("mark-and-sweep sweep stringtable: %ld freed, %ld kept", (long) count_free, (long) count_keep)); -#endif - *out_count_keep = count_keep; -} - -/* - * Sweep heap. - */ - -DUK_LOCAL void duk__sweep_heap(duk_heap *heap, duk_small_uint_t flags, duk_size_t *out_count_keep) { - duk_heaphdr *prev; /* last element that was left in the heap */ - duk_heaphdr *curr; - duk_heaphdr *next; -#if defined(DUK_USE_DEBUG) - duk_size_t count_free = 0; - duk_size_t count_finalize = 0; - duk_size_t count_rescue = 0; -#endif - duk_size_t count_keep = 0; - - DUK_DD(DUK_DDPRINT("duk__sweep_heap: %p", (void *) heap)); - - prev = NULL; - curr = heap->heap_allocated; - heap->heap_allocated = NULL; - while (curr) { - /* Strings and ROM objects are never placed on the heap allocated list. */ - DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(curr) != DUK_HTYPE_STRING); - DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY(curr)); - - next = DUK_HEAPHDR_GET_NEXT(heap, curr); - - if (DUK_HEAPHDR_HAS_REACHABLE(curr)) { - /* - * Reachable object: - * - If FINALIZABLE -> actually unreachable (but marked - * artificially reachable), queue to finalize_list. - * - If !FINALIZABLE but FINALIZED -> rescued after - * finalizer execution. - * - Otherwise just a normal, reachable object. - * - * Objects which are kept are queued to heap_allocated - * tail (we're essentially filtering heap_allocated in - * practice). - */ - -#if defined(DUK_USE_FINALIZER_SUPPORT) - if (DUK_UNLIKELY(DUK_HEAPHDR_HAS_FINALIZABLE(curr))) { - DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED(curr)); - DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT); - DUK_DD(DUK_DDPRINT("sweep; reachable, finalizable --> move to finalize_list: %p", (void *) curr)); - -#if defined(DUK_USE_REFERENCE_COUNTING) - DUK_HEAPHDR_PREINC_REFCOUNT( - curr); /* Bump refcount so that refzero never occurs when pending a finalizer call. */ -#endif - DUK_HEAP_INSERT_INTO_FINALIZE_LIST(heap, curr); -#if defined(DUK_USE_DEBUG) - count_finalize++; -#endif - } else -#endif /* DUK_USE_FINALIZER_SUPPORT */ - { - if (DUK_UNLIKELY(DUK_HEAPHDR_HAS_FINALIZED(curr))) { - DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZABLE(curr)); - DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT); - - if (flags & DUK_MS_FLAG_POSTPONE_RESCUE) { - DUK_DD(DUK_DDPRINT("sweep; reachable, finalized, but postponing rescue decisions " - "--> keep object (with FINALIZED set): %!iO", - curr)); - count_keep++; - } else { - DUK_DD(DUK_DDPRINT("sweep; reachable, finalized --> rescued after finalization: %p", - (void *) curr)); -#if defined(DUK_USE_FINALIZER_SUPPORT) - DUK_HEAPHDR_CLEAR_FINALIZED(curr); -#endif -#if defined(DUK_USE_DEBUG) - count_rescue++; -#endif - } - } else { - DUK_DD(DUK_DDPRINT("sweep; reachable --> keep: %!iO", curr)); - count_keep++; - } - - if (prev != NULL) { - DUK_ASSERT(heap->heap_allocated != NULL); - DUK_HEAPHDR_SET_NEXT(heap, prev, curr); - } else { - DUK_ASSERT(heap->heap_allocated == NULL); - heap->heap_allocated = curr; - } -#if defined(DUK_USE_DOUBLE_LINKED_HEAP) - DUK_HEAPHDR_SET_PREV(heap, curr, prev); -#endif - DUK_HEAPHDR_ASSERT_LINKS(heap, prev); - DUK_HEAPHDR_ASSERT_LINKS(heap, curr); - prev = curr; - } - - /* - * Shrink check for value stacks here. We're inside - * ms_prevent_count protection which prevents recursive - * mark-and-sweep and refzero finalizers, so there are - * no side effects that would affect the heap lists. - */ - if (DUK_HEAPHDR_IS_OBJECT(curr) && DUK_HOBJECT_IS_THREAD((duk_hobject *) curr)) { - duk_hthread *thr_curr = (duk_hthread *) curr; - DUK_DD(DUK_DDPRINT("value stack shrink check for thread: %!O", curr)); - duk_valstack_shrink_check_nothrow(thr_curr, flags & DUK_MS_FLAG_EMERGENCY /*snug*/); - } - - DUK_HEAPHDR_CLEAR_REACHABLE(curr); - /* Keep FINALIZED if set, used if rescue decisions are postponed. */ - /* Keep FINALIZABLE for objects on finalize_list. */ - DUK_ASSERT(!DUK_HEAPHDR_HAS_REACHABLE(curr)); - } else { - /* - * Unreachable object: - * - If FINALIZED, object was finalized but not - * rescued. This doesn't affect freeing. - * - Otherwise normal unreachable object. - * - * There's no guard preventing a FINALIZED object - * from being freed while finalizers execute: the - * artificial finalize_list reachability roots can't - * cause an incorrect free decision (but can cause - * an incorrect rescue decision). - */ - -#if defined(DUK_USE_REFERENCE_COUNTING) - /* Non-zero refcounts should not happen because we refcount - * finalize all unreachable objects which should cancel out - * refcounts (even for cycles). - */ - DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(curr) == 0); -#endif - DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZABLE(curr)); - -#if defined(DUK_USE_DEBUG) - if (DUK_HEAPHDR_HAS_FINALIZED(curr)) { - DUK_DD(DUK_DDPRINT("sweep; unreachable, finalized --> finalized object not rescued: %p", - (void *) curr)); - } else { - DUK_DD(DUK_DDPRINT("sweep; not reachable --> free: %p", (void *) curr)); - } - -#endif - - /* Note: object cannot be a finalizable unreachable object, as - * they have been marked temporarily reachable for this round, - * and are handled above. - */ - -#if defined(DUK_USE_DEBUG) - count_free++; -#endif - - /* Weak refs should be handled here, but no weak refs for - * any non-string objects exist right now. - */ - - /* Free object and all auxiliary (non-heap) allocs. */ - duk_heap_free_heaphdr_raw(heap, curr); - } - - curr = next; - } - - if (prev != NULL) { - DUK_HEAPHDR_SET_NEXT(heap, prev, NULL); - } - DUK_HEAPHDR_ASSERT_LINKS(heap, prev); - -#if defined(DUK_USE_DEBUG) - DUK_D(DUK_DPRINT("mark-and-sweep sweep objects (non-string): %ld freed, %ld kept, %ld rescued, %ld queued for finalization", - (long) count_free, - (long) count_keep, - (long) count_rescue, - (long) count_finalize)); -#endif - *out_count_keep = count_keep; -} - -/* - * Litcache helpers. - */ - -#if defined(DUK_USE_LITCACHE_SIZE) -DUK_LOCAL void duk__wipe_litcache(duk_heap *heap) { - duk_uint_t i; - duk_litcache_entry *e; - - e = heap->litcache; - for (i = 0; i < DUK_USE_LITCACHE_SIZE; i++) { - e->addr = NULL; - /* e->h does not need to be invalidated: when e->addr is - * NULL, e->h is considered garbage. - */ - e++; - } -} -#endif /* DUK_USE_LITCACHE_SIZE */ - -/* - * Object compaction. - * - * Compaction is assumed to never throw an error. - */ - -DUK_LOCAL int duk__protected_compact_object(duk_hthread *thr, void *udata) { - duk_hobject *obj; - /* XXX: for threads, compact stacks? */ - - DUK_UNREF(udata); - obj = duk_known_hobject(thr, -1); - duk_hobject_compact_props(thr, obj); - return 0; -} - -#if defined(DUK_USE_DEBUG) -DUK_LOCAL void duk__compact_object_list(duk_heap *heap, - duk_hthread *thr, - duk_heaphdr *start, - duk_size_t *p_count_check, - duk_size_t *p_count_compact, - duk_size_t *p_count_bytes_saved) { -#else -DUK_LOCAL void duk__compact_object_list(duk_heap *heap, duk_hthread *thr, duk_heaphdr *start) { -#endif - duk_heaphdr *curr; -#if defined(DUK_USE_DEBUG) - duk_size_t old_size, new_size; -#endif - duk_hobject *obj; - - DUK_UNREF(heap); - - curr = start; - while (curr) { - DUK_DDD(DUK_DDDPRINT("mark-and-sweep compact: %p", (void *) curr)); - - if (DUK_HEAPHDR_GET_TYPE(curr) != DUK_HTYPE_OBJECT) { - goto next; - } - obj = (duk_hobject *) curr; - -#if defined(DUK_USE_DEBUG) - old_size = - DUK_HOBJECT_P_COMPUTE_SIZE(DUK_HOBJECT_GET_ESIZE(obj), DUK_HOBJECT_GET_ASIZE(obj), DUK_HOBJECT_GET_HSIZE(obj)); -#endif - - DUK_DD(DUK_DDPRINT("compact object: %p", (void *) obj)); - duk_push_hobject(thr, obj); - /* XXX: disable error handlers for duration of compaction? */ - duk_safe_call(thr, duk__protected_compact_object, NULL, 1, 0); - -#if defined(DUK_USE_DEBUG) - new_size = - DUK_HOBJECT_P_COMPUTE_SIZE(DUK_HOBJECT_GET_ESIZE(obj), DUK_HOBJECT_GET_ASIZE(obj), DUK_HOBJECT_GET_HSIZE(obj)); -#endif - -#if defined(DUK_USE_DEBUG) - (*p_count_compact)++; - (*p_count_bytes_saved) += (duk_size_t) (old_size - new_size); -#endif - - next: - curr = DUK_HEAPHDR_GET_NEXT(heap, curr); -#if defined(DUK_USE_DEBUG) - (*p_count_check)++; -#endif - } -} - -DUK_LOCAL void duk__compact_objects(duk_heap *heap) { - /* XXX: which lists should participate? to be finalized? */ -#if defined(DUK_USE_DEBUG) - duk_size_t count_check = 0; - duk_size_t count_compact = 0; - duk_size_t count_bytes_saved = 0; -#endif - - DUK_DD(DUK_DDPRINT("duk__compact_objects: %p", (void *) heap)); - - DUK_ASSERT(heap->heap_thread != NULL); - -#if defined(DUK_USE_DEBUG) - duk__compact_object_list(heap, heap->heap_thread, heap->heap_allocated, &count_check, &count_compact, &count_bytes_saved); -#if defined(DUK_USE_FINALIZER_SUPPORT) - duk__compact_object_list(heap, heap->heap_thread, heap->finalize_list, &count_check, &count_compact, &count_bytes_saved); -#endif -#else - duk__compact_object_list(heap, heap->heap_thread, heap->heap_allocated); -#if defined(DUK_USE_FINALIZER_SUPPORT) - duk__compact_object_list(heap, heap->heap_thread, heap->finalize_list); -#endif -#endif -#if defined(DUK_USE_REFERENCE_COUNTING) - DUK_ASSERT(heap->refzero_list == NULL); /* Always handled to completion inline in DECREF. */ -#endif - -#if defined(DUK_USE_DEBUG) - DUK_D(DUK_DPRINT("mark-and-sweep compact objects: %ld checked, %ld compaction attempts, %ld bytes saved by compaction", - (long) count_check, - (long) count_compact, - (long) count_bytes_saved)); -#endif -} - -/* - * Assertion helpers. - */ - -#if defined(DUK_USE_ASSERTIONS) -typedef void (*duk__gc_heaphdr_assert)(duk_heap *heap, duk_heaphdr *h); -typedef void (*duk__gc_hstring_assert)(duk_heap *heap, duk_hstring *h); - -DUK_LOCAL void duk__assert_walk_list(duk_heap *heap, duk_heaphdr *start, duk__gc_heaphdr_assert func) { - duk_heaphdr *curr; - for (curr = start; curr != NULL; curr = DUK_HEAPHDR_GET_NEXT(heap, curr)) { - func(heap, curr); - } -} - -DUK_LOCAL void duk__assert_walk_strtable(duk_heap *heap, duk__gc_hstring_assert func) { - duk_uint32_t i; - - for (i = 0; i < heap->st_size; i++) { - duk_hstring *h; - -#if defined(DUK_USE_STRTAB_PTRCOMP) - h = DUK_USE_HEAPPTR_DEC16(heap->heap_udata, heap->strtable16[i]); -#else - h = heap->strtable[i]; -#endif - while (h != NULL) { - func(heap, h); - h = h->hdr.h_next; - } - } -} - -DUK_LOCAL void duk__assert_heaphdr_flags_cb(duk_heap *heap, duk_heaphdr *h) { - DUK_UNREF(heap); - DUK_ASSERT(!DUK_HEAPHDR_HAS_REACHABLE(h)); - DUK_ASSERT(!DUK_HEAPHDR_HAS_TEMPROOT(h)); - DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZABLE(h)); - /* may have FINALIZED */ -} -DUK_LOCAL void duk__assert_heaphdr_flags(duk_heap *heap) { - duk__assert_walk_list(heap, heap->heap_allocated, duk__assert_heaphdr_flags_cb); -#if defined(DUK_USE_REFERENCE_COUNTING) - DUK_ASSERT(heap->refzero_list == NULL); /* Always handled to completion inline in DECREF. */ -#endif - /* XXX: Assertions for finalize_list? */ -} - -DUK_LOCAL void duk__assert_validity_cb1(duk_heap *heap, duk_heaphdr *h) { - DUK_UNREF(heap); - DUK_ASSERT(DUK_HEAPHDR_IS_OBJECT(h) || DUK_HEAPHDR_IS_BUFFER(h)); - duk_heaphdr_assert_valid_subclassed(h); -} -DUK_LOCAL void duk__assert_validity_cb2(duk_heap *heap, duk_hstring *h) { - DUK_UNREF(heap); - DUK_ASSERT(DUK_HEAPHDR_IS_STRING((duk_heaphdr *) h)); - duk_heaphdr_assert_valid_subclassed((duk_heaphdr *) h); -} -DUK_LOCAL void duk__assert_validity(duk_heap *heap) { - duk__assert_walk_list(heap, heap->heap_allocated, duk__assert_validity_cb1); -#if defined(DUK_USE_FINALIZER_SUPPORT) - duk__assert_walk_list(heap, heap->finalize_list, duk__assert_validity_cb1); -#endif -#if defined(DUK_USE_REFERENCE_COUNTING) - duk__assert_walk_list(heap, heap->refzero_list, duk__assert_validity_cb1); -#endif - duk__assert_walk_strtable(heap, duk__assert_validity_cb2); -} - -#if defined(DUK_USE_REFERENCE_COUNTING) -DUK_LOCAL void duk__assert_valid_refcounts_cb(duk_heap *heap, duk_heaphdr *h) { - /* Cannot really assert much w.r.t. refcounts now. */ - - DUK_UNREF(heap); - if (DUK_HEAPHDR_GET_REFCOUNT(h) == 0 && DUK_HEAPHDR_HAS_FINALIZED(h)) { - /* An object may be in heap_allocated list with a zero - * refcount if it has just been finalized and is waiting - * to be collected by the next cycle. - * (This doesn't currently happen however.) - */ - } else if (DUK_HEAPHDR_GET_REFCOUNT(h) == 0) { - /* An object may be in heap_allocated list with a zero - * refcount also if it is a temporary object created - * during debugger paused state. It will get collected - * by mark-and-sweep based on its reachability status - * (presumably not reachable because refcount is 0). - */ - } - DUK_ASSERT_DISABLE(DUK_HEAPHDR_GET_REFCOUNT(h) >= 0); /* Unsigned. */ -} -DUK_LOCAL void duk__assert_valid_refcounts(duk_heap *heap) { - duk__assert_walk_list(heap, heap->heap_allocated, duk__assert_valid_refcounts_cb); -} - -DUK_LOCAL void duk__clear_assert_refcounts_cb1(duk_heap *heap, duk_heaphdr *h) { - DUK_UNREF(heap); - h->h_assert_refcount = 0; -} -DUK_LOCAL void duk__clear_assert_refcounts_cb2(duk_heap *heap, duk_hstring *h) { - DUK_UNREF(heap); - ((duk_heaphdr *) h)->h_assert_refcount = 0; -} -DUK_LOCAL void duk__clear_assert_refcounts(duk_heap *heap) { - duk__assert_walk_list(heap, heap->heap_allocated, duk__clear_assert_refcounts_cb1); -#if defined(DUK_USE_FINALIZER_SUPPORT) - duk__assert_walk_list(heap, heap->finalize_list, duk__clear_assert_refcounts_cb1); -#endif -#if defined(DUK_USE_REFERENCE_COUNTING) - duk__assert_walk_list(heap, heap->refzero_list, duk__clear_assert_refcounts_cb1); -#endif - duk__assert_walk_strtable(heap, duk__clear_assert_refcounts_cb2); -} - -DUK_LOCAL void duk__check_refcount_heaphdr(duk_heaphdr *hdr) { - duk_bool_t count_ok; - duk_size_t expect_refc; - - /* The refcount check only makes sense for reachable objects on - * heap_allocated or string table, after the sweep phase. Prior to - * sweep phase refcounts will include references that are not visible - * via reachability roots. - * - * Because we're called after the sweep phase, all heap objects on - * heap_allocated are reachable. REACHABLE flags have already been - * cleared so we can't check them. - */ - - /* ROM objects have intentionally incorrect refcount (1), but we won't - * check them. - */ - DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY(hdr)); - - expect_refc = hdr->h_assert_refcount; - if (DUK_HEAPHDR_IS_STRING(hdr) && DUK_HSTRING_HAS_PINNED_LITERAL((duk_hstring *) hdr)) { - expect_refc++; - } - count_ok = ((duk_size_t) DUK_HEAPHDR_GET_REFCOUNT(hdr) == expect_refc); - if (!count_ok) { - DUK_D(DUK_DPRINT("refcount mismatch for: %p: header=%ld counted=%ld --> %!iO", - (void *) hdr, - (long) DUK_HEAPHDR_GET_REFCOUNT(hdr), - (long) hdr->h_assert_refcount, - hdr)); - DUK_ASSERT(0); - } -} - -DUK_LOCAL void duk__check_assert_refcounts_cb1(duk_heap *heap, duk_heaphdr *h) { - DUK_UNREF(heap); - duk__check_refcount_heaphdr(h); -} -DUK_LOCAL void duk__check_assert_refcounts_cb2(duk_heap *heap, duk_hstring *h) { - DUK_UNREF(heap); - duk__check_refcount_heaphdr((duk_heaphdr *) h); -} -DUK_LOCAL void duk__check_assert_refcounts(duk_heap *heap) { - duk__assert_walk_list(heap, heap->heap_allocated, duk__check_assert_refcounts_cb1); -#if defined(DUK_USE_FINALIZER_SUPPORT) - duk__assert_walk_list(heap, heap->finalize_list, duk__check_assert_refcounts_cb1); -#endif - /* XXX: Assert anything for refzero_list? */ - duk__assert_walk_strtable(heap, duk__check_assert_refcounts_cb2); -} -#endif /* DUK_USE_REFERENCE_COUNTING */ - -#if defined(DUK_USE_LITCACHE_SIZE) -DUK_LOCAL void duk__assert_litcache_nulls(duk_heap *heap) { - duk_uint_t i; - duk_litcache_entry *e; - - e = heap->litcache; - for (i = 0; i < DUK_USE_LITCACHE_SIZE; i++) { - /* Entry addresses were NULLed before mark-and-sweep, check - * that they're still NULL afterwards to ensure no pointers - * were recorded through any side effects. - */ - DUK_ASSERT(e->addr == NULL); - } -} -#endif /* DUK_USE_LITCACHE_SIZE */ -#endif /* DUK_USE_ASSERTIONS */ - -/* - * Stats dump. - */ - -#if defined(DUK_USE_DEBUG) -DUK_LOCAL void duk__dump_stats(duk_heap *heap) { - DUK_D(DUK_DPRINT("stats executor: opcodes=%ld, interrupt=%ld, throw=%ld", - (long) heap->stats_exec_opcodes, - (long) heap->stats_exec_interrupt, - (long) heap->stats_exec_throw)); - DUK_D(DUK_DPRINT("stats call: all=%ld, tailcall=%ld, ecmatoecma=%ld", - (long) heap->stats_call_all, - (long) heap->stats_call_tailcall, - (long) heap->stats_call_ecmatoecma)); - DUK_D(DUK_DPRINT("stats safecall: all=%ld, nothrow=%ld, throw=%ld", - (long) heap->stats_safecall_all, - (long) heap->stats_safecall_nothrow, - (long) heap->stats_safecall_throw)); - DUK_D(DUK_DPRINT("stats mark-and-sweep: try_count=%ld, skip_count=%ld, emergency_count=%ld", - (long) heap->stats_ms_try_count, - (long) heap->stats_ms_skip_count, - (long) heap->stats_ms_emergency_count)); - DUK_D(DUK_DPRINT("stats stringtable: intern_hit=%ld, intern_miss=%ld, " - "resize_check=%ld, resize_grow=%ld, resize_shrink=%ld, " - "litcache_hit=%ld, litcache_miss=%ld, litcache_pin=%ld", - (long) heap->stats_strtab_intern_hit, - (long) heap->stats_strtab_intern_miss, - (long) heap->stats_strtab_resize_check, - (long) heap->stats_strtab_resize_grow, - (long) heap->stats_strtab_resize_shrink, - (long) heap->stats_strtab_litcache_hit, - (long) heap->stats_strtab_litcache_miss, - (long) heap->stats_strtab_litcache_pin)); - DUK_D(DUK_DPRINT("stats object: realloc_props=%ld, abandon_array=%ld", - (long) heap->stats_object_realloc_props, - (long) heap->stats_object_abandon_array)); - DUK_D(DUK_DPRINT("stats getownpropdesc: count=%ld, hit=%ld, miss=%ld", - (long) heap->stats_getownpropdesc_count, - (long) heap->stats_getownpropdesc_hit, - (long) heap->stats_getownpropdesc_miss)); - DUK_D(DUK_DPRINT("stats getpropdesc: count=%ld, hit=%ld, miss=%ld", - (long) heap->stats_getpropdesc_count, - (long) heap->stats_getpropdesc_hit, - (long) heap->stats_getpropdesc_miss)); - DUK_D(DUK_DPRINT("stats getprop: all=%ld, arrayidx=%ld, bufobjidx=%ld, " - "bufferidx=%ld, bufferlen=%ld, stringidx=%ld, stringlen=%ld, " - "proxy=%ld, arguments=%ld", - (long) heap->stats_getprop_all, - (long) heap->stats_getprop_arrayidx, - (long) heap->stats_getprop_bufobjidx, - (long) heap->stats_getprop_bufferidx, - (long) heap->stats_getprop_bufferlen, - (long) heap->stats_getprop_stringidx, - (long) heap->stats_getprop_stringlen, - (long) heap->stats_getprop_proxy, - (long) heap->stats_getprop_arguments)); - DUK_D(DUK_DPRINT("stats putprop: all=%ld, arrayidx=%ld, bufobjidx=%ld, " - "bufferidx=%ld, proxy=%ld", - (long) heap->stats_putprop_all, - (long) heap->stats_putprop_arrayidx, - (long) heap->stats_putprop_bufobjidx, - (long) heap->stats_putprop_bufferidx, - (long) heap->stats_putprop_proxy)); - DUK_D(DUK_DPRINT("stats getvar: all=%ld", (long) heap->stats_getvar_all)); - DUK_D(DUK_DPRINT("stats putvar: all=%ld", (long) heap->stats_putvar_all)); - DUK_D(DUK_DPRINT("stats envrec: delayedcreate=%ld, create=%ld, newenv=%ld, oldenv=%ld, pushclosure=%ld", - (long) heap->stats_envrec_delayedcreate, - (long) heap->stats_envrec_create, - (long) heap->stats_envrec_newenv, - (long) heap->stats_envrec_oldenv, - (long) heap->stats_envrec_pushclosure)); -} -#endif /* DUK_USE_DEBUG */ - -/* - * Main mark-and-sweep function. - * - * 'flags' represents the features requested by the caller. The current - * heap->ms_base_flags is ORed automatically into the flags; the base flags - * mask typically prevents certain mark-and-sweep operation to avoid trouble. - */ - -DUK_INTERNAL void duk_heap_mark_and_sweep(duk_heap *heap, duk_small_uint_t flags) { - duk_size_t count_keep_obj; - duk_size_t count_keep_str; -#if defined(DUK_USE_VOLUNTARY_GC) - duk_size_t tmp; -#endif - duk_bool_t entry_creating_error; - - DUK_STATS_INC(heap, stats_ms_try_count); -#if defined(DUK_USE_DEBUG) - if (flags & DUK_MS_FLAG_EMERGENCY) { - DUK_STATS_INC(heap, stats_ms_emergency_count); - } -#endif - - /* If debugger is paused, garbage collection is disabled by default. - * This is achieved by bumping ms_prevent_count when becoming paused. - */ - DUK_ASSERT(!DUK_HEAP_HAS_DEBUGGER_PAUSED(heap) || heap->ms_prevent_count > 0); - - /* Prevention/recursion check as soon as possible because we may - * be called a number of times when voluntary mark-and-sweep is - * pending. - */ - if (heap->ms_prevent_count != 0) { - DUK_DD(DUK_DDPRINT("reject recursive mark-and-sweep")); - DUK_STATS_INC(heap, stats_ms_skip_count); - return; - } - DUK_ASSERT(heap->ms_running == 0); /* ms_prevent_count is bumped when ms_running is set */ - - /* Heap_thread is used during mark-and-sweep for refcount finalization - * (it's also used for finalizer execution once mark-and-sweep is - * complete). Heap allocation code ensures heap_thread is set and - * properly initialized before setting ms_prevent_count to 0. - */ - DUK_ASSERT(heap->heap_thread != NULL); - DUK_ASSERT(heap->heap_thread->valstack != NULL); - - DUK_D(DUK_DPRINT("garbage collect (mark-and-sweep) starting, requested flags: 0x%08lx, effective flags: 0x%08lx", - (unsigned long) flags, - (unsigned long) (flags | heap->ms_base_flags))); - - flags |= heap->ms_base_flags; -#if defined(DUK_USE_FINALIZER_SUPPORT) - if (heap->finalize_list != NULL) { - flags |= DUK_MS_FLAG_POSTPONE_RESCUE; - } -#endif - - /* - * Assertions before - */ - -#if defined(DUK_USE_ASSERTIONS) - DUK_ASSERT(heap->ms_prevent_count == 0); - DUK_ASSERT(heap->ms_running == 0); - DUK_ASSERT(!DUK_HEAP_HAS_DEBUGGER_PAUSED(heap)); - DUK_ASSERT(!DUK_HEAP_HAS_MARKANDSWEEP_RECLIMIT_REACHED(heap)); - DUK_ASSERT(heap->ms_recursion_depth == 0); - duk__assert_heaphdr_flags(heap); - duk__assert_validity(heap); -#if defined(DUK_USE_REFERENCE_COUNTING) - /* Note: heap->refzero_free_running may be true; a refcount - * finalizer may trigger a mark-and-sweep. - */ - duk__assert_valid_refcounts(heap); -#endif /* DUK_USE_REFERENCE_COUNTING */ -#endif /* DUK_USE_ASSERTIONS */ - - /* - * Begin - */ - - DUK_ASSERT(heap->ms_prevent_count == 0); - DUK_ASSERT(heap->ms_running == 0); - heap->ms_prevent_count = 1; - heap->ms_running = 1; - entry_creating_error = heap->creating_error; - heap->creating_error = 0; - - /* - * Free activation/catcher freelists on every mark-and-sweep for now. - * This is an initial rough draft; ideally we'd keep count of the - * freelist size and free only excess entries. - */ - - DUK_D(DUK_DPRINT("freeing temporary freelists")); - duk_heap_free_freelists(heap); - - /* - * Mark roots, hoping that recursion limit is not normally hit. - * If recursion limit is hit, run additional reachability rounds - * starting from "temproots" until marking is complete. - * - * Marking happens in two phases: first we mark actual reachability - * roots (and run "temproots" to complete the process). Then we - * check which objects are unreachable and are finalizable; such - * objects are marked as FINALIZABLE and marked as reachability - * (and "temproots" is run again to complete the process). - * - * The heap finalize_list must also be marked as a reachability root. - * There may be objects on the list from a previous round if the - * previous run had finalizer skip flag. - */ - -#if defined(DUK_USE_ASSERTIONS) && defined(DUK_USE_REFERENCE_COUNTING) - duk__clear_assert_refcounts(heap); -#endif -#if defined(DUK_USE_LITCACHE_SIZE) - duk__wipe_litcache(heap); -#endif - duk__mark_roots_heap(heap); /* Mark main reachability roots. */ -#if defined(DUK_USE_REFERENCE_COUNTING) - DUK_ASSERT(heap->refzero_list == NULL); /* Always handled to completion inline in DECREF. */ -#endif - duk__mark_temproots_by_heap_scan(heap); /* Temproots. */ - -#if defined(DUK_USE_FINALIZER_SUPPORT) - duk__mark_finalizable(heap); /* Mark finalizable as reachability roots. */ - duk__mark_finalize_list(heap); /* Mark finalizer work list as reachability roots. */ -#endif - duk__mark_temproots_by_heap_scan(heap); /* Temproots. */ - - /* - * Sweep garbage and remove marking flags, and move objects with - * finalizers to the finalizer work list. - * - * Objects to be swept need to get their refcounts finalized before - * they are swept. In other words, their target object refcounts - * need to be decreased. This has to be done before freeing any - * objects to avoid decref'ing dangling pointers (which may happen - * even without bugs, e.g. with reference loops) - * - * Because strings don't point to other heap objects, similar - * finalization is not necessary for strings. - */ - - /* XXX: more emergency behavior, e.g. find smaller hash sizes etc */ - -#if defined(DUK_USE_REFERENCE_COUNTING) - duk__finalize_refcounts(heap); -#endif - duk__sweep_heap(heap, flags, &count_keep_obj); - duk__sweep_stringtable(heap, &count_keep_str); -#if defined(DUK_USE_ASSERTIONS) && defined(DUK_USE_REFERENCE_COUNTING) - duk__check_assert_refcounts(heap); -#endif -#if defined(DUK_USE_REFERENCE_COUNTING) - DUK_ASSERT(heap->refzero_list == NULL); /* Always handled to completion inline in DECREF. */ -#endif -#if defined(DUK_USE_FINALIZER_SUPPORT) - duk__clear_finalize_list_flags(heap); -#endif - - /* - * Object compaction (emergency only). - * - * Object compaction is a separate step after sweeping, as there is - * more free memory for it to work with. Also, currently compaction - * may insert new objects into the heap allocated list and the string - * table which we don't want to do during a sweep (the reachability - * flags of such objects would be incorrect). The objects inserted - * are currently: - * - * - a temporary duk_hbuffer for a new properties allocation - * - if array part is abandoned, string keys are interned - * - * The object insertions go to the front of the list, so they do not - * cause an infinite loop (they are not compacted). - * - * At present compaction is not allowed when mark-and-sweep runs - * during error handling because it involves a duk_safe_call() - * interfering with error state. - */ - - if ((flags & DUK_MS_FLAG_EMERGENCY) && !(flags & DUK_MS_FLAG_NO_OBJECT_COMPACTION)) { - if (heap->lj.type != DUK_LJ_TYPE_UNKNOWN) { - DUK_D(DUK_DPRINT("lj.type (%ld) not DUK_LJ_TYPE_UNKNOWN, skip object compaction", (long) heap->lj.type)); - } else { - DUK_D(DUK_DPRINT("object compaction")); - duk__compact_objects(heap); - } - } - - /* - * String table resize check. - * - * This is mainly useful in emergency GC: if the string table load - * factor is really low for some reason, we can shrink the string - * table to a smaller size and free some memory in the process. - * Only execute in emergency GC. String table has internal flags - * to protect against recursive resizing if this mark-and-sweep pass - * was triggered by a string table resize. - */ - - if (flags & DUK_MS_FLAG_EMERGENCY) { - DUK_D(DUK_DPRINT("stringtable resize check in emergency gc")); - duk_heap_strtable_force_resize(heap); - } - - /* - * Finish - */ - - DUK_ASSERT(heap->ms_prevent_count == 1); - DUK_ASSERT(heap->ms_running == 1); - heap->ms_prevent_count = 0; - heap->ms_running = 0; - heap->creating_error = entry_creating_error; /* for nested error handling, see GH-2278 */ - - /* - * Assertions after - */ - -#if defined(DUK_USE_ASSERTIONS) - DUK_ASSERT(heap->ms_prevent_count == 0); - DUK_ASSERT(!DUK_HEAP_HAS_MARKANDSWEEP_RECLIMIT_REACHED(heap)); - DUK_ASSERT(heap->ms_recursion_depth == 0); - duk__assert_heaphdr_flags(heap); - duk__assert_validity(heap); -#if defined(DUK_USE_REFERENCE_COUNTING) - /* Note: heap->refzero_free_running may be true; a refcount - * finalizer may trigger a mark-and-sweep. - */ - duk__assert_valid_refcounts(heap); -#endif /* DUK_USE_REFERENCE_COUNTING */ -#if defined(DUK_USE_LITCACHE_SIZE) - duk__assert_litcache_nulls(heap); -#endif /* DUK_USE_LITCACHE_SIZE */ -#endif /* DUK_USE_ASSERTIONS */ - - /* - * Reset trigger counter - */ - -#if defined(DUK_USE_VOLUNTARY_GC) - tmp = (count_keep_obj + count_keep_str) / 256; - heap->ms_trigger_counter = (duk_int_t) ((tmp * DUK_HEAP_MARK_AND_SWEEP_TRIGGER_MULT) + DUK_HEAP_MARK_AND_SWEEP_TRIGGER_ADD); - DUK_D(DUK_DPRINT("garbage collect (mark-and-sweep) finished: %ld objects kept, %ld strings kept, trigger reset to %ld", - (long) count_keep_obj, - (long) count_keep_str, - (long) heap->ms_trigger_counter)); -#else - DUK_D(DUK_DPRINT("garbage collect (mark-and-sweep) finished: %ld objects kept, %ld strings kept, no voluntary trigger", - (long) count_keep_obj, - (long) count_keep_str)); -#endif - - /* - * Stats dump - */ - -#if defined(DUK_USE_DEBUG) - duk__dump_stats(heap); -#endif - - /* - * Finalize objects in the finalization work list. Finalized - * objects are queued back to heap_allocated with FINALIZED set. - * - * Since finalizers may cause arbitrary side effects, they are - * prevented e.g. during string table and object property allocation - * resizing using heap->pf_prevent_count. In this case the objects - * remain in the finalization work list after mark-and-sweep exits - * and they may be finalized on the next pass or any DECREF checking - * for finalize_list. - * - * As of Duktape 2.1 finalization happens outside mark-and-sweep - * protection. Mark-and-sweep is allowed while the finalize_list - * is being processed, but no rescue decisions are done while the - * process is on-going. This avoids incorrect rescue decisions - * if an object is considered reachable (and thus rescued) because - * of a reference via finalize_list (which is considered a reachability - * root). When finalize_list is being processed, reachable objects - * with FINALIZED set will just keep their FINALIZED flag for later - * mark-and-sweep processing. - * - * This could also be handled (a bit better) by having a more refined - * notion of reachability for rescue/free decisions. - * - * XXX: avoid finalizer execution when doing emergency GC? - */ - -#if defined(DUK_USE_FINALIZER_SUPPORT) - /* Attempt to process finalize_list, pf_prevent_count check - * is inside the target. - */ - duk_heap_process_finalize_list(heap); -#endif /* DUK_USE_FINALIZER_SUPPORT */ -} -/* - * Memory allocation handling. - */ - -/* #include duk_internal.h -> already included */ - -/* - * Allocate memory with garbage collection. - */ - -/* Slow path: voluntary GC triggered, first alloc attempt failed, or zero size. */ -DUK_LOCAL DUK_NOINLINE_PERF DUK_COLD void *duk__heap_mem_alloc_slowpath(duk_heap *heap, duk_size_t size) { - void *res; - duk_small_int_t i; - - DUK_ASSERT(heap != NULL); - DUK_ASSERT(heap->alloc_func != NULL); - DUK_ASSERT_DISABLE(size >= 0); - - if (size == 0) { - DUK_D(DUK_DPRINT("zero size alloc in slow path, return NULL")); - return NULL; - } - - DUK_D(DUK_DPRINT("first alloc attempt failed or voluntary GC limit reached, attempt to gc and retry")); - -#if 0 - /* - * If GC is already running there is no point in attempting a GC - * because it will be skipped. This could be checked for explicitly, - * but it isn't actually needed: the loop below will eventually - * fail resulting in a NULL. - */ - - if (heap->ms_prevent_count != 0) { - DUK_D(DUK_DPRINT("duk_heap_mem_alloc() failed, gc in progress (gc skipped), alloc size %ld", (long) size)); - return NULL; - } -#endif - - /* - * Retry with several GC attempts. Initial attempts are made without - * emergency mode; later attempts use emergency mode which minimizes - * memory allocations forcibly. - */ - - for (i = 0; i < DUK_HEAP_ALLOC_FAIL_MARKANDSWEEP_LIMIT; i++) { - duk_small_uint_t flags; - - flags = 0; - if (i >= DUK_HEAP_ALLOC_FAIL_MARKANDSWEEP_EMERGENCY_LIMIT - 1) { - flags |= DUK_MS_FLAG_EMERGENCY; - } - - duk_heap_mark_and_sweep(heap, flags); - - DUK_ASSERT(size > 0); - res = heap->alloc_func(heap->heap_udata, size); - if (res != NULL) { - DUK_D(DUK_DPRINT("duk_heap_mem_alloc() succeeded after gc (pass %ld), alloc size %ld", - (long) (i + 1), - (long) size)); - return res; - } - } - - DUK_D(DUK_DPRINT("duk_heap_mem_alloc() failed even after gc, alloc size %ld", (long) size)); - return NULL; -} - -DUK_INTERNAL DUK_INLINE_PERF DUK_HOT void *duk_heap_mem_alloc(duk_heap *heap, duk_size_t size) { - void *res; - - DUK_ASSERT(heap != NULL); - DUK_ASSERT(heap->alloc_func != NULL); - DUK_ASSERT_DISABLE(size >= 0); - -#if defined(DUK_USE_VOLUNTARY_GC) - /* Voluntary periodic GC (if enabled). */ - if (DUK_UNLIKELY(--(heap)->ms_trigger_counter < 0)) { - goto slowpath; - } -#endif - -#if defined(DUK_USE_GC_TORTURE) - /* Simulate alloc failure on every alloc, except when mark-and-sweep - * is running. - */ - if (heap->ms_prevent_count == 0) { - DUK_DDD(DUK_DDDPRINT("gc torture enabled, pretend that first alloc attempt fails")); - res = NULL; - DUK_UNREF(res); - goto slowpath; - } -#endif - - /* Zero-size allocation should happen very rarely (if at all), so - * don't check zero size on NULL; handle it in the slow path - * instead. This reduces size of inlined code. - */ - res = heap->alloc_func(heap->heap_udata, size); - if (DUK_LIKELY(res != NULL)) { - return res; - } - -slowpath: - - if (size == 0) { - DUK_D(DUK_DPRINT("first alloc attempt returned NULL for zero size alloc, use slow path to deal with it")); - } else { - DUK_D(DUK_DPRINT("first alloc attempt failed, attempt to gc and retry")); - } - return duk__heap_mem_alloc_slowpath(heap, size); -} - -DUK_INTERNAL DUK_INLINE_PERF DUK_HOT void *duk_heap_mem_alloc_zeroed(duk_heap *heap, duk_size_t size) { - void *res; - - DUK_ASSERT(heap != NULL); - DUK_ASSERT(heap->alloc_func != NULL); - DUK_ASSERT_DISABLE(size >= 0); - - res = DUK_ALLOC(heap, size); - if (DUK_LIKELY(res != NULL)) { - duk_memzero(res, size); - } - return res; -} - -DUK_INTERNAL DUK_INLINE_PERF DUK_HOT void *duk_heap_mem_alloc_checked(duk_hthread *thr, duk_size_t size) { - void *res; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - DUK_ASSERT(thr->heap->alloc_func != NULL); - - res = duk_heap_mem_alloc(thr->heap, size); - if (DUK_LIKELY(res != NULL)) { - return res; - } else if (size == 0) { - DUK_ASSERT(res == NULL); - return res; - } - DUK_ERROR_ALLOC_FAILED(thr); - DUK_WO_NORETURN(return NULL;); -} - -DUK_INTERNAL DUK_INLINE_PERF DUK_HOT void *duk_heap_mem_alloc_checked_zeroed(duk_hthread *thr, duk_size_t size) { - void *res; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - DUK_ASSERT(thr->heap->alloc_func != NULL); - - res = duk_heap_mem_alloc(thr->heap, size); - if (DUK_LIKELY(res != NULL)) { - duk_memzero(res, size); - return res; - } else if (size == 0) { - DUK_ASSERT(res == NULL); - return res; - } - DUK_ERROR_ALLOC_FAILED(thr); - DUK_WO_NORETURN(return NULL;); -} - -/* - * Reallocate memory with garbage collection. - */ - -/* Slow path: voluntary GC triggered, first realloc attempt failed, or zero size. */ -DUK_LOCAL DUK_NOINLINE_PERF DUK_COLD void *duk__heap_mem_realloc_slowpath(duk_heap *heap, void *ptr, duk_size_t newsize) { - void *res; - duk_small_int_t i; - - DUK_ASSERT(heap != NULL); - DUK_ASSERT(heap->realloc_func != NULL); - /* ptr may be NULL */ - DUK_ASSERT_DISABLE(newsize >= 0); - - /* Unlike for malloc(), zero size NULL result check happens at the call site. */ - - DUK_D(DUK_DPRINT("first realloc attempt failed, attempt to gc and retry")); - -#if 0 - /* - * Avoid a GC if GC is already running. See duk_heap_mem_alloc(). - */ - - if (heap->ms_prevent_count != 0) { - DUK_D(DUK_DPRINT("duk_heap_mem_realloc() failed, gc in progress (gc skipped), alloc size %ld", (long) newsize)); - return NULL; - } -#endif - - /* - * Retry with several GC attempts. Initial attempts are made without - * emergency mode; later attempts use emergency mode which minimizes - * memory allocations forcibly. - */ - - for (i = 0; i < DUK_HEAP_ALLOC_FAIL_MARKANDSWEEP_LIMIT; i++) { - duk_small_uint_t flags; - - flags = 0; - if (i >= DUK_HEAP_ALLOC_FAIL_MARKANDSWEEP_EMERGENCY_LIMIT - 1) { - flags |= DUK_MS_FLAG_EMERGENCY; - } - - duk_heap_mark_and_sweep(heap, flags); - - res = heap->realloc_func(heap->heap_udata, ptr, newsize); - if (res != NULL || newsize == 0) { - DUK_D(DUK_DPRINT("duk_heap_mem_realloc() succeeded after gc (pass %ld), alloc size %ld", - (long) (i + 1), - (long) newsize)); - return res; - } - } - - DUK_D(DUK_DPRINT("duk_heap_mem_realloc() failed even after gc, alloc size %ld", (long) newsize)); - return NULL; -} - -DUK_INTERNAL DUK_INLINE_PERF DUK_HOT void *duk_heap_mem_realloc(duk_heap *heap, void *ptr, duk_size_t newsize) { - void *res; - - DUK_ASSERT(heap != NULL); - DUK_ASSERT(heap->realloc_func != NULL); - /* ptr may be NULL */ - DUK_ASSERT_DISABLE(newsize >= 0); - -#if defined(DUK_USE_VOLUNTARY_GC) - /* Voluntary periodic GC (if enabled). */ - if (DUK_UNLIKELY(--(heap)->ms_trigger_counter < 0)) { - goto gc_retry; - } -#endif - -#if defined(DUK_USE_GC_TORTURE) - /* Simulate alloc failure on every realloc, except when mark-and-sweep - * is running. - */ - if (heap->ms_prevent_count == 0) { - DUK_DDD(DUK_DDDPRINT("gc torture enabled, pretend that first realloc attempt fails")); - res = NULL; - DUK_UNREF(res); - goto gc_retry; - } -#endif - - res = heap->realloc_func(heap->heap_udata, ptr, newsize); - if (DUK_LIKELY(res != NULL) || newsize == 0) { - if (res != NULL && newsize == 0) { - DUK_DD(DUK_DDPRINT("first realloc attempt returned NULL for zero size realloc, accept and return NULL")); - } - return res; - } else { - goto gc_retry; - } - /* Never here. */ - -gc_retry: - return duk__heap_mem_realloc_slowpath(heap, ptr, newsize); -} - -/* - * Reallocate memory with garbage collection, using a callback to provide - * the current allocated pointer. This variant is used when a mark-and-sweep - * (e.g. finalizers) might change the original pointer. - */ - -/* Slow path: voluntary GC triggered, first realloc attempt failed, or zero size. */ -DUK_LOCAL DUK_NOINLINE_PERF DUK_COLD void *duk__heap_mem_realloc_indirect_slowpath(duk_heap *heap, - duk_mem_getptr cb, - void *ud, - duk_size_t newsize) { - void *res; - duk_small_int_t i; - - DUK_ASSERT(heap != NULL); - DUK_ASSERT(heap->realloc_func != NULL); - DUK_ASSERT_DISABLE(newsize >= 0); - - /* Unlike for malloc(), zero size NULL result check happens at the call site. */ - - DUK_D(DUK_DPRINT("first indirect realloc attempt failed, attempt to gc and retry")); - -#if 0 - /* - * Avoid a GC if GC is already running. See duk_heap_mem_alloc(). - */ - - if (heap->ms_prevent_count != 0) { - DUK_D(DUK_DPRINT("duk_heap_mem_realloc_indirect() failed, gc in progress (gc skipped), alloc size %ld", (long) newsize)); - return NULL; - } -#endif - - /* - * Retry with several GC attempts. Initial attempts are made without - * emergency mode; later attempts use emergency mode which minimizes - * memory allocations forcibly. - */ - - for (i = 0; i < DUK_HEAP_ALLOC_FAIL_MARKANDSWEEP_LIMIT; i++) { - duk_small_uint_t flags; - -#if defined(DUK_USE_DEBUG) - void *ptr_pre; - void *ptr_post; -#endif - -#if defined(DUK_USE_DEBUG) - ptr_pre = cb(heap, ud); -#endif - flags = 0; - if (i >= DUK_HEAP_ALLOC_FAIL_MARKANDSWEEP_EMERGENCY_LIMIT - 1) { - flags |= DUK_MS_FLAG_EMERGENCY; - } - - duk_heap_mark_and_sweep(heap, flags); -#if defined(DUK_USE_DEBUG) - ptr_post = cb(heap, ud); - if (ptr_pre != ptr_post) { - DUK_DD(DUK_DDPRINT("realloc base pointer changed by mark-and-sweep: %p -> %p", - (void *) ptr_pre, - (void *) ptr_post)); - } -#endif - - /* Note: key issue here is to re-lookup the base pointer on every attempt. - * The pointer being reallocated may change after every mark-and-sweep. - */ - - res = heap->realloc_func(heap->heap_udata, cb(heap, ud), newsize); - if (res != NULL || newsize == 0) { - DUK_D(DUK_DPRINT("duk_heap_mem_realloc_indirect() succeeded after gc (pass %ld), alloc size %ld", - (long) (i + 1), - (long) newsize)); - return res; - } - } - - DUK_D(DUK_DPRINT("duk_heap_mem_realloc_indirect() failed even after gc, alloc size %ld", (long) newsize)); - return NULL; -} - -DUK_INTERNAL DUK_INLINE_PERF DUK_HOT void *duk_heap_mem_realloc_indirect(duk_heap *heap, - duk_mem_getptr cb, - void *ud, - duk_size_t newsize) { - void *res; - - DUK_ASSERT(heap != NULL); - DUK_ASSERT(heap->realloc_func != NULL); - DUK_ASSERT_DISABLE(newsize >= 0); - -#if defined(DUK_USE_VOLUNTARY_GC) - /* Voluntary periodic GC (if enabled). */ - if (DUK_UNLIKELY(--(heap)->ms_trigger_counter < 0)) { - goto gc_retry; - } -#endif - -#if defined(DUK_USE_GC_TORTURE) - /* Simulate alloc failure on every realloc, except when mark-and-sweep - * is running. - */ - if (heap->ms_prevent_count == 0) { - DUK_DDD(DUK_DDDPRINT("gc torture enabled, pretend that first indirect realloc attempt fails")); - res = NULL; - DUK_UNREF(res); - goto gc_retry; - } -#endif - - res = heap->realloc_func(heap->heap_udata, cb(heap, ud), newsize); - if (DUK_LIKELY(res != NULL) || newsize == 0) { - if (res != NULL && newsize == 0) { - DUK_DD(DUK_DDPRINT( - "first indirect realloc attempt returned NULL for zero size realloc, accept and return NULL")); - } - return res; - } else { - goto gc_retry; - } - /* Never here. */ - -gc_retry: - return duk__heap_mem_realloc_indirect_slowpath(heap, cb, ud, newsize); -} - -/* - * Free memory - */ - -DUK_INTERNAL DUK_INLINE_PERF DUK_HOT void duk_heap_mem_free(duk_heap *heap, void *ptr) { - DUK_ASSERT(heap != NULL); - DUK_ASSERT(heap->free_func != NULL); - /* ptr may be NULL */ - - /* Must behave like a no-op with NULL and any pointer returned from - * malloc/realloc with zero size. - */ - heap->free_func(heap->heap_udata, ptr); - - /* Never perform a GC (even voluntary) in a memory free, otherwise - * all call sites doing frees would need to deal with the side effects. - * No need to update voluntary GC counter either. - */ -} -/* - * Support functions for duk_heap. - */ - -/* #include duk_internal.h -> already included */ - -DUK_INTERNAL void duk_heap_insert_into_heap_allocated(duk_heap *heap, duk_heaphdr *hdr) { - duk_heaphdr *root; - - DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(hdr) != DUK_HTYPE_STRING); - - root = heap->heap_allocated; -#if defined(DUK_USE_DOUBLE_LINKED_HEAP) - if (root != NULL) { - DUK_ASSERT(DUK_HEAPHDR_GET_PREV(heap, root) == NULL); - DUK_HEAPHDR_SET_PREV(heap, root, hdr); - } - DUK_HEAPHDR_SET_PREV(heap, hdr, NULL); -#endif - DUK_HEAPHDR_SET_NEXT(heap, hdr, root); - DUK_HEAPHDR_ASSERT_LINKS(heap, hdr); - DUK_HEAPHDR_ASSERT_LINKS(heap, root); - heap->heap_allocated = hdr; -} - -#if defined(DUK_USE_REFERENCE_COUNTING) -DUK_INTERNAL void duk_heap_remove_from_heap_allocated(duk_heap *heap, duk_heaphdr *hdr) { - duk_heaphdr *prev; - duk_heaphdr *next; - - /* Strings are in string table. */ - DUK_ASSERT(hdr != NULL); - DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(hdr) != DUK_HTYPE_STRING); - - /* Target 'hdr' must be in heap_allocated (not e.g. finalize_list). - * If not, heap lists will become corrupted so assert early for it. - */ -#if defined(DUK_USE_ASSERTIONS) - { - duk_heaphdr *tmp; - for (tmp = heap->heap_allocated; tmp != NULL; tmp = DUK_HEAPHDR_GET_NEXT(heap, tmp)) { - if (tmp == hdr) { - break; - } - } - DUK_ASSERT(tmp == hdr); - } -#endif - - /* Read/write only once to minimize pointer compression calls. */ - prev = DUK_HEAPHDR_GET_PREV(heap, hdr); - next = DUK_HEAPHDR_GET_NEXT(heap, hdr); - - if (prev != NULL) { - DUK_ASSERT(heap->heap_allocated != hdr); - DUK_HEAPHDR_SET_NEXT(heap, prev, next); - } else { - DUK_ASSERT(heap->heap_allocated == hdr); - heap->heap_allocated = next; - } - if (next != NULL) { - DUK_HEAPHDR_SET_PREV(heap, next, prev); - } else { - ; - } -} -#endif /* DUK_USE_REFERENCE_COUNTING */ - -#if defined(DUK_USE_FINALIZER_SUPPORT) -DUK_INTERNAL void duk_heap_insert_into_finalize_list(duk_heap *heap, duk_heaphdr *hdr) { - duk_heaphdr *root; - - root = heap->finalize_list; -#if defined(DUK_USE_DOUBLE_LINKED_HEAP) - DUK_HEAPHDR_SET_PREV(heap, hdr, NULL); - if (root != NULL) { - DUK_ASSERT(DUK_HEAPHDR_GET_PREV(heap, root) == NULL); - DUK_HEAPHDR_SET_PREV(heap, root, hdr); - } -#endif - DUK_HEAPHDR_SET_NEXT(heap, hdr, root); - DUK_HEAPHDR_ASSERT_LINKS(heap, hdr); - DUK_HEAPHDR_ASSERT_LINKS(heap, root); - heap->finalize_list = hdr; -} -#endif /* DUK_USE_FINALIZER_SUPPORT */ - -#if defined(DUK_USE_FINALIZER_SUPPORT) -DUK_INTERNAL void duk_heap_remove_from_finalize_list(duk_heap *heap, duk_heaphdr *hdr) { -#if defined(DUK_USE_DOUBLE_LINKED_HEAP) - duk_heaphdr *next; - duk_heaphdr *prev; - - next = DUK_HEAPHDR_GET_NEXT(heap, hdr); - prev = DUK_HEAPHDR_GET_PREV(heap, hdr); - if (next != NULL) { - DUK_ASSERT(DUK_HEAPHDR_GET_PREV(heap, next) == hdr); - DUK_HEAPHDR_SET_PREV(heap, next, prev); - } - if (prev == NULL) { - DUK_ASSERT(hdr == heap->finalize_list); - heap->finalize_list = next; - } else { - DUK_ASSERT(hdr != heap->finalize_list); - DUK_HEAPHDR_SET_NEXT(heap, prev, next); - } -#else - duk_heaphdr *next; - duk_heaphdr *curr; - - /* Random removal is expensive: we need to locate the previous element - * because we don't have a 'prev' pointer. - */ - curr = heap->finalize_list; - if (curr == hdr) { - heap->finalize_list = DUK_HEAPHDR_GET_NEXT(heap, curr); - } else { - DUK_ASSERT(hdr != heap->finalize_list); - for (;;) { - DUK_ASSERT(curr != NULL); /* Caller responsibility. */ - - next = DUK_HEAPHDR_GET_NEXT(heap, curr); - if (next == hdr) { - next = DUK_HEAPHDR_GET_NEXT(heap, hdr); - DUK_HEAPHDR_SET_NEXT(heap, curr, next); - break; - } - } - } -#endif -} -#endif /* DUK_USE_FINALIZER_SUPPORT */ - -#if defined(DUK_USE_ASSERTIONS) -DUK_INTERNAL duk_bool_t duk_heap_in_heap_allocated(duk_heap *heap, duk_heaphdr *ptr) { - duk_heaphdr *curr; - DUK_ASSERT(heap != NULL); - - for (curr = heap->heap_allocated; curr != NULL; curr = DUK_HEAPHDR_GET_NEXT(heap, curr)) { - if (curr == ptr) { - return 1; - } - } - return 0; -} -#endif /* DUK_USE_ASSERTIONS */ - -#if defined(DUK_USE_INTERRUPT_COUNTER) -DUK_INTERNAL void duk_heap_switch_thread(duk_heap *heap, duk_hthread *new_thr) { - duk_hthread *curr_thr; - - DUK_ASSERT(heap != NULL); - - if (new_thr != NULL) { - curr_thr = heap->curr_thread; - if (curr_thr == NULL) { - /* For initial entry use default value; zero forces an - * interrupt before executing the first insturction. - */ - DUK_DD(DUK_DDPRINT("switch thread, initial entry, init default interrupt counter")); - new_thr->interrupt_counter = 0; - new_thr->interrupt_init = 0; - } else { - /* Copy interrupt counter/init value state to new thread (if any). - * It's OK for new_thr to be the same as curr_thr. - */ -#if defined(DUK_USE_DEBUG) - if (new_thr != curr_thr) { - DUK_DD(DUK_DDPRINT("switch thread, not initial entry, copy interrupt counter")); - } -#endif - new_thr->interrupt_counter = curr_thr->interrupt_counter; - new_thr->interrupt_init = curr_thr->interrupt_init; - } - } else { - DUK_DD(DUK_DDPRINT("switch thread, new thread is NULL, no interrupt counter changes")); - } - - heap->curr_thread = new_thr; /* may be NULL */ -} -#endif /* DUK_USE_INTERRUPT_COUNTER */ - -#if defined(DUK_USE_ASSERTIONS) -DUK_INTERNAL void duk_heap_assert_valid(duk_heap *heap) { - DUK_ASSERT(heap != NULL); -} -#endif -/* - * Reference counting implementation. - * - * INCREF/DECREF, finalization and freeing of objects whose refcount reaches - * zero (refzero). These operations are very performance sensitive, so - * various small tricks are used in an attempt to maximize speed. - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_REFERENCE_COUNTING) - -#if !defined(DUK_USE_DOUBLE_LINKED_HEAP) -#error internal error, reference counting requires a double linked heap -#endif - -/* - * Heap object refcount finalization. - * - * When an object is about to be freed, all other objects it refers to must - * be decref'd. Refcount finalization does NOT free the object or its inner - * allocations (mark-and-sweep shares these helpers), it just manipulates - * the refcounts. - * - * Note that any of the DECREFs may cause a refcount to drop to zero. If so, - * the object won't be refzero processed inline, but will just be queued to - * refzero_list and processed by an earlier caller working on refzero_list, - * eliminating C recursion from even long refzero cascades. If refzero - * finalization is triggered by mark-and-sweep, refzero conditions are ignored - * (objects are not even queued to refzero_list) because mark-and-sweep deals - * with them; refcounts are still updated so that they remain in sync with - * actual references. - */ - -DUK_LOCAL void duk__decref_tvals_norz(duk_hthread *thr, duk_tval *tv, duk_idx_t count) { - DUK_ASSERT(count == 0 || tv != NULL); - - while (count-- > 0) { - DUK_TVAL_DECREF_NORZ(thr, tv); - tv++; - } -} - -DUK_INTERNAL void duk_hobject_refcount_finalize_norz(duk_heap *heap, duk_hobject *h) { - duk_hthread *thr; - duk_uint_fast32_t i; - duk_uint_fast32_t n; - duk_propvalue *p_val; - duk_tval *p_tv; - duk_hstring **p_key; - duk_uint8_t *p_flag; - duk_hobject *h_proto; - - DUK_ASSERT(heap != NULL); - DUK_ASSERT(heap->heap_thread != NULL); - DUK_ASSERT(h); - DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) h) == DUK_HTYPE_OBJECT); - - thr = heap->heap_thread; - DUK_ASSERT(thr != NULL); - - p_key = DUK_HOBJECT_E_GET_KEY_BASE(heap, h); - p_val = DUK_HOBJECT_E_GET_VALUE_BASE(heap, h); - p_flag = DUK_HOBJECT_E_GET_FLAGS_BASE(heap, h); - n = DUK_HOBJECT_GET_ENEXT(h); - while (n-- > 0) { - duk_hstring *key; - - key = p_key[n]; - if (DUK_UNLIKELY(key == NULL)) { - continue; - } - DUK_HSTRING_DECREF_NORZ(thr, key); - if (DUK_UNLIKELY(p_flag[n] & DUK_PROPDESC_FLAG_ACCESSOR)) { - duk_hobject *h_getset; - h_getset = p_val[n].a.get; - DUK_ASSERT(h_getset == NULL || DUK_HEAPHDR_IS_OBJECT((duk_heaphdr *) h_getset)); - DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, h_getset); - h_getset = p_val[n].a.set; - DUK_ASSERT(h_getset == NULL || DUK_HEAPHDR_IS_OBJECT((duk_heaphdr *) h_getset)); - DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, h_getset); - } else { - duk_tval *tv_val; - tv_val = &p_val[n].v; - DUK_TVAL_DECREF_NORZ(thr, tv_val); - } - } - - p_tv = DUK_HOBJECT_A_GET_BASE(heap, h); - n = DUK_HOBJECT_GET_ASIZE(h); - while (n-- > 0) { - duk_tval *tv_val; - tv_val = p_tv + n; - DUK_TVAL_DECREF_NORZ(thr, tv_val); - } - - /* Hash part is a 'weak reference' and doesn't contribute to refcounts. */ - - h_proto = (duk_hobject *) DUK_HOBJECT_GET_PROTOTYPE(heap, h); - DUK_ASSERT(h_proto == NULL || DUK_HEAPHDR_IS_OBJECT((duk_heaphdr *) h_proto)); - DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, h_proto); - - /* XXX: Object subclass tests are quite awkward at present, ideally - * we should be able to switch-case here with a dense index (subtype - * number or something). For now, fast path plain objects and arrays - * and bit test the rest individually. - */ - - if (DUK_HOBJECT_HAS_FASTREFS(h)) { - /* Plain object or array, nothing more to do. While a - * duk_harray has additional fields, none of them need - * DECREF updates. - */ - DUK_ASSERT(DUK_HOBJECT_ALLOWS_FASTREFS(h)); - return; - } - DUK_ASSERT(DUK_HOBJECT_PROHIBITS_FASTREFS(h)); - - /* Slow path: special object, start bit checks from most likely. */ - - /* XXX: reorg, more common first */ - if (DUK_HOBJECT_IS_COMPFUNC(h)) { - duk_hcompfunc *f = (duk_hcompfunc *) h; - duk_tval *tv, *tv_end; - duk_hobject **funcs, **funcs_end; - - DUK_HCOMPFUNC_ASSERT_VALID(f); - - if (DUK_LIKELY(DUK_HCOMPFUNC_GET_DATA(heap, f) != NULL)) { - tv = DUK_HCOMPFUNC_GET_CONSTS_BASE(heap, f); - tv_end = DUK_HCOMPFUNC_GET_CONSTS_END(heap, f); - while (tv < tv_end) { - DUK_TVAL_DECREF_NORZ(thr, tv); - tv++; - } - - funcs = DUK_HCOMPFUNC_GET_FUNCS_BASE(heap, f); - funcs_end = DUK_HCOMPFUNC_GET_FUNCS_END(heap, f); - while (funcs < funcs_end) { - duk_hobject *h_func; - h_func = *funcs; - DUK_ASSERT(h_func != NULL); - DUK_ASSERT(DUK_HEAPHDR_IS_OBJECT((duk_heaphdr *) h_func)); - DUK_HCOMPFUNC_DECREF_NORZ(thr, (duk_hcompfunc *) h_func); - funcs++; - } - } else { - /* May happen in some out-of-memory corner cases. */ - DUK_D(DUK_DPRINT("duk_hcompfunc 'data' is NULL, skipping decref")); - } - - DUK_HEAPHDR_DECREF_ALLOWNULL(thr, (duk_heaphdr *) DUK_HCOMPFUNC_GET_LEXENV(heap, f)); - DUK_HEAPHDR_DECREF_ALLOWNULL(thr, (duk_heaphdr *) DUK_HCOMPFUNC_GET_VARENV(heap, f)); - DUK_HEAPHDR_DECREF_ALLOWNULL(thr, (duk_hbuffer *) DUK_HCOMPFUNC_GET_DATA(heap, f)); - } else if (DUK_HOBJECT_IS_DECENV(h)) { - duk_hdecenv *e = (duk_hdecenv *) h; - DUK_HDECENV_ASSERT_VALID(e); - DUK_HTHREAD_DECREF_NORZ_ALLOWNULL(thr, e->thread); - DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, e->varmap); - } else if (DUK_HOBJECT_IS_OBJENV(h)) { - duk_hobjenv *e = (duk_hobjenv *) h; - DUK_HOBJENV_ASSERT_VALID(e); - DUK_ASSERT(e->target != NULL); /* Required for object environments. */ - DUK_HOBJECT_DECREF_NORZ(thr, e->target); -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) - } else if (DUK_HOBJECT_IS_BUFOBJ(h)) { - duk_hbufobj *b = (duk_hbufobj *) h; - DUK_HBUFOBJ_ASSERT_VALID(b); - DUK_HBUFFER_DECREF_NORZ_ALLOWNULL(thr, (duk_hbuffer *) b->buf); - DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, (duk_hobject *) b->buf_prop); -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - } else if (DUK_HOBJECT_IS_BOUNDFUNC(h)) { - duk_hboundfunc *f = (duk_hboundfunc *) (void *) h; - DUK_HBOUNDFUNC_ASSERT_VALID(f); - DUK_TVAL_DECREF_NORZ(thr, &f->target); - DUK_TVAL_DECREF_NORZ(thr, &f->this_binding); - duk__decref_tvals_norz(thr, f->args, f->nargs); -#if defined(DUK_USE_ES6_PROXY) - } else if (DUK_HOBJECT_IS_PROXY(h)) { - duk_hproxy *p = (duk_hproxy *) h; - DUK_HPROXY_ASSERT_VALID(p); - DUK_HOBJECT_DECREF_NORZ(thr, p->target); - DUK_HOBJECT_DECREF_NORZ(thr, p->handler); -#endif /* DUK_USE_ES6_PROXY */ - } else if (DUK_HOBJECT_IS_THREAD(h)) { - duk_hthread *t = (duk_hthread *) h; - duk_activation *act; - duk_tval *tv; - - DUK_HTHREAD_ASSERT_VALID(t); - - tv = t->valstack; - while (tv < t->valstack_top) { - DUK_TVAL_DECREF_NORZ(thr, tv); - tv++; - } - - for (act = t->callstack_curr; act != NULL; act = act->parent) { - DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, (duk_hobject *) DUK_ACT_GET_FUNC(act)); - DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, (duk_hobject *) act->var_env); - DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, (duk_hobject *) act->lex_env); -#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY) - DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, (duk_hobject *) act->prev_caller); -#endif -#if 0 /* nothing now */ - for (cat = act->cat; cat != NULL; cat = cat->parent) { - } -#endif - } - - for (i = 0; i < DUK_NUM_BUILTINS; i++) { - DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, (duk_hobject *) t->builtins[i]); - } - - DUK_HTHREAD_DECREF_NORZ_ALLOWNULL(thr, (duk_hthread *) t->resumer); - } else { - /* We may come here if the object should have a FASTREFS flag - * but it's missing for some reason. Assert for never getting - * here; however, other than performance, this is harmless. - */ - DUK_D(DUK_DPRINT("missing FASTREFS flag for: %!iO", h)); - DUK_ASSERT(0); - } -} - -DUK_INTERNAL void duk_heaphdr_refcount_finalize_norz(duk_heap *heap, duk_heaphdr *hdr) { - DUK_ASSERT(heap != NULL); - DUK_ASSERT(heap->heap_thread != NULL); - DUK_ASSERT(hdr != NULL); - - if (DUK_HEAPHDR_IS_OBJECT(hdr)) { - duk_hobject_refcount_finalize_norz(heap, (duk_hobject *) hdr); - } - /* DUK_HTYPE_BUFFER: nothing to finalize */ - /* DUK_HTYPE_STRING: nothing to finalize */ -} - -/* - * Refzero processing for duk_hobject: queue a refzero'ed object to either - * finalize_list or refzero_list and process the relevent list(s) if - * necessary. - * - * Refzero_list is single linked, with only 'prev' pointers set and valid. - * All 'next' pointers are intentionally left as garbage. This doesn't - * matter because refzero_list is processed to completion before any other - * code (like mark-and-sweep) might walk the list. - * - * In more detail: - * - * - On first insert refzero_list is NULL and the new object becomes the - * first and only element on the list; duk__refcount_free_pending() is - * called and it starts processing the list from the initial element, - * i.e. the list tail. - * - * - As each object is refcount finalized, new objects may be queued to - * refzero_list head. Their 'next' pointers are left as garbage, but - * 'prev' points are set correctly, with the element at refzero_list - * having a NULL 'prev' pointer. The fact that refzero_list is non-NULL - * is used to reject (1) recursive duk__refcount_free_pending() and - * (2) finalize_list processing calls. - * - * - When we're done with the current object, read its 'prev' pointer and - * free the object. If 'prev' is NULL, we've reached head of list and are - * done: set refzero_list to NULL and process pending finalizers. Otherwise - * continue processing the list. - * - * A refzero cascade is free of side effects because it only involves - * queueing more objects and freeing memory; finalizer execution is blocked - * in the code path queueing objects to finalize_list. As a result the - * initial refzero call (which triggers duk__refcount_free_pending()) must - * check finalize_list so that finalizers are executed snappily. - * - * If finalize_list processing starts first, refzero may occur while we're - * processing finalizers. That's fine: that particular refzero cascade is - * handled to completion without side effects. Once the cascade is complete, - * we'll run pending finalizers but notice that we're already doing that and - * return. - * - * This could be expanded to allow incremental freeing: just bail out - * early and resume at a future alloc/decref/refzero. However, if that - * were done, the list structure would need to be kept consistent at all - * times, mark-and-sweep would need to handle refzero_list, etc. - */ - -DUK_LOCAL void duk__refcount_free_pending(duk_heap *heap) { - duk_heaphdr *curr; -#if defined(DUK_USE_DEBUG) - duk_int_t count = 0; -#endif - - DUK_ASSERT(heap != NULL); - - curr = heap->refzero_list; - DUK_ASSERT(curr != NULL); - DUK_ASSERT(DUK_HEAPHDR_GET_PREV(heap, curr) == NULL); /* We're called on initial insert only. */ - /* curr->next is GARBAGE. */ - - do { - duk_heaphdr *prev; - - DUK_DDD(DUK_DDDPRINT("refzero processing %p: %!O", (void *) curr, (duk_heaphdr *) curr)); - -#if defined(DUK_USE_DEBUG) - count++; -#endif - - DUK_ASSERT(curr != NULL); - DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT); /* currently, always the case */ - /* FINALIZED may be set; don't care about flags here. */ - - /* Refcount finalize 'curr'. Refzero_list must be non-NULL - * here to prevent recursive entry to duk__refcount_free_pending(). - */ - DUK_ASSERT(heap->refzero_list != NULL); - duk_hobject_refcount_finalize_norz(heap, (duk_hobject *) curr); - - prev = DUK_HEAPHDR_GET_PREV(heap, curr); - DUK_ASSERT((prev == NULL && heap->refzero_list == curr) || (prev != NULL && heap->refzero_list != curr)); - /* prev->next is intentionally not updated and is garbage. */ - - duk_free_hobject(heap, (duk_hobject *) curr); /* Invalidates 'curr'. */ - - curr = prev; - } while (curr != NULL); - - heap->refzero_list = NULL; - - DUK_DD(DUK_DDPRINT("refzero processed %ld objects", (long) count)); -} - -DUK_LOCAL DUK_INLINE void duk__refcount_refzero_hobject(duk_heap *heap, duk_hobject *obj, duk_bool_t skip_free_pending) { - duk_heaphdr *hdr; - duk_heaphdr *root; - - DUK_ASSERT(heap != NULL); - DUK_ASSERT(heap->heap_thread != NULL); - DUK_ASSERT(obj != NULL); - DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) obj) == DUK_HTYPE_OBJECT); - - hdr = (duk_heaphdr *) obj; - - /* Refzero'd objects must be in heap_allocated. They can't be in - * finalize_list because all objects on finalize_list have an - * artificial +1 refcount bump. - */ -#if defined(DUK_USE_ASSERTIONS) - DUK_ASSERT(duk_heap_in_heap_allocated(heap, (duk_heaphdr *) obj)); -#endif - - DUK_HEAP_REMOVE_FROM_HEAP_ALLOCATED(heap, hdr); - -#if defined(DUK_USE_FINALIZER_SUPPORT) - /* This finalizer check MUST BE side effect free. It should also be - * as fast as possible because it's applied to every object freed. - */ - if (DUK_UNLIKELY(DUK_HOBJECT_HAS_FINALIZER_FAST(heap, (duk_hobject *) hdr) != 0U)) { - /* Special case: FINALIZED may be set if mark-and-sweep queued - * object for finalization, the finalizer was executed (and - * FINALIZED set), mark-and-sweep hasn't yet processed the - * object again, but its refcount drops to zero. Free without - * running the finalizer again. - */ - if (DUK_HEAPHDR_HAS_FINALIZED(hdr)) { - DUK_D(DUK_DPRINT("refzero'd object has finalizer and FINALIZED is set -> free")); - } else { - /* Set FINALIZABLE flag so that all objects on finalize_list - * will have it set and are thus detectable based on the - * flag alone. - */ - DUK_HEAPHDR_SET_FINALIZABLE(hdr); - DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED(hdr)); - -#if defined(DUK_USE_REFERENCE_COUNTING) - /* Bump refcount on finalize_list insert so that a - * refzero can never occur when an object is waiting - * for its finalizer call. Refzero might otherwise - * now happen because we allow duk_push_heapptr() for - * objects pending finalization. - */ - DUK_HEAPHDR_PREINC_REFCOUNT(hdr); -#endif - DUK_HEAP_INSERT_INTO_FINALIZE_LIST(heap, hdr); - - /* Process finalizers unless skipping is explicitly - * requested (NORZ) or refzero_list is being processed - * (avoids side effects during a refzero cascade). - * If refzero_list is processed, the initial refzero - * call will run pending finalizers when refzero_list - * is done. - */ - if (!skip_free_pending && heap->refzero_list == NULL) { - duk_heap_process_finalize_list(heap); - } - return; - } - } -#endif /* DUK_USE_FINALIZER_SUPPORT */ - - /* No need to finalize, free object via refzero_list. */ - - root = heap->refzero_list; - - DUK_HEAPHDR_SET_PREV(heap, hdr, NULL); - /* 'next' is left as GARBAGE. */ - heap->refzero_list = hdr; - - if (root == NULL) { - /* Object is now queued. Refzero_list was NULL so - * no-one is currently processing it; do it here. - * With refzero processing just doing a cascade of - * free calls, we can process it directly even when - * NORZ macros are used: there are no side effects. - */ - duk__refcount_free_pending(heap); - DUK_ASSERT(heap->refzero_list == NULL); - - /* Process finalizers only after the entire cascade - * is finished. In most cases there's nothing to - * finalize, so fast path check to avoid a call. - */ -#if defined(DUK_USE_FINALIZER_SUPPORT) - if (!skip_free_pending && DUK_UNLIKELY(heap->finalize_list != NULL)) { - duk_heap_process_finalize_list(heap); - } -#endif - } else { - DUK_ASSERT(DUK_HEAPHDR_GET_PREV(heap, root) == NULL); - DUK_HEAPHDR_SET_PREV(heap, root, hdr); - - /* Object is now queued. Because refzero_list was - * non-NULL, it's already being processed by someone - * in the C call stack, so we're done. - */ - } -} - -#if defined(DUK_USE_FINALIZER_SUPPORT) -DUK_INTERNAL DUK_ALWAYS_INLINE void duk_refzero_check_fast(duk_hthread *thr) { - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - DUK_ASSERT(thr->heap->refzero_list == NULL); /* Processed to completion inline. */ - - if (DUK_UNLIKELY(thr->heap->finalize_list != NULL)) { - duk_heap_process_finalize_list(thr->heap); - } -} - -DUK_INTERNAL void duk_refzero_check_slow(duk_hthread *thr) { - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - DUK_ASSERT(thr->heap->refzero_list == NULL); /* Processed to completion inline. */ - - if (DUK_UNLIKELY(thr->heap->finalize_list != NULL)) { - duk_heap_process_finalize_list(thr->heap); - } -} -#endif /* DUK_USE_FINALIZER_SUPPORT */ - -/* - * Refzero processing for duk_hstring. - */ - -DUK_LOCAL DUK_INLINE void duk__refcount_refzero_hstring(duk_heap *heap, duk_hstring *str) { - DUK_ASSERT(heap != NULL); - DUK_ASSERT(heap->heap_thread != NULL); - DUK_ASSERT(str != NULL); - DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) str) == DUK_HTYPE_STRING); - - duk_heap_strcache_string_remove(heap, str); - duk_heap_strtable_unlink(heap, str); - duk_free_hstring(heap, str); -} - -/* - * Refzero processing for duk_hbuffer. - */ - -DUK_LOCAL DUK_INLINE void duk__refcount_refzero_hbuffer(duk_heap *heap, duk_hbuffer *buf) { - DUK_ASSERT(heap != NULL); - DUK_ASSERT(heap->heap_thread != NULL); - DUK_ASSERT(buf != NULL); - DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) buf) == DUK_HTYPE_BUFFER); - - DUK_HEAP_REMOVE_FROM_HEAP_ALLOCATED(heap, (duk_heaphdr *) buf); - duk_free_hbuffer(heap, buf); -} - -/* - * Incref and decref functions. - * - * Decref may trigger immediate refzero handling, which may free and finalize - * an arbitrary number of objects (a "DECREF cascade"). - * - * Refzero handling is skipped entirely if (1) mark-and-sweep is running or - * (2) execution is paused in the debugger. The objects are left in the heap, - * and will be freed by mark-and-sweep or eventual heap destruction. - * - * This is necessary during mark-and-sweep because refcounts are also updated - * during the sweep phase (otherwise objects referenced by a swept object - * would have incorrect refcounts) which then calls here. This could be - * avoided by using separate decref macros in mark-and-sweep; however, - * mark-and-sweep also calls finalizers which would use the ordinary decref - * macros anyway. - * - * We can't process refzeros (= free objects) when the debugger is running - * as the debugger might make an object unreachable but still continue - * inspecting it (or even cause it to be pushed back). So we must rely on - * mark-and-sweep to collect them. - * - * The DUK__RZ_SUPPRESS_CHECK() condition is also used in heap destruction - * when running finalizers for remaining objects: the flag prevents objects - * from being moved around in heap linked lists while that's being done. - * - * The suppress condition is important to performance. - */ - -#define DUK__RZ_SUPPRESS_ASSERT1() \ - do { \ - DUK_ASSERT(thr != NULL); \ - DUK_ASSERT(thr->heap != NULL); \ - /* When mark-and-sweep runs, heap_thread must exist. */ \ - DUK_ASSERT(thr->heap->ms_running == 0 || thr->heap->heap_thread != NULL); \ - /* In normal operation finalizers are executed with ms_running == 0 \ - * so we should never see ms_running == 1 and thr != heap_thread. \ - * In heap destruction finalizers are executed with ms_running != 0 \ - * to e.g. prevent refzero; a special value ms_running == 2 is used \ - * in that case so it can be distinguished from the normal runtime \ - * case, and allows a stronger assertion here (GH-2030). \ - */ \ - DUK_ASSERT(!(thr->heap->ms_running == 1 && thr != thr->heap->heap_thread)); \ - /* We may be called when the heap is initializing and we process \ - * refzeros normally, but mark-and-sweep and finalizers are prevented \ - * if that's the case. \ - */ \ - DUK_ASSERT(thr->heap->heap_initializing == 0 || thr->heap->ms_prevent_count > 0); \ - DUK_ASSERT(thr->heap->heap_initializing == 0 || thr->heap->pf_prevent_count > 0); \ - } while (0) - -#if defined(DUK_USE_DEBUGGER_SUPPORT) -#define DUK__RZ_SUPPRESS_ASSERT2() \ - do { \ - /* When debugger is paused, ms_running is set. */ \ - DUK_ASSERT(!DUK_HEAP_HAS_DEBUGGER_PAUSED(thr->heap) || thr->heap->ms_running != 0); \ - } while (0) -#define DUK__RZ_SUPPRESS_COND() (heap->ms_running != 0) -#else -#define DUK__RZ_SUPPRESS_ASSERT2() \ - do { \ - } while (0) -#define DUK__RZ_SUPPRESS_COND() (heap->ms_running != 0) -#endif /* DUK_USE_DEBUGGER_SUPPORT */ - -#define DUK__RZ_SUPPRESS_CHECK() \ - do { \ - DUK__RZ_SUPPRESS_ASSERT1(); \ - DUK__RZ_SUPPRESS_ASSERT2(); \ - if (DUK_UNLIKELY(DUK__RZ_SUPPRESS_COND())) { \ - DUK_DDD( \ - DUK_DDDPRINT("refzero handling suppressed (not even queued) when mark-and-sweep running, object: %p", \ - (void *) h)); \ - return; \ - } \ - } while (0) - -#define DUK__RZ_STRING() \ - do { \ - duk__refcount_refzero_hstring(heap, (duk_hstring *) h); \ - } while (0) -#define DUK__RZ_BUFFER() \ - do { \ - duk__refcount_refzero_hbuffer(heap, (duk_hbuffer *) h); \ - } while (0) -#define DUK__RZ_OBJECT() \ - do { \ - duk__refcount_refzero_hobject(heap, (duk_hobject *) h, skip_free_pending); \ - } while (0) - -/* XXX: test the effect of inlining here vs. NOINLINE in refzero helpers */ -#if defined(DUK_USE_FAST_REFCOUNT_DEFAULT) -#define DUK__RZ_INLINE DUK_ALWAYS_INLINE -#else -#define DUK__RZ_INLINE /*nop*/ -#endif - -DUK_LOCAL DUK__RZ_INLINE void duk__hstring_refzero_helper(duk_hthread *thr, duk_hstring *h) { - duk_heap *heap; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(h != NULL); - heap = thr->heap; - - DUK__RZ_SUPPRESS_CHECK(); - DUK__RZ_STRING(); -} - -DUK_LOCAL DUK__RZ_INLINE void duk__hbuffer_refzero_helper(duk_hthread *thr, duk_hbuffer *h) { - duk_heap *heap; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(h != NULL); - heap = thr->heap; - - DUK__RZ_SUPPRESS_CHECK(); - DUK__RZ_BUFFER(); -} - -DUK_LOCAL DUK__RZ_INLINE void duk__hobject_refzero_helper(duk_hthread *thr, duk_hobject *h, duk_bool_t skip_free_pending) { - duk_heap *heap; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(h != NULL); - heap = thr->heap; - - DUK__RZ_SUPPRESS_CHECK(); - DUK__RZ_OBJECT(); -} - -DUK_LOCAL DUK__RZ_INLINE void duk__heaphdr_refzero_helper(duk_hthread *thr, duk_heaphdr *h, duk_bool_t skip_free_pending) { - duk_heap *heap; - duk_small_uint_t htype; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(h != NULL); - heap = thr->heap; - - htype = (duk_small_uint_t) DUK_HEAPHDR_GET_TYPE(h); - DUK_DDD(DUK_DDDPRINT("ms_running=%ld, heap_thread=%p", (long) thr->heap->ms_running, thr->heap->heap_thread)); - DUK__RZ_SUPPRESS_CHECK(); - - switch (htype) { - case DUK_HTYPE_STRING: - /* Strings have no internal references but do have "weak" - * references in the string cache. Also note that strings - * are not on the heap_allocated list like other heap - * elements. - */ - - DUK__RZ_STRING(); - break; - - case DUK_HTYPE_OBJECT: - /* Objects have internal references. Must finalize through - * the "refzero" work list. - */ - - DUK__RZ_OBJECT(); - break; - - default: - /* Buffers have no internal references. However, a dynamic - * buffer has a separate allocation for the buffer. This is - * freed by duk_heap_free_heaphdr_raw(). - */ - - DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(h) == DUK_HTYPE_BUFFER); - DUK__RZ_BUFFER(); - break; - } -} - -DUK_INTERNAL DUK_NOINLINE void duk_heaphdr_refzero(duk_hthread *thr, duk_heaphdr *h) { - duk__heaphdr_refzero_helper(thr, h, 0 /*skip_free_pending*/); -} - -DUK_INTERNAL DUK_NOINLINE void duk_heaphdr_refzero_norz(duk_hthread *thr, duk_heaphdr *h) { - duk__heaphdr_refzero_helper(thr, h, 1 /*skip_free_pending*/); -} - -DUK_INTERNAL DUK_NOINLINE void duk_hstring_refzero(duk_hthread *thr, duk_hstring *h) { - duk__hstring_refzero_helper(thr, h); -} - -DUK_INTERNAL DUK_NOINLINE void duk_hbuffer_refzero(duk_hthread *thr, duk_hbuffer *h) { - duk__hbuffer_refzero_helper(thr, h); -} - -DUK_INTERNAL DUK_NOINLINE void duk_hobject_refzero(duk_hthread *thr, duk_hobject *h) { - duk__hobject_refzero_helper(thr, h, 0 /*skip_free_pending*/); -} - -DUK_INTERNAL DUK_NOINLINE void duk_hobject_refzero_norz(duk_hthread *thr, duk_hobject *h) { - duk__hobject_refzero_helper(thr, h, 1 /*skip_free_pending*/); -} - -#if !defined(DUK_USE_FAST_REFCOUNT_DEFAULT) -DUK_INTERNAL void duk_tval_incref(duk_tval *tv) { - DUK_ASSERT(tv != NULL); - - if (DUK_TVAL_NEEDS_REFCOUNT_UPDATE(tv)) { - duk_heaphdr *h = DUK_TVAL_GET_HEAPHDR(tv); - DUK_ASSERT(h != NULL); - DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h)); - DUK_ASSERT_DISABLE(h->h_refcount >= 0); - DUK_HEAPHDR_PREINC_REFCOUNT(h); - DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(h) != 0); /* No wrapping. */ - } -} - -DUK_INTERNAL void duk_tval_decref(duk_hthread *thr, duk_tval *tv) { - DUK_ASSERT(thr != NULL); - DUK_ASSERT(tv != NULL); - - if (DUK_TVAL_NEEDS_REFCOUNT_UPDATE(tv)) { - duk_heaphdr *h = DUK_TVAL_GET_HEAPHDR(tv); - DUK_ASSERT(h != NULL); - DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h)); - DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(h) >= 1); -#if 0 - if (DUK_HEAPHDR_PREDEC_REFCOUNT(h) != 0) { - return; - } - duk_heaphdr_refzero(thr, h); -#else - duk_heaphdr_decref(thr, h); -#endif - } -} - -DUK_INTERNAL void duk_tval_decref_norz(duk_hthread *thr, duk_tval *tv) { - DUK_ASSERT(thr != NULL); - DUK_ASSERT(tv != NULL); - - if (DUK_TVAL_NEEDS_REFCOUNT_UPDATE(tv)) { - duk_heaphdr *h = DUK_TVAL_GET_HEAPHDR(tv); - DUK_ASSERT(h != NULL); - DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h)); - DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(h) >= 1); -#if 0 - if (DUK_HEAPHDR_PREDEC_REFCOUNT(h) != 0) { - return; - } - duk_heaphdr_refzero_norz(thr, h); -#else - duk_heaphdr_decref_norz(thr, h); -#endif - } -} -#endif /* !DUK_USE_FAST_REFCOUNT_DEFAULT */ - -#define DUK__DECREF_ASSERTS() \ - do { \ - DUK_ASSERT(thr != NULL); \ - DUK_ASSERT(thr->heap != NULL); \ - DUK_ASSERT(h != NULL); \ - DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID((duk_heaphdr *) h)); \ - DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h) >= 1); \ - } while (0) -#if defined(DUK_USE_ROM_OBJECTS) -#define DUK__INCREF_SHARED() \ - do { \ - if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) h)) { \ - return; \ - } \ - DUK_HEAPHDR_PREINC_REFCOUNT((duk_heaphdr *) h); \ - DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h) != 0); /* No wrapping. */ \ - } while (0) -#define DUK__DECREF_SHARED() \ - do { \ - if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) h)) { \ - return; \ - } \ - if (DUK_HEAPHDR_PREDEC_REFCOUNT((duk_heaphdr *) h) != 0) { \ - return; \ - } \ - } while (0) -#else -#define DUK__INCREF_SHARED() \ - do { \ - DUK_HEAPHDR_PREINC_REFCOUNT((duk_heaphdr *) h); \ - DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h) != 0); /* No wrapping. */ \ - } while (0) -#define DUK__DECREF_SHARED() \ - do { \ - if (DUK_HEAPHDR_PREDEC_REFCOUNT((duk_heaphdr *) h) != 0) { \ - return; \ - } \ - } while (0) -#endif - -#if !defined(DUK_USE_FAST_REFCOUNT_DEFAULT) -/* This will in practice be inlined because it's just an INC instructions - * and a bit test + INC when ROM objects are enabled. - */ -DUK_INTERNAL void duk_heaphdr_incref(duk_heaphdr *h) { - DUK_ASSERT(h != NULL); - DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h)); - DUK_ASSERT_DISABLE(DUK_HEAPHDR_GET_REFCOUNT(h) >= 0); - - DUK__INCREF_SHARED(); -} - -DUK_INTERNAL void duk_heaphdr_decref(duk_hthread *thr, duk_heaphdr *h) { - DUK__DECREF_ASSERTS(); - DUK__DECREF_SHARED(); - duk_heaphdr_refzero(thr, h); - - /* Forced mark-and-sweep when GC torture enabled; this could happen - * on any DECREF (but not DECREF_NORZ). - */ - DUK_GC_TORTURE(thr->heap); -} -DUK_INTERNAL void duk_heaphdr_decref_norz(duk_hthread *thr, duk_heaphdr *h) { - DUK__DECREF_ASSERTS(); - DUK__DECREF_SHARED(); - duk_heaphdr_refzero_norz(thr, h); -} -#endif /* !DUK_USE_FAST_REFCOUNT_DEFAULT */ - -#if 0 /* Not needed. */ -DUK_INTERNAL void duk_hstring_decref(duk_hthread *thr, duk_hstring *h) { - DUK__DECREF_ASSERTS(); - DUK__DECREF_SHARED(); - duk_hstring_refzero(thr, h); -} -DUK_INTERNAL void duk_hstring_decref_norz(duk_hthread *thr, duk_hstring *h) { - DUK__DECREF_ASSERTS(); - DUK__DECREF_SHARED(); - duk_hstring_refzero_norz(thr, h); -} -DUK_INTERNAL void duk_hbuffer_decref(duk_hthread *thr, duk_hbuffer *h) { - DUK__DECREF_ASSERTS(); - DUK__DECREF_SHARED(); - duk_hbuffer_refzero(thr, h); -} -DUK_INTERNAL void duk_hbuffer_decref_norz(duk_hthread *thr, duk_hbuffer *h) { - DUK__DECREF_ASSERTS(); - DUK__DECREF_SHARED(); - duk_hbuffer_refzero_norz(thr, h); -} -DUK_INTERNAL void duk_hobject_decref(duk_hthread *thr, duk_hobject *h) { - DUK__DECREF_ASSERTS(); - DUK__DECREF_SHARED(); - duk_hobject_refzero(thr, h); -} -DUK_INTERNAL void duk_hobject_decref_norz(duk_hthread *thr, duk_hobject *h) { - DUK__DECREF_ASSERTS(); - DUK__DECREF_SHARED(); - duk_hobject_refzero_norz(thr, h); -} -#endif - -#else /* DUK_USE_REFERENCE_COUNTING */ - -/* no refcounting */ - -#endif /* DUK_USE_REFERENCE_COUNTING */ - -/* automatic undefs */ -#undef DUK__DECREF_ASSERTS -#undef DUK__DECREF_SHARED -#undef DUK__INCREF_SHARED -#undef DUK__RZ_BUFFER -#undef DUK__RZ_INLINE -#undef DUK__RZ_OBJECT -#undef DUK__RZ_STRING -#undef DUK__RZ_SUPPRESS_ASSERT1 -#undef DUK__RZ_SUPPRESS_ASSERT2 -#undef DUK__RZ_SUPPRESS_CHECK -#undef DUK__RZ_SUPPRESS_COND -/* - * String cache. - * - * Provides a cache to optimize indexed string lookups. The cache keeps - * track of (byte offset, char offset) states for a fixed number of strings. - * Otherwise we'd need to scan from either end of the string, as we store - * strings in (extended) UTF-8. - */ - -/* #include duk_internal.h -> already included */ - -/* - * Delete references to given hstring from the heap string cache. - * - * String cache references are 'weak': they are not counted towards - * reference counts, nor serve as roots for mark-and-sweep. When an - * object is about to be freed, such references need to be removed. - */ - -DUK_INTERNAL void duk_heap_strcache_string_remove(duk_heap *heap, duk_hstring *h) { - duk_uint_t i; - for (i = 0; i < DUK_HEAP_STRCACHE_SIZE; i++) { - duk_strcache_entry *c = heap->strcache + i; - if (c->h == h) { - DUK_DD( - DUK_DDPRINT("deleting weak strcache reference to hstring %p from heap %p", (void *) h, (void *) heap)); - c->h = NULL; - - /* XXX: the string shouldn't appear twice, but we now loop to the - * end anyway; if fixed, add a looping assertion to ensure there - * is no duplicate. - */ - } - } -} - -/* - * String scanning helpers - * - * All bytes other than UTF-8 continuation bytes ([0x80,0xbf]) are - * considered to contribute a character. This must match how string - * character length is computed. - */ - -DUK_LOCAL const duk_uint8_t *duk__scan_forwards(const duk_uint8_t *p, const duk_uint8_t *q, duk_uint_fast32_t n) { - while (n > 0) { - for (;;) { - p++; - if (p >= q) { - return NULL; - } - if ((*p & 0xc0) != 0x80) { - break; - } - } - n--; - } - return p; -} - -DUK_LOCAL const duk_uint8_t *duk__scan_backwards(const duk_uint8_t *p, const duk_uint8_t *q, duk_uint_fast32_t n) { - while (n > 0) { - for (;;) { - p--; - if (p < q) { - return NULL; - } - if ((*p & 0xc0) != 0x80) { - break; - } - } - n--; - } - return p; -} - -/* - * Convert char offset to byte offset - * - * Avoid using the string cache if possible: for ASCII strings byte and - * char offsets are equal and for short strings direct scanning may be - * better than using the string cache (which may evict a more important - * entry). - * - * Typing now assumes 32-bit string byte/char offsets (duk_uint_fast32_t). - * Better typing might be to use duk_size_t. - * - * Caller should ensure 'char_offset' is within the string bounds [0,charlen] - * (endpoint is inclusive). If this is not the case, no memory unsafe - * behavior will happen but an error will be thrown. - */ - -DUK_INTERNAL duk_uint_fast32_t duk_heap_strcache_offset_char2byte(duk_hthread *thr, duk_hstring *h, duk_uint_fast32_t char_offset) { - duk_heap *heap; - duk_strcache_entry *sce; - duk_uint_fast32_t byte_offset; - duk_uint_t i; - duk_bool_t use_cache; - duk_uint_fast32_t dist_start, dist_end, dist_sce; - duk_uint_fast32_t char_length; - const duk_uint8_t *p_start; - const duk_uint8_t *p_end; - const duk_uint8_t *p_found; - - /* - * For ASCII strings, the answer is simple. - */ - - if (DUK_LIKELY(DUK_HSTRING_IS_ASCII(h))) { - return char_offset; - } - - char_length = (duk_uint_fast32_t) DUK_HSTRING_GET_CHARLEN(h); - DUK_ASSERT(char_offset <= char_length); - - if (DUK_LIKELY(DUK_HSTRING_IS_ASCII(h))) { - /* Must recheck because the 'is ascii' flag may be set - * lazily. Alternatively, we could just compare charlen - * to bytelen. - */ - return char_offset; - } - - /* - * For non-ASCII strings, we need to scan forwards or backwards - * from some starting point. The starting point may be the start - * or end of the string, or some cached midpoint in the string - * cache. - * - * For "short" strings we simply scan without checking or updating - * the cache. For longer strings we check and update the cache as - * necessary, inserting a new cache entry if none exists. - */ - - DUK_DDD(DUK_DDDPRINT("non-ascii string %p, char_offset=%ld, clen=%ld, blen=%ld", - (void *) h, - (long) char_offset, - (long) DUK_HSTRING_GET_CHARLEN(h), - (long) DUK_HSTRING_GET_BYTELEN(h))); - - heap = thr->heap; - sce = NULL; - use_cache = (char_length > DUK_HEAP_STRINGCACHE_NOCACHE_LIMIT); - - if (use_cache) { -#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2) - DUK_DDD(DUK_DDDPRINT("stringcache before char2byte (using cache):")); - for (i = 0; i < DUK_HEAP_STRCACHE_SIZE; i++) { - duk_strcache_entry *c = heap->strcache + i; - DUK_DDD(DUK_DDDPRINT(" [%ld] -> h=%p, cidx=%ld, bidx=%ld", - (long) i, - (void *) c->h, - (long) c->cidx, - (long) c->bidx)); - } -#endif - - for (i = 0; i < DUK_HEAP_STRCACHE_SIZE; i++) { - duk_strcache_entry *c = heap->strcache + i; - - if (c->h == h) { - sce = c; - break; - } - } - } - - /* - * Scan from shortest distance: - * - start of string - * - end of string - * - cache entry (if exists) - */ - - DUK_ASSERT(DUK_HSTRING_GET_CHARLEN(h) >= char_offset); - dist_start = char_offset; - dist_end = char_length - char_offset; - dist_sce = 0; - DUK_UNREF(dist_sce); /* initialize for debug prints, needed if sce==NULL */ - - p_start = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h); - p_end = (const duk_uint8_t *) (p_start + DUK_HSTRING_GET_BYTELEN(h)); - p_found = NULL; - - if (sce) { - if (char_offset >= sce->cidx) { - dist_sce = char_offset - sce->cidx; - if ((dist_sce <= dist_start) && (dist_sce <= dist_end)) { - DUK_DDD(DUK_DDDPRINT("non-ascii string, use_cache=%ld, sce=%p:%ld:%ld, " - "dist_start=%ld, dist_end=%ld, dist_sce=%ld => " - "scan forwards from sce", - (long) use_cache, - (void *) (sce ? sce->h : NULL), - (sce ? (long) sce->cidx : (long) -1), - (sce ? (long) sce->bidx : (long) -1), - (long) dist_start, - (long) dist_end, - (long) dist_sce)); - - p_found = duk__scan_forwards(p_start + sce->bidx, p_end, dist_sce); - goto scan_done; - } - } else { - dist_sce = sce->cidx - char_offset; - if ((dist_sce <= dist_start) && (dist_sce <= dist_end)) { - DUK_DDD(DUK_DDDPRINT("non-ascii string, use_cache=%ld, sce=%p:%ld:%ld, " - "dist_start=%ld, dist_end=%ld, dist_sce=%ld => " - "scan backwards from sce", - (long) use_cache, - (void *) (sce ? sce->h : NULL), - (sce ? (long) sce->cidx : (long) -1), - (sce ? (long) sce->bidx : (long) -1), - (long) dist_start, - (long) dist_end, - (long) dist_sce)); - - p_found = duk__scan_backwards(p_start + sce->bidx, p_start, dist_sce); - goto scan_done; - } - } - } - - /* no sce, or sce scan not best */ - - if (dist_start <= dist_end) { - DUK_DDD(DUK_DDDPRINT("non-ascii string, use_cache=%ld, sce=%p:%ld:%ld, " - "dist_start=%ld, dist_end=%ld, dist_sce=%ld => " - "scan forwards from string start", - (long) use_cache, - (void *) (sce ? sce->h : NULL), - (sce ? (long) sce->cidx : (long) -1), - (sce ? (long) sce->bidx : (long) -1), - (long) dist_start, - (long) dist_end, - (long) dist_sce)); - - p_found = duk__scan_forwards(p_start, p_end, dist_start); - } else { - DUK_DDD(DUK_DDDPRINT("non-ascii string, use_cache=%ld, sce=%p:%ld:%ld, " - "dist_start=%ld, dist_end=%ld, dist_sce=%ld => " - "scan backwards from string end", - (long) use_cache, - (void *) (sce ? sce->h : NULL), - (sce ? (long) sce->cidx : (long) -1), - (sce ? (long) sce->bidx : (long) -1), - (long) dist_start, - (long) dist_end, - (long) dist_sce)); - - p_found = duk__scan_backwards(p_end, p_start, dist_end); - } - -scan_done: - - if (DUK_UNLIKELY(p_found == NULL)) { - /* Scan error: this shouldn't normally happen; it could happen if - * string is not valid UTF-8 data, and clen/blen are not consistent - * with the scanning algorithm. - */ - goto scan_error; - } - - DUK_ASSERT(p_found >= p_start); - DUK_ASSERT(p_found <= p_end); /* may be equal */ - byte_offset = (duk_uint32_t) (p_found - p_start); - - DUK_DDD(DUK_DDDPRINT("-> string %p, cidx %ld -> bidx %ld", (void *) h, (long) char_offset, (long) byte_offset)); - - /* - * Update cache entry (allocating if necessary), and move the - * cache entry to the first place (in an "LRU" policy). - */ - - if (use_cache) { - /* update entry, allocating if necessary */ - if (!sce) { - sce = heap->strcache + DUK_HEAP_STRCACHE_SIZE - 1; /* take last entry */ - sce->h = h; - } - DUK_ASSERT(sce != NULL); - sce->bidx = (duk_uint32_t) (p_found - p_start); - sce->cidx = (duk_uint32_t) char_offset; - - /* LRU: move our entry to first */ - if (sce > &heap->strcache[0]) { - /* - * A C - * B A - * C <- sce ==> B - * D D - */ - duk_strcache_entry tmp; - - tmp = *sce; - duk_memmove((void *) (&heap->strcache[1]), - (const void *) (&heap->strcache[0]), - (size_t) (((char *) sce) - ((char *) &heap->strcache[0]))); - heap->strcache[0] = tmp; - - /* 'sce' points to the wrong entry here, but is no longer used */ - } -#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2) - DUK_DDD(DUK_DDDPRINT("stringcache after char2byte (using cache):")); - for (i = 0; i < DUK_HEAP_STRCACHE_SIZE; i++) { - duk_strcache_entry *c = heap->strcache + i; - DUK_DDD(DUK_DDDPRINT(" [%ld] -> h=%p, cidx=%ld, bidx=%ld", - (long) i, - (void *) c->h, - (long) c->cidx, - (long) c->bidx)); - } -#endif - } - - return byte_offset; - -scan_error: - DUK_ERROR_INTERNAL(thr); - DUK_WO_NORETURN(return 0;); -} -/* - * Heap string table handling, string interning. - */ - -/* #include duk_internal.h -> already included */ - -/* Resize checks not needed if minsize == maxsize, typical for low memory - * targets. - */ -#define DUK__STRTAB_RESIZE_CHECK -#if (DUK_USE_STRTAB_MINSIZE == DUK_USE_STRTAB_MAXSIZE) -#undef DUK__STRTAB_RESIZE_CHECK -#endif - -#if defined(DUK_USE_STRTAB_PTRCOMP) -#define DUK__HEAPPTR_ENC16(heap, ptr) DUK_USE_HEAPPTR_ENC16((heap)->heap_udata, (ptr)) -#define DUK__HEAPPTR_DEC16(heap, val) DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, (val)) -#define DUK__GET_STRTABLE(heap) ((heap)->strtable16) -#else -#define DUK__HEAPPTR_ENC16(heap, ptr) (ptr) -#define DUK__HEAPPTR_DEC16(heap, val) (val) -#define DUK__GET_STRTABLE(heap) ((heap)->strtable) -#endif - -#define DUK__STRTAB_U32_MAX_STRLEN 10 /* 4'294'967'295 */ - -/* - * Debug dump stringtable. - */ - -#if defined(DUK_USE_DEBUG) -DUK_INTERNAL void duk_heap_strtable_dump(duk_heap *heap) { -#if defined(DUK_USE_STRTAB_PTRCOMP) - duk_uint16_t *strtable; -#else - duk_hstring **strtable; -#endif - duk_uint32_t i; - duk_hstring *h; - duk_size_t count_total = 0; - duk_size_t count_chain; - duk_size_t count_chain_min = DUK_SIZE_MAX; - duk_size_t count_chain_max = 0; - duk_size_t count_len[8]; /* chain lengths from 0 to 7 */ - - if (heap == NULL) { - DUK_D(DUK_DPRINT("string table, heap=NULL")); - return; - } - - strtable = DUK__GET_STRTABLE(heap); - if (strtable == NULL) { - DUK_D(DUK_DPRINT("string table, strtab=NULL")); - return; - } - - duk_memzero((void *) count_len, sizeof(count_len)); - for (i = 0; i < heap->st_size; i++) { - h = DUK__HEAPPTR_DEC16(heap, strtable[i]); - count_chain = 0; - while (h != NULL) { - count_chain++; - h = h->hdr.h_next; - } - if (count_chain < sizeof(count_len) / sizeof(duk_size_t)) { - count_len[count_chain]++; - } - count_chain_max = (count_chain > count_chain_max ? count_chain : count_chain_max); - count_chain_min = (count_chain < count_chain_min ? count_chain : count_chain_min); - count_total += count_chain; - } - - DUK_D(DUK_DPRINT("string table, strtab=%p, count=%lu, chain min=%lu max=%lu avg=%lf: " - "counts: %lu %lu %lu %lu %lu %lu %lu %lu ...", - (void *) heap->strtable, - (unsigned long) count_total, - (unsigned long) count_chain_min, - (unsigned long) count_chain_max, - (double) count_total / (double) heap->st_size, - (unsigned long) count_len[0], - (unsigned long) count_len[1], - (unsigned long) count_len[2], - (unsigned long) count_len[3], - (unsigned long) count_len[4], - (unsigned long) count_len[5], - (unsigned long) count_len[6], - (unsigned long) count_len[7])); -} -#endif /* DUK_USE_DEBUG */ - -/* - * Assertion helper to ensure strtable is populated correctly. - */ - -#if defined(DUK_USE_ASSERTIONS) -DUK_LOCAL void duk__strtable_assert_checks(duk_heap *heap) { -#if defined(DUK_USE_STRTAB_PTRCOMP) - duk_uint16_t *strtable; -#else - duk_hstring **strtable; -#endif - duk_uint32_t i; - duk_hstring *h; - duk_size_t count = 0; - - DUK_ASSERT(heap != NULL); - - strtable = DUK__GET_STRTABLE(heap); - if (strtable != NULL) { - DUK_ASSERT(heap->st_size != 0); - DUK_ASSERT(heap->st_mask == heap->st_size - 1); - - for (i = 0; i < heap->st_size; i++) { - h = DUK__HEAPPTR_DEC16(heap, strtable[i]); - while (h != NULL) { - DUK_ASSERT((DUK_HSTRING_GET_HASH(h) & heap->st_mask) == i); - count++; - h = h->hdr.h_next; - } - } - } else { - DUK_ASSERT(heap->st_size == 0); - DUK_ASSERT(heap->st_mask == 0); - } - -#if defined(DUK__STRTAB_RESIZE_CHECK) - DUK_ASSERT(count == (duk_size_t) heap->st_count); -#endif -} -#endif /* DUK_USE_ASSERTIONS */ - -/* - * Allocate and initialize a duk_hstring. - * - * Returns a NULL if allocation or initialization fails for some reason. - * - * The string won't be inserted into the string table and isn't tracked in - * any way (link pointers will be NULL). The caller must place the string - * into the string table without any risk of a longjmp, otherwise the string - * is leaked. - */ - -DUK_LOCAL duk_hstring *duk__strtable_alloc_hstring(duk_heap *heap, - const duk_uint8_t *str, - duk_uint32_t blen, - duk_uint32_t strhash, - const duk_uint8_t *extdata) { - duk_hstring *res; - const duk_uint8_t *data; -#if !defined(DUK_USE_HSTRING_ARRIDX) - duk_uarridx_t dummy; -#endif - - DUK_ASSERT(heap != NULL); - DUK_UNREF(extdata); - -#if defined(DUK_USE_STRLEN16) - /* If blen <= 0xffffUL, clen is also guaranteed to be <= 0xffffUL. */ - if (blen > 0xffffUL) { - DUK_D(DUK_DPRINT("16-bit string blen/clen active and blen over 16 bits, reject intern")); - goto alloc_error; - } -#endif - - /* XXX: Memzeroing the allocated structure is not really necessary - * because we could just initialize all fields explicitly (almost - * all fields are initialized explicitly anyway). - */ -#if defined(DUK_USE_HSTRING_EXTDATA) && defined(DUK_USE_EXTSTR_INTERN_CHECK) - if (extdata) { - res = (duk_hstring *) DUK_ALLOC(heap, sizeof(duk_hstring_external)); - if (DUK_UNLIKELY(res == NULL)) { - goto alloc_error; - } - duk_memzero(res, sizeof(duk_hstring_external)); -#if defined(DUK_USE_EXPLICIT_NULL_INIT) - DUK_HEAPHDR_STRING_INIT_NULLS(&res->hdr); -#endif - DUK_HEAPHDR_SET_TYPE_AND_FLAGS(&res->hdr, DUK_HTYPE_STRING, DUK_HSTRING_FLAG_EXTDATA); - - DUK_ASSERT(extdata[blen] == 0); /* Application responsibility. */ - data = extdata; - ((duk_hstring_external *) res)->extdata = extdata; - } else -#endif /* DUK_USE_HSTRING_EXTDATA && DUK_USE_EXTSTR_INTERN_CHECK */ - { - duk_uint8_t *data_tmp; - - /* NUL terminate for convenient C access */ - DUK_ASSERT(sizeof(duk_hstring) + blen + 1 > blen); /* No wrap, limits ensure. */ - res = (duk_hstring *) DUK_ALLOC(heap, sizeof(duk_hstring) + blen + 1); - if (DUK_UNLIKELY(res == NULL)) { - goto alloc_error; - } - duk_memzero(res, sizeof(duk_hstring)); -#if defined(DUK_USE_EXPLICIT_NULL_INIT) - DUK_HEAPHDR_STRING_INIT_NULLS(&res->hdr); -#endif - DUK_HEAPHDR_SET_TYPE_AND_FLAGS(&res->hdr, DUK_HTYPE_STRING, 0); - - data_tmp = (duk_uint8_t *) (res + 1); - duk_memcpy(data_tmp, str, blen); - data_tmp[blen] = (duk_uint8_t) 0; - data = (const duk_uint8_t *) data_tmp; - } - - DUK_HSTRING_SET_BYTELEN(res, blen); - DUK_HSTRING_SET_HASH(res, strhash); - - DUK_ASSERT(!DUK_HSTRING_HAS_ARRIDX(res)); -#if defined(DUK_USE_HSTRING_ARRIDX) - res->arridx = duk_js_to_arrayindex_string(data, blen); - if (res->arridx != DUK_HSTRING_NO_ARRAY_INDEX) { -#else - dummy = duk_js_to_arrayindex_string(data, blen); - if (dummy != DUK_HSTRING_NO_ARRAY_INDEX) { -#endif - /* Array index strings cannot be symbol strings, - * and they're always pure ASCII so blen == clen. - */ - DUK_HSTRING_SET_ARRIDX(res); - DUK_HSTRING_SET_ASCII(res); - DUK_ASSERT(duk_unicode_unvalidated_utf8_length(data, (duk_size_t) blen) == blen); - } else { - /* Because 'data' is NUL-terminated, we don't need a - * blen > 0 check here. For NUL (0x00) the symbol - * checks will be false. - */ - if (DUK_UNLIKELY(data[0] >= 0x80U)) { - if (data[0] <= 0x81) { - DUK_HSTRING_SET_SYMBOL(res); - } else if (data[0] == 0x82U || data[0] == 0xffU) { - DUK_HSTRING_SET_HIDDEN(res); - DUK_HSTRING_SET_SYMBOL(res); - } - } - - /* Using an explicit 'ASCII' flag has larger footprint (one call site - * only) but is quite useful for the case when there's no explicit - * 'clen' in duk_hstring. - * - * The flag is set lazily for RAM strings. - */ - DUK_ASSERT(!DUK_HSTRING_HAS_ASCII(res)); - -#if defined(DUK_USE_HSTRING_LAZY_CLEN) - /* Charlen initialized to 0, updated on-the-fly. */ -#else - duk_hstring_init_charlen(res); /* Also sets ASCII flag. */ -#endif - } - - DUK_DDD(DUK_DDDPRINT("interned string, hash=0x%08lx, blen=%ld, has_arridx=%ld, has_extdata=%ld", - (unsigned long) DUK_HSTRING_GET_HASH(res), - (long) DUK_HSTRING_GET_BYTELEN(res), - (long) (DUK_HSTRING_HAS_ARRIDX(res) ? 1 : 0), - (long) (DUK_HSTRING_HAS_EXTDATA(res) ? 1 : 0))); - - DUK_ASSERT(res != NULL); - return res; - -alloc_error: - return NULL; -} - -/* - * Grow strtable allocation in-place. - */ - -#if defined(DUK__STRTAB_RESIZE_CHECK) -DUK_LOCAL void duk__strtable_grow_inplace(duk_heap *heap) { - duk_uint32_t new_st_size; - duk_uint32_t old_st_size; - duk_uint32_t i; - duk_hstring *h; - duk_hstring *next; - duk_hstring *prev; -#if defined(DUK_USE_STRTAB_PTRCOMP) - duk_uint16_t *new_ptr; - duk_uint16_t *new_ptr_high; -#else - duk_hstring **new_ptr; - duk_hstring **new_ptr_high; -#endif - - DUK_DD(DUK_DDPRINT("grow in-place: %lu -> %lu", (unsigned long) heap->st_size, (unsigned long) heap->st_size * 2)); - - DUK_ASSERT(heap != NULL); - DUK_ASSERT(heap->st_resizing == 1); - DUK_ASSERT(heap->st_size >= 2); - DUK_ASSERT((heap->st_size & (heap->st_size - 1)) == 0); /* 2^N */ - DUK_ASSERT(DUK__GET_STRTABLE(heap) != NULL); - - DUK_STATS_INC(heap, stats_strtab_resize_grow); - - new_st_size = heap->st_size << 1U; - DUK_ASSERT(new_st_size > heap->st_size); /* No overflow. */ - - /* Reallocate the strtable first and then work in-place to rehash - * strings. We don't need an indirect allocation here: even if GC - * is triggered to satisfy the allocation, recursive strtable resize - * is prevented by flags. This is also why we don't need to use - * DUK_REALLOC_INDIRECT(). - */ - -#if defined(DUK_USE_STRTAB_PTRCOMP) - new_ptr = (duk_uint16_t *) DUK_REALLOC(heap, heap->strtable16, sizeof(duk_uint16_t) * new_st_size); -#else - new_ptr = (duk_hstring **) DUK_REALLOC(heap, heap->strtable, sizeof(duk_hstring *) * new_st_size); -#endif - if (DUK_UNLIKELY(new_ptr == NULL)) { - /* If realloc fails we can continue normally: the string table - * won't "fill up" although chains will gradually get longer. - * When string insertions continue, we'll quite soon try again - * with no special handling. - */ - DUK_D(DUK_DPRINT("string table grow failed, ignoring")); - return; - } -#if defined(DUK_USE_STRTAB_PTRCOMP) - heap->strtable16 = new_ptr; -#else - heap->strtable = new_ptr; -#endif - - /* Rehash a single bucket into two separate ones. When we grow - * by x2 the highest 'new' bit determines whether a string remains - * in its old position (bit is 0) or goes to a new one (bit is 1). - */ - - old_st_size = heap->st_size; - new_ptr_high = new_ptr + old_st_size; - for (i = 0; i < old_st_size; i++) { - duk_hstring *new_root; - duk_hstring *new_root_high; - - h = DUK__HEAPPTR_DEC16(heap, new_ptr[i]); - new_root = h; - new_root_high = NULL; - - prev = NULL; - while (h != NULL) { - duk_uint32_t mask; - - DUK_ASSERT((DUK_HSTRING_GET_HASH(h) & heap->st_mask) == i); - next = h->hdr.h_next; - - /* Example: if previous size was 256, previous mask is 0xFF - * and size is 0x100 which corresponds to the new bit that - * comes into play. - */ - DUK_ASSERT(heap->st_mask == old_st_size - 1); - mask = old_st_size; - if (DUK_HSTRING_GET_HASH(h) & mask) { - if (prev != NULL) { - prev->hdr.h_next = h->hdr.h_next; - } else { - DUK_ASSERT(h == new_root); - new_root = h->hdr.h_next; - } - - h->hdr.h_next = new_root_high; - new_root_high = h; - } else { - prev = h; - } - h = next; - } - - new_ptr[i] = DUK__HEAPPTR_ENC16(heap, new_root); - new_ptr_high[i] = DUK__HEAPPTR_ENC16(heap, new_root_high); - } - - heap->st_size = new_st_size; - heap->st_mask = new_st_size - 1; - -#if defined(DUK_USE_ASSERTIONS) - duk__strtable_assert_checks(heap); -#endif -} -#endif /* DUK__STRTAB_RESIZE_CHECK */ - -/* - * Shrink strtable allocation in-place. - */ - -#if defined(DUK__STRTAB_RESIZE_CHECK) -DUK_LOCAL void duk__strtable_shrink_inplace(duk_heap *heap) { - duk_uint32_t new_st_size; - duk_uint32_t i; - duk_hstring *h; - duk_hstring *other; - duk_hstring *root; -#if defined(DUK_USE_STRTAB_PTRCOMP) - duk_uint16_t *old_ptr; - duk_uint16_t *old_ptr_high; - duk_uint16_t *new_ptr; -#else - duk_hstring **old_ptr; - duk_hstring **old_ptr_high; - duk_hstring **new_ptr; -#endif - - DUK_DD(DUK_DDPRINT("shrink in-place: %lu -> %lu", (unsigned long) heap->st_size, (unsigned long) heap->st_size / 2)); - - DUK_ASSERT(heap != NULL); - DUK_ASSERT(heap->st_resizing == 1); - DUK_ASSERT(heap->st_size >= 2); - DUK_ASSERT((heap->st_size & (heap->st_size - 1)) == 0); /* 2^N */ - DUK_ASSERT(DUK__GET_STRTABLE(heap) != NULL); - - DUK_STATS_INC(heap, stats_strtab_resize_shrink); - - new_st_size = heap->st_size >> 1U; - - /* Combine two buckets into a single one. When we shrink, one hash - * bit (highest) disappears. - */ - old_ptr = DUK__GET_STRTABLE(heap); - old_ptr_high = old_ptr + new_st_size; - for (i = 0; i < new_st_size; i++) { - h = DUK__HEAPPTR_DEC16(heap, old_ptr[i]); - other = DUK__HEAPPTR_DEC16(heap, old_ptr_high[i]); - - if (h == NULL) { - /* First chain is empty, so use second one as is. */ - root = other; - } else { - /* Find end of first chain, and link in the second. */ - root = h; - while (h->hdr.h_next != NULL) { - h = h->hdr.h_next; - } - h->hdr.h_next = other; - } - - old_ptr[i] = DUK__HEAPPTR_ENC16(heap, root); - } - - heap->st_size = new_st_size; - heap->st_mask = new_st_size - 1; - - /* The strtable is now consistent and we can realloc safely. Even - * if side effects cause string interning or removal the strtable - * updates are safe. Recursive resize has been prevented by caller. - * This is also why we don't need to use DUK_REALLOC_INDIRECT(). - * - * We assume a realloc() to a smaller size is guaranteed to succeed. - * It would be relatively straightforward to handle the error by - * essentially performing a "grow" step to recover. - */ - -#if defined(DUK_USE_STRTAB_PTRCOMP) - new_ptr = (duk_uint16_t *) DUK_REALLOC(heap, heap->strtable16, sizeof(duk_uint16_t) * new_st_size); - DUK_ASSERT(new_ptr != NULL); - heap->strtable16 = new_ptr; -#else - new_ptr = (duk_hstring **) DUK_REALLOC(heap, heap->strtable, sizeof(duk_hstring *) * new_st_size); - DUK_ASSERT(new_ptr != NULL); - heap->strtable = new_ptr; -#endif - -#if defined(DUK_USE_ASSERTIONS) - duk__strtable_assert_checks(heap); -#endif -} -#endif /* DUK__STRTAB_RESIZE_CHECK */ - -/* - * Grow/shrink check. - */ - -#if defined(DUK__STRTAB_RESIZE_CHECK) -DUK_LOCAL DUK_COLD DUK_NOINLINE void duk__strtable_resize_check(duk_heap *heap) { - duk_uint32_t load_factor; /* fixed point */ - - DUK_ASSERT(heap != NULL); -#if defined(DUK_USE_STRTAB_PTRCOMP) - DUK_ASSERT(heap->strtable16 != NULL); -#else - DUK_ASSERT(heap->strtable != NULL); -#endif - - DUK_STATS_INC(heap, stats_strtab_resize_check); - - /* Prevent recursive resizing. */ - if (DUK_UNLIKELY(heap->st_resizing != 0U)) { - DUK_D(DUK_DPRINT("prevent recursive strtable resize")); - return; - } - - heap->st_resizing = 1; - - DUK_ASSERT(heap->st_size >= 16U); - DUK_ASSERT((heap->st_size >> 4U) >= 1); - load_factor = heap->st_count / (heap->st_size >> 4U); - - DUK_DD(DUK_DDPRINT("resize check string table: size=%lu, count=%lu, load_factor=%lu (fixed point .4; float %lf)", - (unsigned long) heap->st_size, - (unsigned long) heap->st_count, - (unsigned long) load_factor, - (double) heap->st_count / (double) heap->st_size)); - - if (load_factor >= DUK_USE_STRTAB_GROW_LIMIT) { - if (heap->st_size >= DUK_USE_STRTAB_MAXSIZE) { - DUK_DD(DUK_DDPRINT("want to grow strtable (based on load factor) but already maximum size")); - } else { - DUK_D(DUK_DPRINT("grow string table: %lu -> %lu", - (unsigned long) heap->st_size, - (unsigned long) heap->st_size * 2)); -#if defined(DUK_USE_DEBUG) - duk_heap_strtable_dump(heap); -#endif - duk__strtable_grow_inplace(heap); - } - } else if (load_factor <= DUK_USE_STRTAB_SHRINK_LIMIT) { - if (heap->st_size <= DUK_USE_STRTAB_MINSIZE) { - DUK_DD(DUK_DDPRINT("want to shrink strtable (based on load factor) but already minimum size")); - } else { - DUK_D(DUK_DPRINT("shrink string table: %lu -> %lu", - (unsigned long) heap->st_size, - (unsigned long) heap->st_size / 2)); -#if defined(DUK_USE_DEBUG) - duk_heap_strtable_dump(heap); -#endif - duk__strtable_shrink_inplace(heap); - } - } else { - DUK_DD(DUK_DDPRINT("no need for strtable resize")); - } - - heap->st_resizing = 0; -} -#endif /* DUK__STRTAB_RESIZE_CHECK */ - -/* - * Torture grow/shrink: unconditionally grow and shrink back. - */ - -#if defined(DUK_USE_STRTAB_TORTURE) && defined(DUK__STRTAB_RESIZE_CHECK) -DUK_LOCAL void duk__strtable_resize_torture(duk_heap *heap) { - duk_uint32_t old_st_size; - - DUK_ASSERT(heap != NULL); - - old_st_size = heap->st_size; - if (old_st_size >= DUK_USE_STRTAB_MAXSIZE) { - return; - } - - heap->st_resizing = 1; - duk__strtable_grow_inplace(heap); - if (heap->st_size > old_st_size) { - duk__strtable_shrink_inplace(heap); - } - heap->st_resizing = 0; -} -#endif /* DUK_USE_STRTAB_TORTURE && DUK__STRTAB_RESIZE_CHECK */ - -/* - * Raw intern; string already checked not to be present. - */ - -DUK_LOCAL duk_hstring *duk__strtable_do_intern(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) { - duk_hstring *res; - const duk_uint8_t *extdata; -#if defined(DUK_USE_STRTAB_PTRCOMP) - duk_uint16_t *slot; -#else - duk_hstring **slot; -#endif - - DUK_DDD(DUK_DDDPRINT("do_intern: heap=%p, str=%p, blen=%lu, strhash=%lx, st_size=%lu, st_count=%lu, load=%lf", - (void *) heap, - (const void *) str, - (unsigned long) blen, - (unsigned long) strhash, - (unsigned long) heap->st_size, - (unsigned long) heap->st_count, - (double) heap->st_count / (double) heap->st_size)); - - DUK_ASSERT(heap != NULL); - - /* Prevent any side effects on the string table and the caller provided - * str/blen arguments while interning is in progress. For example, if - * the caller provided str/blen from a dynamic buffer, a finalizer - * might resize or modify that dynamic buffer, invalidating the call - * arguments. - * - * While finalizers must be prevented, mark-and-sweep itself is fine. - * Recursive string table resize is prevented explicitly here. - */ - - heap->pf_prevent_count++; - DUK_ASSERT(heap->pf_prevent_count != 0); /* Wrap. */ - -#if defined(DUK_USE_STRTAB_TORTURE) && defined(DUK__STRTAB_RESIZE_CHECK) - duk__strtable_resize_torture(heap); -#endif - - /* String table grow/shrink check. Because of chaining (and no - * accumulation issues as with hash probe chains and DELETED - * markers) there's never a mandatory need to resize right now. - * Check for the resize only periodically, based on st_count - * bit pattern. Because string table removal doesn't do a shrink - * check, we do that also here. - * - * Do the resize and possible grow/shrink before the new duk_hstring - * has been allocated. Otherwise we may trigger a GC when the result - * duk_hstring is not yet strongly referenced. - */ - -#if defined(DUK__STRTAB_RESIZE_CHECK) - if (DUK_UNLIKELY((heap->st_count & DUK_USE_STRTAB_RESIZE_CHECK_MASK) == 0)) { - duk__strtable_resize_check(heap); - } -#endif - - /* External string check (low memory optimization). */ - -#if defined(DUK_USE_HSTRING_EXTDATA) && defined(DUK_USE_EXTSTR_INTERN_CHECK) - extdata = - (const duk_uint8_t *) DUK_USE_EXTSTR_INTERN_CHECK(heap->heap_udata, (void *) DUK_LOSE_CONST(str), (duk_size_t) blen); -#else - extdata = (const duk_uint8_t *) NULL; -#endif - - /* Allocate and initialize string, not yet linked. This may cause a - * GC which may cause other strings to be interned and inserted into - * the string table before we insert our string. Finalizer execution - * is disabled intentionally to avoid a finalizer from e.g. resizing - * a buffer used as a data area for 'str'. - */ - - res = duk__strtable_alloc_hstring(heap, str, blen, strhash, extdata); - - /* Allow side effects again: GC must be avoided until duk_hstring - * result (if successful) has been INCREF'd. - */ - DUK_ASSERT(heap->pf_prevent_count > 0); - heap->pf_prevent_count--; - - /* Alloc error handling. */ - - if (DUK_UNLIKELY(res == NULL)) { -#if defined(DUK_USE_HSTRING_EXTDATA) && defined(DUK_USE_EXTSTR_INTERN_CHECK) - if (extdata != NULL) { - DUK_USE_EXTSTR_FREE(heap->heap_udata, (const void *) extdata); - } -#endif - return NULL; - } - - /* Insert into string table. */ - -#if defined(DUK_USE_STRTAB_PTRCOMP) - slot = heap->strtable16 + (strhash & heap->st_mask); -#else - slot = heap->strtable + (strhash & heap->st_mask); -#endif - DUK_ASSERT(res->hdr.h_next == NULL); /* This is the case now, but unnecessary zeroing/NULLing. */ - res->hdr.h_next = DUK__HEAPPTR_DEC16(heap, *slot); - *slot = DUK__HEAPPTR_ENC16(heap, res); - - /* Update string count only for successful inserts. */ - -#if defined(DUK__STRTAB_RESIZE_CHECK) - heap->st_count++; -#endif - - /* The duk_hstring is in the string table but is not yet strongly - * reachable. Calling code MUST NOT make any allocations or other - * side effects before the duk_hstring has been INCREF'd and made - * reachable. - */ - - return res; -} - -/* - * Intern a string from str/blen, returning either an existing duk_hstring - * or adding a new one into the string table. The input string does -not- - * need to be NUL terminated. - * - * The input 'str' argument may point to a Duktape managed data area such as - * the data area of a dynamic buffer. It's crucial to avoid any side effects - * that might affect the data area (e.g. resize the dynamic buffer, or write - * to the buffer) before the string is fully interned. - */ - -#if defined(DUK_USE_ROM_STRINGS) -DUK_LOCAL duk_hstring *duk__strtab_romstring_lookup(duk_heap *heap, const duk_uint8_t *str, duk_size_t blen, duk_uint32_t strhash) { - duk_size_t lookup_hash; - duk_hstring *curr; - - DUK_ASSERT(heap != NULL); - DUK_UNREF(heap); - - lookup_hash = (blen << 4); - if (blen > 0) { - lookup_hash += str[0]; - } - lookup_hash &= 0xff; - - curr = (duk_hstring *) DUK_LOSE_CONST(duk_rom_strings_lookup[lookup_hash]); - while (curr != NULL) { - /* Unsafe memcmp() because for zero blen, str may be NULL. */ - if (strhash == DUK_HSTRING_GET_HASH(curr) && blen == DUK_HSTRING_GET_BYTELEN(curr) && - duk_memcmp_unsafe((const void *) str, (const void *) DUK_HSTRING_GET_DATA(curr), blen) == 0) { - DUK_DDD(DUK_DDDPRINT("intern check: rom string: %!O, computed hash 0x%08lx, rom hash 0x%08lx", - curr, - (unsigned long) strhash, - (unsigned long) DUK_HSTRING_GET_HASH(curr))); - return curr; - } - curr = curr->hdr.h_next; - } - - return NULL; -} -#endif /* DUK_USE_ROM_STRINGS */ - -DUK_INTERNAL duk_hstring *duk_heap_strtable_intern(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen) { - duk_uint32_t strhash; - duk_hstring *h; - - DUK_DDD(DUK_DDDPRINT("intern check: heap=%p, str=%p, blen=%lu", (void *) heap, (const void *) str, (unsigned long) blen)); - - /* Preliminaries. */ - - /* XXX: maybe just require 'str != NULL' even for zero size? */ - DUK_ASSERT(heap != NULL); - DUK_ASSERT(blen == 0 || str != NULL); - DUK_ASSERT(blen <= DUK_HSTRING_MAX_BYTELEN); /* Caller is responsible for ensuring this. */ - strhash = duk_heap_hashstring(heap, str, (duk_size_t) blen); - - /* String table lookup. */ - - DUK_ASSERT(DUK__GET_STRTABLE(heap) != NULL); - DUK_ASSERT(heap->st_size > 0); - DUK_ASSERT(heap->st_size == heap->st_mask + 1); -#if defined(DUK_USE_STRTAB_PTRCOMP) - h = DUK__HEAPPTR_DEC16(heap, heap->strtable16[strhash & heap->st_mask]); -#else - h = heap->strtable[strhash & heap->st_mask]; -#endif - while (h != NULL) { - if (DUK_HSTRING_GET_HASH(h) == strhash && DUK_HSTRING_GET_BYTELEN(h) == blen && - duk_memcmp_unsafe((const void *) str, (const void *) DUK_HSTRING_GET_DATA(h), (size_t) blen) == 0) { - /* Found existing entry. */ - DUK_STATS_INC(heap, stats_strtab_intern_hit); - return h; - } - h = h->hdr.h_next; - } - - /* ROM table lookup. Because this lookup is slower, do it only after - * RAM lookup. This works because no ROM string is ever interned into - * the RAM string table. - */ - -#if defined(DUK_USE_ROM_STRINGS) - h = duk__strtab_romstring_lookup(heap, str, blen, strhash); - if (h != NULL) { - DUK_STATS_INC(heap, stats_strtab_intern_hit); - return h; - } -#endif - - /* Not found in string table; insert. */ - - DUK_STATS_INC(heap, stats_strtab_intern_miss); - h = duk__strtable_do_intern(heap, str, blen, strhash); - return h; /* may be NULL */ -} - -/* - * Intern a string from u32. - */ - -/* XXX: Could arrange some special handling because we know that the result - * will have an arridx flag and an ASCII flag, won't need a clen check, etc. - */ - -DUK_INTERNAL duk_hstring *duk_heap_strtable_intern_u32(duk_heap *heap, duk_uint32_t val) { - duk_uint8_t buf[DUK__STRTAB_U32_MAX_STRLEN]; - duk_uint8_t *p; - - DUK_ASSERT(heap != NULL); - - /* This is smaller and faster than a %lu sprintf. */ - p = buf + sizeof(buf); - do { - p--; - *p = duk_lc_digits[val % 10]; - val = val / 10; - } while (val != 0); /* For val == 0, emit exactly one '0'. */ - DUK_ASSERT(p >= buf); - - return duk_heap_strtable_intern(heap, (const duk_uint8_t *) p, (duk_uint32_t) ((buf + sizeof(buf)) - p)); -} - -/* - * Checked convenience variants. - * - * XXX: Because the main use case is for the checked variants, make them the - * main functionality and provide a safe variant separately (it is only needed - * during heap init). The problem with that is that longjmp state and error - * creation must already be possible to throw. - */ - -DUK_INTERNAL duk_hstring *duk_heap_strtable_intern_checked(duk_hthread *thr, const duk_uint8_t *str, duk_uint32_t blen) { - duk_hstring *res; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - DUK_ASSERT(blen == 0 || str != NULL); - - res = duk_heap_strtable_intern(thr->heap, str, blen); - if (DUK_UNLIKELY(res == NULL)) { - DUK_ERROR_ALLOC_FAILED(thr); - DUK_WO_NORETURN(return NULL;); - } - return res; -} - -#if defined(DUK_USE_LITCACHE_SIZE) -DUK_LOCAL duk_uint_t duk__strtable_litcache_key(const duk_uint8_t *str, duk_uint32_t blen) { - duk_uintptr_t key; - - DUK_ASSERT(DUK_USE_LITCACHE_SIZE > 0); - DUK_ASSERT(DUK_IS_POWER_OF_TWO((duk_uint_t) DUK_USE_LITCACHE_SIZE)); - - key = (duk_uintptr_t) blen ^ (duk_uintptr_t) str; - key &= (duk_uintptr_t) (DUK_USE_LITCACHE_SIZE - 1); /* Assumes size is power of 2. */ - /* Due to masking, cast is in 32-bit range. */ - DUK_ASSERT(key <= DUK_UINT_MAX); - return (duk_uint_t) key; -} - -DUK_INTERNAL duk_hstring *duk_heap_strtable_intern_literal_checked(duk_hthread *thr, const duk_uint8_t *str, duk_uint32_t blen) { - duk_uint_t key; - duk_litcache_entry *ent; - duk_hstring *h; - - /* Fast path check: literal exists in literal cache. */ - key = duk__strtable_litcache_key(str, blen); - ent = thr->heap->litcache + key; - if (ent->addr == str) { - DUK_DD(DUK_DDPRINT("intern check for cached, pinned literal: str=%p, blen=%ld -> duk_hstring %!O", - (const void *) str, - (long) blen, - (duk_heaphdr *) ent->h)); - DUK_ASSERT(ent->h != NULL); - DUK_ASSERT(DUK_HSTRING_HAS_PINNED_LITERAL(ent->h)); - DUK_STATS_INC(thr->heap, stats_strtab_litcache_hit); - return ent->h; - } - - /* Intern and update (overwrite) cache entry. */ - h = duk_heap_strtable_intern_checked(thr, str, blen); - ent->addr = str; - ent->h = h; - DUK_STATS_INC(thr->heap, stats_strtab_litcache_miss); - - /* Pin the duk_hstring until the next mark-and-sweep. This means - * litcache entries don't need to be invalidated until the next - * mark-and-sweep as their target duk_hstring is not freed before - * the mark-and-sweep happens. The pin remains even if the literal - * cache entry is overwritten, and is still useful to avoid string - * table traffic. - */ - if (!DUK_HSTRING_HAS_PINNED_LITERAL(h)) { - DUK_DD(DUK_DDPRINT("pin duk_hstring because it is a literal: %!O", (duk_heaphdr *) h)); - DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) h)); - DUK_HSTRING_INCREF(thr, h); - DUK_HSTRING_SET_PINNED_LITERAL(h); - DUK_STATS_INC(thr->heap, stats_strtab_litcache_pin); - } - - return h; -} -#endif /* DUK_USE_LITCACHE_SIZE */ - -DUK_INTERNAL duk_hstring *duk_heap_strtable_intern_u32_checked(duk_hthread *thr, duk_uint32_t val) { - duk_hstring *res; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - - res = duk_heap_strtable_intern_u32(thr->heap, val); - if (DUK_UNLIKELY(res == NULL)) { - DUK_ERROR_ALLOC_FAILED(thr); - DUK_WO_NORETURN(return NULL;); - } - return res; -} - -/* - * Remove (unlink) a string from the string table. - * - * Just unlinks the duk_hstring, leaving link pointers as garbage. - * Caller must free the string itself. - */ - -#if defined(DUK_USE_REFERENCE_COUNTING) -/* Unlink without a 'prev' pointer. */ -DUK_INTERNAL void duk_heap_strtable_unlink(duk_heap *heap, duk_hstring *h) { -#if defined(DUK_USE_STRTAB_PTRCOMP) - duk_uint16_t *slot; -#else - duk_hstring **slot; -#endif - duk_hstring *other; - duk_hstring *prev; - - DUK_DDD(DUK_DDDPRINT("remove: heap=%p, h=%p, blen=%lu, strhash=%lx", - (void *) heap, - (void *) h, - (unsigned long) (h != NULL ? DUK_HSTRING_GET_BYTELEN(h) : 0), - (unsigned long) (h != NULL ? DUK_HSTRING_GET_HASH(h) : 0))); - - DUK_ASSERT(heap != NULL); - DUK_ASSERT(h != NULL); - -#if defined(DUK__STRTAB_RESIZE_CHECK) - DUK_ASSERT(heap->st_count > 0); - heap->st_count--; -#endif - -#if defined(DUK_USE_STRTAB_PTRCOMP) - slot = heap->strtable16 + (DUK_HSTRING_GET_HASH(h) & heap->st_mask); -#else - slot = heap->strtable + (DUK_HSTRING_GET_HASH(h) & heap->st_mask); -#endif - other = DUK__HEAPPTR_DEC16(heap, *slot); - DUK_ASSERT(other != NULL); /* At least argument string is in the chain. */ - - prev = NULL; - while (other != h) { - prev = other; - other = other->hdr.h_next; - DUK_ASSERT(other != NULL); /* We'll eventually find 'h'. */ - } - if (prev != NULL) { - /* Middle of list. */ - prev->hdr.h_next = h->hdr.h_next; - } else { - /* Head of list. */ - *slot = DUK__HEAPPTR_ENC16(heap, h->hdr.h_next); - } - - /* There's no resize check on a string free. The next string - * intern will do one. - */ -} -#endif /* DUK_USE_REFERENCE_COUNTING */ - -/* Unlink with a 'prev' pointer. */ -DUK_INTERNAL void duk_heap_strtable_unlink_prev(duk_heap *heap, duk_hstring *h, duk_hstring *prev) { -#if defined(DUK_USE_STRTAB_PTRCOMP) - duk_uint16_t *slot; -#else - duk_hstring **slot; -#endif - - DUK_DDD(DUK_DDDPRINT("remove: heap=%p, prev=%p, h=%p, blen=%lu, strhash=%lx", - (void *) heap, - (void *) prev, - (void *) h, - (unsigned long) (h != NULL ? DUK_HSTRING_GET_BYTELEN(h) : 0), - (unsigned long) (h != NULL ? DUK_HSTRING_GET_HASH(h) : 0))); - - DUK_ASSERT(heap != NULL); - DUK_ASSERT(h != NULL); - DUK_ASSERT(prev == NULL || prev->hdr.h_next == h); - -#if defined(DUK__STRTAB_RESIZE_CHECK) - DUK_ASSERT(heap->st_count > 0); - heap->st_count--; -#endif - - if (prev != NULL) { - /* Middle of list. */ - prev->hdr.h_next = h->hdr.h_next; - } else { - /* Head of list. */ -#if defined(DUK_USE_STRTAB_PTRCOMP) - slot = heap->strtable16 + (DUK_HSTRING_GET_HASH(h) & heap->st_mask); -#else - slot = heap->strtable + (DUK_HSTRING_GET_HASH(h) & heap->st_mask); -#endif - DUK_ASSERT(DUK__HEAPPTR_DEC16(heap, *slot) == h); - *slot = DUK__HEAPPTR_ENC16(heap, h->hdr.h_next); - } -} - -/* - * Force string table resize check in mark-and-sweep. - */ - -DUK_INTERNAL void duk_heap_strtable_force_resize(duk_heap *heap) { - /* Does only one grow/shrink step if needed. The heap->st_resizing - * flag protects against recursive resizing. - */ - - DUK_ASSERT(heap != NULL); - DUK_UNREF(heap); - -#if defined(DUK__STRTAB_RESIZE_CHECK) -#if defined(DUK_USE_STRTAB_PTRCOMP) - if (heap->strtable16 != NULL) { -#else - if (heap->strtable != NULL) { -#endif - duk__strtable_resize_check(heap); - } -#endif -} - -/* - * Free strings in the string table and the string table itself. - */ - -DUK_INTERNAL void duk_heap_strtable_free(duk_heap *heap) { -#if defined(DUK_USE_STRTAB_PTRCOMP) - duk_uint16_t *strtable; - duk_uint16_t *st; -#else - duk_hstring **strtable; - duk_hstring **st; -#endif - duk_hstring *h; - - DUK_ASSERT(heap != NULL); - -#if defined(DUK_USE_ASSERTIONS) - duk__strtable_assert_checks(heap); -#endif - - /* Strtable can be NULL if heap init fails. However, in that case - * heap->st_size is 0, so strtable == strtable_end and we skip the - * loop without a special check. - */ - strtable = DUK__GET_STRTABLE(heap); - st = strtable + heap->st_size; - DUK_ASSERT(strtable != NULL || heap->st_size == 0); - - while (strtable != st) { - --st; - h = DUK__HEAPPTR_DEC16(heap, *st); - while (h) { - duk_hstring *h_next; - h_next = h->hdr.h_next; - - /* Strings may have inner refs (extdata) in some cases. */ - duk_free_hstring(heap, h); - - h = h_next; - } - } - - DUK_FREE(heap, strtable); -} - -/* automatic undefs */ -#undef DUK__GET_STRTABLE -#undef DUK__HEAPPTR_DEC16 -#undef DUK__HEAPPTR_ENC16 -#undef DUK__STRTAB_U32_MAX_STRLEN -/* - * duk_heaphdr assertion helpers - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_ASSERTIONS) - -#if defined(DUK_USE_DOUBLE_LINKED_HEAP) -DUK_INTERNAL void duk_heaphdr_assert_links(duk_heap *heap, duk_heaphdr *h) { - DUK_UNREF(heap); - if (h != NULL) { - duk_heaphdr *h_prev, *h_next; - h_prev = DUK_HEAPHDR_GET_PREV(heap, h); - h_next = DUK_HEAPHDR_GET_NEXT(heap, h); - DUK_ASSERT(h_prev == NULL || (DUK_HEAPHDR_GET_NEXT(heap, h_prev) == h)); - DUK_ASSERT(h_next == NULL || (DUK_HEAPHDR_GET_PREV(heap, h_next) == h)); - } -} -#else -DUK_INTERNAL void duk_heaphdr_assert_links(duk_heap *heap, duk_heaphdr *h) { - DUK_UNREF(heap); - DUK_UNREF(h); -} -#endif - -DUK_INTERNAL void duk_heaphdr_assert_valid(duk_heaphdr *h) { - DUK_ASSERT(h != NULL); - DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h)); -} - -/* Assert validity of a heaphdr, including all subclasses. */ -DUK_INTERNAL void duk_heaphdr_assert_valid_subclassed(duk_heaphdr *h) { - switch (DUK_HEAPHDR_GET_TYPE(h)) { - case DUK_HTYPE_OBJECT: { - duk_hobject *h_obj = (duk_hobject *) h; - DUK_HOBJECT_ASSERT_VALID(h_obj); - if (DUK_HOBJECT_IS_COMPFUNC(h_obj)) { - DUK_HCOMPFUNC_ASSERT_VALID((duk_hcompfunc *) h_obj); - } else if (DUK_HOBJECT_IS_NATFUNC(h_obj)) { - DUK_HNATFUNC_ASSERT_VALID((duk_hnatfunc *) h_obj); - } else if (DUK_HOBJECT_IS_DECENV(h_obj)) { - DUK_HDECENV_ASSERT_VALID((duk_hdecenv *) h_obj); - } else if (DUK_HOBJECT_IS_OBJENV(h_obj)) { - DUK_HOBJENV_ASSERT_VALID((duk_hobjenv *) h_obj); - } else if (DUK_HOBJECT_IS_BUFOBJ(h_obj)) { -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) - DUK_HBUFOBJ_ASSERT_VALID((duk_hbufobj *) h_obj); -#endif - } else if (DUK_HOBJECT_IS_BOUNDFUNC(h_obj)) { - DUK_HBOUNDFUNC_ASSERT_VALID((duk_hboundfunc *) h_obj); - } else if (DUK_HOBJECT_IS_PROXY(h_obj)) { - DUK_HPROXY_ASSERT_VALID((duk_hproxy *) h_obj); - } else if (DUK_HOBJECT_IS_THREAD(h_obj)) { - DUK_HTHREAD_ASSERT_VALID((duk_hthread *) h_obj); - } else { - /* Just a plain object. */ - ; - } - break; - } - case DUK_HTYPE_STRING: { - duk_hstring *h_str = (duk_hstring *) h; - DUK_HSTRING_ASSERT_VALID(h_str); - break; - } - case DUK_HTYPE_BUFFER: { - duk_hbuffer *h_buf = (duk_hbuffer *) h; - DUK_HBUFFER_ASSERT_VALID(h_buf); - break; - } - default: { - DUK_ASSERT(0); - } - } -} - -#endif /* DUK_USE_ASSERTIONS */ -/* - * Hobject allocation. - * - * Provides primitive allocation functions for all object types (plain object, - * compiled function, native function, thread). The object return is not yet - * in "heap allocated" list and has a refcount of zero, so caller must careful. - */ - -/* XXX: In most cases there's no need for plain allocation without pushing - * to the value stack. Maybe rework contract? - */ - -/* #include duk_internal.h -> already included */ - -/* - * Helpers. - */ - -DUK_LOCAL void duk__init_object_parts(duk_heap *heap, duk_uint_t hobject_flags, duk_hobject *obj) { - DUK_ASSERT(obj != NULL); - /* Zeroed by caller. */ - - obj->hdr.h_flags = hobject_flags | DUK_HTYPE_OBJECT; - DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(&obj->hdr) == DUK_HTYPE_OBJECT); /* Assume zero shift. */ - -#if defined(DUK_USE_EXPLICIT_NULL_INIT) - DUK_HOBJECT_SET_PROTOTYPE(heap, obj, NULL); - DUK_HOBJECT_SET_PROPS(heap, obj, NULL); -#endif -#if defined(DUK_USE_HEAPPTR16) - /* Zero encoded pointer is required to match NULL. */ - DUK_HEAPHDR_SET_NEXT(heap, &obj->hdr, NULL); -#if defined(DUK_USE_DOUBLE_LINKED_HEAP) - DUK_HEAPHDR_SET_PREV(heap, &obj->hdr, NULL); -#endif -#endif - DUK_HEAPHDR_ASSERT_LINKS(heap, &obj->hdr); - DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap, &obj->hdr); - - /* obj->props is intentionally left as NULL, and duk_hobject_props.c must deal - * with this properly. This is intentional: empty objects consume a minimum - * amount of memory. Further, an initial allocation might fail and cause - * 'obj' to "leak" (require a mark-and-sweep) since it is not reachable yet. - */ -} - -DUK_LOCAL void *duk__hobject_alloc_init(duk_hthread *thr, duk_uint_t hobject_flags, duk_size_t size) { - void *res; - - res = (void *) DUK_ALLOC_CHECKED_ZEROED(thr, size); - DUK_ASSERT(res != NULL); - duk__init_object_parts(thr->heap, hobject_flags, (duk_hobject *) res); - return res; -} - -/* - * Allocate an duk_hobject. - * - * The allocated object has no allocation for properties; the caller may - * want to force a resize if a desired size is known. - * - * The allocated object has zero reference count and is not reachable. - * The caller MUST make the object reachable and increase its reference - * count before invoking any operation that might require memory allocation. - */ - -DUK_INTERNAL duk_hobject *duk_hobject_alloc_unchecked(duk_heap *heap, duk_uint_t hobject_flags) { - duk_hobject *res; - - DUK_ASSERT(heap != NULL); - - /* different memory layout, alloc size, and init */ - DUK_ASSERT((hobject_flags & DUK_HOBJECT_FLAG_COMPFUNC) == 0); - DUK_ASSERT((hobject_flags & DUK_HOBJECT_FLAG_NATFUNC) == 0); - DUK_ASSERT((hobject_flags & DUK_HOBJECT_FLAG_BOUNDFUNC) == 0); - - res = (duk_hobject *) DUK_ALLOC_ZEROED(heap, sizeof(duk_hobject)); - if (DUK_UNLIKELY(res == NULL)) { - return NULL; - } - DUK_ASSERT(!DUK_HOBJECT_IS_THREAD(res)); - - duk__init_object_parts(heap, hobject_flags, res); - - DUK_ASSERT(!DUK_HOBJECT_IS_THREAD(res)); - return res; -} - -DUK_INTERNAL duk_hobject *duk_hobject_alloc(duk_hthread *thr, duk_uint_t hobject_flags) { - duk_hobject *res; - - res = (duk_hobject *) duk__hobject_alloc_init(thr, hobject_flags, sizeof(duk_hobject)); - return res; -} - -DUK_INTERNAL duk_hcompfunc *duk_hcompfunc_alloc(duk_hthread *thr, duk_uint_t hobject_flags) { - duk_hcompfunc *res; - - res = (duk_hcompfunc *) duk__hobject_alloc_init(thr, hobject_flags, sizeof(duk_hcompfunc)); -#if defined(DUK_USE_EXPLICIT_NULL_INIT) -#if defined(DUK_USE_HEAPPTR16) - /* NULL pointer is required to encode to zero, so memset is enough. */ -#else - res->data = NULL; - res->funcs = NULL; - res->bytecode = NULL; -#endif - res->lex_env = NULL; - res->var_env = NULL; -#endif - - return res; -} - -DUK_INTERNAL duk_hnatfunc *duk_hnatfunc_alloc(duk_hthread *thr, duk_uint_t hobject_flags) { - duk_hnatfunc *res; - - res = (duk_hnatfunc *) duk__hobject_alloc_init(thr, hobject_flags, sizeof(duk_hnatfunc)); -#if defined(DUK_USE_EXPLICIT_NULL_INIT) - res->func = NULL; -#endif - - return res; -} - -DUK_INTERNAL duk_hboundfunc *duk_hboundfunc_alloc(duk_heap *heap, duk_uint_t hobject_flags) { - duk_hboundfunc *res; - - res = (duk_hboundfunc *) DUK_ALLOC(heap, sizeof(duk_hboundfunc)); - if (!res) { - return NULL; - } - duk_memzero(res, sizeof(duk_hboundfunc)); - - duk__init_object_parts(heap, hobject_flags, &res->obj); - - DUK_TVAL_SET_UNDEFINED(&res->target); - DUK_TVAL_SET_UNDEFINED(&res->this_binding); - -#if defined(DUK_USE_EXPLICIT_NULL_INIT) - res->args = NULL; -#endif - - return res; -} - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_hbufobj *duk_hbufobj_alloc(duk_hthread *thr, duk_uint_t hobject_flags) { - duk_hbufobj *res; - - res = (duk_hbufobj *) duk__hobject_alloc_init(thr, hobject_flags, sizeof(duk_hbufobj)); -#if defined(DUK_USE_EXPLICIT_NULL_INIT) - res->buf = NULL; - res->buf_prop = NULL; -#endif - - DUK_HBUFOBJ_ASSERT_VALID(res); - return res; -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -/* Allocate a new thread. - * - * Leaves the built-ins array uninitialized. The caller must either - * initialize a new global context or share existing built-ins from - * another thread. - */ -DUK_INTERNAL duk_hthread *duk_hthread_alloc_unchecked(duk_heap *heap, duk_uint_t hobject_flags) { - duk_hthread *res; - - res = (duk_hthread *) DUK_ALLOC(heap, sizeof(duk_hthread)); - if (DUK_UNLIKELY(res == NULL)) { - return NULL; - } - duk_memzero(res, sizeof(duk_hthread)); - - duk__init_object_parts(heap, hobject_flags, &res->obj); - -#if defined(DUK_USE_EXPLICIT_NULL_INIT) - res->ptr_curr_pc = NULL; - res->heap = NULL; - res->valstack = NULL; - res->valstack_end = NULL; - res->valstack_alloc_end = NULL; - res->valstack_bottom = NULL; - res->valstack_top = NULL; - res->callstack_curr = NULL; - res->resumer = NULL; - res->compile_ctx = NULL, -#if defined(DUK_USE_HEAPPTR16) - res->strs16 = NULL; -#else - res->strs = NULL; -#endif - { - duk_small_uint_t i; - for (i = 0; i < DUK_NUM_BUILTINS; i++) { - res->builtins[i] = NULL; - } - } -#endif - /* When nothing is running, API calls are in non-strict mode. */ - DUK_ASSERT(res->strict == 0); - - res->heap = heap; - - /* XXX: Any reason not to merge duk_hthread_alloc.c here? */ - return res; -} - -DUK_INTERNAL duk_hthread *duk_hthread_alloc(duk_hthread *thr, duk_uint_t hobject_flags) { - duk_hthread *res; - - res = duk_hthread_alloc_unchecked(thr->heap, hobject_flags); - if (res == NULL) { - DUK_ERROR_ALLOC_FAILED(thr); - DUK_WO_NORETURN(return NULL;); - } - return res; -} - -DUK_INTERNAL duk_harray *duk_harray_alloc(duk_hthread *thr, duk_uint_t hobject_flags) { - duk_harray *res; - - res = (duk_harray *) duk__hobject_alloc_init(thr, hobject_flags, sizeof(duk_harray)); - - DUK_ASSERT(res->length == 0); - - return res; -} - -DUK_INTERNAL duk_hdecenv *duk_hdecenv_alloc(duk_hthread *thr, duk_uint_t hobject_flags) { - duk_hdecenv *res; - - res = (duk_hdecenv *) duk__hobject_alloc_init(thr, hobject_flags, sizeof(duk_hdecenv)); -#if defined(DUK_USE_EXPLICIT_NULL_INIT) - res->thread = NULL; - res->varmap = NULL; -#endif - - DUK_ASSERT(res->thread == NULL); - DUK_ASSERT(res->varmap == NULL); - DUK_ASSERT(res->regbase_byteoff == 0); - - return res; -} - -DUK_INTERNAL duk_hobjenv *duk_hobjenv_alloc(duk_hthread *thr, duk_uint_t hobject_flags) { - duk_hobjenv *res; - - res = (duk_hobjenv *) duk__hobject_alloc_init(thr, hobject_flags, sizeof(duk_hobjenv)); -#if defined(DUK_USE_EXPLICIT_NULL_INIT) - res->target = NULL; -#endif - - DUK_ASSERT(res->target == NULL); - - return res; -} - -DUK_INTERNAL duk_hproxy *duk_hproxy_alloc(duk_hthread *thr, duk_uint_t hobject_flags) { - duk_hproxy *res; - - res = (duk_hproxy *) duk__hobject_alloc_init(thr, hobject_flags, sizeof(duk_hproxy)); - - /* Leave ->target and ->handler uninitialized, as caller will always - * explicitly initialize them before any side effects are possible. - */ - - return res; -} -/* - * duk_hobject and subclass assertion helpers - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_ASSERTIONS) - -DUK_INTERNAL void duk_hobject_assert_valid(duk_hobject *h) { - DUK_ASSERT(h != NULL); - DUK_ASSERT(!DUK_HOBJECT_IS_CALLABLE(h) || DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_FUNCTION); - DUK_ASSERT(!DUK_HOBJECT_IS_BUFOBJ(h) || (DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_ARRAYBUFFER || - DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_DATAVIEW || - DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_INT8ARRAY || - DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_UINT8ARRAY || - DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_UINT8CLAMPEDARRAY || - DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_INT16ARRAY || - DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_UINT16ARRAY || - DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_INT32ARRAY || - DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_UINT32ARRAY || - DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_FLOAT32ARRAY || - DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_FLOAT64ARRAY)); - /* Object is an Array <=> object has exotic array behavior */ - DUK_ASSERT((DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_ARRAY && DUK_HOBJECT_HAS_EXOTIC_ARRAY(h)) || - (DUK_HOBJECT_GET_CLASS_NUMBER(h) != DUK_HOBJECT_CLASS_ARRAY && !DUK_HOBJECT_HAS_EXOTIC_ARRAY(h))); -} - -DUK_INTERNAL void duk_harray_assert_valid(duk_harray *h) { - DUK_ASSERT(h != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_ARRAY((duk_hobject *) h)); - DUK_ASSERT(DUK_HOBJECT_HAS_EXOTIC_ARRAY((duk_hobject *) h)); -} - -DUK_INTERNAL void duk_hboundfunc_assert_valid(duk_hboundfunc *h) { - DUK_ASSERT(h != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_BOUNDFUNC((duk_hobject *) h)); - DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(&h->target) || - (DUK_TVAL_IS_OBJECT(&h->target) && DUK_HOBJECT_IS_CALLABLE(DUK_TVAL_GET_OBJECT(&h->target)))); - DUK_ASSERT(!DUK_TVAL_IS_UNUSED(&h->this_binding)); - DUK_ASSERT(h->nargs == 0 || h->args != NULL); -} - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL void duk_hbufobj_assert_valid(duk_hbufobj *h) { - DUK_ASSERT(h != NULL); - DUK_ASSERT(h->shift <= 3); - DUK_ASSERT(h->elem_type <= DUK_HBUFOBJ_ELEM_MAX); - DUK_ASSERT((h->shift == 0 && h->elem_type == DUK_HBUFOBJ_ELEM_UINT8) || - (h->shift == 0 && h->elem_type == DUK_HBUFOBJ_ELEM_UINT8CLAMPED) || - (h->shift == 0 && h->elem_type == DUK_HBUFOBJ_ELEM_INT8) || - (h->shift == 1 && h->elem_type == DUK_HBUFOBJ_ELEM_UINT16) || - (h->shift == 1 && h->elem_type == DUK_HBUFOBJ_ELEM_INT16) || - (h->shift == 2 && h->elem_type == DUK_HBUFOBJ_ELEM_UINT32) || - (h->shift == 2 && h->elem_type == DUK_HBUFOBJ_ELEM_INT32) || - (h->shift == 2 && h->elem_type == DUK_HBUFOBJ_ELEM_FLOAT32) || - (h->shift == 3 && h->elem_type == DUK_HBUFOBJ_ELEM_FLOAT64)); - DUK_ASSERT(h->is_typedarray == 0 || h->is_typedarray == 1); - DUK_ASSERT(DUK_HOBJECT_IS_BUFOBJ((duk_hobject *) h)); - if (h->buf == NULL) { - DUK_ASSERT(h->offset == 0); - DUK_ASSERT(h->length == 0); - } else { - /* No assertions for offset or length; in particular, - * it's OK for length to be longer than underlying - * buffer. Just ensure they don't wrap when added. - */ - DUK_ASSERT(h->offset + h->length >= h->offset); - } -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -DUK_INTERNAL void duk_hcompfunc_assert_valid(duk_hcompfunc *h) { - DUK_ASSERT(h != NULL); -} - -DUK_INTERNAL void duk_hnatfunc_assert_valid(duk_hnatfunc *h) { - DUK_ASSERT(h != NULL); -} - -DUK_INTERNAL void duk_hdecenv_assert_valid(duk_hdecenv *h) { - DUK_ASSERT(h != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_DECENV((duk_hobject *) h)); - DUK_ASSERT(h->thread == NULL || h->varmap != NULL); -} - -DUK_INTERNAL void duk_hobjenv_assert_valid(duk_hobjenv *h) { - DUK_ASSERT(h != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_OBJENV((duk_hobject *) h)); - DUK_ASSERT(h->target != NULL); - DUK_ASSERT(h->has_this == 0 || h->has_this == 1); -} - -DUK_INTERNAL void duk_hproxy_assert_valid(duk_hproxy *h) { - DUK_ASSERT(h != NULL); - DUK_ASSERT(h->target != NULL); - DUK_ASSERT(h->handler != NULL); - DUK_ASSERT(DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ((duk_hobject *) h)); -} - -DUK_INTERNAL void duk_hthread_assert_valid(duk_hthread *thr) { - DUK_ASSERT(thr != NULL); - DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) thr) == DUK_HTYPE_OBJECT); - DUK_ASSERT(DUK_HOBJECT_IS_THREAD((duk_hobject *) thr)); - DUK_ASSERT(thr->unused1 == 0); - DUK_ASSERT(thr->unused2 == 0); -} - -DUK_INTERNAL void duk_ctx_assert_valid(duk_hthread *thr) { - DUK_ASSERT(thr != NULL); - DUK_HTHREAD_ASSERT_VALID(thr); - DUK_ASSERT(thr->valstack != NULL); - DUK_ASSERT(thr->valstack_bottom != NULL); - DUK_ASSERT(thr->valstack_top != NULL); - DUK_ASSERT(thr->valstack_end != NULL); - DUK_ASSERT(thr->valstack_alloc_end != NULL); - DUK_ASSERT(thr->valstack_alloc_end >= thr->valstack); - DUK_ASSERT(thr->valstack_end >= thr->valstack); - DUK_ASSERT(thr->valstack_top >= thr->valstack); - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - DUK_ASSERT(thr->valstack_end >= thr->valstack_top); - DUK_ASSERT(thr->valstack_alloc_end >= thr->valstack_end); -} - -#endif /* DUK_USE_ASSERTIONS */ -/* - * Object enumeration support. - * - * Creates an internal enumeration state object to be used e.g. with for-in - * enumeration. The state object contains a snapshot of target object keys - * and internal control state for enumeration. Enumerator flags allow caller - * to e.g. request internal/non-enumerable properties, and to enumerate only - * "own" properties. - * - * Also creates the result value for e.g. Object.keys() based on the same - * internal structure. - * - * This snapshot-based enumeration approach is used to simplify enumeration: - * non-snapshot-based approaches are difficult to reconcile with mutating - * the enumeration target, running multiple long-lived enumerators at the - * same time, garbage collection details, etc. The downside is that the - * enumerator object is memory inefficient especially for iterating arrays. - */ - -/* #include duk_internal.h -> already included */ - -/* XXX: identify enumeration target with an object index (not top of stack) */ - -/* First enumerated key index in enumerator object, must match exactly the - * number of control properties inserted to the enumerator. - */ -#define DUK__ENUM_START_INDEX 2 - -/* Current implementation suffices for ES2015 for now because there's no symbol - * sorting, so commented out for now. - */ - -/* - * Helper to sort enumeration keys using a callback for pairwise duk_hstring - * comparisons. The keys are in the enumeration object entry part, starting - * from DUK__ENUM_START_INDEX, and the entry part is dense. Entry part values - * are all "true", e.g. "1" -> true, "3" -> true, "foo" -> true, "2" -> true, - * so it suffices to just switch keys without switching values. - * - * ES2015 [[OwnPropertyKeys]] enumeration order for ordinary objects: - * (1) array indices in ascending order, - * (2) non-array-index keys in insertion order, and - * (3) symbols in insertion order. - * http://www.ecma-international.org/ecma-262/6.0/#sec-ordinary-object-internal-methods-and-internal-slots-ownpropertykeys. - * - * This rule is applied to "own properties" at each inheritance level; - * non-duplicate parent keys always follow child keys. For example, - * an inherited array index will enumerate -after- a symbol in the - * child. - * - * Insertion sort is used because (1) it's simple and compact, (2) works - * in-place, (3) minimizes operations if data is already nearly sorted, - * (4) doesn't reorder elements considered equal. - * http://en.wikipedia.org/wiki/Insertion_sort - */ - -/* Sort key, must hold array indices, "not array index" marker, and one more - * higher value for symbols. - */ -#if !defined(DUK_USE_SYMBOL_BUILTIN) -typedef duk_uint32_t duk__sort_key_t; -#elif defined(DUK_USE_64BIT_OPS) -typedef duk_uint64_t duk__sort_key_t; -#else -typedef duk_double_t duk__sort_key_t; -#endif - -/* Get sort key for a duk_hstring. */ -DUK_LOCAL duk__sort_key_t duk__hstring_sort_key(duk_hstring *x) { - duk__sort_key_t val; - - /* For array indices [0,0xfffffffe] use the array index as is. - * For strings, use 0xffffffff, the marker 'arridx' already in - * duk_hstring. For symbols, any value above 0xffffffff works, - * as long as it is the same for all symbols; currently just add - * the masked flag field into the arridx temporary. - */ - DUK_ASSERT(x != NULL); - DUK_ASSERT(!DUK_HSTRING_HAS_SYMBOL(x) || DUK_HSTRING_GET_ARRIDX_FAST(x) == DUK_HSTRING_NO_ARRAY_INDEX); - - val = (duk__sort_key_t) DUK_HSTRING_GET_ARRIDX_FAST(x); - -#if defined(DUK_USE_SYMBOL_BUILTIN) - val = val + (duk__sort_key_t) (DUK_HEAPHDR_GET_FLAGS_RAW((duk_heaphdr *) x) & DUK_HSTRING_FLAG_SYMBOL); -#endif - - return (duk__sort_key_t) val; -} - -/* Insert element 'b' after element 'a'? */ -DUK_LOCAL duk_bool_t duk__sort_compare_es6(duk_hstring *a, duk_hstring *b, duk__sort_key_t val_b) { - duk__sort_key_t val_a; - - DUK_ASSERT(a != NULL); - DUK_ASSERT(b != NULL); - DUK_UNREF(b); /* Not actually needed now, val_b suffices. */ - - val_a = duk__hstring_sort_key(a); - - if (val_a > val_b) { - return 0; - } else { - return 1; - } -} - -DUK_LOCAL void duk__sort_enum_keys_es6(duk_hthread *thr, duk_hobject *h_obj, duk_int_fast32_t idx_start, duk_int_fast32_t idx_end) { - duk_hstring **keys; - duk_int_fast32_t idx; - - DUK_ASSERT(h_obj != NULL); - DUK_ASSERT(idx_start >= DUK__ENUM_START_INDEX); - DUK_ASSERT(idx_end >= idx_start); - DUK_UNREF(thr); - - if (idx_end <= idx_start + 1) { - return; /* Zero or one element(s). */ - } - - keys = DUK_HOBJECT_E_GET_KEY_BASE(thr->heap, h_obj); - - for (idx = idx_start + 1; idx < idx_end; idx++) { - duk_hstring *h_curr; - duk_int_fast32_t idx_insert; - duk__sort_key_t val_curr; - - h_curr = keys[idx]; - DUK_ASSERT(h_curr != NULL); - - /* Scan backwards for insertion place. This works very well - * when the elements are nearly in order which is the common - * (and optimized for) case. - */ - - val_curr = duk__hstring_sort_key(h_curr); /* Remains same during scanning. */ - for (idx_insert = idx - 1; idx_insert >= idx_start; idx_insert--) { - duk_hstring *h_insert; - h_insert = keys[idx_insert]; - DUK_ASSERT(h_insert != NULL); - - if (duk__sort_compare_es6(h_insert, h_curr, val_curr)) { - break; - } - } - /* If we're out of indices, idx_insert == idx_start - 1 and idx_insert++ - * brings us back to idx_start. - */ - idx_insert++; - DUK_ASSERT(idx_insert >= 0 && idx_insert <= idx); - - /* .-- p_insert .-- p_curr - * v v - * | ... | insert | ... | curr - */ - - /* This could also done when the keys are in order, i.e. - * idx_insert == idx. The result would be an unnecessary - * memmove() but we use an explicit check because the keys - * are very often in order already. - */ - if (idx != idx_insert) { - duk_memmove((void *) (keys + idx_insert + 1), - (const void *) (keys + idx_insert), - ((size_t) (idx - idx_insert) * sizeof(duk_hstring *))); - keys[idx_insert] = h_curr; - } - } - - /* Entry part has been reordered now with no side effects. - * If the object has a hash part, it will now be incorrect - * and we need to rehash. Do that by forcing a resize to - * the current size. - */ - duk_hobject_resize_entrypart(thr, h_obj, DUK_HOBJECT_GET_ESIZE(h_obj)); -} - -/* - * Create an internal enumerator object E, which has its keys ordered - * to match desired enumeration ordering. Also initialize internal control - * properties for enumeration. - * - * Note: if an array was used to hold enumeration keys instead, an array - * scan would be needed to eliminate duplicates found in the prototype chain. - */ - -DUK_LOCAL void duk__add_enum_key(duk_hthread *thr, duk_hstring *k) { - /* 'k' may be unreachable on entry so must push without any - * potential for GC. - */ - duk_push_hstring(thr, k); - duk_push_true(thr); - duk_put_prop(thr, -3); -} - -DUK_LOCAL void duk__add_enum_key_stridx(duk_hthread *thr, duk_small_uint_t stridx) { - duk__add_enum_key(thr, DUK_HTHREAD_GET_STRING(thr, stridx)); -} - -DUK_INTERNAL void duk_hobject_enumerator_create(duk_hthread *thr, duk_small_uint_t enum_flags) { - duk_hobject *enum_target; - duk_hobject *curr; - duk_hobject *res; -#if defined(DUK_USE_ES6_PROXY) - duk_hobject *h_proxy_target; - duk_hobject *h_proxy_handler; - duk_hobject *h_trap_result; -#endif - duk_uint_fast32_t i, len; /* used for array, stack, and entry indices */ - duk_uint_fast32_t sort_start_index; - - DUK_ASSERT(thr != NULL); - - enum_target = duk_require_hobject(thr, -1); - DUK_ASSERT(enum_target != NULL); - - duk_push_bare_object(thr); - res = duk_known_hobject(thr, -1); - - /* [enum_target res] */ - - /* Target must be stored so that we can recheck whether or not - * keys still exist when we enumerate. This is not done if the - * enumeration result comes from a proxy trap as there is no - * real object to check against. - */ - duk_push_hobject(thr, enum_target); - duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_INT_TARGET); /* Target is bare, plain put OK. */ - - /* Initialize index so that we skip internal control keys. */ - duk_push_int(thr, DUK__ENUM_START_INDEX); - duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_INT_NEXT); /* Target is bare, plain put OK. */ - - /* - * Proxy object handling - */ - -#if defined(DUK_USE_ES6_PROXY) - if (DUK_LIKELY((enum_flags & DUK_ENUM_NO_PROXY_BEHAVIOR) != 0)) { - goto skip_proxy; - } - if (DUK_LIKELY(!duk_hobject_proxy_check(enum_target, &h_proxy_target, &h_proxy_handler))) { - goto skip_proxy; - } - - /* XXX: share code with Object.keys() Proxy handling */ - - /* In ES2015 for-in invoked the "enumerate" trap; in ES2016 "enumerate" - * has been obsoleted and "ownKeys" is used instead. - */ - DUK_DDD(DUK_DDDPRINT("proxy enumeration")); - duk_push_hobject(thr, h_proxy_handler); - if (!duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_OWN_KEYS)) { - /* No need to replace the 'enum_target' value in stack, only the - * enum_target reference. This also ensures that the original - * enum target is reachable, which keeps the proxy and the proxy - * target reachable. We do need to replace the internal _Target. - */ - DUK_DDD(DUK_DDDPRINT("no ownKeys trap, enumerate proxy target instead")); - DUK_DDD(DUK_DDDPRINT("h_proxy_target=%!O", (duk_heaphdr *) h_proxy_target)); - enum_target = h_proxy_target; - - duk_push_hobject(thr, enum_target); /* -> [ ... enum_target res handler undefined target ] */ - duk_put_prop_stridx_short(thr, -4, DUK_STRIDX_INT_TARGET); /* Target is bare, plain put OK. */ - - duk_pop_2(thr); /* -> [ ... enum_target res ] */ - goto skip_proxy; - } - - /* [ ... enum_target res handler trap ] */ - duk_insert(thr, -2); - duk_push_hobject(thr, h_proxy_target); /* -> [ ... enum_target res trap handler target ] */ - duk_call_method(thr, 1 /*nargs*/); /* -> [ ... enum_target res trap_result ] */ - h_trap_result = duk_require_hobject(thr, -1); - DUK_UNREF(h_trap_result); - - duk_proxy_ownkeys_postprocess(thr, h_proxy_target, enum_flags); - /* -> [ ... enum_target res trap_result keys_array ] */ - - /* Copy cleaned up trap result keys into the enumerator object. */ - /* XXX: result is a dense array; could make use of that. */ - DUK_ASSERT(duk_is_array(thr, -1)); - len = (duk_uint_fast32_t) duk_get_length(thr, -1); - for (i = 0; i < len; i++) { - (void) duk_get_prop_index(thr, -1, (duk_uarridx_t) i); - DUK_ASSERT(duk_is_string(thr, -1)); /* postprocess cleaned up */ - /* [ ... enum_target res trap_result keys_array val ] */ - duk_push_true(thr); - /* [ ... enum_target res trap_result keys_array val true ] */ - duk_put_prop(thr, -5); - } - /* [ ... enum_target res trap_result keys_array ] */ - duk_pop_2(thr); - duk_remove_m2(thr); - - /* [ ... res ] */ - - /* The internal _Target property is kept pointing to the original - * enumeration target (the proxy object), so that the enumerator - * 'next' operation can read property values if so requested. The - * fact that the _Target is a proxy disables key existence check - * during enumeration. - */ - DUK_DDD(DUK_DDDPRINT("proxy enumeration, final res: %!O", (duk_heaphdr *) res)); - goto compact_and_return; - -skip_proxy: -#endif /* DUK_USE_ES6_PROXY */ - - curr = enum_target; - sort_start_index = DUK__ENUM_START_INDEX; - DUK_ASSERT(DUK_HOBJECT_GET_ENEXT(res) == DUK__ENUM_START_INDEX); - while (curr) { - duk_uint_fast32_t sort_end_index; -#if !defined(DUK_USE_PREFER_SIZE) - duk_bool_t need_sort = 0; -#endif - duk_bool_t cond; - - /* Enumeration proceeds by inheritance level. Virtual - * properties need to be handled specially, followed by - * array part, and finally entry part. - * - * If there are array index keys in the entry part or any - * other risk of the ES2015 [[OwnPropertyKeys]] order being - * violated, need_sort is set and an explicit ES2015 sort is - * done for the inheritance level. - */ - - /* XXX: inheriting from proxy */ - - /* - * Virtual properties. - * - * String and buffer indices are virtual and always enumerable, - * 'length' is virtual and non-enumerable. Array and arguments - * object props have special behavior but are concrete. - * - * String and buffer objects don't have an array part so as long - * as virtual array index keys are enumerated first, we don't - * need to set need_sort. - */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) - cond = DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(curr) || DUK_HOBJECT_IS_BUFOBJ(curr); -#else - cond = DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(curr); -#endif - cond = cond && !(enum_flags & DUK_ENUM_EXCLUDE_STRINGS); - if (cond) { - duk_bool_t have_length = 1; - - /* String and buffer enumeration behavior is identical now, - * so use shared handler. - */ - if (DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(curr)) { - duk_hstring *h_val; - h_val = duk_hobject_get_internal_value_string(thr->heap, curr); - DUK_ASSERT(h_val != NULL); /* string objects must not created without internal value */ - len = (duk_uint_fast32_t) DUK_HSTRING_GET_CHARLEN(h_val); - } -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) - else { - duk_hbufobj *h_bufobj; - DUK_ASSERT(DUK_HOBJECT_IS_BUFOBJ(curr)); - h_bufobj = (duk_hbufobj *) curr; - - if (h_bufobj == NULL || !h_bufobj->is_typedarray) { - /* Zero length seems like a good behavior for neutered buffers. - * ArrayBuffer (non-view) and DataView don't have index properties - * or .length property. - */ - len = 0; - have_length = 0; - } else { - /* There's intentionally no check for - * current underlying buffer length. - */ - len = (duk_uint_fast32_t) (h_bufobj->length >> h_bufobj->shift); - } - } -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - - for (i = 0; i < len; i++) { - duk_hstring *k; - - /* This is a bit fragile: the string is not - * reachable until it is pushed by the helper. - */ - k = duk_heap_strtable_intern_u32_checked(thr, (duk_uint32_t) i); - DUK_ASSERT(k); - - duk__add_enum_key(thr, k); - - /* [enum_target res] */ - } - - /* 'length' and other virtual properties are not - * enumerable, but are included if non-enumerable - * properties are requested. - */ - - if (have_length && (enum_flags & DUK_ENUM_INCLUDE_NONENUMERABLE)) { - duk__add_enum_key_stridx(thr, DUK_STRIDX_LENGTH); - } - } - - /* - * Array part - */ - - cond = !(enum_flags & DUK_ENUM_EXCLUDE_STRINGS); - if (cond) { - for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(curr); i++) { - duk_hstring *k; - duk_tval *tv; - - tv = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, curr, i); - if (DUK_TVAL_IS_UNUSED(tv)) { - continue; - } - k = duk_heap_strtable_intern_u32_checked(thr, (duk_uint32_t) i); /* Fragile reachability. */ - DUK_ASSERT(k); - - duk__add_enum_key(thr, k); - - /* [enum_target res] */ - } - - if (DUK_HOBJECT_HAS_EXOTIC_ARRAY(curr)) { - /* Array .length comes after numeric indices. */ - if (enum_flags & DUK_ENUM_INCLUDE_NONENUMERABLE) { - duk__add_enum_key_stridx(thr, DUK_STRIDX_LENGTH); - } - } - } - - /* - * Entries part - */ - - for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(curr); i++) { - duk_hstring *k; - - k = DUK_HOBJECT_E_GET_KEY(thr->heap, curr, i); - if (!k) { - continue; - } - if (!(enum_flags & DUK_ENUM_INCLUDE_NONENUMERABLE) && - !DUK_HOBJECT_E_SLOT_IS_ENUMERABLE(thr->heap, curr, i)) { - continue; - } - if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(k))) { - if (!(enum_flags & DUK_ENUM_INCLUDE_HIDDEN) && DUK_HSTRING_HAS_HIDDEN(k)) { - continue; - } - if (!(enum_flags & DUK_ENUM_INCLUDE_SYMBOLS)) { - continue; - } -#if !defined(DUK_USE_PREFER_SIZE) - need_sort = 1; -#endif - } else { - DUK_ASSERT(!DUK_HSTRING_HAS_HIDDEN(k)); /* would also have symbol flag */ - if (enum_flags & DUK_ENUM_EXCLUDE_STRINGS) { - continue; - } - } - if (DUK_HSTRING_HAS_ARRIDX(k)) { - /* This in currently only possible if the - * object has no array part: the array part - * is exhaustive when it is present. - */ -#if !defined(DUK_USE_PREFER_SIZE) - need_sort = 1; -#endif - } else { - if (enum_flags & DUK_ENUM_ARRAY_INDICES_ONLY) { - continue; - } - } - - DUK_ASSERT(DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, curr, i) || - !DUK_TVAL_IS_UNUSED(&DUK_HOBJECT_E_GET_VALUE_PTR(thr->heap, curr, i)->v)); - - duk__add_enum_key(thr, k); - - /* [enum_target res] */ - } - - /* Sort enumerated keys according to ES2015 requirements for - * the "inheritance level" just processed. This is far from - * optimal, ES2015 semantics could be achieved more efficiently - * by handling array index string keys (and symbol keys) - * specially above in effect doing the sort inline. - * - * Skip the sort if array index sorting is requested because - * we must consider all keys, also inherited, so an explicit - * sort is done for the whole result after we're done with the - * prototype chain. - * - * Also skip the sort if need_sort == 0, i.e. we know for - * certain that the enumerated order is already correct. - */ - sort_end_index = DUK_HOBJECT_GET_ENEXT(res); - - if (!(enum_flags & DUK_ENUM_SORT_ARRAY_INDICES)) { -#if defined(DUK_USE_PREFER_SIZE) - duk__sort_enum_keys_es6(thr, res, (duk_int_fast32_t) sort_start_index, (duk_int_fast32_t) sort_end_index); -#else - if (need_sort) { - DUK_DDD(DUK_DDDPRINT("need to sort")); - duk__sort_enum_keys_es6(thr, - res, - (duk_int_fast32_t) sort_start_index, - (duk_int_fast32_t) sort_end_index); - } else { - DUK_DDD(DUK_DDDPRINT("no need to sort")); - } -#endif - } - - sort_start_index = sort_end_index; - - if (enum_flags & DUK_ENUM_OWN_PROPERTIES_ONLY) { - break; - } - - curr = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, curr); - } - - /* [enum_target res] */ - - duk_remove_m2(thr); - - /* [res] */ - - if (enum_flags & DUK_ENUM_SORT_ARRAY_INDICES) { - /* Some E5/E5.1 algorithms require that array indices are iterated - * in a strictly ascending order. This is the case for e.g. - * Array.prototype.forEach() and JSON.stringify() PropertyList - * handling. The caller can request an explicit sort in these - * cases. - */ - - /* Sort to ES2015 order which works for pure array incides but - * also for mixed keys. - */ - duk__sort_enum_keys_es6(thr, - res, - (duk_int_fast32_t) DUK__ENUM_START_INDEX, - (duk_int_fast32_t) DUK_HOBJECT_GET_ENEXT(res)); - } - -#if defined(DUK_USE_ES6_PROXY) -compact_and_return: -#endif - /* compact; no need to seal because object is internal */ - duk_hobject_compact_props(thr, res); - - DUK_DDD(DUK_DDDPRINT("created enumerator object: %!iT", (duk_tval *) duk_get_tval(thr, -1))); -} - -/* - * Returns non-zero if a key and/or value was enumerated, and: - * - * [enum] -> [key] (get_value == 0) - * [enum] -> [key value] (get_value == 1) - * - * Returns zero without pushing anything on the stack otherwise. - */ -DUK_INTERNAL duk_bool_t duk_hobject_enumerator_next(duk_hthread *thr, duk_bool_t get_value) { - duk_hobject *e; - duk_hobject *enum_target; - duk_hstring *res = NULL; - duk_uint_fast32_t idx; - duk_bool_t check_existence; - - DUK_ASSERT(thr != NULL); - - /* [... enum] */ - - e = duk_require_hobject(thr, -1); - - /* XXX use get tval ptr, more efficient */ - duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_INT_NEXT); - idx = (duk_uint_fast32_t) duk_require_uint(thr, -1); - duk_pop(thr); - DUK_DDD(DUK_DDDPRINT("enumeration: index is: %ld", (long) idx)); - - /* Enumeration keys are checked against the enumeration target (to see - * that they still exist). In the proxy enumeration case _Target will - * be the proxy, and checking key existence against the proxy is not - * required (or sensible, as the keys may be fully virtual). - */ - duk_xget_owndataprop_stridx_short(thr, -1, DUK_STRIDX_INT_TARGET); - enum_target = duk_require_hobject(thr, -1); - DUK_ASSERT(enum_target != NULL); -#if defined(DUK_USE_ES6_PROXY) - check_existence = (!DUK_HOBJECT_IS_PROXY(enum_target)); -#else - check_existence = 1; -#endif - duk_pop(thr); /* still reachable */ - - DUK_DDD(DUK_DDDPRINT("getting next enum value, enum_target=%!iO, enumerator=%!iT", - (duk_heaphdr *) enum_target, - (duk_tval *) duk_get_tval(thr, -1))); - - /* no array part */ - for (;;) { - duk_hstring *k; - - if (idx >= DUK_HOBJECT_GET_ENEXT(e)) { - DUK_DDD(DUK_DDDPRINT("enumeration: ran out of elements")); - break; - } - - /* we know these because enum objects are internally created */ - k = DUK_HOBJECT_E_GET_KEY(thr->heap, e, idx); - DUK_ASSERT(k != NULL); - DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, e, idx)); - DUK_ASSERT(!DUK_TVAL_IS_UNUSED(&DUK_HOBJECT_E_GET_VALUE(thr->heap, e, idx).v)); - - idx++; - - /* recheck that the property still exists */ - if (check_existence && !duk_hobject_hasprop_raw(thr, enum_target, k)) { - DUK_DDD(DUK_DDDPRINT("property deleted during enumeration, skip")); - continue; - } - - DUK_DDD(DUK_DDDPRINT("enumeration: found element, key: %!O", (duk_heaphdr *) k)); - res = k; - break; - } - - DUK_DDD(DUK_DDDPRINT("enumeration: updating next index to %ld", (long) idx)); - - duk_push_u32(thr, (duk_uint32_t) idx); - duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_INT_NEXT); - - /* [... enum] */ - - if (res) { - duk_push_hstring(thr, res); - if (get_value) { - duk_push_hobject(thr, enum_target); - duk_dup_m2(thr); /* -> [... enum key enum_target key] */ - duk_get_prop(thr, -2); /* -> [... enum key enum_target val] */ - duk_remove_m2(thr); /* -> [... enum key val] */ - duk_remove(thr, -3); /* -> [... key val] */ - } else { - duk_remove_m2(thr); /* -> [... key] */ - } - return 1; - } else { - duk_pop(thr); /* -> [...] */ - return 0; - } -} - -/* - * Get enumerated keys in an ECMAScript array. Matches Object.keys() behavior - * described in E5 Section 15.2.3.14. - */ - -DUK_INTERNAL duk_ret_t duk_hobject_get_enumerated_keys(duk_hthread *thr, duk_small_uint_t enum_flags) { - duk_hobject *e; - duk_hstring **keys; - duk_tval *tv; - duk_uint_fast32_t count; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(duk_get_hobject(thr, -1) != NULL); - - /* Create a temporary enumerator to get the (non-duplicated) key list; - * the enumerator state is initialized without being needed, but that - * has little impact. - */ - - duk_hobject_enumerator_create(thr, enum_flags); - e = duk_known_hobject(thr, -1); - - /* [enum_target enum res] */ - - /* Create dense result array to exact size. */ - DUK_ASSERT(DUK_HOBJECT_GET_ENEXT(e) >= DUK__ENUM_START_INDEX); - count = (duk_uint32_t) (DUK_HOBJECT_GET_ENEXT(e) - DUK__ENUM_START_INDEX); - - /* XXX: uninit would be OK */ - tv = duk_push_harray_with_size_outptr(thr, (duk_uint32_t) count); - DUK_ASSERT(count == 0 || tv != NULL); - DUK_ASSERT(!duk_is_bare_object(thr, -1)); - - /* Fill result array, no side effects. */ - - keys = DUK_HOBJECT_E_GET_KEY_BASE(thr->heap, e); - keys += DUK__ENUM_START_INDEX; - - while (count-- > 0) { - duk_hstring *k; - - k = *keys++; - DUK_ASSERT(k != NULL); /* enumerator must have no keys deleted */ - - DUK_TVAL_SET_STRING(tv, k); - tv++; - DUK_HSTRING_INCREF(thr, k); - } - - /* [enum_target enum res] */ - duk_remove_m2(thr); - - /* [enum_target res] */ - - return 1; /* return 1 to allow callers to tail call */ -} - -/* automatic undefs */ -#undef DUK__ENUM_START_INDEX -/* - * Misc support functions - */ - -/* #include duk_internal.h -> already included */ - -DUK_INTERNAL duk_bool_t duk_hobject_prototype_chain_contains(duk_hthread *thr, - duk_hobject *h, - duk_hobject *p, - duk_bool_t ignore_loop) { - duk_uint_t sanity; - - DUK_ASSERT(thr != NULL); - - /* False if the object is NULL or the prototype 'p' is NULL. - * In particular, false if both are NULL (don't compare equal). - */ - if (h == NULL || p == NULL) { - return 0; - } - - sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY; - do { - if (h == p) { - return 1; - } - - if (sanity-- == 0) { - if (ignore_loop) { - break; - } else { - DUK_ERROR_RANGE(thr, DUK_STR_PROTOTYPE_CHAIN_LIMIT); - DUK_WO_NORETURN(return 0;); - } - } - h = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h); - } while (h); - - return 0; -} - -DUK_INTERNAL void duk_hobject_set_prototype_updref(duk_hthread *thr, duk_hobject *h, duk_hobject *p) { -#if defined(DUK_USE_REFERENCE_COUNTING) - duk_hobject *tmp; - - DUK_ASSERT(h); - tmp = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h); - DUK_HOBJECT_SET_PROTOTYPE(thr->heap, h, p); - DUK_HOBJECT_INCREF_ALLOWNULL(thr, p); /* avoid problems if p == h->prototype */ - DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp); -#else - DUK_ASSERT(h); - DUK_UNREF(thr); - DUK_HOBJECT_SET_PROTOTYPE(thr->heap, h, p); -#endif -} -/* - * Helpers for creating and querying pc2line debug data, which - * converts a bytecode program counter to a source line number. - * - * The run-time pc2line data is bit-packed, and documented in: - * - * doc/function-objects.rst - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_PC2LINE) - -/* Generate pc2line data for an instruction sequence, leaving a buffer on stack top. */ -DUK_INTERNAL void duk_hobject_pc2line_pack(duk_hthread *thr, duk_compiler_instr *instrs, duk_uint_fast32_t length) { - duk_hbuffer_dynamic *h_buf; - duk_bitencoder_ctx be_ctx_alloc; - duk_bitencoder_ctx *be_ctx = &be_ctx_alloc; - duk_uint32_t *hdr; - duk_size_t new_size; - duk_uint_fast32_t num_header_entries; - duk_uint_fast32_t curr_offset; - duk_int_fast32_t curr_line, next_line, diff_line; - duk_uint_fast32_t curr_pc; - duk_uint_fast32_t hdr_index; - - DUK_ASSERT(length <= DUK_COMPILER_MAX_BYTECODE_LENGTH); - - num_header_entries = (length + DUK_PC2LINE_SKIP - 1) / DUK_PC2LINE_SKIP; - curr_offset = (duk_uint_fast32_t) (sizeof(duk_uint32_t) + num_header_entries * sizeof(duk_uint32_t) * 2); - - duk_push_dynamic_buffer(thr, (duk_size_t) curr_offset); - h_buf = (duk_hbuffer_dynamic *) duk_known_hbuffer(thr, -1); - DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(h_buf) && !DUK_HBUFFER_HAS_EXTERNAL(h_buf)); - - hdr = (duk_uint32_t *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, h_buf); - DUK_ASSERT(hdr != NULL); - hdr[0] = (duk_uint32_t) length; /* valid pc range is [0, length[ */ - - curr_pc = 0U; - while (curr_pc < length) { - new_size = (duk_size_t) (curr_offset + DUK_PC2LINE_MAX_DIFF_LENGTH); - duk_hbuffer_resize(thr, h_buf, new_size); - - hdr = (duk_uint32_t *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, h_buf); - DUK_ASSERT(hdr != NULL); - DUK_ASSERT(curr_pc < length); - hdr_index = 1 + (curr_pc / DUK_PC2LINE_SKIP) * 2; - curr_line = (duk_int_fast32_t) instrs[curr_pc].line; - hdr[hdr_index + 0] = (duk_uint32_t) curr_line; - hdr[hdr_index + 1] = (duk_uint32_t) curr_offset; - -#if 0 - DUK_DDD(DUK_DDDPRINT("hdr[%ld]: pc=%ld line=%ld offset=%ld", - (long) (curr_pc / DUK_PC2LINE_SKIP), - (long) curr_pc, - (long) hdr[hdr_index + 0], - (long) hdr[hdr_index + 1])); -#endif - - duk_memzero(be_ctx, sizeof(*be_ctx)); - be_ctx->data = ((duk_uint8_t *) hdr) + curr_offset; - be_ctx->length = (duk_size_t) DUK_PC2LINE_MAX_DIFF_LENGTH; - - for (;;) { - curr_pc++; - if (((curr_pc % DUK_PC2LINE_SKIP) == 0) || /* end of diff run */ - (curr_pc >= length)) { /* end of bytecode */ - break; - } - DUK_ASSERT(curr_pc < length); - next_line = (duk_int32_t) instrs[curr_pc].line; - diff_line = next_line - curr_line; - -#if 0 - DUK_DDD(DUK_DDDPRINT("curr_line=%ld, next_line=%ld -> diff_line=%ld", - (long) curr_line, (long) next_line, (long) diff_line)); -#endif - - if (diff_line == 0) { - /* 0 */ - duk_be_encode(be_ctx, 0, 1); - } else if (diff_line >= 1 && diff_line <= 4) { - /* 1 0 <2 bits> */ - duk_be_encode(be_ctx, (duk_uint32_t) ((0x02 << 2) + (diff_line - 1)), 4); - } else if (diff_line >= -0x80 && diff_line <= 0x7f) { - /* 1 1 0 <8 bits> */ - DUK_ASSERT(diff_line + 0x80 >= 0 && diff_line + 0x80 <= 0xff); - duk_be_encode(be_ctx, (duk_uint32_t) ((0x06 << 8) + (diff_line + 0x80)), 11); - } else { - /* 1 1 1 <32 bits> - * Encode in two parts to avoid bitencode 24-bit limitation - */ - duk_be_encode(be_ctx, (duk_uint32_t) ((0x07 << 16) + ((next_line >> 16) & 0xffff)), 19); - duk_be_encode(be_ctx, (duk_uint32_t) (next_line & 0xffff), 16); - } - - curr_line = next_line; - } - - duk_be_finish(be_ctx); - DUK_ASSERT(!be_ctx->truncated); - - /* be_ctx->offset == length of encoded bitstream */ - curr_offset += (duk_uint_fast32_t) be_ctx->offset; - } - - /* compact */ - new_size = (duk_size_t) curr_offset; - duk_hbuffer_resize(thr, h_buf, new_size); - - (void) duk_to_fixed_buffer(thr, -1, NULL); - - DUK_DDD(DUK_DDDPRINT("final pc2line data: pc_limit=%ld, length=%ld, %lf bits/opcode --> %!ixT", - (long) length, - (long) new_size, - (double) new_size * 8.0 / (double) length, - (duk_tval *) duk_get_tval(thr, -1))); -} - -/* PC is unsigned. If caller does PC arithmetic and gets a negative result, - * it will map to a large PC which is out of bounds and causes a zero to be - * returned. - */ -DUK_LOCAL duk_uint_fast32_t duk__hobject_pc2line_query_raw(duk_hthread *thr, duk_hbuffer_fixed *buf, duk_uint_fast32_t pc) { - duk_bitdecoder_ctx bd_ctx_alloc; - duk_bitdecoder_ctx *bd_ctx = &bd_ctx_alloc; - duk_uint32_t *hdr; - duk_uint_fast32_t start_offset; - duk_uint_fast32_t pc_limit; - duk_uint_fast32_t hdr_index; - duk_uint_fast32_t pc_base; - duk_uint_fast32_t n; - duk_uint_fast32_t curr_line; - - DUK_ASSERT(buf != NULL); - DUK_ASSERT(!DUK_HBUFFER_HAS_DYNAMIC((duk_hbuffer *) buf) && !DUK_HBUFFER_HAS_EXTERNAL((duk_hbuffer *) buf)); - DUK_UNREF(thr); - - /* - * Use the index in the header to find the right starting point - */ - - hdr_index = pc / DUK_PC2LINE_SKIP; - pc_base = hdr_index * DUK_PC2LINE_SKIP; - n = pc - pc_base; - - if (DUK_HBUFFER_FIXED_GET_SIZE(buf) <= sizeof(duk_uint32_t)) { - DUK_DD(DUK_DDPRINT("pc2line lookup failed: buffer is smaller than minimal header")); - goto pc2line_error; - } - - hdr = (duk_uint32_t *) (void *) DUK_HBUFFER_FIXED_GET_DATA_PTR(thr->heap, buf); - pc_limit = hdr[0]; - if (pc >= pc_limit) { - /* Note: pc is unsigned and cannot be negative */ - DUK_DD(DUK_DDPRINT("pc2line lookup failed: pc out of bounds (pc=%ld, limit=%ld)", (long) pc, (long) pc_limit)); - goto pc2line_error; - } - - curr_line = hdr[1 + hdr_index * 2]; - start_offset = hdr[1 + hdr_index * 2 + 1]; - if ((duk_size_t) start_offset > DUK_HBUFFER_FIXED_GET_SIZE(buf)) { - DUK_DD(DUK_DDPRINT("pc2line lookup failed: start_offset out of bounds (start_offset=%ld, buffer_size=%ld)", - (long) start_offset, - (long) DUK_HBUFFER_GET_SIZE((duk_hbuffer *) buf))); - goto pc2line_error; - } - - /* - * Iterate the bitstream (line diffs) until PC is reached - */ - - duk_memzero(bd_ctx, sizeof(*bd_ctx)); - bd_ctx->data = ((duk_uint8_t *) hdr) + start_offset; - bd_ctx->length = (duk_size_t) (DUK_HBUFFER_FIXED_GET_SIZE(buf) - start_offset); - -#if 0 - DUK_DDD(DUK_DDDPRINT("pc2line lookup: pc=%ld -> hdr_index=%ld, pc_base=%ld, n=%ld, start_offset=%ld", - (long) pc, (long) hdr_index, (long) pc_base, (long) n, (long) start_offset)); -#endif - - while (n > 0) { -#if 0 - DUK_DDD(DUK_DDDPRINT("lookup: n=%ld, curr_line=%ld", (long) n, (long) curr_line)); -#endif - - if (duk_bd_decode_flag(bd_ctx)) { - if (duk_bd_decode_flag(bd_ctx)) { - if (duk_bd_decode_flag(bd_ctx)) { - /* 1 1 1 <32 bits> */ - duk_uint_fast32_t t; - t = duk_bd_decode(bd_ctx, 16); /* workaround: max nbits = 24 now */ - t = (t << 16) + duk_bd_decode(bd_ctx, 16); - curr_line = t; - } else { - /* 1 1 0 <8 bits> */ - duk_uint_fast32_t t; - t = duk_bd_decode(bd_ctx, 8); - curr_line = curr_line + t - 0x80; - } - } else { - /* 1 0 <2 bits> */ - duk_uint_fast32_t t; - t = duk_bd_decode(bd_ctx, 2); - curr_line = curr_line + t + 1; - } - } else { - /* 0: no change */ - } - - n--; - } - - DUK_DDD(DUK_DDDPRINT("pc2line lookup result: pc %ld -> line %ld", (long) pc, (long) curr_line)); - return curr_line; - -pc2line_error: - DUK_D(DUK_DPRINT("pc2line conversion failed for pc=%ld", (long) pc)); - return 0; -} - -DUK_INTERNAL duk_uint_fast32_t duk_hobject_pc2line_query(duk_hthread *thr, duk_idx_t idx_func, duk_uint_fast32_t pc) { - duk_hbuffer_fixed *pc2line; - duk_uint_fast32_t line; - - /* XXX: now that pc2line is used by the debugger quite heavily in - * checked execution, this should be optimized to avoid value stack - * and perhaps also implement some form of pc2line caching (see - * future work in debugger.rst). - */ - - duk_xget_owndataprop_stridx_short(thr, idx_func, DUK_STRIDX_INT_PC2LINE); - pc2line = (duk_hbuffer_fixed *) (void *) duk_get_hbuffer(thr, -1); - if (pc2line != NULL) { - DUK_ASSERT(!DUK_HBUFFER_HAS_DYNAMIC((duk_hbuffer *) pc2line) && !DUK_HBUFFER_HAS_EXTERNAL((duk_hbuffer *) pc2line)); - line = duk__hobject_pc2line_query_raw(thr, pc2line, (duk_uint_fast32_t) pc); - } else { - line = 0; - } - duk_pop(thr); - - return line; -} - -#endif /* DUK_USE_PC2LINE */ -/* - * duk_hobject property access functionality. - * - * This is very central functionality for size, performance, and compliance. - * It is also rather intricate; see hobject-algorithms.rst for discussion on - * the algorithms and memory-management.rst for discussion on refcounts and - * side effect issues. - * - * Notes: - * - * - It might be tempting to assert "refcount nonzero" for objects - * being operated on, but that's not always correct: objects with - * a zero refcount may be operated on by the refcount implementation - * (finalization) for instance. Hence, no refcount assertions are made. - * - * - Many operations (memory allocation, identifier operations, etc) - * may cause arbitrary side effects (e.g. through GC and finalization). - * These side effects may invalidate duk_tval pointers which point to - * areas subject to reallocation (like value stack). Heap objects - * themselves have stable pointers. Holding heap object pointers or - * duk_tval copies is not problematic with respect to side effects; - * care must be taken when holding and using argument duk_tval pointers. - * - * - If a finalizer is executed, it may operate on the the same object - * we're currently dealing with. For instance, the finalizer might - * delete a certain property which has already been looked up and - * confirmed to exist. Ideally finalizers would be disabled if GC - * happens during property access. At the moment property table realloc - * disables finalizers, and all DECREFs may cause arbitrary changes so - * handle DECREF carefully. - * - * - The order of operations for a DECREF matters. When DECREF is executed, - * the entire object graph must be consistent; note that a refzero may - * lead to a mark-and-sweep through a refcount finalizer. Use NORZ macros - * and an explicit DUK_REFZERO_CHECK_xxx() if achieving correct order is hard. - */ - -/* - * XXX: array indices are mostly typed as duk_uint32_t here; duk_uarridx_t - * might be more appropriate. - */ - -/* #include duk_internal.h -> already included */ - -/* - * Local defines - */ - -#define DUK__NO_ARRAY_INDEX DUK_HSTRING_NO_ARRAY_INDEX - -/* Marker values for hash part. */ -#define DUK__HASH_UNUSED DUK_HOBJECT_HASHIDX_UNUSED -#define DUK__HASH_DELETED DUK_HOBJECT_HASHIDX_DELETED - -/* Valstack space that suffices for all local calls, excluding any recursion - * into ECMAScript or Duktape/C calls (Proxy, getters, etc). - */ -#define DUK__VALSTACK_SPACE 10 - -/* Valstack space allocated especially for proxy lookup which does a - * recursive property lookup. - */ -#define DUK__VALSTACK_PROXY_LOOKUP 20 - -/* - * Local prototypes - */ - -DUK_LOCAL_DECL duk_bool_t duk__check_arguments_map_for_get(duk_hthread *thr, - duk_hobject *obj, - duk_hstring *key, - duk_propdesc *temp_desc); -DUK_LOCAL_DECL void duk__check_arguments_map_for_put(duk_hthread *thr, - duk_hobject *obj, - duk_hstring *key, - duk_propdesc *temp_desc, - duk_bool_t throw_flag); -DUK_LOCAL_DECL void duk__check_arguments_map_for_delete(duk_hthread *thr, - duk_hobject *obj, - duk_hstring *key, - duk_propdesc *temp_desc); - -DUK_LOCAL_DECL duk_bool_t duk__handle_put_array_length_smaller(duk_hthread *thr, - duk_hobject *obj, - duk_uint32_t old_len, - duk_uint32_t new_len, - duk_bool_t force_flag, - duk_uint32_t *out_result_len); -DUK_LOCAL_DECL duk_bool_t duk__handle_put_array_length(duk_hthread *thr, duk_hobject *obj); - -DUK_LOCAL_DECL duk_bool_t -duk__get_propdesc(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_propdesc *out_desc, duk_small_uint_t flags); -DUK_LOCAL_DECL duk_bool_t duk__get_own_propdesc_raw(duk_hthread *thr, - duk_hobject *obj, - duk_hstring *key, - duk_uint32_t arr_idx, - duk_propdesc *out_desc, - duk_small_uint_t flags); - -DUK_LOCAL_DECL void duk__abandon_array_part(duk_hthread *thr, duk_hobject *obj); -DUK_LOCAL_DECL void duk__grow_props_for_array_item(duk_hthread *thr, duk_hobject *obj, duk_uint32_t highest_arr_idx); - -/* - * Misc helpers - */ - -/* Convert a duk_tval number (caller checks) to a 32-bit index. Returns - * DUK__NO_ARRAY_INDEX if the number is not whole or not a valid array - * index. - */ -/* XXX: for fastints, could use a variant which assumes a double duk_tval - * (and doesn't need to check for fastint again). - */ -DUK_LOCAL duk_uint32_t duk__tval_number_to_arr_idx(duk_tval *tv) { - duk_double_t dbl; - duk_uint32_t idx; - - DUK_ASSERT(tv != NULL); - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); - - /* -0 is accepted here as index 0 because ToString(-0) == "0" which is - * in canonical form and thus an array index. - */ - dbl = DUK_TVAL_GET_NUMBER(tv); - idx = (duk_uint32_t) dbl; - if (duk_double_equals((duk_double_t) idx, dbl)) { - /* Is whole and within 32 bit range. If the value happens to be 0xFFFFFFFF, - * it's not a valid array index but will then match DUK__NO_ARRAY_INDEX. - */ - return idx; - } - return DUK__NO_ARRAY_INDEX; -} - -#if defined(DUK_USE_FASTINT) -/* Convert a duk_tval fastint (caller checks) to a 32-bit index. */ -DUK_LOCAL duk_uint32_t duk__tval_fastint_to_arr_idx(duk_tval *tv) { - duk_int64_t t; - - DUK_ASSERT(tv != NULL); - DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv)); - - t = DUK_TVAL_GET_FASTINT(tv); - if (((duk_uint64_t) t & ~DUK_U64_CONSTANT(0xffffffff)) != 0) { - /* Catches >0x100000000 and negative values. */ - return DUK__NO_ARRAY_INDEX; - } - - /* If the value happens to be 0xFFFFFFFF, it's not a valid array index - * but will then match DUK__NO_ARRAY_INDEX. - */ - return (duk_uint32_t) t; -} -#endif /* DUK_USE_FASTINT */ - -/* Convert a duk_tval on the value stack (in a trusted index we don't validate) - * to a string or symbol using ES2015 ToPropertyKey(): - * http://www.ecma-international.org/ecma-262/6.0/#sec-topropertykey. - * - * Also check if it's a valid array index and return that (or DUK__NO_ARRAY_INDEX - * if not). - */ -DUK_LOCAL duk_uint32_t duk__to_property_key(duk_hthread *thr, duk_idx_t idx, duk_hstring **out_h) { - duk_uint32_t arr_idx; - duk_hstring *h; - duk_tval *tv_dst; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(out_h != NULL); - DUK_ASSERT(duk_is_valid_index(thr, idx)); - DUK_ASSERT(idx < 0); - - /* XXX: The revised ES2015 ToPropertyKey() handling (ES5.1 was just - * ToString()) involves a ToPrimitive(), a symbol check, and finally - * a ToString(). Figure out the best way to have a good fast path - * but still be compliant and share code. - */ - - tv_dst = DUK_GET_TVAL_NEGIDX(thr, idx); /* intentionally unvalidated */ - if (DUK_TVAL_IS_STRING(tv_dst)) { - /* Most important path: strings and plain symbols are used as - * is. For symbols the array index check below is unnecessary - * (they're never valid array indices) but checking that the - * string is a symbol would make the plain string path slower - * unnecessarily. - */ - h = DUK_TVAL_GET_STRING(tv_dst); - } else { - h = duk_to_property_key_hstring(thr, idx); - } - DUK_ASSERT(h != NULL); - *out_h = h; - - arr_idx = DUK_HSTRING_GET_ARRIDX_FAST(h); - return arr_idx; -} - -DUK_LOCAL duk_uint32_t duk__push_tval_to_property_key(duk_hthread *thr, duk_tval *tv_key, duk_hstring **out_h) { - duk_push_tval(thr, tv_key); /* XXX: could use an unsafe push here */ - return duk__to_property_key(thr, -1, out_h); -} - -/* String is an own (virtual) property of a plain buffer. */ -DUK_LOCAL duk_bool_t duk__key_is_plain_buf_ownprop(duk_hthread *thr, duk_hbuffer *buf, duk_hstring *key, duk_uint32_t arr_idx) { - DUK_UNREF(thr); - - /* Virtual index properties. Checking explicitly for - * 'arr_idx != DUK__NO_ARRAY_INDEX' is not necessary - * because DUK__NO_ARRAY_INDEXi is always larger than - * maximum allowed buffer size. - */ - DUK_ASSERT(DUK__NO_ARRAY_INDEX >= DUK_HBUFFER_GET_SIZE(buf)); - if (arr_idx < DUK_HBUFFER_GET_SIZE(buf)) { - return 1; - } - - /* Other virtual properties. */ - return (key == DUK_HTHREAD_STRING_LENGTH(thr)); -} - -/* - * Helpers for managing property storage size - */ - -/* Get default hash part size for a certain entry part size. */ -#if defined(DUK_USE_HOBJECT_HASH_PART) -DUK_LOCAL duk_uint32_t duk__get_default_h_size(duk_uint32_t e_size) { - DUK_ASSERT(e_size <= DUK_HOBJECT_MAX_PROPERTIES); - - if (e_size >= DUK_USE_HOBJECT_HASH_PROP_LIMIT) { - duk_uint32_t res; - duk_uint32_t tmp; - - /* Hash size should be 2^N where N is chosen so that 2^N is - * larger than e_size. Extra shifting is used to ensure hash - * is relatively sparse. - */ - tmp = e_size; - res = 2; /* Result will be 2 ** (N + 1). */ - while (tmp >= 0x40) { - tmp >>= 6; - res <<= 6; - } - while (tmp != 0) { - tmp >>= 1; - res <<= 1; - } - DUK_ASSERT((DUK_HOBJECT_MAX_PROPERTIES << 2U) > DUK_HOBJECT_MAX_PROPERTIES); /* Won't wrap, even shifted by 2. */ - DUK_ASSERT(res > e_size); - return res; - } else { - return 0; - } -} -#endif /* USE_PROP_HASH_PART */ - -/* Get minimum entry part growth for a certain size. */ -DUK_LOCAL duk_uint32_t duk__get_min_grow_e(duk_uint32_t e_size) { - duk_uint32_t res; - - res = (e_size + DUK_USE_HOBJECT_ENTRY_MINGROW_ADD) / DUK_USE_HOBJECT_ENTRY_MINGROW_DIVISOR; - DUK_ASSERT(res >= 1); /* important for callers */ - return res; -} - -/* Get minimum array part growth for a certain size. */ -DUK_LOCAL duk_uint32_t duk__get_min_grow_a(duk_uint32_t a_size) { - duk_uint32_t res; - - res = (a_size + DUK_USE_HOBJECT_ARRAY_MINGROW_ADD) / DUK_USE_HOBJECT_ARRAY_MINGROW_DIVISOR; - DUK_ASSERT(res >= 1); /* important for callers */ - return res; -} - -/* Count actually used entry part entries (non-NULL keys). */ -DUK_LOCAL duk_uint32_t duk__count_used_e_keys(duk_hthread *thr, duk_hobject *obj) { - duk_uint_fast32_t i; - duk_uint_fast32_t n = 0; - duk_hstring **e; - - DUK_ASSERT(obj != NULL); - DUK_UNREF(thr); - - e = DUK_HOBJECT_E_GET_KEY_BASE(thr->heap, obj); - for (i = 0; i < DUK_HOBJECT_GET_ENEXT(obj); i++) { - if (*e++) { - n++; - } - } - return (duk_uint32_t) n; -} - -/* Count actually used array part entries and array minimum size. - * NOTE: 'out_min_size' can be computed much faster by starting from the - * end and breaking out early when finding first used entry, but this is - * not needed now. - */ -DUK_LOCAL void duk__compute_a_stats(duk_hthread *thr, duk_hobject *obj, duk_uint32_t *out_used, duk_uint32_t *out_min_size) { - duk_uint_fast32_t i; - duk_uint_fast32_t used = 0; - duk_uint_fast32_t highest_idx = (duk_uint_fast32_t) -1; /* see below */ - duk_tval *a; - - DUK_ASSERT(obj != NULL); - DUK_ASSERT(out_used != NULL); - DUK_ASSERT(out_min_size != NULL); - DUK_UNREF(thr); - - a = DUK_HOBJECT_A_GET_BASE(thr->heap, obj); - for (i = 0; i < DUK_HOBJECT_GET_ASIZE(obj); i++) { - duk_tval *tv = a++; - if (!DUK_TVAL_IS_UNUSED(tv)) { - used++; - highest_idx = i; - } - } - - /* Initial value for highest_idx is -1 coerced to unsigned. This - * is a bit odd, but (highest_idx + 1) will then wrap to 0 below - * for out_min_size as intended. - */ - - *out_used = (duk_uint32_t) used; - *out_min_size = (duk_uint32_t) (highest_idx + 1); /* 0 if no used entries */ -} - -/* Check array density and indicate whether or not the array part should be abandoned. */ -DUK_LOCAL duk_bool_t duk__abandon_array_density_check(duk_uint32_t a_used, duk_uint32_t a_size) { - /* - * Array abandon check; abandon if: - * - * new_used / new_size < limit - * new_used < limit * new_size || limit is 3 bits fixed point - * new_used < limit' / 8 * new_size || *8 - * 8*new_used < limit' * new_size || :8 - * new_used < limit' * (new_size / 8) - * - * Here, new_used = a_used, new_size = a_size. - * - * Note: some callers use approximate values for a_used and/or a_size - * (e.g. dropping a '+1' term). This doesn't affect the usefulness - * of the check, but may confuse debugging. - */ - - return (a_used < DUK_USE_HOBJECT_ARRAY_ABANDON_LIMIT * (a_size >> 3)); -} - -/* Fast check for extending array: check whether or not a slow density check is required. */ -DUK_LOCAL duk_bool_t duk__abandon_array_slow_check_required(duk_uint32_t arr_idx, duk_uint32_t old_size) { - duk_uint32_t new_size_min; - - /* - * In a fast check we assume old_size equals old_used (i.e., existing - * array is fully dense). - * - * Slow check if: - * - * (new_size - old_size) / old_size > limit - * new_size - old_size > limit * old_size - * new_size > (1 + limit) * old_size || limit' is 3 bits fixed point - * new_size > (1 + (limit' / 8)) * old_size || * 8 - * 8 * new_size > (8 + limit') * old_size || : 8 - * new_size > (8 + limit') * (old_size / 8) - * new_size > limit'' * (old_size / 8) || limit'' = 9 -> max 25% increase - * arr_idx + 1 > limit'' * (old_size / 8) - * - * This check doesn't work well for small values, so old_size is rounded - * up for the check (and the '+ 1' of arr_idx can be ignored in practice): - * - * arr_idx > limit'' * ((old_size + 7) / 8) - */ - - new_size_min = arr_idx + 1; - return (new_size_min >= DUK_USE_HOBJECT_ARRAY_ABANDON_MINSIZE) && - (arr_idx > DUK_USE_HOBJECT_ARRAY_FAST_RESIZE_LIMIT * ((old_size + 7) >> 3)); -} - -DUK_LOCAL duk_bool_t duk__abandon_array_check(duk_hthread *thr, duk_uint32_t arr_idx, duk_hobject *obj) { - duk_uint32_t min_size; - duk_uint32_t old_used; - duk_uint32_t old_size; - - if (!duk__abandon_array_slow_check_required(arr_idx, DUK_HOBJECT_GET_ASIZE(obj))) { - DUK_DDD(DUK_DDDPRINT("=> fast resize is OK")); - return 0; - } - - duk__compute_a_stats(thr, obj, &old_used, &old_size); - - DUK_DDD(DUK_DDDPRINT("abandon check, array stats: old_used=%ld, old_size=%ld, arr_idx=%ld", - (long) old_used, - (long) old_size, - (long) arr_idx)); - - min_size = arr_idx + 1; -#if defined(DUK_USE_OBJSIZES16) - if (min_size > DUK_UINT16_MAX) { - goto do_abandon; - } -#endif - DUK_UNREF(min_size); - - /* Note: intentionally use approximations to shave a few instructions: - * a_used = old_used (accurate: old_used + 1) - * a_size = arr_idx (accurate: arr_idx + 1) - */ - if (duk__abandon_array_density_check(old_used, arr_idx)) { - DUK_DD(DUK_DDPRINT("write to new array entry beyond current length, " - "decided to abandon array part (would become too sparse)")); - - /* Abandoning requires a props allocation resize and - * 'rechecks' the valstack, invalidating any existing - * valstack value pointers. - */ - goto do_abandon; - } - - DUK_DDD(DUK_DDDPRINT("=> decided to keep array part")); - return 0; - -do_abandon: - duk__abandon_array_part(thr, obj); - DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(obj)); - return 1; -} - -DUK_LOCAL duk_tval *duk__obtain_arridx_slot_slowpath(duk_hthread *thr, duk_uint32_t arr_idx, duk_hobject *obj) { - /* - * Array needs to grow, but we don't want it becoming too sparse. - * If it were to become sparse, abandon array part, moving all - * array entries into the entries part (for good). - * - * Since we don't keep track of actual density (used vs. size) of - * the array part, we need to estimate somehow. The check is made - * in two parts: - * - * - Check whether the resize need is small compared to the - * current size (relatively); if so, resize without further - * checking (essentially we assume that the original part is - * "dense" so that the result would be dense enough). - * - * - Otherwise, compute the resize using an actual density - * measurement based on counting the used array entries. - */ - - DUK_DDD(DUK_DDDPRINT("write to new array requires array resize, decide whether to do a " - "fast resize without abandon check (arr_idx=%ld, old_size=%ld)", - (long) arr_idx, - (long) DUK_HOBJECT_GET_ASIZE(obj))); - - if (DUK_UNLIKELY(duk__abandon_array_check(thr, arr_idx, obj) != 0)) { - DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(obj)); - return NULL; - } - - DUK_DD(DUK_DDPRINT("write to new array entry beyond current length, " - "decided to extend current allocation")); - - /* In principle it's possible to run out of memory extending the - * array but with the allocation going through if we were to abandon - * the array part and try again. In practice this should be rare - * because abandoned arrays have a higher per-entry footprint. - */ - - duk__grow_props_for_array_item(thr, obj, arr_idx); - - DUK_ASSERT(DUK_HOBJECT_HAS_ARRAY_PART(obj)); - DUK_ASSERT(arr_idx < DUK_HOBJECT_GET_ASIZE(obj)); - return DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, arr_idx); -} - -DUK_LOCAL DUK_INLINE duk_tval *duk__obtain_arridx_slot(duk_hthread *thr, duk_uint32_t arr_idx, duk_hobject *obj) { - if (DUK_LIKELY(arr_idx < DUK_HOBJECT_GET_ASIZE(obj))) { - return DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, arr_idx); - } else { - return duk__obtain_arridx_slot_slowpath(thr, arr_idx, obj); - } -} - -/* - * Proxy helpers - */ - -#if defined(DUK_USE_ES6_PROXY) -DUK_INTERNAL duk_bool_t duk_hobject_proxy_check(duk_hobject *obj, duk_hobject **out_target, duk_hobject **out_handler) { - duk_hproxy *h_proxy; - - DUK_ASSERT(obj != NULL); - DUK_ASSERT(out_target != NULL); - DUK_ASSERT(out_handler != NULL); - - /* Caller doesn't need to check exotic proxy behavior (but does so for - * some fast paths). - */ - if (DUK_LIKELY(!DUK_HOBJECT_IS_PROXY(obj))) { - return 0; - } - h_proxy = (duk_hproxy *) obj; - DUK_HPROXY_ASSERT_VALID(h_proxy); - - DUK_ASSERT(h_proxy->handler != NULL); - DUK_ASSERT(h_proxy->target != NULL); - *out_handler = h_proxy->handler; - *out_target = h_proxy->target; - - return 1; -} -#endif /* DUK_USE_ES6_PROXY */ - -/* Get Proxy target object. If the argument is not a Proxy, return it as is. - * If a Proxy is revoked, an error is thrown. - */ -#if defined(DUK_USE_ES6_PROXY) -DUK_INTERNAL duk_hobject *duk_hobject_resolve_proxy_target(duk_hobject *obj) { - DUK_ASSERT(obj != NULL); - - /* Resolve Proxy targets until Proxy chain ends. No explicit check for - * a Proxy loop: user code cannot create such a loop (it would only be - * possible by editing duk_hproxy references directly). - */ - - while (DUK_HOBJECT_IS_PROXY(obj)) { - duk_hproxy *h_proxy; - - h_proxy = (duk_hproxy *) obj; - DUK_HPROXY_ASSERT_VALID(h_proxy); - obj = h_proxy->target; - DUK_ASSERT(obj != NULL); - } - - DUK_ASSERT(obj != NULL); - return obj; -} -#endif /* DUK_USE_ES6_PROXY */ - -#if defined(DUK_USE_ES6_PROXY) -DUK_LOCAL duk_bool_t duk__proxy_check_prop(duk_hthread *thr, - duk_hobject *obj, - duk_small_uint_t stridx_trap, - duk_tval *tv_key, - duk_hobject **out_target) { - duk_hobject *h_handler; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(obj != NULL); - DUK_ASSERT(tv_key != NULL); - DUK_ASSERT(out_target != NULL); - - if (!duk_hobject_proxy_check(obj, out_target, &h_handler)) { - return 0; - } - DUK_ASSERT(*out_target != NULL); - DUK_ASSERT(h_handler != NULL); - - /* XXX: At the moment Duktape accesses internal keys like _Finalizer using a - * normal property set/get which would allow a proxy handler to interfere with - * such behavior and to get access to internal key strings. This is not a problem - * as such because internal key strings can be created in other ways too (e.g. - * through buffers). The best fix is to change Duktape internal lookups to - * skip proxy behavior. Until that, internal property accesses bypass the - * proxy and are applied to the target (as if the handler did not exist). - * This has some side effects, see test-bi-proxy-internal-keys.js. - */ - - if (DUK_TVAL_IS_STRING(tv_key)) { - duk_hstring *h_key = (duk_hstring *) DUK_TVAL_GET_STRING(tv_key); - DUK_ASSERT(h_key != NULL); - if (DUK_HSTRING_HAS_HIDDEN(h_key)) { - /* Symbol accesses must go through proxy lookup in ES2015. - * Hidden symbols behave like Duktape 1.x internal keys - * and currently won't. - */ - DUK_DDD(DUK_DDDPRINT("hidden key, skip proxy handler and apply to target")); - return 0; - } - } - - /* The handler is looked up with a normal property lookup; it may be an - * accessor or the handler object itself may be a proxy object. If the - * handler is a proxy, we need to extend the valstack as we make a - * recursive proxy check without a function call in between (in fact - * there is no limit to the potential recursion here). - * - * (For sanity, proxy creation rejects another proxy object as either - * the handler or the target at the moment so recursive proxy cases - * are not realized now.) - */ - - /* XXX: C recursion limit if proxies are allowed as handler/target values */ - - duk_require_stack(thr, DUK__VALSTACK_PROXY_LOOKUP); - duk_push_hobject(thr, h_handler); - if (duk_get_prop_stridx_short(thr, -1, stridx_trap)) { - /* -> [ ... handler trap ] */ - duk_insert(thr, -2); /* -> [ ... trap handler ] */ - - /* stack prepped for func call: [ ... trap handler ] */ - return 1; - } else { - duk_pop_2_unsafe(thr); - return 0; - } -} -#endif /* DUK_USE_ES6_PROXY */ - -/* - * Reallocate property allocation, moving properties to the new allocation. - * - * Includes key compaction, rehashing, and can also optionally abandon - * the array part, 'migrating' array entries into the beginning of the - * new entry part. - * - * There is no support for in-place reallocation or just compacting keys - * without resizing the property allocation. This is intentional to keep - * code size minimal, but would be useful future work. - * - * The implementation is relatively straightforward, except for the array - * abandonment process. Array abandonment requires that new string keys - * are interned, which may trigger GC. All keys interned so far must be - * reachable for GC at all times and correctly refcounted for; valstack is - * used for that now. - * - * Also, a GC triggered during this reallocation process must not interfere - * with the object being resized. This is currently controlled by preventing - * finalizers (as they may affect ANY object) and object compaction in - * mark-and-sweep. It would suffice to protect only this particular object - * from compaction, however. DECREF refzero cascades are side effect free - * and OK. - * - * Note: because we need to potentially resize the valstack (as part - * of abandoning the array part), any tval pointers to the valstack - * will become invalid after this call. - */ - -DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr, - duk_hobject *obj, - duk_uint32_t new_e_size, - duk_uint32_t new_a_size, - duk_uint32_t new_h_size, - duk_bool_t abandon_array) { - duk_small_uint_t prev_ms_base_flags; - duk_uint32_t new_alloc_size; - duk_uint32_t new_e_size_adjusted; - duk_uint8_t *new_p; - duk_hstring **new_e_k; - duk_propvalue *new_e_pv; - duk_uint8_t *new_e_f; - duk_tval *new_a; - duk_uint32_t *new_h; - duk_uint32_t new_e_next; - duk_uint_fast32_t i; - duk_size_t array_copy_size; -#if defined(DUK_USE_ASSERTIONS) - duk_bool_t prev_error_not_allowed; -#endif - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(obj != NULL); - DUK_ASSERT(!abandon_array || new_a_size == 0); /* if abandon_array, new_a_size must be 0 */ - DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, obj) != NULL || - (DUK_HOBJECT_GET_ESIZE(obj) == 0 && DUK_HOBJECT_GET_ASIZE(obj) == 0)); - DUK_ASSERT(new_h_size == 0 || new_h_size >= new_e_size); /* required to guarantee success of rehashing, - * intentionally use unadjusted new_e_size - */ - DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)); - DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE); - - DUK_STATS_INC(thr->heap, stats_object_realloc_props); - - /* - * Pre resize assertions. - */ - -#if defined(DUK_USE_ASSERTIONS) - /* XXX: pre-checks (such as no duplicate keys) */ -#endif - - /* - * For property layout 1, tweak e_size to ensure that the whole entry - * part (key + val + flags) is a suitable multiple for alignment - * (platform specific). - * - * Property layout 2 does not require this tweaking and is preferred - * on low RAM platforms requiring alignment. - */ - -#if defined(DUK_USE_HOBJECT_LAYOUT_2) || defined(DUK_USE_HOBJECT_LAYOUT_3) - DUK_DDD(DUK_DDDPRINT("using layout 2 or 3, no need to pad e_size: %ld", (long) new_e_size)); - new_e_size_adjusted = new_e_size; -#elif defined(DUK_USE_HOBJECT_LAYOUT_1) && (DUK_HOBJECT_ALIGN_TARGET == 1) - DUK_DDD(DUK_DDDPRINT("using layout 1, but no need to pad e_size: %ld", (long) new_e_size)); - new_e_size_adjusted = new_e_size; -#elif defined(DUK_USE_HOBJECT_LAYOUT_1) && ((DUK_HOBJECT_ALIGN_TARGET == 4) || (DUK_HOBJECT_ALIGN_TARGET == 8)) - new_e_size_adjusted = - (new_e_size + (duk_uint32_t) DUK_HOBJECT_ALIGN_TARGET - 1U) & (~((duk_uint32_t) DUK_HOBJECT_ALIGN_TARGET - 1U)); - DUK_DDD(DUK_DDDPRINT("using layout 1, and alignment target is %ld, adjusted e_size: %ld -> %ld", - (long) DUK_HOBJECT_ALIGN_TARGET, - (long) new_e_size, - (long) new_e_size_adjusted)); - DUK_ASSERT(new_e_size_adjusted >= new_e_size); -#else -#error invalid hobject layout defines -#endif - - /* - * Debug logging after adjustment. - */ - - DUK_DDD(DUK_DDDPRINT( - "attempt to resize hobject %p props (%ld -> %ld bytes), from {p=%p,e_size=%ld,e_next=%ld,a_size=%ld,h_size=%ld} to " - "{e_size=%ld,a_size=%ld,h_size=%ld}, abandon_array=%ld, unadjusted new_e_size=%ld", - (void *) obj, - (long) DUK_HOBJECT_P_COMPUTE_SIZE(DUK_HOBJECT_GET_ESIZE(obj), DUK_HOBJECT_GET_ASIZE(obj), DUK_HOBJECT_GET_HSIZE(obj)), - (long) DUK_HOBJECT_P_COMPUTE_SIZE(new_e_size_adjusted, new_a_size, new_h_size), - (void *) DUK_HOBJECT_GET_PROPS(thr->heap, obj), - (long) DUK_HOBJECT_GET_ESIZE(obj), - (long) DUK_HOBJECT_GET_ENEXT(obj), - (long) DUK_HOBJECT_GET_ASIZE(obj), - (long) DUK_HOBJECT_GET_HSIZE(obj), - (long) new_e_size_adjusted, - (long) new_a_size, - (long) new_h_size, - (long) abandon_array, - (long) new_e_size)); - - /* - * Property count check. This is the only point where we ensure that - * we don't get more (allocated) property space that we can handle. - * There aren't hard limits as such, but some algorithms may fail - * if we get too close to the 4G property limit. - * - * Since this works based on allocation size (not actually used size), - * the limit is a bit approximate but good enough in practice. - */ - - if (new_e_size_adjusted + new_a_size > DUK_HOBJECT_MAX_PROPERTIES) { - DUK_ERROR_ALLOC_FAILED(thr); - DUK_WO_NORETURN(return;); - } -#if defined(DUK_USE_OBJSIZES16) - if (new_e_size_adjusted > DUK_UINT16_MAX || new_a_size > DUK_UINT16_MAX) { - /* If caller gave us sizes larger than what we can store, - * fail memory safely with an internal error rather than - * truncating the sizes. - */ - DUK_ERROR_INTERNAL(thr); - DUK_WO_NORETURN(return;); - } -#endif - - /* - * Compute new alloc size and alloc new area. - * - * The new area is not tracked in the heap at all, so it's critical - * we get to free/keep it in a controlled manner. - */ - -#if defined(DUK_USE_ASSERTIONS) - /* Whole path must be error throw free, but we may be called from - * within error handling so can't assert for error_not_allowed == 0. - */ - prev_error_not_allowed = thr->heap->error_not_allowed; - thr->heap->error_not_allowed = 1; -#endif - prev_ms_base_flags = thr->heap->ms_base_flags; - thr->heap->ms_base_flags |= - DUK_MS_FLAG_NO_OBJECT_COMPACTION; /* Avoid attempt to compact the current object (all objects really). */ - thr->heap->pf_prevent_count++; /* Avoid finalizers. */ - DUK_ASSERT(thr->heap->pf_prevent_count != 0); /* Wrap. */ - - new_alloc_size = DUK_HOBJECT_P_COMPUTE_SIZE(new_e_size_adjusted, new_a_size, new_h_size); - DUK_DDD(DUK_DDDPRINT("new hobject allocation size is %ld", (long) new_alloc_size)); - if (new_alloc_size == 0) { - DUK_ASSERT(new_e_size_adjusted == 0); - DUK_ASSERT(new_a_size == 0); - DUK_ASSERT(new_h_size == 0); - new_p = NULL; - } else { - /* Alloc may trigger mark-and-sweep but no compaction, and - * cannot throw. - */ -#if 0 /* XXX: inject test */ - if (1) { - new_p = NULL; - goto alloc_failed; - } -#endif - new_p = (duk_uint8_t *) DUK_ALLOC(thr->heap, new_alloc_size); - if (new_p == NULL) { - /* NULL always indicates alloc failure because - * new_alloc_size > 0. - */ - goto alloc_failed; - } - } - - /* Set up pointers to the new property area: this is hidden behind a macro - * because it is memory layout specific. - */ - DUK_HOBJECT_P_SET_REALLOC_PTRS(new_p, - new_e_k, - new_e_pv, - new_e_f, - new_a, - new_h, - new_e_size_adjusted, - new_a_size, - new_h_size); - DUK_UNREF(new_h); /* happens when hash part dropped */ - new_e_next = 0; - - /* if new_p == NULL, all of these pointers are NULL */ - DUK_ASSERT((new_p != NULL) || (new_e_k == NULL && new_e_pv == NULL && new_e_f == NULL && new_a == NULL && new_h == NULL)); - - DUK_DDD(DUK_DDDPRINT("new alloc size %ld, new_e_k=%p, new_e_pv=%p, new_e_f=%p, new_a=%p, new_h=%p", - (long) new_alloc_size, - (void *) new_e_k, - (void *) new_e_pv, - (void *) new_e_f, - (void *) new_a, - (void *) new_h)); - - /* - * Migrate array part to start of entries if requested. - * - * Note: from an enumeration perspective the order of entry keys matters. - * Array keys should appear wherever they appeared before the array abandon - * operation. (This no longer matters much because keys are ES2015 sorted.) - */ - - if (abandon_array) { - /* Assuming new_a_size == 0, and that entry part contains - * no conflicting keys, refcounts do not need to be adjusted for - * the values, as they remain exactly the same. - * - * The keys, however, need to be interned, incref'd, and be - * reachable for GC. Any intern attempt may trigger a GC and - * claim any non-reachable strings, so every key must be reachable - * at all times. Refcounts must be correct to satisfy refcount - * assertions. - * - * A longjmp must not occur here, as the new_p allocation would - * leak. Refcounts would come out correctly as the interned - * strings are valstack tracked. - */ - DUK_ASSERT(new_a_size == 0); - - DUK_STATS_INC(thr->heap, stats_object_abandon_array); - - for (i = 0; i < DUK_HOBJECT_GET_ASIZE(obj); i++) { - duk_tval *tv1; - duk_tval *tv2; - duk_hstring *key; - - DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, obj) != NULL); - - tv1 = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, i); - if (DUK_TVAL_IS_UNUSED(tv1)) { - continue; - } - - DUK_ASSERT(new_p != NULL && new_e_k != NULL && new_e_pv != NULL && new_e_f != NULL); - - /* - * Intern key via the valstack to ensure reachability behaves - * properly. We must avoid longjmp's here so use non-checked - * primitives. - * - * Note: duk_check_stack() potentially reallocs the valstack, - * invalidating any duk_tval pointers to valstack. Callers - * must be careful. - */ - -#if 0 /* XXX: inject test */ - if (1) { - goto abandon_error; - } -#endif - /* Never shrinks; auto-adds DUK_VALSTACK_INTERNAL_EXTRA, which - * is generous. - */ - if (!duk_check_stack(thr, 1)) { - goto abandon_error; - } - DUK_ASSERT_VALSTACK_SPACE(thr, 1); - key = duk_heap_strtable_intern_u32(thr->heap, (duk_uint32_t) i); - if (key == NULL) { - goto abandon_error; - } - duk_push_hstring(thr, key); /* keep key reachable for GC etc; guaranteed not to fail */ - - /* Key is now reachable in the valstack, don't INCREF - * the new allocation yet (we'll steal the refcounts - * from the value stack once all keys are done). - */ - - new_e_k[new_e_next] = key; - tv2 = &new_e_pv[new_e_next].v; /* array entries are all plain values */ - DUK_TVAL_SET_TVAL(tv2, tv1); - new_e_f[new_e_next] = - DUK_PROPDESC_FLAG_WRITABLE | DUK_PROPDESC_FLAG_ENUMERABLE | DUK_PROPDESC_FLAG_CONFIGURABLE; - new_e_next++; - - /* Note: new_e_next matches pushed temp key count, and nothing can - * fail above between the push and this point. - */ - } - - /* Steal refcounts from value stack. */ - DUK_DDD(DUK_DDDPRINT("abandon array: pop %ld key temps from valstack", (long) new_e_next)); - duk_pop_n_nodecref_unsafe(thr, (duk_idx_t) new_e_next); - } - - /* - * Copy keys and values in the entry part (compacting them at the same time). - */ - - for (i = 0; i < DUK_HOBJECT_GET_ENEXT(obj); i++) { - duk_hstring *key; - - DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, obj) != NULL); - - key = DUK_HOBJECT_E_GET_KEY(thr->heap, obj, i); - if (key == NULL) { - continue; - } - - DUK_ASSERT(new_p != NULL && new_e_k != NULL && new_e_pv != NULL && new_e_f != NULL); - - new_e_k[new_e_next] = key; - new_e_pv[new_e_next] = DUK_HOBJECT_E_GET_VALUE(thr->heap, obj, i); - new_e_f[new_e_next] = DUK_HOBJECT_E_GET_FLAGS(thr->heap, obj, i); - new_e_next++; - } - /* the entries [new_e_next, new_e_size_adjusted[ are left uninitialized on purpose (ok, not gc reachable) */ - - /* - * Copy array elements to new array part. If the new array part is - * larger, initialize the unused entries as UNUSED because they are - * GC reachable. - */ - -#if defined(DUK_USE_ASSERTIONS) - /* Caller must have decref'd values above new_a_size (if that is necessary). */ - if (!abandon_array) { - for (i = new_a_size; i < DUK_HOBJECT_GET_ASIZE(obj); i++) { - duk_tval *tv; - tv = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, i); - DUK_ASSERT(DUK_TVAL_IS_UNUSED(tv)); - } - } -#endif - if (new_a_size > DUK_HOBJECT_GET_ASIZE(obj)) { - array_copy_size = sizeof(duk_tval) * DUK_HOBJECT_GET_ASIZE(obj); - } else { - array_copy_size = sizeof(duk_tval) * new_a_size; - } - - DUK_ASSERT(new_a != NULL || array_copy_size == 0U); - DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, obj) != NULL || array_copy_size == 0U); - DUK_ASSERT(DUK_HOBJECT_GET_ASIZE(obj) > 0 || array_copy_size == 0U); - duk_memcpy_unsafe((void *) new_a, (const void *) DUK_HOBJECT_A_GET_BASE(thr->heap, obj), array_copy_size); - - for (i = DUK_HOBJECT_GET_ASIZE(obj); i < new_a_size; i++) { - duk_tval *tv = &new_a[i]; - DUK_TVAL_SET_UNUSED(tv); - } - - /* - * Rebuild the hash part always from scratch (guaranteed to finish - * as long as caller gave consistent parameters). - * - * Any resize of hash part requires rehashing. In addition, by rehashing - * get rid of any elements marked deleted (DUK__HASH_DELETED) which is critical - * to ensuring the hash part never fills up. - */ - -#if defined(DUK_USE_HOBJECT_HASH_PART) - if (new_h_size == 0) { - DUK_DDD(DUK_DDDPRINT("no hash part, no rehash")); - } else { - duk_uint32_t mask; - - DUK_ASSERT(new_h != NULL); - - /* fill new_h with u32 0xff = UNUSED */ - DUK_ASSERT(new_h_size > 0); - duk_memset(new_h, 0xff, sizeof(duk_uint32_t) * new_h_size); - - DUK_ASSERT(new_e_next <= new_h_size); /* equality not actually possible */ - - mask = new_h_size - 1; - for (i = 0; i < new_e_next; i++) { - duk_hstring *key = new_e_k[i]; - duk_uint32_t j, step; - - DUK_ASSERT(key != NULL); - j = DUK_HSTRING_GET_HASH(key) & mask; - step = 1; /* Cache friendly but clustering prone. */ - - for (;;) { - DUK_ASSERT(new_h[j] != DUK__HASH_DELETED); /* should never happen */ - if (new_h[j] == DUK__HASH_UNUSED) { - DUK_DDD(DUK_DDDPRINT("rebuild hit %ld -> %ld", (long) j, (long) i)); - new_h[j] = (duk_uint32_t) i; - break; - } - DUK_DDD(DUK_DDDPRINT("rebuild miss %ld, step %ld", (long) j, (long) step)); - j = (j + step) & mask; - - /* Guaranteed to finish (hash is larger than #props). */ - } - } - } -#endif /* DUK_USE_HOBJECT_HASH_PART */ - - /* - * Nice debug log. - */ - - DUK_DD(DUK_DDPRINT( - "resized hobject %p props (%ld -> %ld bytes), from {p=%p,e_size=%ld,e_next=%ld,a_size=%ld,h_size=%ld} to " - "{p=%p,e_size=%ld,e_next=%ld,a_size=%ld,h_size=%ld}, abandon_array=%ld, unadjusted new_e_size=%ld", - (void *) obj, - (long) DUK_HOBJECT_P_COMPUTE_SIZE(DUK_HOBJECT_GET_ESIZE(obj), DUK_HOBJECT_GET_ASIZE(obj), DUK_HOBJECT_GET_HSIZE(obj)), - (long) new_alloc_size, - (void *) DUK_HOBJECT_GET_PROPS(thr->heap, obj), - (long) DUK_HOBJECT_GET_ESIZE(obj), - (long) DUK_HOBJECT_GET_ENEXT(obj), - (long) DUK_HOBJECT_GET_ASIZE(obj), - (long) DUK_HOBJECT_GET_HSIZE(obj), - (void *) new_p, - (long) new_e_size_adjusted, - (long) new_e_next, - (long) new_a_size, - (long) new_h_size, - (long) abandon_array, - (long) new_e_size)); - - /* - * All done, switch properties ('p') allocation to new one. - */ - - DUK_FREE_CHECKED(thr, DUK_HOBJECT_GET_PROPS(thr->heap, obj)); /* NULL obj->p is OK */ - DUK_HOBJECT_SET_PROPS(thr->heap, obj, new_p); - DUK_HOBJECT_SET_ESIZE(obj, new_e_size_adjusted); - DUK_HOBJECT_SET_ENEXT(obj, new_e_next); - DUK_HOBJECT_SET_ASIZE(obj, new_a_size); - DUK_HOBJECT_SET_HSIZE(obj, new_h_size); - - /* Clear array part flag only after switching. */ - if (abandon_array) { - DUK_HOBJECT_CLEAR_ARRAY_PART(obj); - } - - DUK_DDD(DUK_DDDPRINT("resize result: %!O", (duk_heaphdr *) obj)); - - DUK_ASSERT(thr->heap->pf_prevent_count > 0); - thr->heap->pf_prevent_count--; - thr->heap->ms_base_flags = prev_ms_base_flags; -#if defined(DUK_USE_ASSERTIONS) - DUK_ASSERT(thr->heap->error_not_allowed == 1); - thr->heap->error_not_allowed = prev_error_not_allowed; -#endif - - /* - * Post resize assertions. - */ - -#if defined(DUK_USE_ASSERTIONS) - /* XXX: post-checks (such as no duplicate keys) */ -#endif - return; - - /* - * Abandon array failed. We don't need to DECREF anything - * because the references in the new allocation are not - * INCREF'd until abandon is complete. The string interned - * keys are on the value stack and are handled normally by - * unwind. - */ - -abandon_error: -alloc_failed: - DUK_D(DUK_DPRINT("object property table resize failed")); - - DUK_FREE_CHECKED(thr, new_p); /* OK for NULL. */ - - thr->heap->pf_prevent_count--; - thr->heap->ms_base_flags = prev_ms_base_flags; -#if defined(DUK_USE_ASSERTIONS) - DUK_ASSERT(thr->heap->error_not_allowed == 1); - thr->heap->error_not_allowed = prev_error_not_allowed; -#endif - - DUK_ERROR_ALLOC_FAILED(thr); - DUK_WO_NORETURN(return;); -} - -/* - * Helpers to resize properties allocation on specific needs. - */ - -DUK_INTERNAL void duk_hobject_resize_entrypart(duk_hthread *thr, duk_hobject *obj, duk_uint32_t new_e_size) { - duk_uint32_t old_e_size; - duk_uint32_t new_a_size; - duk_uint32_t new_h_size; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(obj != NULL); - - old_e_size = DUK_HOBJECT_GET_ESIZE(obj); - if (old_e_size > new_e_size) { - new_e_size = old_e_size; - } -#if defined(DUK_USE_HOBJECT_HASH_PART) - new_h_size = duk__get_default_h_size(new_e_size); -#else - new_h_size = 0; -#endif - new_a_size = DUK_HOBJECT_GET_ASIZE(obj); - - duk_hobject_realloc_props(thr, obj, new_e_size, new_a_size, new_h_size, 0); -} - -/* Grow entry part allocation for one additional entry. */ -DUK_LOCAL void duk__grow_props_for_new_entry_item(duk_hthread *thr, duk_hobject *obj) { - duk_uint32_t old_e_used; /* actually used, non-NULL entries */ - duk_uint32_t new_e_size_minimum; - duk_uint32_t new_e_size; - duk_uint32_t new_a_size; - duk_uint32_t new_h_size; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(obj != NULL); - - /* Duktape 0.11.0 and prior tried to optimize the resize by not - * counting the number of actually used keys prior to the resize. - * This worked mostly well but also caused weird leak-like behavior - * as in: test-bug-object-prop-alloc-unbounded.js. So, now we count - * the keys explicitly to compute the new entry part size. - */ - - old_e_used = duk__count_used_e_keys(thr, obj); - new_e_size_minimum = old_e_used + 1; - new_e_size = old_e_used + duk__get_min_grow_e(old_e_used); -#if defined(DUK_USE_HOBJECT_HASH_PART) - new_h_size = duk__get_default_h_size(new_e_size); -#else - new_h_size = 0; -#endif - new_a_size = DUK_HOBJECT_GET_ASIZE(obj); - -#if defined(DUK_USE_OBJSIZES16) - if (new_e_size > DUK_UINT16_MAX) { - new_e_size = DUK_UINT16_MAX; - } - if (new_h_size > DUK_UINT16_MAX) { - new_h_size = DUK_UINT16_MAX; - } - if (new_a_size > DUK_UINT16_MAX) { - new_a_size = DUK_UINT16_MAX; - } -#endif - DUK_ASSERT(new_h_size == 0 || new_h_size >= new_e_size); - - if (!(new_e_size >= new_e_size_minimum)) { - DUK_ERROR_ALLOC_FAILED(thr); - DUK_WO_NORETURN(return;); - } - - duk_hobject_realloc_props(thr, obj, new_e_size, new_a_size, new_h_size, 0); -} - -/* Grow array part for a new highest array index. */ -DUK_LOCAL void duk__grow_props_for_array_item(duk_hthread *thr, duk_hobject *obj, duk_uint32_t highest_arr_idx) { - duk_uint32_t new_e_size; - duk_uint32_t new_a_size; - duk_uint32_t new_a_size_minimum; - duk_uint32_t new_h_size; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(obj != NULL); - DUK_ASSERT(highest_arr_idx >= DUK_HOBJECT_GET_ASIZE(obj)); - - new_e_size = DUK_HOBJECT_GET_ESIZE(obj); - new_h_size = DUK_HOBJECT_GET_HSIZE(obj); - new_a_size_minimum = highest_arr_idx + 1; - new_a_size = highest_arr_idx + duk__get_min_grow_a(highest_arr_idx); - DUK_ASSERT(new_a_size >= highest_arr_idx + 1); /* duk__get_min_grow_a() is always >= 1 */ - -#if defined(DUK_USE_OBJSIZES16) - if (new_e_size > DUK_UINT16_MAX) { - new_e_size = DUK_UINT16_MAX; - } - if (new_h_size > DUK_UINT16_MAX) { - new_h_size = DUK_UINT16_MAX; - } - if (new_a_size > DUK_UINT16_MAX) { - new_a_size = DUK_UINT16_MAX; - } -#endif - - if (!(new_a_size >= new_a_size_minimum)) { - DUK_ERROR_ALLOC_FAILED(thr); - DUK_WO_NORETURN(return;); - } - - duk_hobject_realloc_props(thr, obj, new_e_size, new_a_size, new_h_size, 0); -} - -/* Abandon array part, moving array entries into entries part. - * This requires a props resize, which is a heavy operation. - * We also compact the entries part while we're at it, although - * this is not strictly required. - */ -DUK_LOCAL void duk__abandon_array_part(duk_hthread *thr, duk_hobject *obj) { - duk_uint32_t new_e_size_minimum; - duk_uint32_t new_e_size; - duk_uint32_t new_a_size; - duk_uint32_t new_h_size; - duk_uint32_t e_used; /* actually used, non-NULL keys */ - duk_uint32_t a_used; - duk_uint32_t a_size; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(obj != NULL); - - e_used = duk__count_used_e_keys(thr, obj); - duk__compute_a_stats(thr, obj, &a_used, &a_size); - - /* - * Must guarantee all actually used array entries will fit into - * new entry part. Add one growth step to ensure we don't run out - * of space right away. - */ - - new_e_size_minimum = e_used + a_used; - new_e_size = new_e_size_minimum + duk__get_min_grow_e(new_e_size_minimum); - new_a_size = 0; -#if defined(DUK_USE_HOBJECT_HASH_PART) - new_h_size = duk__get_default_h_size(new_e_size); -#else - new_h_size = 0; -#endif - -#if defined(DUK_USE_OBJSIZES16) - if (new_e_size > DUK_UINT16_MAX) { - new_e_size = DUK_UINT16_MAX; - } - if (new_h_size > DUK_UINT16_MAX) { - new_h_size = DUK_UINT16_MAX; - } - if (new_a_size > DUK_UINT16_MAX) { - new_a_size = DUK_UINT16_MAX; - } -#endif - - if (!(new_e_size >= new_e_size_minimum)) { - DUK_ERROR_ALLOC_FAILED(thr); - DUK_WO_NORETURN(return;); - } - - DUK_DD(DUK_DDPRINT("abandon array part for hobject %p, " - "array stats before: e_used=%ld, a_used=%ld, a_size=%ld; " - "resize to e_size=%ld, a_size=%ld, h_size=%ld", - (void *) obj, - (long) e_used, - (long) a_used, - (long) a_size, - (long) new_e_size, - (long) new_a_size, - (long) new_h_size)); - - duk_hobject_realloc_props(thr, obj, new_e_size, new_a_size, new_h_size, 1); -} - -/* - * Compact an object. Minimizes allocation size for objects which are - * not likely to be extended. This is useful for internal and non- - * extensible objects, but can also be called for non-extensible objects. - * May abandon the array part if it is computed to be too sparse. - * - * This call is relatively expensive, as it needs to scan both the - * entries and the array part. - * - * The call may fail due to allocation error. - */ - -DUK_INTERNAL void duk_hobject_compact_props(duk_hthread *thr, duk_hobject *obj) { - duk_uint32_t e_size; /* currently used -> new size */ - duk_uint32_t a_size; /* currently required */ - duk_uint32_t a_used; /* actually used */ - duk_uint32_t h_size; - duk_bool_t abandon_array; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(obj != NULL); - -#if defined(DUK_USE_ROM_OBJECTS) - if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)) { - DUK_DD(DUK_DDPRINT("ignore attempt to compact a rom object")); - return; - } -#endif - - e_size = duk__count_used_e_keys(thr, obj); - duk__compute_a_stats(thr, obj, &a_used, &a_size); - - DUK_DD(DUK_DDPRINT("compacting hobject, used e keys %ld, used a keys %ld, min a size %ld, " - "resized array density would be: %ld/%ld = %lf", - (long) e_size, - (long) a_used, - (long) a_size, - (long) a_used, - (long) a_size, - (double) a_used / (double) a_size)); - - if (duk__abandon_array_density_check(a_used, a_size)) { - DUK_DD(DUK_DDPRINT("decided to abandon array during compaction, a_used=%ld, a_size=%ld", - (long) a_used, - (long) a_size)); - abandon_array = 1; - e_size += a_used; - a_size = 0; - } else { - DUK_DD(DUK_DDPRINT("decided to keep array during compaction")); - abandon_array = 0; - } - -#if defined(DUK_USE_HOBJECT_HASH_PART) - if (e_size >= DUK_USE_HOBJECT_HASH_PROP_LIMIT) { - h_size = duk__get_default_h_size(e_size); - } else { - h_size = 0; - } -#else - h_size = 0; -#endif - - DUK_DD(DUK_DDPRINT("compacting hobject -> new e_size %ld, new a_size=%ld, new h_size=%ld, abandon_array=%ld", - (long) e_size, - (long) a_size, - (long) h_size, - (long) abandon_array)); - - duk_hobject_realloc_props(thr, obj, e_size, a_size, h_size, abandon_array); -} - -/* - * Find an existing key from entry part either by linear scan or by - * using the hash index (if it exists). - * - * Sets entry index (and possibly the hash index) to output variables, - * which allows the caller to update the entry and hash entries in-place. - * If entry is not found, both values are set to -1. If entry is found - * but there is no hash part, h_idx is set to -1. - */ - -DUK_INTERNAL duk_bool_t -duk_hobject_find_entry(duk_heap *heap, duk_hobject *obj, duk_hstring *key, duk_int_t *e_idx, duk_int_t *h_idx) { - DUK_ASSERT(obj != NULL); - DUK_ASSERT(key != NULL); - DUK_ASSERT(e_idx != NULL); - DUK_ASSERT(h_idx != NULL); - DUK_UNREF(heap); - - if (DUK_LIKELY(DUK_HOBJECT_GET_HSIZE(obj) == 0)) { - /* Linear scan: more likely because most objects are small. - * This is an important fast path. - * - * XXX: this might be worth inlining for property lookups. - */ - duk_uint_fast32_t i; - duk_uint_fast32_t n; - duk_hstring **h_keys_base; - DUK_DDD(DUK_DDDPRINT("duk_hobject_find_entry() using linear scan for lookup")); - - h_keys_base = DUK_HOBJECT_E_GET_KEY_BASE(heap, obj); - n = DUK_HOBJECT_GET_ENEXT(obj); - for (i = 0; i < n; i++) { - if (h_keys_base[i] == key) { - *e_idx = (duk_int_t) i; - *h_idx = -1; - return 1; - } - } - } -#if defined(DUK_USE_HOBJECT_HASH_PART) - else { - /* hash lookup */ - duk_uint32_t n; - duk_uint32_t i, step; - duk_uint32_t *h_base; - duk_uint32_t mask; - - DUK_DDD(DUK_DDDPRINT("duk_hobject_find_entry() using hash part for lookup")); - - h_base = DUK_HOBJECT_H_GET_BASE(heap, obj); - n = DUK_HOBJECT_GET_HSIZE(obj); - mask = n - 1; - i = DUK_HSTRING_GET_HASH(key) & mask; - step = 1; /* Cache friendly but clustering prone. */ - - for (;;) { - duk_uint32_t t; - - DUK_ASSERT_DISABLE(i >= 0); /* unsigned */ - DUK_ASSERT(i < DUK_HOBJECT_GET_HSIZE(obj)); - t = h_base[i]; - DUK_ASSERT(t == DUK__HASH_UNUSED || t == DUK__HASH_DELETED || - (t < DUK_HOBJECT_GET_ESIZE(obj))); /* t >= 0 always true, unsigned */ - - if (t == DUK__HASH_UNUSED) { - break; - } else if (t == DUK__HASH_DELETED) { - DUK_DDD(DUK_DDDPRINT("lookup miss (deleted) i=%ld, t=%ld", (long) i, (long) t)); - } else { - DUK_ASSERT(t < DUK_HOBJECT_GET_ESIZE(obj)); - if (DUK_HOBJECT_E_GET_KEY(heap, obj, t) == key) { - DUK_DDD( - DUK_DDDPRINT("lookup hit i=%ld, t=%ld -> key %p", (long) i, (long) t, (void *) key)); - *e_idx = (duk_int_t) t; - *h_idx = (duk_int_t) i; - return 1; - } - DUK_DDD(DUK_DDDPRINT("lookup miss i=%ld, t=%ld", (long) i, (long) t)); - } - i = (i + step) & mask; - - /* Guaranteed to finish (hash is larger than #props). */ - } - } -#endif /* DUK_USE_HOBJECT_HASH_PART */ - - /* Not found, leave e_idx and h_idx unset. */ - return 0; -} - -/* For internal use: get non-accessor entry value */ -DUK_INTERNAL duk_tval *duk_hobject_find_entry_tval_ptr(duk_heap *heap, duk_hobject *obj, duk_hstring *key) { - duk_int_t e_idx; - duk_int_t h_idx; - - DUK_ASSERT(obj != NULL); - DUK_ASSERT(key != NULL); - DUK_UNREF(heap); - - if (duk_hobject_find_entry(heap, obj, key, &e_idx, &h_idx)) { - DUK_ASSERT(e_idx >= 0); - if (!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(heap, obj, e_idx)) { - return DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(heap, obj, e_idx); - } - } - return NULL; -} - -DUK_INTERNAL duk_tval *duk_hobject_find_entry_tval_ptr_stridx(duk_heap *heap, duk_hobject *obj, duk_small_uint_t stridx) { - return duk_hobject_find_entry_tval_ptr(heap, obj, DUK_HEAP_GET_STRING(heap, stridx)); -} - -/* For internal use: get non-accessor entry value and attributes */ -DUK_INTERNAL duk_tval *duk_hobject_find_entry_tval_ptr_and_attrs(duk_heap *heap, - duk_hobject *obj, - duk_hstring *key, - duk_uint_t *out_attrs) { - duk_int_t e_idx; - duk_int_t h_idx; - - DUK_ASSERT(obj != NULL); - DUK_ASSERT(key != NULL); - DUK_ASSERT(out_attrs != NULL); - DUK_UNREF(heap); - - if (duk_hobject_find_entry(heap, obj, key, &e_idx, &h_idx)) { - DUK_ASSERT(e_idx >= 0); - if (!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(heap, obj, e_idx)) { - *out_attrs = DUK_HOBJECT_E_GET_FLAGS(heap, obj, e_idx); - return DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(heap, obj, e_idx); - } - } - /* If not found, out_attrs is left unset. */ - return NULL; -} - -/* For internal use: get array part value */ -DUK_INTERNAL duk_tval *duk_hobject_find_array_entry_tval_ptr(duk_heap *heap, duk_hobject *obj, duk_uarridx_t i) { - duk_tval *tv; - - DUK_ASSERT(obj != NULL); - DUK_UNREF(heap); - - if (!DUK_HOBJECT_HAS_ARRAY_PART(obj)) { - return NULL; - } - if (i >= DUK_HOBJECT_GET_ASIZE(obj)) { - return NULL; - } - tv = DUK_HOBJECT_A_GET_VALUE_PTR(heap, obj, i); - return tv; -} - -/* - * Allocate and initialize a new entry, resizing the properties allocation - * if necessary. Returns entry index (e_idx) or throws an error if alloc fails. - * - * Sets the key of the entry (increasing the key's refcount), and updates - * the hash part if it exists. Caller must set value and flags, and update - * the entry value refcount. A decref for the previous value is not necessary. - */ - -DUK_LOCAL duk_int_t duk__hobject_alloc_entry_checked(duk_hthread *thr, duk_hobject *obj, duk_hstring *key) { - duk_uint32_t idx; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(obj != NULL); - DUK_ASSERT(key != NULL); - DUK_ASSERT(DUK_HOBJECT_GET_ENEXT(obj) <= DUK_HOBJECT_GET_ESIZE(obj)); - -#if defined(DUK_USE_ASSERTIONS) - /* key must not already exist in entry part */ - { - duk_uint_fast32_t i; - for (i = 0; i < DUK_HOBJECT_GET_ENEXT(obj); i++) { - DUK_ASSERT(DUK_HOBJECT_E_GET_KEY(thr->heap, obj, i) != key); - } - } -#endif - - if (DUK_HOBJECT_GET_ENEXT(obj) >= DUK_HOBJECT_GET_ESIZE(obj)) { - /* only need to guarantee 1 more slot, but allocation growth is in chunks */ - DUK_DDD(DUK_DDDPRINT("entry part full, allocate space for one more entry")); - duk__grow_props_for_new_entry_item(thr, obj); - } - DUK_ASSERT(DUK_HOBJECT_GET_ENEXT(obj) < DUK_HOBJECT_GET_ESIZE(obj)); - idx = DUK_HOBJECT_POSTINC_ENEXT(obj); - - /* previous value is assumed to be garbage, so don't touch it */ - DUK_HOBJECT_E_SET_KEY(thr->heap, obj, idx, key); - DUK_HSTRING_INCREF(thr, key); - -#if defined(DUK_USE_HOBJECT_HASH_PART) - if (DUK_UNLIKELY(DUK_HOBJECT_GET_HSIZE(obj) > 0)) { - duk_uint32_t n, mask; - duk_uint32_t i, step; - duk_uint32_t *h_base = DUK_HOBJECT_H_GET_BASE(thr->heap, obj); - - n = DUK_HOBJECT_GET_HSIZE(obj); - mask = n - 1; - i = DUK_HSTRING_GET_HASH(key) & mask; - step = 1; /* Cache friendly but clustering prone. */ - - for (;;) { - duk_uint32_t t = h_base[i]; - if (t == DUK__HASH_UNUSED || t == DUK__HASH_DELETED) { - DUK_DDD(DUK_DDDPRINT("duk__hobject_alloc_entry_checked() inserted key into hash part, %ld -> %ld", - (long) i, - (long) idx)); - DUK_ASSERT_DISABLE(i >= 0); /* unsigned */ - DUK_ASSERT(i < DUK_HOBJECT_GET_HSIZE(obj)); - DUK_ASSERT_DISABLE(idx >= 0); - DUK_ASSERT(idx < DUK_HOBJECT_GET_ESIZE(obj)); - h_base[i] = idx; - break; - } - DUK_DDD(DUK_DDDPRINT("duk__hobject_alloc_entry_checked() miss %ld", (long) i)); - i = (i + step) & mask; - - /* Guaranteed to finish (hash is larger than #props). */ - } - } -#endif /* DUK_USE_HOBJECT_HASH_PART */ - - /* Note: we could return the hash index here too, but it's not - * needed right now. - */ - - DUK_ASSERT_DISABLE(idx >= 0); - DUK_ASSERT(idx < DUK_HOBJECT_GET_ESIZE(obj)); - DUK_ASSERT(idx < DUK_HOBJECT_GET_ENEXT(obj)); - return (duk_int_t) idx; -} - -/* - * Object internal value - * - * Returned value is guaranteed to be reachable / incref'd, caller does not need - * to incref OR decref. No proxies or accessors are invoked, no prototype walk. - */ - -DUK_INTERNAL duk_tval *duk_hobject_get_internal_value_tval_ptr(duk_heap *heap, duk_hobject *obj) { - return duk_hobject_find_entry_tval_ptr_stridx(heap, obj, DUK_STRIDX_INT_VALUE); -} - -DUK_LOCAL duk_heaphdr *duk_hobject_get_internal_value_heaphdr(duk_heap *heap, duk_hobject *obj) { - duk_tval *tv; - - DUK_ASSERT(heap != NULL); - DUK_ASSERT(obj != NULL); - - tv = duk_hobject_get_internal_value_tval_ptr(heap, obj); - if (tv != NULL) { - duk_heaphdr *h = DUK_TVAL_GET_HEAPHDR(tv); - DUK_ASSERT(h != NULL); - return h; - } - - return NULL; -} - -DUK_INTERNAL duk_hstring *duk_hobject_get_internal_value_string(duk_heap *heap, duk_hobject *obj) { - duk_hstring *h; - - h = (duk_hstring *) duk_hobject_get_internal_value_heaphdr(heap, obj); - if (h != NULL) { - DUK_ASSERT(DUK_HEAPHDR_IS_STRING((duk_heaphdr *) h)); - } - return h; -} - -DUK_LOCAL duk_hobject *duk__hobject_get_entry_object_stridx(duk_heap *heap, duk_hobject *obj, duk_small_uint_t stridx) { - duk_tval *tv; - duk_hobject *h; - - tv = duk_hobject_find_entry_tval_ptr_stridx(heap, obj, stridx); - if (tv != NULL && DUK_TVAL_IS_OBJECT(tv)) { - h = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - return h; - } - return NULL; -} - -DUK_INTERNAL duk_harray *duk_hobject_get_formals(duk_hthread *thr, duk_hobject *obj) { - duk_harray *h; - - h = (duk_harray *) duk__hobject_get_entry_object_stridx(thr->heap, obj, DUK_STRIDX_INT_FORMALS); - if (h != NULL) { - DUK_ASSERT(DUK_HOBJECT_IS_ARRAY((duk_hobject *) h)); - DUK_ASSERT(h->length <= DUK_HOBJECT_GET_ASIZE((duk_hobject *) h)); - } - return h; -} - -DUK_INTERNAL duk_hobject *duk_hobject_get_varmap(duk_hthread *thr, duk_hobject *obj) { - duk_hobject *h; - - h = duk__hobject_get_entry_object_stridx(thr->heap, obj, DUK_STRIDX_INT_VARMAP); - return h; -} - -/* - * Arguments handling helpers (argument map mainly). - * - * An arguments object has exotic behavior for some numeric indices. - * Accesses may translate to identifier operations which may have - * arbitrary side effects (potentially invalidating any duk_tval - * pointers). - */ - -/* Lookup 'key' from arguments internal 'map', perform a variable lookup - * if mapped, and leave the result on top of stack (and return non-zero). - * Used in E5 Section 10.6 algorithms [[Get]] and [[GetOwnProperty]]. - */ -DUK_LOCAL -duk_bool_t duk__lookup_arguments_map(duk_hthread *thr, - duk_hobject *obj, - duk_hstring *key, - duk_propdesc *temp_desc, - duk_hobject **out_map, - duk_hobject **out_varenv) { - duk_hobject *map; - duk_hobject *varenv; - duk_bool_t rc; - - DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE); - - DUK_DDD(DUK_DDDPRINT("arguments map lookup: thr=%p, obj=%p, key=%p, temp_desc=%p " - "(obj -> %!O, key -> %!O)", - (void *) thr, - (void *) obj, - (void *) key, - (void *) temp_desc, - (duk_heaphdr *) obj, - (duk_heaphdr *) key)); - - if (!duk_hobject_get_own_propdesc(thr, obj, DUK_HTHREAD_STRING_INT_MAP(thr), temp_desc, DUK_GETDESC_FLAG_PUSH_VALUE)) { - DUK_DDD(DUK_DDDPRINT("-> no 'map'")); - return 0; - } - - map = duk_require_hobject(thr, -1); - DUK_ASSERT(map != NULL); - duk_pop_unsafe(thr); /* map is reachable through obj */ - - if (!duk_hobject_get_own_propdesc(thr, map, key, temp_desc, DUK_GETDESC_FLAG_PUSH_VALUE)) { - DUK_DDD(DUK_DDDPRINT("-> 'map' exists, but key not in map")); - return 0; - } - - /* [... varname] */ - DUK_DDD(DUK_DDDPRINT("-> 'map' exists, and contains key, key is mapped to argument/variable binding %!T", - (duk_tval *) duk_get_tval(thr, -1))); - DUK_ASSERT(duk_is_string(thr, -1)); /* guaranteed when building arguments */ - - /* get varenv for varname (callee's declarative lexical environment) */ - rc = duk_hobject_get_own_propdesc(thr, obj, DUK_HTHREAD_STRING_INT_VARENV(thr), temp_desc, DUK_GETDESC_FLAG_PUSH_VALUE); - DUK_UNREF(rc); - DUK_ASSERT(rc != 0); /* arguments MUST have an initialized lexical environment reference */ - varenv = duk_require_hobject(thr, -1); - DUK_ASSERT(varenv != NULL); - duk_pop_unsafe(thr); /* varenv remains reachable through 'obj' */ - - DUK_DDD(DUK_DDDPRINT("arguments varenv is: %!dO", (duk_heaphdr *) varenv)); - - /* success: leave varname in stack */ - *out_map = map; - *out_varenv = varenv; - return 1; /* [... varname] */ -} - -/* Lookup 'key' from arguments internal 'map', and leave replacement value - * on stack top if mapped (and return non-zero). - * Used in E5 Section 10.6 algorithm for [[GetOwnProperty]] (used by [[Get]]). - */ -DUK_LOCAL duk_bool_t duk__check_arguments_map_for_get(duk_hthread *thr, - duk_hobject *obj, - duk_hstring *key, - duk_propdesc *temp_desc) { - duk_hobject *map; - duk_hobject *varenv; - duk_hstring *varname; - - DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE); - - if (!duk__lookup_arguments_map(thr, obj, key, temp_desc, &map, &varenv)) { - DUK_DDD(DUK_DDDPRINT("arguments: key not mapped, no exotic get behavior")); - return 0; - } - - /* [... varname] */ - - varname = duk_require_hstring(thr, -1); - DUK_ASSERT(varname != NULL); - duk_pop_unsafe(thr); /* varname is still reachable */ - - DUK_DDD(DUK_DDDPRINT("arguments object automatic getvar for a bound variable; " - "key=%!O, varname=%!O", - (duk_heaphdr *) key, - (duk_heaphdr *) varname)); - - (void) duk_js_getvar_envrec(thr, varenv, varname, 1 /*throw*/); - - /* [... value this_binding] */ - - duk_pop_unsafe(thr); - - /* leave result on stack top */ - return 1; -} - -/* Lookup 'key' from arguments internal 'map', perform a variable write if mapped. - * Used in E5 Section 10.6 algorithm for [[DefineOwnProperty]] (used by [[Put]]). - * Assumes stack top contains 'put' value (which is NOT popped). - */ -DUK_LOCAL void duk__check_arguments_map_for_put(duk_hthread *thr, - duk_hobject *obj, - duk_hstring *key, - duk_propdesc *temp_desc, - duk_bool_t throw_flag) { - duk_hobject *map; - duk_hobject *varenv; - duk_hstring *varname; - - DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE); - - if (!duk__lookup_arguments_map(thr, obj, key, temp_desc, &map, &varenv)) { - DUK_DDD(DUK_DDDPRINT("arguments: key not mapped, no exotic put behavior")); - return; - } - - /* [... put_value varname] */ - - varname = duk_require_hstring(thr, -1); - DUK_ASSERT(varname != NULL); - duk_pop_unsafe(thr); /* varname is still reachable */ - - DUK_DDD(DUK_DDDPRINT("arguments object automatic putvar for a bound variable; " - "key=%!O, varname=%!O, value=%!T", - (duk_heaphdr *) key, - (duk_heaphdr *) varname, - (duk_tval *) duk_require_tval(thr, -1))); - - /* [... put_value] */ - - /* - * Note: although arguments object variable mappings are only established - * for non-strict functions (and a call to a non-strict function created - * the arguments object in question), an inner strict function may be doing - * the actual property write. Hence the throw_flag applied here comes from - * the property write call. - */ - - duk_js_putvar_envrec(thr, varenv, varname, duk_require_tval(thr, -1), throw_flag); - - /* [... put_value] */ -} - -/* Lookup 'key' from arguments internal 'map', delete mapping if found. - * Used in E5 Section 10.6 algorithm for [[Delete]]. Note that the - * variable/argument itself (where the map points) is not deleted. - */ -DUK_LOCAL void duk__check_arguments_map_for_delete(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_propdesc *temp_desc) { - duk_hobject *map; - - DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE); - - if (!duk_hobject_get_own_propdesc(thr, obj, DUK_HTHREAD_STRING_INT_MAP(thr), temp_desc, DUK_GETDESC_FLAG_PUSH_VALUE)) { - DUK_DDD(DUK_DDDPRINT("arguments: key not mapped, no exotic delete behavior")); - return; - } - - map = duk_require_hobject(thr, -1); - DUK_ASSERT(map != NULL); - duk_pop_unsafe(thr); /* map is reachable through obj */ - - DUK_DDD(DUK_DDDPRINT("-> have 'map', delete key %!O from map (if exists)); ignore result", (duk_heaphdr *) key)); - - /* Note: no recursion issue, we can trust 'map' to behave */ - DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_BEHAVIOR(map)); - DUK_DDD(DUK_DDDPRINT("map before deletion: %!O", (duk_heaphdr *) map)); - (void) duk_hobject_delprop_raw(thr, map, key, 0); /* ignore result */ - DUK_DDD(DUK_DDDPRINT("map after deletion: %!O", (duk_heaphdr *) map)); -} - -/* - * ECMAScript compliant [[GetOwnProperty]](P), for internal use only. - * - * If property is found: - * - Fills descriptor fields to 'out_desc' - * - If DUK_GETDESC_FLAG_PUSH_VALUE is set, pushes a value related to the - * property onto the stack ('undefined' for accessor properties). - * - Returns non-zero - * - * If property is not found: - * - 'out_desc' is left in untouched state (possibly garbage) - * - Nothing is pushed onto the stack (not even with DUK_GETDESC_FLAG_PUSH_VALUE - * set) - * - Returns zero - * - * Notes: - * - * - Getting a property descriptor may cause an allocation (and hence - * GC) to take place, hence reachability and refcount of all related - * values matter. Reallocation of value stack, properties, etc may - * invalidate many duk_tval pointers (concretely, those which reside - * in memory areas subject to reallocation). However, heap object - * pointers are never affected (heap objects have stable pointers). - * - * - The value of a plain property is always reachable and has a non-zero - * reference count. - * - * - The value of a virtual property is not necessarily reachable from - * elsewhere and may have a refcount of zero. Hence we push it onto - * the valstack for the caller, which ensures it remains reachable - * while it is needed. - * - * - There are no virtual accessor properties. Hence, all getters and - * setters are always related to concretely stored properties, which - * ensures that the get/set functions in the resulting descriptor are - * reachable and have non-zero refcounts. Should there be virtual - * accessor properties later, this would need to change. - */ - -DUK_LOCAL duk_bool_t duk__get_own_propdesc_raw(duk_hthread *thr, - duk_hobject *obj, - duk_hstring *key, - duk_uint32_t arr_idx, - duk_propdesc *out_desc, - duk_small_uint_t flags) { - duk_tval *tv; - - DUK_DDD(DUK_DDDPRINT("duk_hobject_get_own_propdesc: thr=%p, obj=%p, key=%p, out_desc=%p, flags=%lx, " - "arr_idx=%ld (obj -> %!O, key -> %!O)", - (void *) thr, - (void *) obj, - (void *) key, - (void *) out_desc, - (long) flags, - (long) arr_idx, - (duk_heaphdr *) obj, - (duk_heaphdr *) key)); - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - DUK_ASSERT(obj != NULL); - DUK_ASSERT(key != NULL); - DUK_ASSERT(out_desc != NULL); - DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE); - - DUK_STATS_INC(thr->heap, stats_getownpropdesc_count); - - /* Each code path returning 1 (= found) must fill in all the output - * descriptor fields. We don't do it beforehand because it'd be - * unnecessary work if the property isn't found and would happen - * multiple times for an inheritance chain. - */ - DUK_ASSERT_SET_GARBAGE(out_desc, sizeof(*out_desc)); -#if 0 - out_desc->flags = 0; - out_desc->get = NULL; - out_desc->set = NULL; - out_desc->e_idx = -1; - out_desc->h_idx = -1; - out_desc->a_idx = -1; -#endif - - /* - * Try entries part first because it's the common case. - * - * Array part lookups are usually handled by the array fast path, and - * are not usually inherited. Array and entry parts never contain the - * same keys so the entry part vs. array part order doesn't matter. - */ - - if (duk_hobject_find_entry(thr->heap, obj, key, &out_desc->e_idx, &out_desc->h_idx)) { - duk_int_t e_idx = out_desc->e_idx; - DUK_ASSERT(out_desc->e_idx >= 0); - out_desc->a_idx = -1; - out_desc->flags = DUK_HOBJECT_E_GET_FLAGS(thr->heap, obj, e_idx); - out_desc->get = NULL; - out_desc->set = NULL; - if (DUK_UNLIKELY(out_desc->flags & DUK_PROPDESC_FLAG_ACCESSOR)) { - DUK_DDD(DUK_DDDPRINT("-> found accessor property in entry part")); - out_desc->get = DUK_HOBJECT_E_GET_VALUE_GETTER(thr->heap, obj, e_idx); - out_desc->set = DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, obj, e_idx); - if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) { - /* a dummy undefined value is pushed to make valstack - * behavior uniform for caller - */ - duk_push_undefined(thr); - } - } else { - DUK_DDD(DUK_DDDPRINT("-> found plain property in entry part")); - tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, e_idx); - if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) { - duk_push_tval(thr, tv); - } - } - goto prop_found; - } - - /* - * Try array part. - */ - - if (DUK_HOBJECT_HAS_ARRAY_PART(obj) && arr_idx != DUK__NO_ARRAY_INDEX) { - if (arr_idx < DUK_HOBJECT_GET_ASIZE(obj)) { - tv = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, arr_idx); - if (!DUK_TVAL_IS_UNUSED(tv)) { - DUK_DDD(DUK_DDDPRINT("-> found in array part")); - if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) { - duk_push_tval(thr, tv); - } - /* implicit attributes */ - out_desc->flags = - DUK_PROPDESC_FLAG_WRITABLE | DUK_PROPDESC_FLAG_CONFIGURABLE | DUK_PROPDESC_FLAG_ENUMERABLE; - out_desc->get = NULL; - out_desc->set = NULL; - out_desc->e_idx = -1; - out_desc->h_idx = -1; - out_desc->a_idx = (duk_int_t) arr_idx; /* XXX: limit 2G due to being signed */ - goto prop_found; - } - } - } - - DUK_DDD(DUK_DDDPRINT("-> not found as a concrete property")); - - /* - * Not found as a concrete property, check for virtual properties. - */ - - if (!DUK_HOBJECT_HAS_VIRTUAL_PROPERTIES(obj)) { - /* Quick skip. */ - goto prop_not_found; - } - - if (DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj)) { - duk_harray *a; - - DUK_DDD(DUK_DDDPRINT("array object exotic property get for key: %!O, arr_idx: %ld", - (duk_heaphdr *) key, - (long) arr_idx)); - - a = (duk_harray *) obj; - DUK_HARRAY_ASSERT_VALID(a); - - if (key == DUK_HTHREAD_STRING_LENGTH(thr)) { - DUK_DDD(DUK_DDDPRINT("-> found, key is 'length', length exotic behavior")); - - if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) { - duk_push_uint(thr, (duk_uint_t) a->length); - } - out_desc->flags = DUK_PROPDESC_FLAG_VIRTUAL; - if (DUK_HARRAY_LENGTH_WRITABLE(a)) { - out_desc->flags |= DUK_PROPDESC_FLAG_WRITABLE; - } - out_desc->get = NULL; - out_desc->set = NULL; - out_desc->e_idx = -1; - out_desc->h_idx = -1; - out_desc->a_idx = -1; - - DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj)); - goto prop_found_noexotic; /* cannot be arguments exotic */ - } - } else if (DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(obj)) { - DUK_DDD(DUK_DDDPRINT("string object exotic property get for key: %!O, arr_idx: %ld", - (duk_heaphdr *) key, - (long) arr_idx)); - - /* XXX: charlen; avoid multiple lookups? */ - - if (arr_idx != DUK__NO_ARRAY_INDEX) { - duk_hstring *h_val; - - DUK_DDD(DUK_DDDPRINT("array index exists")); - - h_val = duk_hobject_get_internal_value_string(thr->heap, obj); - DUK_ASSERT(h_val); - if (arr_idx < DUK_HSTRING_GET_CHARLEN(h_val)) { - DUK_DDD(DUK_DDDPRINT("-> found, array index inside string")); - if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) { - duk_push_hstring(thr, h_val); - duk_substring(thr, -1, arr_idx, arr_idx + 1); /* [str] -> [substr] */ - } - out_desc->flags = DUK_PROPDESC_FLAG_ENUMERABLE | /* E5 Section 15.5.5.2 */ - DUK_PROPDESC_FLAG_VIRTUAL; - out_desc->get = NULL; - out_desc->set = NULL; - out_desc->e_idx = -1; - out_desc->h_idx = -1; - out_desc->a_idx = -1; - - DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj)); - goto prop_found_noexotic; /* cannot be arguments exotic */ - } else { - /* index is above internal string length -> property is fully normal */ - DUK_DDD(DUK_DDDPRINT("array index outside string -> normal property")); - } - } else if (key == DUK_HTHREAD_STRING_LENGTH(thr)) { - duk_hstring *h_val; - - DUK_DDD(DUK_DDDPRINT("-> found, key is 'length', length exotic behavior")); - - h_val = duk_hobject_get_internal_value_string(thr->heap, obj); - DUK_ASSERT(h_val != NULL); - if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) { - duk_push_uint(thr, (duk_uint_t) DUK_HSTRING_GET_CHARLEN(h_val)); - } - out_desc->flags = DUK_PROPDESC_FLAG_VIRTUAL; /* E5 Section 15.5.5.1 */ - out_desc->get = NULL; - out_desc->set = NULL; - out_desc->e_idx = -1; - out_desc->h_idx = -1; - out_desc->a_idx = -1; - - DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj)); - goto prop_found_noexotic; /* cannot be arguments exotic */ - } - } -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) - else if (DUK_HOBJECT_IS_BUFOBJ(obj)) { - duk_hbufobj *h_bufobj; - duk_uint_t byte_off; - duk_small_uint_t elem_size; - - h_bufobj = (duk_hbufobj *) obj; - DUK_HBUFOBJ_ASSERT_VALID(h_bufobj); - DUK_DDD(DUK_DDDPRINT("bufobj property get for key: %!O, arr_idx: %ld", (duk_heaphdr *) key, (long) arr_idx)); - - if (arr_idx != DUK__NO_ARRAY_INDEX && DUK_HBUFOBJ_HAS_VIRTUAL_INDICES(h_bufobj)) { - DUK_DDD(DUK_DDDPRINT("array index exists")); - - /* Careful with wrapping: arr_idx upshift may easily wrap, whereas - * length downshift won't. - */ - if (arr_idx < (h_bufobj->length >> h_bufobj->shift)) { - byte_off = arr_idx << h_bufobj->shift; /* no wrap assuming h_bufobj->length is valid */ - elem_size = (duk_small_uint_t) (1U << h_bufobj->shift); - if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) { - duk_uint8_t *data; - - if (h_bufobj->buf != NULL && - DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h_bufobj, byte_off + elem_size)) { - data = (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_bufobj->buf) + - h_bufobj->offset + byte_off; - duk_hbufobj_push_validated_read(thr, h_bufobj, data, elem_size); - } else { - DUK_D(DUK_DPRINT("bufobj access out of underlying buffer, ignoring (read zero)")); - duk_push_uint(thr, 0); - } - } - out_desc->flags = DUK_PROPDESC_FLAG_WRITABLE | DUK_PROPDESC_FLAG_VIRTUAL; - if (DUK_HOBJECT_GET_CLASS_NUMBER(obj) != DUK_HOBJECT_CLASS_ARRAYBUFFER) { - /* ArrayBuffer indices are non-standard and are - * non-enumerable to avoid their serialization. - */ - out_desc->flags |= DUK_PROPDESC_FLAG_ENUMERABLE; - } - out_desc->get = NULL; - out_desc->set = NULL; - out_desc->e_idx = -1; - out_desc->h_idx = -1; - out_desc->a_idx = -1; - - DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj)); - goto prop_found_noexotic; /* cannot be e.g. arguments exotic, since exotic 'traits' are mutually - exclusive */ - } else { - /* index is above internal buffer length -> property is fully normal */ - DUK_DDD(DUK_DDDPRINT("array index outside buffer -> normal property")); - } - } else if (key == DUK_HTHREAD_STRING_LENGTH(thr) && DUK_HBUFOBJ_HAS_VIRTUAL_INDICES(h_bufobj)) { - DUK_DDD(DUK_DDDPRINT("-> found, key is 'length', length exotic behavior")); - - if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) { - /* Length in elements: take into account shift, but - * intentionally don't check the underlying buffer here. - */ - duk_push_uint(thr, h_bufobj->length >> h_bufobj->shift); - } - out_desc->flags = DUK_PROPDESC_FLAG_VIRTUAL; - out_desc->get = NULL; - out_desc->set = NULL; - out_desc->e_idx = -1; - out_desc->h_idx = -1; - out_desc->a_idx = -1; - - DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj)); - goto prop_found_noexotic; /* cannot be arguments exotic */ - } - } -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - - /* Array properties have exotic behavior but they are concrete, - * so no special handling here. - * - * Arguments exotic behavior (E5 Section 10.6, [[GetOwnProperty]] - * is only relevant as a post-check implemented below; hence no - * check here. - */ - - /* - * Not found as concrete or virtual. - */ - -prop_not_found: - DUK_DDD(DUK_DDDPRINT("-> not found (virtual, entry part, or array part)")); - DUK_STATS_INC(thr->heap, stats_getownpropdesc_miss); - return 0; - - /* - * Found. - * - * Arguments object has exotic post-processing, see E5 Section 10.6, - * description of [[GetOwnProperty]] variant for arguments. - */ - -prop_found: - DUK_DDD(DUK_DDDPRINT("-> property found, checking for arguments exotic post-behavior")); - - /* Notes: - * - Only numbered indices are relevant, so arr_idx fast reject is good - * (this is valid unless there are more than 4**32-1 arguments). - * - Since variable lookup has no side effects, this can be skipped if - * DUK_GETDESC_FLAG_PUSH_VALUE is not set. - */ - - if (DUK_UNLIKELY(DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj) && arr_idx != DUK__NO_ARRAY_INDEX && - (flags & DUK_GETDESC_FLAG_PUSH_VALUE))) { - duk_propdesc temp_desc; - - /* Magically bound variable cannot be an accessor. However, - * there may be an accessor property (or a plain property) in - * place with magic behavior removed. This happens e.g. when - * a magic property is redefined with defineProperty(). - * Cannot assert for "not accessor" here. - */ - - /* replaces top of stack with new value if necessary */ - DUK_ASSERT((flags & DUK_GETDESC_FLAG_PUSH_VALUE) != 0); - - /* This can perform a variable lookup but only into a declarative - * environment which has no side effects. - */ - if (duk__check_arguments_map_for_get(thr, obj, key, &temp_desc)) { - DUK_DDD(DUK_DDDPRINT("-> arguments exotic behavior overrides result: %!T -> %!T", - (duk_tval *) duk_get_tval(thr, -2), - (duk_tval *) duk_get_tval(thr, -1))); - /* [... old_result result] -> [... result] */ - duk_remove_m2(thr); - } - } - -prop_found_noexotic: - DUK_STATS_INC(thr->heap, stats_getownpropdesc_hit); - return 1; -} - -DUK_INTERNAL duk_bool_t -duk_hobject_get_own_propdesc(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_propdesc *out_desc, duk_small_uint_t flags) { - DUK_ASSERT(thr != NULL); - DUK_ASSERT(obj != NULL); - DUK_ASSERT(key != NULL); - DUK_ASSERT(out_desc != NULL); - DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE); - - return duk__get_own_propdesc_raw(thr, obj, key, DUK_HSTRING_GET_ARRIDX_SLOW(key), out_desc, flags); -} - -/* - * ECMAScript compliant [[GetProperty]](P), for internal use only. - * - * If property is found: - * - Fills descriptor fields to 'out_desc' - * - If DUK_GETDESC_FLAG_PUSH_VALUE is set, pushes a value related to the - * property onto the stack ('undefined' for accessor properties). - * - Returns non-zero - * - * If property is not found: - * - 'out_desc' is left in untouched state (possibly garbage) - * - Nothing is pushed onto the stack (not even with DUK_GETDESC_FLAG_PUSH_VALUE - * set) - * - Returns zero - * - * May cause arbitrary side effects and invalidate (most) duk_tval - * pointers. - */ - -DUK_LOCAL duk_bool_t -duk__get_propdesc(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_propdesc *out_desc, duk_small_uint_t flags) { - duk_hobject *curr; - duk_uint32_t arr_idx; - duk_uint_t sanity; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - DUK_ASSERT(obj != NULL); - DUK_ASSERT(key != NULL); - DUK_ASSERT(out_desc != NULL); - DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE); - - DUK_STATS_INC(thr->heap, stats_getpropdesc_count); - - arr_idx = DUK_HSTRING_GET_ARRIDX_FAST(key); - - DUK_DDD(DUK_DDDPRINT("duk__get_propdesc: thr=%p, obj=%p, key=%p, out_desc=%p, flags=%lx, " - "arr_idx=%ld (obj -> %!O, key -> %!O)", - (void *) thr, - (void *) obj, - (void *) key, - (void *) out_desc, - (long) flags, - (long) arr_idx, - (duk_heaphdr *) obj, - (duk_heaphdr *) key)); - - curr = obj; - DUK_ASSERT(curr != NULL); - sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY; - do { - if (duk__get_own_propdesc_raw(thr, curr, key, arr_idx, out_desc, flags)) { - /* stack contains value (if requested), 'out_desc' is set */ - DUK_STATS_INC(thr->heap, stats_getpropdesc_hit); - return 1; - } - - /* not found in 'curr', next in prototype chain; impose max depth */ - if (DUK_UNLIKELY(sanity-- == 0)) { - if (flags & DUK_GETDESC_FLAG_IGNORE_PROTOLOOP) { - /* treat like property not found */ - break; - } else { - DUK_ERROR_RANGE(thr, DUK_STR_PROTOTYPE_CHAIN_LIMIT); - DUK_WO_NORETURN(return 0;); - } - } - curr = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, curr); - } while (curr != NULL); - - /* out_desc is left untouched (possibly garbage), caller must use return - * value to determine whether out_desc can be looked up - */ - - DUK_STATS_INC(thr->heap, stats_getpropdesc_miss); - return 0; -} - -/* - * Shallow fast path checks for accessing array elements with numeric - * indices. The goal is to try to avoid coercing an array index to an - * (interned) string for the most common lookups, in particular, for - * standard Array objects. - * - * Interning is avoided but only for a very narrow set of cases: - * - Object has array part, index is within array allocation, and - * value is not unused (= key exists) - * - Object has no interfering exotic behavior (e.g. arguments or - * string object exotic behaviors interfere, array exotic - * behavior does not). - * - * Current shortcoming: if key does not exist (even if it is within - * the array allocation range) a slow path lookup with interning is - * always required. This can probably be fixed so that there is a - * quick fast path for non-existent elements as well, at least for - * standard Array objects. - */ - -#if defined(DUK_USE_ARRAY_PROP_FASTPATH) -DUK_LOCAL duk_tval *duk__getprop_shallow_fastpath_array_tval(duk_hthread *thr, duk_hobject *obj, duk_tval *tv_key) { - duk_tval *tv; - duk_uint32_t idx; - - DUK_UNREF(thr); - - if (!(DUK_HOBJECT_HAS_ARRAY_PART(obj) && !DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj) && !DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(obj) && - !DUK_HOBJECT_IS_BUFOBJ(obj) && !DUK_HOBJECT_IS_PROXY(obj))) { - /* Must have array part and no conflicting exotic behaviors. - * Doesn't need to have array special behavior, e.g. Arguments - * object has array part. - */ - return NULL; - } - - /* Arrays never have other exotic behaviors. */ - - DUK_DDD(DUK_DDDPRINT("fast path attempt (no exotic string/arguments/buffer " - "behavior, object has array part)")); - -#if defined(DUK_USE_FASTINT) - if (DUK_TVAL_IS_FASTINT(tv_key)) { - idx = duk__tval_fastint_to_arr_idx(tv_key); - } else -#endif - if (DUK_TVAL_IS_DOUBLE(tv_key)) { - idx = duk__tval_number_to_arr_idx(tv_key); - } else { - DUK_DDD(DUK_DDDPRINT("key is not a number")); - return NULL; - } - - /* If index is not valid, idx will be DUK__NO_ARRAY_INDEX which - * is 0xffffffffUL. We don't need to check for that explicitly - * because 0xffffffffUL will never be inside object 'a_size'. - */ - - if (idx >= DUK_HOBJECT_GET_ASIZE(obj)) { - DUK_DDD(DUK_DDDPRINT("key is not an array index or outside array part")); - return NULL; - } - DUK_ASSERT(idx != 0xffffffffUL); - DUK_ASSERT(idx != DUK__NO_ARRAY_INDEX); - - /* XXX: for array instances we could take a shortcut here and assume - * Array.prototype doesn't contain an array index property. - */ - - DUK_DDD(DUK_DDDPRINT("key is a valid array index and inside array part")); - tv = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, idx); - if (!DUK_TVAL_IS_UNUSED(tv)) { - DUK_DDD(DUK_DDDPRINT("-> fast path successful")); - return tv; - } - - DUK_DDD(DUK_DDDPRINT("fast path attempt failed, fall back to slow path")); - return NULL; -} - -DUK_LOCAL duk_bool_t duk__putprop_shallow_fastpath_array_tval(duk_hthread *thr, - duk_hobject *obj, - duk_tval *tv_key, - duk_tval *tv_val) { - duk_tval *tv; - duk_harray *a; - duk_uint32_t idx; - duk_uint32_t old_len, new_len; - - if (!(DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj) && DUK_HOBJECT_HAS_ARRAY_PART(obj) && DUK_HOBJECT_HAS_EXTENSIBLE(obj))) { - return 0; - } - DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)); /* caller ensures */ - - a = (duk_harray *) obj; - DUK_HARRAY_ASSERT_VALID(a); - -#if defined(DUK_USE_FASTINT) - if (DUK_TVAL_IS_FASTINT(tv_key)) { - idx = duk__tval_fastint_to_arr_idx(tv_key); - } else -#endif - if (DUK_TVAL_IS_DOUBLE(tv_key)) { - idx = duk__tval_number_to_arr_idx(tv_key); - } else { - DUK_DDD(DUK_DDDPRINT("key is not a number")); - return 0; - } - - /* If index is not valid, idx will be DUK__NO_ARRAY_INDEX which - * is 0xffffffffUL. We don't need to check for that explicitly - * because 0xffffffffUL will never be inside object 'a_size'. - */ - - if (idx >= DUK_HOBJECT_GET_ASIZE(obj)) { /* for resizing of array part, use slow path */ - return 0; - } - DUK_ASSERT(idx != 0xffffffffUL); - DUK_ASSERT(idx != DUK__NO_ARRAY_INDEX); - - old_len = a->length; - - if (idx >= old_len) { - DUK_DDD(DUK_DDDPRINT("write new array entry requires length update " - "(arr_idx=%ld, old_len=%ld)", - (long) idx, - (long) old_len)); - if (DUK_HARRAY_LENGTH_NONWRITABLE(a)) { - /* The correct behavior here is either a silent error - * or a TypeError, depending on strictness. Fall back - * to the slow path to handle the situation. - */ - return 0; - } - new_len = idx + 1; - - ((duk_harray *) obj)->length = new_len; - } - - tv = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, idx); - DUK_TVAL_SET_TVAL_UPDREF(thr, tv, tv_val); /* side effects */ - - DUK_DDD(DUK_DDDPRINT("array fast path success for index %ld", (long) idx)); - return 1; -} -#endif /* DUK_USE_ARRAY_PROP_FASTPATH */ - -/* - * Fast path for bufobj getprop/putprop - */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_LOCAL duk_bool_t duk__getprop_fastpath_bufobj_tval(duk_hthread *thr, duk_hobject *obj, duk_tval *tv_key) { - duk_uint32_t idx; - duk_hbufobj *h_bufobj; - duk_uint_t byte_off; - duk_small_uint_t elem_size; - duk_uint8_t *data; - - if (!DUK_HOBJECT_IS_BUFOBJ(obj)) { - return 0; - } - h_bufobj = (duk_hbufobj *) obj; - if (!DUK_HBUFOBJ_HAS_VIRTUAL_INDICES(h_bufobj)) { - return 0; - } - -#if defined(DUK_USE_FASTINT) - if (DUK_TVAL_IS_FASTINT(tv_key)) { - idx = duk__tval_fastint_to_arr_idx(tv_key); - } else -#endif - if (DUK_TVAL_IS_DOUBLE(tv_key)) { - idx = duk__tval_number_to_arr_idx(tv_key); - } else { - return 0; - } - - /* If index is not valid, idx will be DUK__NO_ARRAY_INDEX which - * is 0xffffffffUL. We don't need to check for that explicitly - * because 0xffffffffUL will never be inside bufobj length. - */ - - /* Careful with wrapping (left shifting idx would be unsafe). */ - if (idx >= (h_bufobj->length >> h_bufobj->shift)) { - return 0; - } - DUK_ASSERT(idx != DUK__NO_ARRAY_INDEX); - - byte_off = idx << h_bufobj->shift; /* no wrap assuming h_bufobj->length is valid */ - elem_size = (duk_small_uint_t) (1U << h_bufobj->shift); - - if (h_bufobj->buf != NULL && DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h_bufobj, byte_off + elem_size)) { - data = (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_bufobj->buf) + h_bufobj->offset + byte_off; - duk_hbufobj_push_validated_read(thr, h_bufobj, data, elem_size); - } else { - DUK_D(DUK_DPRINT("bufobj access out of underlying buffer, ignoring (read zero)")); - duk_push_uint(thr, 0); - } - - return 1; -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_LOCAL duk_bool_t duk__putprop_fastpath_bufobj_tval(duk_hthread *thr, duk_hobject *obj, duk_tval *tv_key, duk_tval *tv_val) { - duk_uint32_t idx; - duk_hbufobj *h_bufobj; - duk_uint_t byte_off; - duk_small_uint_t elem_size; - duk_uint8_t *data; - - if (!(DUK_HOBJECT_IS_BUFOBJ(obj) && DUK_TVAL_IS_NUMBER(tv_val))) { - return 0; - } - DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)); /* caller ensures; rom objects are never bufobjs now */ - - h_bufobj = (duk_hbufobj *) obj; - if (!DUK_HBUFOBJ_HAS_VIRTUAL_INDICES(h_bufobj)) { - return 0; - } - -#if defined(DUK_USE_FASTINT) - if (DUK_TVAL_IS_FASTINT(tv_key)) { - idx = duk__tval_fastint_to_arr_idx(tv_key); - } else -#endif - if (DUK_TVAL_IS_DOUBLE(tv_key)) { - idx = duk__tval_number_to_arr_idx(tv_key); - } else { - return 0; - } - - /* If index is not valid, idx will be DUK__NO_ARRAY_INDEX which - * is 0xffffffffUL. We don't need to check for that explicitly - * because 0xffffffffUL will never be inside bufobj length. - */ - - /* Careful with wrapping (left shifting idx would be unsafe). */ - if (idx >= (h_bufobj->length >> h_bufobj->shift)) { - return 0; - } - DUK_ASSERT(idx != DUK__NO_ARRAY_INDEX); - - byte_off = idx << h_bufobj->shift; /* no wrap assuming h_bufobj->length is valid */ - elem_size = (duk_small_uint_t) (1U << h_bufobj->shift); - - /* Value is required to be a number in the fast path so there - * are no side effects in write coercion. - */ - duk_push_tval(thr, tv_val); - DUK_ASSERT(duk_is_number(thr, -1)); - - if (h_bufobj->buf != NULL && DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h_bufobj, byte_off + elem_size)) { - data = (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_bufobj->buf) + h_bufobj->offset + byte_off; - duk_hbufobj_validated_write(thr, h_bufobj, data, elem_size); - } else { - DUK_D(DUK_DPRINT("bufobj access out of underlying buffer, ignoring (write skipped)")); - } - - duk_pop_unsafe(thr); - return 1; -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -/* - * GETPROP: ECMAScript property read. - */ - -DUK_INTERNAL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key) { - duk_tval tv_obj_copy; - duk_tval tv_key_copy; - duk_hobject *curr = NULL; - duk_hstring *key = NULL; - duk_uint32_t arr_idx = DUK__NO_ARRAY_INDEX; - duk_propdesc desc; - duk_uint_t sanity; - - DUK_DDD(DUK_DDDPRINT("getprop: thr=%p, obj=%p, key=%p (obj -> %!T, key -> %!T)", - (void *) thr, - (void *) tv_obj, - (void *) tv_key, - (duk_tval *) tv_obj, - (duk_tval *) tv_key)); - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - DUK_ASSERT(tv_obj != NULL); - DUK_ASSERT(tv_key != NULL); - - DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE); - - DUK_STATS_INC(thr->heap, stats_getprop_all); - - /* - * Make a copy of tv_obj, tv_key, and tv_val to avoid any issues of - * them being invalidated by a valstack resize. - * - * XXX: this is now an overkill for many fast paths. Rework this - * to be faster (although switching to a valstack discipline might - * be a better solution overall). - */ - - DUK_TVAL_SET_TVAL(&tv_obj_copy, tv_obj); - DUK_TVAL_SET_TVAL(&tv_key_copy, tv_key); - tv_obj = &tv_obj_copy; - tv_key = &tv_key_copy; - - /* - * Coercion and fast path processing - */ - - switch (DUK_TVAL_GET_TAG(tv_obj)) { - case DUK_TAG_UNDEFINED: - case DUK_TAG_NULL: { - /* Note: unconditional throw */ - DUK_DDD(DUK_DDDPRINT("base object is undefined or null -> reject")); -#if defined(DUK_USE_PARANOID_ERRORS) - DUK_ERROR_TYPE(thr, DUK_STR_INVALID_BASE); -#else - DUK_ERROR_FMT2(thr, - DUK_ERR_TYPE_ERROR, - "cannot read property %s of %s", - duk_push_string_tval_readable(thr, tv_key), - duk_push_string_tval_readable(thr, tv_obj)); -#endif - DUK_WO_NORETURN(return 0;); - break; - } - - case DUK_TAG_BOOLEAN: { - DUK_DDD(DUK_DDDPRINT("base object is a boolean, start lookup from boolean prototype")); - curr = thr->builtins[DUK_BIDX_BOOLEAN_PROTOTYPE]; - break; - } - - case DUK_TAG_STRING: { - duk_hstring *h = DUK_TVAL_GET_STRING(tv_obj); - duk_int_t pop_count; - - if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) { - /* Symbols (ES2015 or hidden) don't have virtual properties. */ - DUK_DDD(DUK_DDDPRINT("base object is a symbol, start lookup from symbol prototype")); - curr = thr->builtins[DUK_BIDX_SYMBOL_PROTOTYPE]; - break; - } - -#if defined(DUK_USE_FASTINT) - if (DUK_TVAL_IS_FASTINT(tv_key)) { - arr_idx = duk__tval_fastint_to_arr_idx(tv_key); - DUK_DDD(DUK_DDDPRINT("base object string, key is a fast-path fastint; arr_idx %ld", (long) arr_idx)); - pop_count = 0; - } else -#endif - if (DUK_TVAL_IS_NUMBER(tv_key)) { - arr_idx = duk__tval_number_to_arr_idx(tv_key); - DUK_DDD(DUK_DDDPRINT("base object string, key is a fast-path number; arr_idx %ld", (long) arr_idx)); - pop_count = 0; - } else { - arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key); - DUK_ASSERT(key != NULL); - DUK_DDD(DUK_DDDPRINT("base object string, key is a non-fast-path number; after " - "coercion key is %!T, arr_idx %ld", - (duk_tval *) duk_get_tval(thr, -1), - (long) arr_idx)); - pop_count = 1; - } - - if (arr_idx != DUK__NO_ARRAY_INDEX && arr_idx < DUK_HSTRING_GET_CHARLEN(h)) { - duk_pop_n_unsafe(thr, pop_count); - duk_push_hstring(thr, h); - duk_substring(thr, -1, arr_idx, arr_idx + 1); /* [str] -> [substr] */ - - DUK_STATS_INC(thr->heap, stats_getprop_stringidx); - DUK_DDD(DUK_DDDPRINT("-> %!T (base is string, key is an index inside string length " - "after coercion -> return char)", - (duk_tval *) duk_get_tval(thr, -1))); - return 1; - } - - if (pop_count == 0) { - /* This is a pretty awkward control flow, but we need to recheck the - * key coercion here. - */ - arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key); - DUK_ASSERT(key != NULL); - DUK_DDD(DUK_DDDPRINT("base object string, key is a non-fast-path number; after " - "coercion key is %!T, arr_idx %ld", - (duk_tval *) duk_get_tval(thr, -1), - (long) arr_idx)); - } - - if (key == DUK_HTHREAD_STRING_LENGTH(thr)) { - duk_pop_unsafe(thr); /* [key] -> [] */ - duk_push_uint(thr, (duk_uint_t) DUK_HSTRING_GET_CHARLEN(h)); /* [] -> [res] */ - - DUK_STATS_INC(thr->heap, stats_getprop_stringlen); - DUK_DDD(DUK_DDDPRINT("-> %!T (base is string, key is 'length' after coercion -> " - "return string length)", - (duk_tval *) duk_get_tval(thr, -1))); - return 1; - } - - DUK_DDD(DUK_DDDPRINT("base object is a string, start lookup from string prototype")); - curr = thr->builtins[DUK_BIDX_STRING_PROTOTYPE]; - goto lookup; /* avoid double coercion */ - } - - case DUK_TAG_OBJECT: { -#if defined(DUK_USE_ARRAY_PROP_FASTPATH) - duk_tval *tmp; -#endif - - curr = DUK_TVAL_GET_OBJECT(tv_obj); - DUK_ASSERT(curr != NULL); - - /* XXX: array .length fast path (important in e.g. loops)? */ - -#if defined(DUK_USE_ARRAY_PROP_FASTPATH) - tmp = duk__getprop_shallow_fastpath_array_tval(thr, curr, tv_key); - if (tmp) { - duk_push_tval(thr, tmp); - - DUK_DDD(DUK_DDDPRINT("-> %!T (base is object, key is a number, array part " - "fast path)", - (duk_tval *) duk_get_tval(thr, -1))); - DUK_STATS_INC(thr->heap, stats_getprop_arrayidx); - return 1; - } -#endif - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) - if (duk__getprop_fastpath_bufobj_tval(thr, curr, tv_key) != 0) { - /* Read value pushed on stack. */ - DUK_DDD(DUK_DDDPRINT("-> %!T (base is bufobj, key is a number, bufobj " - "fast path)", - (duk_tval *) duk_get_tval(thr, -1))); - DUK_STATS_INC(thr->heap, stats_getprop_bufobjidx); - return 1; - } -#endif - -#if defined(DUK_USE_ES6_PROXY) - if (DUK_UNLIKELY(DUK_HOBJECT_IS_PROXY(curr))) { - duk_hobject *h_target; - - if (duk__proxy_check_prop(thr, curr, DUK_STRIDX_GET, tv_key, &h_target)) { - /* -> [ ... trap handler ] */ - DUK_DDD(DUK_DDDPRINT("-> proxy object 'get' for key %!T", (duk_tval *) tv_key)); - DUK_STATS_INC(thr->heap, stats_getprop_proxy); - duk_push_hobject(thr, h_target); /* target */ - duk_push_tval(thr, tv_key); /* P */ - duk_push_tval(thr, tv_obj); /* Receiver: Proxy object */ - duk_call_method(thr, 3 /*nargs*/); - - /* Target object must be checked for a conflicting - * non-configurable property. - */ - arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key); - DUK_ASSERT(key != NULL); - - if (duk__get_own_propdesc_raw(thr, h_target, key, arr_idx, &desc, DUK_GETDESC_FLAG_PUSH_VALUE)) { - duk_tval *tv_hook = duk_require_tval(thr, -3); /* value from hook */ - duk_tval *tv_targ = duk_require_tval(thr, -1); /* value from target */ - duk_bool_t datadesc_reject; - duk_bool_t accdesc_reject; - - DUK_DDD(DUK_DDDPRINT("proxy 'get': target has matching property %!O, check for " - "conflicting property; tv_hook=%!T, tv_targ=%!T, desc.flags=0x%08lx, " - "desc.get=%p, desc.set=%p", - (duk_heaphdr *) key, - (duk_tval *) tv_hook, - (duk_tval *) tv_targ, - (unsigned long) desc.flags, - (void *) desc.get, - (void *) desc.set)); - - datadesc_reject = !(desc.flags & DUK_PROPDESC_FLAG_ACCESSOR) && - !(desc.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) && - !(desc.flags & DUK_PROPDESC_FLAG_WRITABLE) && - !duk_js_samevalue(tv_hook, tv_targ); - accdesc_reject = (desc.flags & DUK_PROPDESC_FLAG_ACCESSOR) && - !(desc.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) && (desc.get == NULL) && - !DUK_TVAL_IS_UNDEFINED(tv_hook); - if (datadesc_reject || accdesc_reject) { - DUK_ERROR_TYPE(thr, DUK_STR_PROXY_REJECTED); - DUK_WO_NORETURN(return 0;); - } - - duk_pop_2_unsafe(thr); - } else { - duk_pop_unsafe(thr); - } - return 1; /* return value */ - } - - curr = h_target; /* resume lookup from target */ - DUK_TVAL_SET_OBJECT(tv_obj, curr); - } -#endif /* DUK_USE_ES6_PROXY */ - - if (DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(curr)) { - arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key); - DUK_ASSERT(key != NULL); - - DUK_STATS_INC(thr->heap, stats_getprop_arguments); - if (duk__check_arguments_map_for_get(thr, curr, key, &desc)) { - DUK_DDD(DUK_DDDPRINT("-> %!T (base is object with arguments exotic behavior, " - "key matches magically bound property -> skip standard " - "Get with replacement value)", - (duk_tval *) duk_get_tval(thr, -1))); - - /* no need for 'caller' post-check, because 'key' must be an array index */ - - duk_remove_m2(thr); /* [key result] -> [result] */ - return 1; - } - - goto lookup; /* avoid double coercion */ - } - break; - } - - /* Buffer has virtual properties similar to string, but indexed values - * are numbers, not 1-byte buffers/strings which would perform badly. - */ - case DUK_TAG_BUFFER: { - duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv_obj); - duk_int_t pop_count; - - /* - * Because buffer values are often looped over, a number fast path - * is important. - */ - -#if defined(DUK_USE_FASTINT) - if (DUK_TVAL_IS_FASTINT(tv_key)) { - arr_idx = duk__tval_fastint_to_arr_idx(tv_key); - DUK_DDD(DUK_DDDPRINT("base object buffer, key is a fast-path fastint; arr_idx %ld", (long) arr_idx)); - pop_count = 0; - } else -#endif - if (DUK_TVAL_IS_NUMBER(tv_key)) { - arr_idx = duk__tval_number_to_arr_idx(tv_key); - DUK_DDD(DUK_DDDPRINT("base object buffer, key is a fast-path number; arr_idx %ld", (long) arr_idx)); - pop_count = 0; - } else { - arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key); - DUK_ASSERT(key != NULL); - DUK_DDD(DUK_DDDPRINT("base object buffer, key is a non-fast-path number; after " - "coercion key is %!T, arr_idx %ld", - (duk_tval *) duk_get_tval(thr, -1), - (long) arr_idx)); - pop_count = 1; - } - - if (arr_idx != DUK__NO_ARRAY_INDEX && arr_idx < DUK_HBUFFER_GET_SIZE(h)) { - duk_pop_n_unsafe(thr, pop_count); - duk_push_uint(thr, ((duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h))[arr_idx]); - DUK_STATS_INC(thr->heap, stats_getprop_bufferidx); - DUK_DDD(DUK_DDDPRINT("-> %!T (base is buffer, key is an index inside buffer length " - "after coercion -> return byte as number)", - (duk_tval *) duk_get_tval(thr, -1))); - return 1; - } - - if (pop_count == 0) { - /* This is a pretty awkward control flow, but we need to recheck the - * key coercion here. - */ - arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key); - DUK_ASSERT(key != NULL); - DUK_DDD(DUK_DDDPRINT("base object buffer, key is a non-fast-path number; after " - "coercion key is %!T, arr_idx %ld", - (duk_tval *) duk_get_tval(thr, -1), - (long) arr_idx)); - } - - if (key == DUK_HTHREAD_STRING_LENGTH(thr)) { - duk_pop_unsafe(thr); /* [key] -> [] */ - duk_push_uint(thr, (duk_uint_t) DUK_HBUFFER_GET_SIZE(h)); /* [] -> [res] */ - DUK_STATS_INC(thr->heap, stats_getprop_bufferlen); - - DUK_DDD(DUK_DDDPRINT("-> %!T (base is buffer, key is 'length' " - "after coercion -> return buffer length)", - (duk_tval *) duk_get_tval(thr, -1))); - return 1; - } - - DUK_DDD(DUK_DDDPRINT("base object is a buffer, start lookup from Uint8Array prototype")); - curr = thr->builtins[DUK_BIDX_UINT8ARRAY_PROTOTYPE]; - goto lookup; /* avoid double coercion */ - } - - case DUK_TAG_POINTER: { - DUK_DDD(DUK_DDDPRINT("base object is a pointer, start lookup from pointer prototype")); - curr = thr->builtins[DUK_BIDX_POINTER_PROTOTYPE]; - break; - } - - case DUK_TAG_LIGHTFUNC: { - /* Lightfuncs inherit getter .name and .length from %NativeFunctionPrototype%. */ - DUK_DDD(DUK_DDDPRINT("base object is a lightfunc, start lookup from function prototype")); - curr = thr->builtins[DUK_BIDX_NATIVE_FUNCTION_PROTOTYPE]; - break; - } - -#if defined(DUK_USE_FASTINT) - case DUK_TAG_FASTINT: -#endif - default: { - /* number */ - DUK_DDD(DUK_DDDPRINT("base object is a number, start lookup from number prototype")); - DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv_obj)); - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_obj)); - curr = thr->builtins[DUK_BIDX_NUMBER_PROTOTYPE]; - break; - } - } - - /* key coercion (unless already coerced above) */ - DUK_ASSERT(key == NULL); - arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key); - DUK_ASSERT(key != NULL); - /* - * Property lookup - */ - -lookup: - /* [key] (coerced) */ - DUK_ASSERT(curr != NULL); - DUK_ASSERT(key != NULL); - - sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY; - do { - if (!duk__get_own_propdesc_raw(thr, curr, key, arr_idx, &desc, DUK_GETDESC_FLAG_PUSH_VALUE)) { - goto next_in_chain; - } - - if (desc.get != NULL) { - /* accessor with defined getter */ - DUK_ASSERT((desc.flags & DUK_PROPDESC_FLAG_ACCESSOR) != 0); - - duk_pop_unsafe(thr); /* [key undefined] -> [key] */ - duk_push_hobject(thr, desc.get); - duk_push_tval(thr, tv_obj); /* note: original, uncoerced base */ -#if defined(DUK_USE_NONSTD_GETTER_KEY_ARGUMENT) - duk_dup_m3(thr); - duk_call_method(thr, 1); /* [key getter this key] -> [key retval] */ -#else - duk_call_method(thr, 0); /* [key getter this] -> [key retval] */ -#endif - } else { - /* [key value] or [key undefined] */ - - /* data property or accessor without getter */ - DUK_ASSERT(((desc.flags & DUK_PROPDESC_FLAG_ACCESSOR) == 0) || (desc.get == NULL)); - - /* if accessor without getter, return value is undefined */ - DUK_ASSERT(((desc.flags & DUK_PROPDESC_FLAG_ACCESSOR) == 0) || duk_is_undefined(thr, -1)); - - /* Note: for an accessor without getter, falling through to - * check for "caller" exotic behavior is unnecessary as - * "undefined" will never activate the behavior. But it does - * no harm, so we'll do it anyway. - */ - } - - goto found; /* [key result] */ - - next_in_chain: - /* XXX: option to pretend property doesn't exist if sanity limit is - * hit might be useful. - */ - if (DUK_UNLIKELY(sanity-- == 0)) { - DUK_ERROR_RANGE(thr, DUK_STR_PROTOTYPE_CHAIN_LIMIT); - DUK_WO_NORETURN(return 0;); - } - curr = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, curr); - } while (curr != NULL); - - /* - * Not found - */ - - duk_to_undefined(thr, -1); /* [key] -> [undefined] (default value) */ - - DUK_DDD(DUK_DDDPRINT("-> %!T (not found)", (duk_tval *) duk_get_tval(thr, -1))); - return 0; - - /* - * Found; post-processing (Function and arguments objects) - */ - -found: - /* [key result] */ - -#if !defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY) - /* Special behavior for 'caller' property of (non-bound) function objects - * and non-strict Arguments objects: if 'caller' -value- (!) is a strict - * mode function, throw a TypeError (E5 Sections 15.3.5.4, 10.6). - * Quite interestingly, a non-strict function with no formal arguments - * will get an arguments object -without- special 'caller' behavior! - * - * The E5.1 spec is a bit ambiguous if this special behavior applies when - * a bound function is the base value (not the 'caller' value): Section - * 15.3.4.5 (describing bind()) states that [[Get]] for bound functions - * matches that of Section 15.3.5.4 ([[Get]] for Function instances). - * However, Section 13.3.5.4 has "NOTE: Function objects created using - * Function.prototype.bind use the default [[Get]] internal method." - * The current implementation assumes this means that bound functions - * should not have the special [[Get]] behavior. - * - * The E5.1 spec is also a bit unclear if the TypeError throwing is - * applied if the 'caller' value is a strict bound function. The - * current implementation will throw even for both strict non-bound - * and strict bound functions. - * - * See test-dev-strict-func-as-caller-prop-value.js for quite extensive - * tests. - * - * This exotic behavior is disabled when the non-standard 'caller' property - * is enabled, as it conflicts with the free use of 'caller'. - */ - if (key == DUK_HTHREAD_STRING_CALLER(thr) && DUK_TVAL_IS_OBJECT(tv_obj)) { - duk_hobject *orig = DUK_TVAL_GET_OBJECT(tv_obj); - DUK_ASSERT(orig != NULL); - - if (DUK_HOBJECT_IS_NONBOUND_FUNCTION(orig) || DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(orig)) { - duk_hobject *h; - - /* XXX: The TypeError is currently not applied to bound - * functions because the 'strict' flag is not copied by - * bind(). This may or may not be correct, the specification - * only refers to the value being a "strict mode Function - * object" which is ambiguous. - */ - DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(orig)); - - h = duk_get_hobject(thr, -1); /* NULL if not an object */ - if (h && DUK_HOBJECT_IS_FUNCTION(h) && DUK_HOBJECT_HAS_STRICT(h)) { - /* XXX: sufficient to check 'strict', assert for 'is function' */ - DUK_ERROR_TYPE(thr, DUK_STR_STRICT_CALLER_READ); - DUK_WO_NORETURN(return 0;); - } - } - } -#endif /* !DUK_USE_NONSTD_FUNC_CALLER_PROPERTY */ - - duk_remove_m2(thr); /* [key result] -> [result] */ - - DUK_DDD(DUK_DDDPRINT("-> %!T (found)", (duk_tval *) duk_get_tval(thr, -1))); - return 1; -} - -/* - * HASPROP: ECMAScript property existence check ("in" operator). - * - * Interestingly, the 'in' operator does not do any coercion of - * the target object. - */ - -DUK_INTERNAL duk_bool_t duk_hobject_hasprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key) { - duk_tval tv_key_copy; - duk_hobject *obj; - duk_hstring *key; - duk_uint32_t arr_idx; - duk_bool_t rc; - duk_propdesc desc; - - DUK_DDD(DUK_DDDPRINT("hasprop: thr=%p, obj=%p, key=%p (obj -> %!T, key -> %!T)", - (void *) thr, - (void *) tv_obj, - (void *) tv_key, - (duk_tval *) tv_obj, - (duk_tval *) tv_key)); - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - DUK_ASSERT(tv_obj != NULL); - DUK_ASSERT(tv_key != NULL); - DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE); - - DUK_TVAL_SET_TVAL(&tv_key_copy, tv_key); - tv_key = &tv_key_copy; - - /* - * The 'in' operator requires an object as its right hand side, - * throwing a TypeError unconditionally if this is not the case. - * - * However, lightfuncs need to behave like fully fledged objects - * here to be maximally transparent, so we need to handle them - * here. Same goes for plain buffers which behave like ArrayBuffers. - */ - - /* XXX: Refactor key coercion so that it's only called once. It can't - * be trivially lifted here because the object must be type checked - * first. - */ - - if (DUK_TVAL_IS_OBJECT(tv_obj)) { - obj = DUK_TVAL_GET_OBJECT(tv_obj); - DUK_ASSERT(obj != NULL); - - arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key); - } else if (DUK_TVAL_IS_BUFFER(tv_obj)) { - arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key); - if (duk__key_is_plain_buf_ownprop(thr, DUK_TVAL_GET_BUFFER(tv_obj), key, arr_idx)) { - rc = 1; - goto pop_and_return; - } - obj = thr->builtins[DUK_BIDX_UINT8ARRAY_PROTOTYPE]; - } else if (DUK_TVAL_IS_LIGHTFUNC(tv_obj)) { - arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key); - - /* If not found, resume existence check from %NativeFunctionPrototype%. - * We can just substitute the value in this case; nothing will - * need the original base value (as would be the case with e.g. - * setters/getters. - */ - obj = thr->builtins[DUK_BIDX_NATIVE_FUNCTION_PROTOTYPE]; - } else { - /* Note: unconditional throw */ - DUK_DDD(DUK_DDDPRINT("base object is not an object -> reject")); - DUK_ERROR_TYPE(thr, DUK_STR_INVALID_BASE); - DUK_WO_NORETURN(return 0;); - } - - /* XXX: fast path for arrays? */ - - DUK_ASSERT(key != NULL); - DUK_ASSERT(obj != NULL); - DUK_UNREF(arr_idx); - -#if defined(DUK_USE_ES6_PROXY) - if (DUK_UNLIKELY(DUK_HOBJECT_IS_PROXY(obj))) { - duk_hobject *h_target; - duk_bool_t tmp_bool; - - /* XXX: the key in 'key in obj' is string coerced before we're called - * (which is the required behavior in E5/E5.1/E6) so the key is a string - * here already. - */ - - if (duk__proxy_check_prop(thr, obj, DUK_STRIDX_HAS, tv_key, &h_target)) { - /* [ ... key trap handler ] */ - DUK_DDD(DUK_DDDPRINT("-> proxy object 'has' for key %!T", (duk_tval *) tv_key)); - duk_push_hobject(thr, h_target); /* target */ - duk_push_tval(thr, tv_key); /* P */ - duk_call_method(thr, 2 /*nargs*/); - tmp_bool = duk_to_boolean_top_pop(thr); - if (!tmp_bool) { - /* Target object must be checked for a conflicting - * non-configurable property. - */ - - if (duk__get_own_propdesc_raw(thr, h_target, key, arr_idx, &desc, 0 /*flags*/)) { /* don't push - value */ - DUK_DDD(DUK_DDDPRINT("proxy 'has': target has matching property %!O, check for " - "conflicting property; desc.flags=0x%08lx, " - "desc.get=%p, desc.set=%p", - (duk_heaphdr *) key, - (unsigned long) desc.flags, - (void *) desc.get, - (void *) desc.set)); - /* XXX: Extensibility check for target uses IsExtensible(). If we - * implemented the isExtensible trap and didn't reject proxies as - * proxy targets, it should be respected here. - */ - if (!((desc.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) && /* property is configurable and */ - DUK_HOBJECT_HAS_EXTENSIBLE(h_target))) { /* ... target is extensible */ - DUK_ERROR_TYPE(thr, DUK_STR_PROXY_REJECTED); - DUK_WO_NORETURN(return 0;); - } - } - } - - duk_pop_unsafe(thr); /* [ key ] -> [] */ - return tmp_bool; - } - - obj = h_target; /* resume check from proxy target */ - } -#endif /* DUK_USE_ES6_PROXY */ - - /* XXX: inline into a prototype walking loop? */ - - rc = duk__get_propdesc(thr, obj, key, &desc, 0 /*flags*/); /* don't push value */ - /* fall through */ - -pop_and_return: - duk_pop_unsafe(thr); /* [ key ] -> [] */ - return rc; -} - -/* - * HASPROP variant used internally. - * - * This primitive must never throw an error, callers rely on this. - * In particular, don't throw an error for prototype loops; instead, - * pretend like the property doesn't exist if a prototype sanity limit - * is reached. - * - * Does not implement proxy behavior: if applied to a proxy object, - * returns key existence on the proxy object itself. - */ - -DUK_INTERNAL duk_bool_t duk_hobject_hasprop_raw(duk_hthread *thr, duk_hobject *obj, duk_hstring *key) { - duk_propdesc dummy; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - DUK_ASSERT(obj != NULL); - DUK_ASSERT(key != NULL); - - DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE); - - return duk__get_propdesc(thr, obj, key, &dummy, DUK_GETDESC_FLAG_IGNORE_PROTOLOOP); /* don't push value */ -} - -/* - * Helper: handle Array object 'length' write which automatically - * deletes properties, see E5 Section 15.4.5.1, step 3. This is - * quite tricky to get right. - * - * Used by duk_hobject_putprop(). - */ - -/* Coerce a new .length candidate to a number and check that it's a valid - * .length. - */ -DUK_LOCAL duk_uint32_t duk__to_new_array_length_checked(duk_hthread *thr, duk_tval *tv) { - duk_uint32_t res; - duk_double_t d; - -#if !defined(DUK_USE_PREFER_SIZE) -#if defined(DUK_USE_FASTINT) - /* When fastints are enabled, the most interesting case is assigning - * a fastint to .length (e.g. arr.length = 0). - */ - if (DUK_TVAL_IS_FASTINT(tv)) { - /* Very common case. */ - duk_int64_t fi; - fi = DUK_TVAL_GET_FASTINT(tv); - if (fi < 0 || fi > DUK_I64_CONSTANT(0xffffffff)) { - goto fail_range; - } - return (duk_uint32_t) fi; - } -#else /* DUK_USE_FASTINT */ - /* When fastints are not enabled, the most interesting case is any - * number. - */ - if (DUK_TVAL_IS_DOUBLE(tv)) { - d = DUK_TVAL_GET_NUMBER(tv); - } -#endif /* DUK_USE_FASTINT */ - else -#endif /* !DUK_USE_PREFER_SIZE */ - { - /* In all other cases, and when doing a size optimized build, - * fall back to the comprehensive handler. - */ - d = duk_js_tonumber(thr, tv); - } - - /* Refuse to update an Array's 'length' to a value outside the - * 32-bit range. Negative zero is accepted as zero. - */ - res = duk_double_to_uint32_t(d); - if (!duk_double_equals((duk_double_t) res, d)) { - goto fail_range; - } - - return res; - -fail_range: - DUK_ERROR_RANGE(thr, DUK_STR_INVALID_ARRAY_LENGTH); - DUK_WO_NORETURN(return 0;); -} - -/* Delete elements required by a smaller length, taking into account - * potentially non-configurable elements. Returns non-zero if all - * elements could be deleted, and zero if all or some elements could - * not be deleted. Also writes final "target length" to 'out_result_len'. - * This is the length value that should go into the 'length' property - * (must be set by the caller). Never throws an error. - */ -DUK_LOCAL -duk_bool_t duk__handle_put_array_length_smaller(duk_hthread *thr, - duk_hobject *obj, - duk_uint32_t old_len, - duk_uint32_t new_len, - duk_bool_t force_flag, - duk_uint32_t *out_result_len) { - duk_uint32_t target_len; - duk_uint_fast32_t i; - duk_uint32_t arr_idx; - duk_hstring *key; - duk_tval *tv; - duk_bool_t rc; - - DUK_DDD(DUK_DDDPRINT("new array length smaller than old (%ld -> %ld), " - "probably need to remove elements", - (long) old_len, - (long) new_len)); - - /* - * New length is smaller than old length, need to delete properties above - * the new length. - * - * If array part exists, this is straightforward: array entries cannot - * be non-configurable so this is guaranteed to work. - * - * If array part does not exist, array-indexed values are scattered - * in the entry part, and some may not be configurable (preventing length - * from becoming lower than their index + 1). To handle the algorithm - * in E5 Section 15.4.5.1, step l correctly, we scan the entire property - * set twice. - */ - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(obj != NULL); - DUK_ASSERT(new_len < old_len); - DUK_ASSERT(out_result_len != NULL); - DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE); - - DUK_ASSERT(DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj)); - DUK_ASSERT(DUK_HOBJECT_IS_ARRAY(obj)); - - if (DUK_HOBJECT_HAS_ARRAY_PART(obj)) { - /* - * All defined array-indexed properties are in the array part - * (we assume the array part is comprehensive), and all array - * entries are writable, configurable, and enumerable. Thus, - * nothing can prevent array entries from being deleted. - */ - - DUK_DDD(DUK_DDDPRINT("have array part, easy case")); - - if (old_len < DUK_HOBJECT_GET_ASIZE(obj)) { - /* XXX: assertion that entries >= old_len are already unused */ - i = old_len; - } else { - i = DUK_HOBJECT_GET_ASIZE(obj); - } - DUK_ASSERT(i <= DUK_HOBJECT_GET_ASIZE(obj)); - - while (i > new_len) { - i--; - tv = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, i); - DUK_TVAL_SET_UNUSED_UPDREF(thr, tv); /* side effects */ - } - - *out_result_len = new_len; - return 1; - } else { - /* - * Entries part is a bit more complex. - */ - - /* Stage 1: find highest preventing non-configurable entry (if any). - * When forcing, ignore non-configurability. - */ - - DUK_DDD(DUK_DDDPRINT("no array part, slow case")); - - DUK_DDD(DUK_DDDPRINT("array length write, no array part, stage 1: find target_len " - "(highest preventing non-configurable entry (if any))")); - - target_len = new_len; - if (force_flag) { - DUK_DDD(DUK_DDDPRINT("array length write, no array part; force flag -> skip stage 1")); - goto skip_stage1; - } - for (i = 0; i < DUK_HOBJECT_GET_ENEXT(obj); i++) { - key = DUK_HOBJECT_E_GET_KEY(thr->heap, obj, i); - if (!key) { - DUK_DDD(DUK_DDDPRINT("skip entry index %ld: null key", (long) i)); - continue; - } - if (!DUK_HSTRING_HAS_ARRIDX(key)) { - DUK_DDD(DUK_DDDPRINT("skip entry index %ld: key not an array index", (long) i)); - continue; - } - - DUK_ASSERT( - DUK_HSTRING_HAS_ARRIDX(key)); /* XXX: macro checks for array index flag, which is unnecessary here */ - arr_idx = DUK_HSTRING_GET_ARRIDX_SLOW(key); - DUK_ASSERT(arr_idx != DUK__NO_ARRAY_INDEX); - DUK_ASSERT(arr_idx < old_len); /* consistency requires this */ - - if (arr_idx < new_len) { - DUK_DDD(DUK_DDDPRINT("skip entry index %ld: key is array index %ld, below new_len", - (long) i, - (long) arr_idx)); - continue; - } - if (DUK_HOBJECT_E_SLOT_IS_CONFIGURABLE(thr->heap, obj, i)) { - DUK_DDD(DUK_DDDPRINT("skip entry index %ld: key is a relevant array index %ld, but configurable", - (long) i, - (long) arr_idx)); - continue; - } - - /* relevant array index is non-configurable, blocks write */ - if (arr_idx >= target_len) { - DUK_DDD(DUK_DDDPRINT("entry at index %ld has arr_idx %ld, is not configurable, " - "update target_len %ld -> %ld", - (long) i, - (long) arr_idx, - (long) target_len, - (long) (arr_idx + 1))); - target_len = arr_idx + 1; - } - } - skip_stage1: - - /* stage 2: delete configurable entries above target length */ - - DUK_DDD( - DUK_DDDPRINT("old_len=%ld, new_len=%ld, target_len=%ld", (long) old_len, (long) new_len, (long) target_len)); - - DUK_DDD(DUK_DDDPRINT("array length write, no array part, stage 2: remove " - "entries >= target_len")); - - for (i = 0; i < DUK_HOBJECT_GET_ENEXT(obj); i++) { - key = DUK_HOBJECT_E_GET_KEY(thr->heap, obj, i); - if (!key) { - DUK_DDD(DUK_DDDPRINT("skip entry index %ld: null key", (long) i)); - continue; - } - if (!DUK_HSTRING_HAS_ARRIDX(key)) { - DUK_DDD(DUK_DDDPRINT("skip entry index %ld: key not an array index", (long) i)); - continue; - } - - DUK_ASSERT( - DUK_HSTRING_HAS_ARRIDX(key)); /* XXX: macro checks for array index flag, which is unnecessary here */ - arr_idx = DUK_HSTRING_GET_ARRIDX_SLOW(key); - DUK_ASSERT(arr_idx != DUK__NO_ARRAY_INDEX); - DUK_ASSERT(arr_idx < old_len); /* consistency requires this */ - - if (arr_idx < target_len) { - DUK_DDD(DUK_DDDPRINT("skip entry index %ld: key is array index %ld, below target_len", - (long) i, - (long) arr_idx)); - continue; - } - DUK_ASSERT(force_flag || DUK_HOBJECT_E_SLOT_IS_CONFIGURABLE(thr->heap, obj, i)); /* stage 1 guarantees */ - - DUK_DDD(DUK_DDDPRINT("delete entry index %ld: key is array index %ld", (long) i, (long) arr_idx)); - - /* - * Slow delete, but we don't care as we're already in a very slow path. - * The delete always succeeds: key has no exotic behavior, property - * is configurable, and no resize occurs. - */ - rc = duk_hobject_delprop_raw(thr, obj, key, force_flag ? DUK_DELPROP_FLAG_FORCE : 0); - DUK_UNREF(rc); - DUK_ASSERT(rc != 0); - } - - /* stage 3: update length (done by caller), decide return code */ - - DUK_DDD(DUK_DDDPRINT("array length write, no array part, stage 3: update length (done by caller)")); - - *out_result_len = target_len; - - if (target_len == new_len) { - DUK_DDD(DUK_DDDPRINT("target_len matches new_len, return success")); - return 1; - } - DUK_DDD(DUK_DDDPRINT("target_len does not match new_len (some entry prevented " - "full length adjustment), return error")); - return 0; - } - - DUK_UNREACHABLE(); -} - -/* XXX: is valstack top best place for argument? */ -DUK_LOCAL duk_bool_t duk__handle_put_array_length(duk_hthread *thr, duk_hobject *obj) { - duk_harray *a; - duk_uint32_t old_len; - duk_uint32_t new_len; - duk_uint32_t result_len; - duk_bool_t rc; - - DUK_DDD(DUK_DDDPRINT("handling a put operation to array 'length' exotic property, " - "new val: %!T", - (duk_tval *) duk_get_tval(thr, -1))); - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(obj != NULL); - - DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE); - - DUK_ASSERT(DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj)); - DUK_ASSERT(DUK_HOBJECT_IS_ARRAY(obj)); - a = (duk_harray *) obj; - DUK_HARRAY_ASSERT_VALID(a); - - DUK_ASSERT(duk_is_valid_index(thr, -1)); - - /* - * Get old and new length - */ - - old_len = a->length; - new_len = duk__to_new_array_length_checked(thr, DUK_GET_TVAL_NEGIDX(thr, -1)); - DUK_DDD(DUK_DDDPRINT("old_len=%ld, new_len=%ld", (long) old_len, (long) new_len)); - - /* - * Writability check - */ - - if (DUK_HARRAY_LENGTH_NONWRITABLE(a)) { - DUK_DDD(DUK_DDDPRINT("length is not writable, fail")); - return 0; - } - - /* - * New length not lower than old length => no changes needed - * (not even array allocation). - */ - - if (new_len >= old_len) { - DUK_DDD(DUK_DDDPRINT("new length is same or higher than old length, just update length, no deletions")); - a->length = new_len; - return 1; - } - - DUK_DDD(DUK_DDDPRINT("new length is lower than old length, probably must delete entries")); - - /* - * New length lower than old length => delete elements, then - * update length. - * - * Note: even though a bunch of elements have been deleted, the 'desc' is - * still valid as properties haven't been resized (and entries compacted). - */ - - rc = duk__handle_put_array_length_smaller(thr, obj, old_len, new_len, 0 /*force_flag*/, &result_len); - DUK_ASSERT(result_len >= new_len && result_len <= old_len); - - a->length = result_len; - - /* XXX: shrink array allocation or entries compaction here? */ - - return rc; -} - -/* - * PUTPROP: ECMAScript property write. - * - * Unlike ECMAScript primitive which returns nothing, returns 1 to indicate - * success and 0 to indicate failure (assuming throw is not set). - * - * This is an extremely tricky function. Some examples: - * - * * Currently a decref may trigger a GC, which may compact an object's - * property allocation. Consequently, any entry indices (e_idx) will - * be potentially invalidated by a decref. - * - * * Exotic behaviors (strings, arrays, arguments object) require, - * among other things: - * - * - Preprocessing before and postprocessing after an actual property - * write. For example, array index write requires pre-checking the - * array 'length' property for access control, and may require an - * array 'length' update after the actual write has succeeded (but - * not if it fails). - * - * - Deletion of multiple entries, as a result of array 'length' write. - * - * * Input values are taken as pointers which may point to the valstack. - * If valstack is resized because of the put (this may happen at least - * when the array part is abandoned), the pointers can be invalidated. - * (We currently make a copy of all of the input values to avoid issues.) - */ - -DUK_INTERNAL duk_bool_t -duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key, duk_tval *tv_val, duk_bool_t throw_flag) { - duk_tval tv_obj_copy; - duk_tval tv_key_copy; - duk_tval tv_val_copy; - duk_hobject *orig = NULL; /* NULL if tv_obj is primitive */ - duk_hobject *curr; - duk_hstring *key = NULL; - duk_propdesc desc; - duk_tval *tv; - duk_uint32_t arr_idx; - duk_bool_t rc; - duk_int_t e_idx; - duk_uint_t sanity; - duk_uint32_t new_array_length = 0; /* 0 = no update */ - - DUK_DDD(DUK_DDDPRINT("putprop: thr=%p, obj=%p, key=%p, val=%p, throw=%ld " - "(obj -> %!T, key -> %!T, val -> %!T)", - (void *) thr, - (void *) tv_obj, - (void *) tv_key, - (void *) tv_val, - (long) throw_flag, - (duk_tval *) tv_obj, - (duk_tval *) tv_key, - (duk_tval *) tv_val)); - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - DUK_ASSERT(tv_obj != NULL); - DUK_ASSERT(tv_key != NULL); - DUK_ASSERT(tv_val != NULL); - - DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE); - - DUK_STATS_INC(thr->heap, stats_putprop_all); - - /* - * Make a copy of tv_obj, tv_key, and tv_val to avoid any issues of - * them being invalidated by a valstack resize. - * - * XXX: this is an overkill for some paths, so optimize this later - * (or maybe switch to a stack arguments model entirely). - */ - - DUK_TVAL_SET_TVAL(&tv_obj_copy, tv_obj); - DUK_TVAL_SET_TVAL(&tv_key_copy, tv_key); - DUK_TVAL_SET_TVAL(&tv_val_copy, tv_val); - tv_obj = &tv_obj_copy; - tv_key = &tv_key_copy; - tv_val = &tv_val_copy; - - /* - * Coercion and fast path processing. - */ - - switch (DUK_TVAL_GET_TAG(tv_obj)) { - case DUK_TAG_UNDEFINED: - case DUK_TAG_NULL: { - /* Note: unconditional throw */ - DUK_DDD(DUK_DDDPRINT("base object is undefined or null -> reject (object=%!iT)", (duk_tval *) tv_obj)); -#if defined(DUK_USE_PARANOID_ERRORS) - DUK_ERROR_TYPE(thr, DUK_STR_INVALID_BASE); -#else - DUK_ERROR_FMT2(thr, - DUK_ERR_TYPE_ERROR, - "cannot write property %s of %s", - duk_push_string_tval_readable(thr, tv_key), - duk_push_string_tval_readable(thr, tv_obj)); -#endif - DUK_WO_NORETURN(return 0;); - break; - } - - case DUK_TAG_BOOLEAN: { - DUK_DDD(DUK_DDDPRINT("base object is a boolean, start lookup from boolean prototype")); - curr = thr->builtins[DUK_BIDX_BOOLEAN_PROTOTYPE]; - break; - } - - case DUK_TAG_STRING: { - duk_hstring *h = DUK_TVAL_GET_STRING(tv_obj); - - /* - * Note: currently no fast path for array index writes. - * They won't be possible anyway as strings are immutable. - */ - - DUK_ASSERT(key == NULL); - arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key); - DUK_ASSERT(key != NULL); - - if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) { - /* Symbols (ES2015 or hidden) don't have virtual properties. */ - curr = thr->builtins[DUK_BIDX_SYMBOL_PROTOTYPE]; - goto lookup; - } - - if (key == DUK_HTHREAD_STRING_LENGTH(thr)) { - goto fail_not_writable; - } - - if (arr_idx != DUK__NO_ARRAY_INDEX && arr_idx < DUK_HSTRING_GET_CHARLEN(h)) { - goto fail_not_writable; - } - - DUK_DDD(DUK_DDDPRINT("base object is a string, start lookup from string prototype")); - curr = thr->builtins[DUK_BIDX_STRING_PROTOTYPE]; - goto lookup; /* avoid double coercion */ - } - - case DUK_TAG_OBJECT: { - orig = DUK_TVAL_GET_OBJECT(tv_obj); - DUK_ASSERT(orig != NULL); - -#if defined(DUK_USE_ROM_OBJECTS) - /* With this check in place fast paths won't need read-only - * object checks. This is technically incorrect if there are - * setters that cause no writes to ROM objects, but current - * built-ins don't have such setters. - */ - if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) orig)) { - DUK_DD(DUK_DDPRINT("attempt to putprop on read-only target object")); - goto fail_not_writable_no_pop; /* Must avoid duk_pop() in exit path */ - } -#endif - - /* The fast path for array property put is not fully compliant: - * If one places conflicting number-indexed properties into - * Array.prototype (for example, a non-writable Array.prototype[7]) - * the fast path will incorrectly ignore them. - * - * This fast path could be made compliant by falling through - * to the slow path if the previous value was UNUSED. This would - * also remove the need to check for extensibility. Right now a - * non-extensible array is slower than an extensible one as far - * as writes are concerned. - * - * The fast path behavior is documented in more detail here: - * tests/ecmascript/test-misc-array-fast-write.js - */ - - /* XXX: array .length? */ - -#if defined(DUK_USE_ARRAY_PROP_FASTPATH) - if (duk__putprop_shallow_fastpath_array_tval(thr, orig, tv_key, tv_val) != 0) { - DUK_DDD(DUK_DDDPRINT("array fast path success")); - DUK_STATS_INC(thr->heap, stats_putprop_arrayidx); - return 1; - } -#endif - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) - if (duk__putprop_fastpath_bufobj_tval(thr, orig, tv_key, tv_val) != 0) { - DUK_DDD(DUK_DDDPRINT("base is bufobj, key is a number, bufobj fast path")); - DUK_STATS_INC(thr->heap, stats_putprop_bufobjidx); - return 1; - } -#endif - -#if defined(DUK_USE_ES6_PROXY) - if (DUK_UNLIKELY(DUK_HOBJECT_IS_PROXY(orig))) { - duk_hobject *h_target; - duk_bool_t tmp_bool; - - if (duk__proxy_check_prop(thr, orig, DUK_STRIDX_SET, tv_key, &h_target)) { - /* -> [ ... trap handler ] */ - DUK_DDD(DUK_DDDPRINT("-> proxy object 'set' for key %!T", (duk_tval *) tv_key)); - DUK_STATS_INC(thr->heap, stats_putprop_proxy); - duk_push_hobject(thr, h_target); /* target */ - duk_push_tval(thr, tv_key); /* P */ - duk_push_tval(thr, tv_val); /* V */ - duk_push_tval(thr, tv_obj); /* Receiver: Proxy object */ - duk_call_method(thr, 4 /*nargs*/); - tmp_bool = duk_to_boolean_top_pop(thr); - if (!tmp_bool) { - goto fail_proxy_rejected; - } - - /* Target object must be checked for a conflicting - * non-configurable property. - */ - arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key); - DUK_ASSERT(key != NULL); - - if (duk__get_own_propdesc_raw(thr, h_target, key, arr_idx, &desc, DUK_GETDESC_FLAG_PUSH_VALUE)) { - duk_tval *tv_targ = duk_require_tval(thr, -1); - duk_bool_t datadesc_reject; - duk_bool_t accdesc_reject; - - DUK_DDD(DUK_DDDPRINT("proxy 'set': target has matching property %!O, check for " - "conflicting property; tv_val=%!T, tv_targ=%!T, desc.flags=0x%08lx, " - "desc.get=%p, desc.set=%p", - (duk_heaphdr *) key, - (duk_tval *) tv_val, - (duk_tval *) tv_targ, - (unsigned long) desc.flags, - (void *) desc.get, - (void *) desc.set)); - - datadesc_reject = !(desc.flags & DUK_PROPDESC_FLAG_ACCESSOR) && - !(desc.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) && - !(desc.flags & DUK_PROPDESC_FLAG_WRITABLE) && - !duk_js_samevalue(tv_val, tv_targ); - accdesc_reject = (desc.flags & DUK_PROPDESC_FLAG_ACCESSOR) && - !(desc.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) && (desc.set == NULL); - if (datadesc_reject || accdesc_reject) { - DUK_ERROR_TYPE(thr, DUK_STR_PROXY_REJECTED); - DUK_WO_NORETURN(return 0;); - } - - duk_pop_2_unsafe(thr); - } else { - duk_pop_unsafe(thr); - } - return 1; /* success */ - } - - orig = h_target; /* resume write to target */ - DUK_TVAL_SET_OBJECT(tv_obj, orig); - } -#endif /* DUK_USE_ES6_PROXY */ - - curr = orig; - break; - } - - case DUK_TAG_BUFFER: { - duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv_obj); - duk_int_t pop_count = 0; - - /* - * Because buffer values may be looped over and read/written - * from, an array index fast path is important. - */ - -#if defined(DUK_USE_FASTINT) - if (DUK_TVAL_IS_FASTINT(tv_key)) { - arr_idx = duk__tval_fastint_to_arr_idx(tv_key); - DUK_DDD(DUK_DDDPRINT("base object buffer, key is a fast-path fastint; arr_idx %ld", (long) arr_idx)); - pop_count = 0; - } else -#endif - if (DUK_TVAL_IS_NUMBER(tv_key)) { - arr_idx = duk__tval_number_to_arr_idx(tv_key); - DUK_DDD(DUK_DDDPRINT("base object buffer, key is a fast-path number; arr_idx %ld", (long) arr_idx)); - pop_count = 0; - } else { - arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key); - DUK_ASSERT(key != NULL); - DUK_DDD(DUK_DDDPRINT("base object buffer, key is a non-fast-path number; after " - "coercion key is %!T, arr_idx %ld", - (duk_tval *) duk_get_tval(thr, -1), - (long) arr_idx)); - pop_count = 1; - } - - if (arr_idx != DUK__NO_ARRAY_INDEX && arr_idx < DUK_HBUFFER_GET_SIZE(h)) { - duk_uint8_t *data; - DUK_DDD(DUK_DDDPRINT("writing to buffer data at index %ld", (long) arr_idx)); - data = (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h); - - /* XXX: duk_to_int() ensures we'll get 8 lowest bits as - * as input is within duk_int_t range (capped outside it). - */ -#if defined(DUK_USE_FASTINT) - /* Buffer writes are often integers. */ - if (DUK_TVAL_IS_FASTINT(tv_val)) { - data[arr_idx] = (duk_uint8_t) DUK_TVAL_GET_FASTINT_U32(tv_val); - } else -#endif - { - duk_push_tval(thr, tv_val); - data[arr_idx] = (duk_uint8_t) duk_to_uint32(thr, -1); - pop_count++; - } - - duk_pop_n_unsafe(thr, pop_count); - DUK_DDD(DUK_DDDPRINT("result: success (buffer data write)")); - DUK_STATS_INC(thr->heap, stats_putprop_bufferidx); - return 1; - } - - if (pop_count == 0) { - /* This is a pretty awkward control flow, but we need to recheck the - * key coercion here. - */ - arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key); - DUK_ASSERT(key != NULL); - DUK_DDD(DUK_DDDPRINT("base object buffer, key is a non-fast-path number; after " - "coercion key is %!T, arr_idx %ld", - (duk_tval *) duk_get_tval(thr, -1), - (long) arr_idx)); - } - - if (key == DUK_HTHREAD_STRING_LENGTH(thr)) { - goto fail_not_writable; - } - - DUK_DDD(DUK_DDDPRINT("base object is a buffer, start lookup from Uint8Array prototype")); - curr = thr->builtins[DUK_BIDX_UINT8ARRAY_PROTOTYPE]; - goto lookup; /* avoid double coercion */ - } - - case DUK_TAG_POINTER: { - DUK_DDD(DUK_DDDPRINT("base object is a pointer, start lookup from pointer prototype")); - curr = thr->builtins[DUK_BIDX_POINTER_PROTOTYPE]; - break; - } - - case DUK_TAG_LIGHTFUNC: { - /* Lightfuncs have no own properties and are considered non-extensible. - * However, the write may be captured by an inherited setter which - * means we can't stop the lookup here. - */ - DUK_DDD(DUK_DDDPRINT("base object is a lightfunc, start lookup from function prototype")); - curr = thr->builtins[DUK_BIDX_NATIVE_FUNCTION_PROTOTYPE]; - break; - } - -#if defined(DUK_USE_FASTINT) - case DUK_TAG_FASTINT: -#endif - default: { - /* number */ - DUK_DDD(DUK_DDDPRINT("base object is a number, start lookup from number prototype")); - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_obj)); - curr = thr->builtins[DUK_BIDX_NUMBER_PROTOTYPE]; - break; - } - } - - DUK_ASSERT(key == NULL); - arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key); - DUK_ASSERT(key != NULL); - -lookup: - - /* - * Check whether the property already exists in the prototype chain. - * Note that the actual write goes into the original base object - * (except if an accessor property captures the write). - */ - - /* [key] */ - - DUK_ASSERT(curr != NULL); - sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY; - do { - if (!duk__get_own_propdesc_raw(thr, curr, key, arr_idx, &desc, 0 /*flags*/)) { /* don't push value */ - goto next_in_chain; - } - - if (desc.flags & DUK_PROPDESC_FLAG_ACCESSOR) { - /* - * Found existing accessor property (own or inherited). - * Call setter with 'this' set to orig, and value as the only argument. - * Setter calls are OK even for ROM objects. - * - * Note: no exotic arguments object behavior, because [[Put]] never - * calls [[DefineOwnProperty]] (E5 Section 8.12.5, step 5.b). - */ - - duk_hobject *setter; - - DUK_DD(DUK_DDPRINT("put to an own or inherited accessor, calling setter")); - - setter = DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, curr, desc.e_idx); - if (!setter) { - goto fail_no_setter; - } - duk_push_hobject(thr, setter); - duk_push_tval(thr, tv_obj); /* note: original, uncoerced base */ - duk_push_tval(thr, tv_val); /* [key setter this val] */ -#if defined(DUK_USE_NONSTD_SETTER_KEY_ARGUMENT) - duk_dup_m4(thr); - duk_call_method(thr, 2); /* [key setter this val key] -> [key retval] */ -#else - duk_call_method(thr, 1); /* [key setter this val] -> [key retval] */ -#endif - duk_pop_unsafe(thr); /* ignore retval -> [key] */ - goto success_no_arguments_exotic; - } - - if (orig == NULL) { - /* - * Found existing own or inherited plain property, but original - * base is a primitive value. - */ - DUK_DD(DUK_DDPRINT("attempt to create a new property in a primitive base object")); - goto fail_base_primitive; - } - - if (curr != orig) { - /* - * Found existing inherited plain property. - * Do an access control check, and if OK, write - * new property to 'orig'. - */ - if (!DUK_HOBJECT_HAS_EXTENSIBLE(orig)) { - DUK_DD( - DUK_DDPRINT("found existing inherited plain property, but original object is not extensible")); - goto fail_not_extensible; - } - if (!(desc.flags & DUK_PROPDESC_FLAG_WRITABLE)) { - DUK_DD(DUK_DDPRINT("found existing inherited plain property, original object is extensible, but " - "inherited property is not writable")); - goto fail_not_writable; - } - DUK_DD(DUK_DDPRINT("put to new property, object extensible, inherited property found and is writable")); - goto create_new; - } else { - /* - * Found existing own (non-inherited) plain property. - * Do an access control check and update in place. - */ - - if (!(desc.flags & DUK_PROPDESC_FLAG_WRITABLE)) { - DUK_DD( - DUK_DDPRINT("found existing own (non-inherited) plain property, but property is not writable")); - goto fail_not_writable; - } - if (desc.flags & DUK_PROPDESC_FLAG_VIRTUAL) { - DUK_DD(DUK_DDPRINT("found existing own (non-inherited) virtual property, property is writable")); - - if (DUK_HOBJECT_IS_ARRAY(curr)) { - /* - * Write to 'length' of an array is a very complex case - * handled in a helper which updates both the array elements - * and writes the new 'length'. The write may result in an - * unconditional RangeError or a partial write (indicated - * by a return code). - * - * Note: the helper has an unnecessary writability check - * for 'length', we already know it is writable. - */ - DUK_ASSERT(key == DUK_HTHREAD_STRING_LENGTH(thr)); /* only virtual array property */ - - DUK_DDD(DUK_DDDPRINT( - "writing existing 'length' property to array exotic, invoke complex helper")); - - /* XXX: the helper currently assumes stack top contains new - * 'length' value and the whole calling convention is not very - * compatible with what we need. - */ - - duk_push_tval(thr, tv_val); /* [key val] */ - rc = duk__handle_put_array_length(thr, orig); - duk_pop_unsafe(thr); /* [key val] -> [key] */ - if (!rc) { - goto fail_array_length_partial; - } - - /* key is 'length', cannot match argument exotic behavior */ - goto success_no_arguments_exotic; - } -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) - else if (DUK_HOBJECT_IS_BUFOBJ(curr)) { - duk_hbufobj *h_bufobj; - duk_uint_t byte_off; - duk_small_uint_t elem_size; - - h_bufobj = (duk_hbufobj *) curr; - DUK_HBUFOBJ_ASSERT_VALID(h_bufobj); - - DUK_DD(DUK_DDPRINT("writable virtual property is in buffer object")); - - /* Careful with wrapping: arr_idx upshift may easily wrap, whereas - * length downshift won't. - */ - if (arr_idx < (h_bufobj->length >> h_bufobj->shift) && - DUK_HBUFOBJ_HAS_VIRTUAL_INDICES(h_bufobj)) { - duk_uint8_t *data; - DUK_DDD(DUK_DDDPRINT("writing to buffer data at index %ld", (long) arr_idx)); - - DUK_ASSERT(arr_idx != DUK__NO_ARRAY_INDEX); /* index/length check guarantees */ - byte_off = arr_idx - << h_bufobj->shift; /* no wrap assuming h_bufobj->length is valid */ - elem_size = (duk_small_uint_t) (1U << h_bufobj->shift); - - /* Coerce to number before validating pointers etc so that the - * number coercions in duk_hbufobj_validated_write() are - * guaranteed to be side effect free and not invalidate the - * pointer checks we do here. - */ - duk_push_tval(thr, tv_val); - (void) duk_to_number_m1(thr); - - if (h_bufobj->buf != NULL && - DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h_bufobj, byte_off + elem_size)) { - data = (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_bufobj->buf) + - h_bufobj->offset + byte_off; - duk_hbufobj_validated_write(thr, h_bufobj, data, elem_size); - } else { - DUK_D(DUK_DPRINT( - "bufobj access out of underlying buffer, ignoring (write skipped)")); - } - duk_pop_unsafe(thr); - goto success_no_arguments_exotic; - } - } -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - - DUK_D(DUK_DPRINT("should not happen, key %!O", key)); - goto fail_internal; /* should not happen */ - } - DUK_DD(DUK_DDPRINT("put to existing own plain property, property is writable")); - goto update_old; - } - DUK_UNREACHABLE(); - - next_in_chain: - /* XXX: option to pretend property doesn't exist if sanity limit is - * hit might be useful. - */ - if (DUK_UNLIKELY(sanity-- == 0)) { - DUK_ERROR_RANGE(thr, DUK_STR_PROTOTYPE_CHAIN_LIMIT); - DUK_WO_NORETURN(return 0;); - } - curr = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, curr); - } while (curr != NULL); - - /* - * Property not found in prototype chain. - */ - - DUK_DDD(DUK_DDDPRINT("property not found in prototype chain")); - - if (orig == NULL) { - DUK_DD(DUK_DDPRINT("attempt to create a new property in a primitive base object")); - goto fail_base_primitive; - } - - if (!DUK_HOBJECT_HAS_EXTENSIBLE(orig)) { - DUK_DD(DUK_DDPRINT("put to a new property (not found in prototype chain), but original object not extensible")); - goto fail_not_extensible; - } - - goto create_new; - -update_old: - - /* - * Update an existing property of the base object. - */ - - /* [key] */ - - DUK_DDD(DUK_DDDPRINT("update an existing property of the original object")); - - DUK_ASSERT(orig != NULL); -#if defined(DUK_USE_ROM_OBJECTS) - /* This should not happen because DUK_TAG_OBJECT case checks - * for this already, but check just in case. - */ - if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) orig)) { - goto fail_not_writable; - } -#endif - - /* Although there are writable virtual properties (e.g. plain buffer - * and buffer object number indices), they are handled before we come - * here. - */ - DUK_ASSERT((desc.flags & DUK_PROPDESC_FLAG_VIRTUAL) == 0); - DUK_ASSERT(desc.a_idx >= 0 || desc.e_idx >= 0); - - /* Array own property .length is handled above. */ - DUK_ASSERT(!(DUK_HOBJECT_IS_ARRAY(orig) && key == DUK_HTHREAD_STRING_LENGTH(thr))); - - if (desc.e_idx >= 0) { - tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, orig, desc.e_idx); - DUK_DDD(DUK_DDDPRINT("previous entry value: %!iT", (duk_tval *) tv)); - DUK_TVAL_SET_TVAL_UPDREF(thr, tv, tv_val); /* side effects; e_idx may be invalidated */ - /* don't touch property attributes or hash part */ - DUK_DD(DUK_DDPRINT("put to an existing entry at index %ld -> new value %!iT", (long) desc.e_idx, (duk_tval *) tv)); - } else { - /* Note: array entries are always writable, so the writability check - * above is pointless for them. The check could be avoided with some - * refactoring but is probably not worth it. - */ - - DUK_ASSERT(desc.a_idx >= 0); - tv = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, orig, desc.a_idx); - DUK_DDD(DUK_DDDPRINT("previous array value: %!iT", (duk_tval *) tv)); - DUK_TVAL_SET_TVAL_UPDREF(thr, tv, tv_val); /* side effects; a_idx may be invalidated */ - DUK_DD(DUK_DDPRINT("put to an existing array entry at index %ld -> new value %!iT", - (long) desc.a_idx, - (duk_tval *) tv)); - } - - /* Regardless of whether property is found in entry or array part, - * it may have arguments exotic behavior (array indices may reside - * in entry part for abandoned / non-existent array parts). - */ - goto success_with_arguments_exotic; - -create_new: - - /* - * Create a new property in the original object. - * - * Exotic properties need to be reconsidered here from a write - * perspective (not just property attributes perspective). - * However, the property does not exist in the object already, - * so this limits the kind of exotic properties that apply. - */ - - /* [key] */ - - DUK_DDD(DUK_DDDPRINT("create new property to original object")); - - DUK_ASSERT(orig != NULL); - - /* Array own property .length is handled above. */ - DUK_ASSERT(!(DUK_HOBJECT_IS_ARRAY(orig) && key == DUK_HTHREAD_STRING_LENGTH(thr))); - -#if defined(DUK_USE_ROM_OBJECTS) - /* This should not happen because DUK_TAG_OBJECT case checks - * for this already, but check just in case. - */ - if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) orig)) { - goto fail_not_writable; - } -#endif - - /* Not possible because array object 'length' is present - * from its creation and cannot be deleted, and is thus - * caught as an existing property above. - */ - DUK_ASSERT(!(DUK_HOBJECT_HAS_EXOTIC_ARRAY(orig) && key == DUK_HTHREAD_STRING_LENGTH(thr))); - - if (DUK_HOBJECT_HAS_EXOTIC_ARRAY(orig) && arr_idx != DUK__NO_ARRAY_INDEX) { - /* automatic length update */ - duk_uint32_t old_len; - duk_harray *a; - - a = (duk_harray *) orig; - DUK_HARRAY_ASSERT_VALID(a); - - old_len = a->length; - - if (arr_idx >= old_len) { - DUK_DDD(DUK_DDDPRINT("write new array entry requires length update " - "(arr_idx=%ld, old_len=%ld)", - (long) arr_idx, - (long) old_len)); - - if (DUK_HARRAY_LENGTH_NONWRITABLE(a)) { - DUK_DD(DUK_DDPRINT("attempt to extend array, but array 'length' is not writable")); - goto fail_not_writable; - } - - /* Note: actual update happens once write has been completed - * without error below. The write should always succeed - * from a specification viewpoint, but we may e.g. run out - * of memory. It's safer in this order. - */ - - DUK_ASSERT(arr_idx != 0xffffffffUL); - new_array_length = arr_idx + 1; /* flag for later write */ - } else { - DUK_DDD(DUK_DDDPRINT("write new array entry does not require length update " - "(arr_idx=%ld, old_len=%ld)", - (long) arr_idx, - (long) old_len)); - } - } - - /* write_to_array_part: */ - - /* - * Write to array part? - * - * Note: array abandonding requires a property resize which uses - * 'rechecks' valstack for temporaries and may cause any existing - * valstack pointers to be invalidated. To protect against this, - * tv_obj, tv_key, and tv_val are copies of the original inputs. - */ - - if (arr_idx != DUK__NO_ARRAY_INDEX && DUK_HOBJECT_HAS_ARRAY_PART(orig)) { - tv = duk__obtain_arridx_slot(thr, arr_idx, orig); - if (tv == NULL) { - DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(orig)); - goto write_to_entry_part; - } - - /* prev value must be unused, no decref */ - DUK_ASSERT(DUK_TVAL_IS_UNUSED(tv)); - DUK_TVAL_SET_TVAL(tv, tv_val); - DUK_TVAL_INCREF(thr, tv); - DUK_DD(DUK_DDPRINT("put to new array entry: %ld -> %!T", (long) arr_idx, (duk_tval *) tv)); - - /* Note: array part values are [[Writable]], [[Enumerable]], - * and [[Configurable]] which matches the required attributes - * here. - */ - goto entry_updated; - } - -write_to_entry_part: - - /* - * Write to entry part - */ - - /* entry allocation updates hash part and increases the key - * refcount; may need a props allocation resize but doesn't - * 'recheck' the valstack. - */ - e_idx = duk__hobject_alloc_entry_checked(thr, orig, key); - DUK_ASSERT(e_idx >= 0); - - tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, orig, e_idx); - /* prev value can be garbage, no decref */ - DUK_TVAL_SET_TVAL(tv, tv_val); - DUK_TVAL_INCREF(thr, tv); - DUK_HOBJECT_E_SET_FLAGS(thr->heap, orig, e_idx, DUK_PROPDESC_FLAGS_WEC); - goto entry_updated; - -entry_updated: - - /* - * Possible pending array length update, which must only be done - * if the actual entry write succeeded. - */ - - if (new_array_length > 0) { - /* Note: zero works as a "no update" marker because the new length - * can never be zero after a new property is written. - */ - - DUK_ASSERT(DUK_HOBJECT_HAS_EXOTIC_ARRAY(orig)); - - DUK_DDD(DUK_DDDPRINT("write successful, pending array length update to: %ld", (long) new_array_length)); - - ((duk_harray *) orig)->length = new_array_length; - } - - /* - * Arguments exotic behavior not possible for new properties: all - * magically bound properties are initially present in the arguments - * object, and if they are deleted, the binding is also removed from - * parameter map. - */ - - goto success_no_arguments_exotic; - -success_with_arguments_exotic: - - /* - * Arguments objects have exotic [[DefineOwnProperty]] which updates - * the internal 'map' of arguments for writes to currently mapped - * arguments. More conretely, writes to mapped arguments generate - * a write to a bound variable. - * - * The [[Put]] algorithm invokes [[DefineOwnProperty]] for existing - * data properties and new properties, but not for existing accessors. - * Hence, in E5 Section 10.6 ([[DefinedOwnProperty]] algorithm), we - * have a Desc with 'Value' (and possibly other properties too), and - * we end up in step 5.b.i. - */ - - if (arr_idx != DUK__NO_ARRAY_INDEX && DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(orig)) { - /* Note: only numbered indices are relevant, so arr_idx fast reject - * is good (this is valid unless there are more than 4**32-1 arguments). - */ - - DUK_DDD(DUK_DDDPRINT("putprop successful, arguments exotic behavior needed")); - - /* Note: we can reuse 'desc' here */ - - /* XXX: top of stack must contain value, which helper doesn't touch, - * rework to use tv_val directly? - */ - - duk_push_tval(thr, tv_val); - (void) duk__check_arguments_map_for_put(thr, orig, key, &desc, throw_flag); - duk_pop_unsafe(thr); - } - /* fall thru */ - -success_no_arguments_exotic: - /* shared exit path now */ - DUK_DDD(DUK_DDDPRINT("result: success")); - duk_pop_unsafe(thr); /* remove key */ - return 1; - -#if defined(DUK_USE_ES6_PROXY) -fail_proxy_rejected: - DUK_DDD(DUK_DDDPRINT("result: error, proxy rejects")); - if (throw_flag) { - DUK_ERROR_TYPE(thr, DUK_STR_PROXY_REJECTED); - DUK_WO_NORETURN(return 0;); - } - /* Note: no key on stack */ - return 0; -#endif - -fail_base_primitive: - DUK_DDD(DUK_DDDPRINT("result: error, base primitive")); - if (throw_flag) { -#if defined(DUK_USE_PARANOID_ERRORS) - DUK_ERROR_TYPE(thr, DUK_STR_INVALID_BASE); -#else - DUK_ERROR_FMT2(thr, - DUK_ERR_TYPE_ERROR, - "cannot write property %s of %s", - duk_push_string_tval_readable(thr, tv_key), - duk_push_string_tval_readable(thr, tv_obj)); -#endif - DUK_WO_NORETURN(return 0;); - } - duk_pop_unsafe(thr); /* remove key */ - return 0; - -fail_not_extensible: - DUK_DDD(DUK_DDDPRINT("result: error, not extensible")); - if (throw_flag) { - DUK_ERROR_TYPE(thr, DUK_STR_NOT_EXTENSIBLE); - DUK_WO_NORETURN(return 0;); - } - duk_pop_unsafe(thr); /* remove key */ - return 0; - -fail_not_writable: - DUK_DDD(DUK_DDDPRINT("result: error, not writable")); - if (throw_flag) { - DUK_ERROR_TYPE(thr, DUK_STR_NOT_WRITABLE); - DUK_WO_NORETURN(return 0;); - } - duk_pop_unsafe(thr); /* remove key */ - return 0; - -#if defined(DUK_USE_ROM_OBJECTS) -fail_not_writable_no_pop: - DUK_DDD(DUK_DDDPRINT("result: error, not writable")); - if (throw_flag) { - DUK_ERROR_TYPE(thr, DUK_STR_NOT_WRITABLE); - DUK_WO_NORETURN(return 0;); - } - return 0; -#endif - -fail_array_length_partial: - DUK_DD(DUK_DDPRINT("result: error, array length write only partially successful")); - if (throw_flag) { - DUK_ERROR_TYPE(thr, DUK_STR_NOT_CONFIGURABLE); - DUK_WO_NORETURN(return 0;); - } - duk_pop_unsafe(thr); /* remove key */ - return 0; - -fail_no_setter: - DUK_DDD(DUK_DDDPRINT("result: error, accessor property without setter")); - if (throw_flag) { - DUK_ERROR_TYPE(thr, DUK_STR_SETTER_UNDEFINED); - DUK_WO_NORETURN(return 0;); - } - duk_pop_unsafe(thr); /* remove key */ - return 0; - -fail_internal: - DUK_DDD(DUK_DDDPRINT("result: error, internal")); - if (throw_flag) { - DUK_ERROR_INTERNAL(thr); - DUK_WO_NORETURN(return 0;); - } - duk_pop_unsafe(thr); /* remove key */ - return 0; -} - -/* - * ECMAScript compliant [[Delete]](P, Throw). - */ - -DUK_INTERNAL duk_bool_t duk_hobject_delprop_raw(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_small_uint_t flags) { - duk_propdesc desc; - duk_tval *tv; - duk_uint32_t arr_idx; - duk_bool_t throw_flag; - duk_bool_t force_flag; - - throw_flag = (flags & DUK_DELPROP_FLAG_THROW); - force_flag = (flags & DUK_DELPROP_FLAG_FORCE); - - DUK_DDD(DUK_DDDPRINT("delprop_raw: thr=%p, obj=%p, key=%p, throw=%ld, force=%ld (obj -> %!O, key -> %!O)", - (void *) thr, - (void *) obj, - (void *) key, - (long) throw_flag, - (long) force_flag, - (duk_heaphdr *) obj, - (duk_heaphdr *) key)); - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - DUK_ASSERT(obj != NULL); - DUK_ASSERT(key != NULL); - - DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE); - - arr_idx = DUK_HSTRING_GET_ARRIDX_FAST(key); - - /* 0 = don't push current value */ - if (!duk__get_own_propdesc_raw(thr, obj, key, arr_idx, &desc, 0 /*flags*/)) { /* don't push value */ - DUK_DDD(DUK_DDDPRINT("property not found, succeed always")); - goto success; - } - -#if defined(DUK_USE_ROM_OBJECTS) - if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)) { - DUK_DD(DUK_DDPRINT("attempt to delprop on read-only target object")); - goto fail_not_configurable; - } -#endif - - if ((desc.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) == 0 && !force_flag) { - goto fail_not_configurable; - } - if (desc.a_idx < 0 && desc.e_idx < 0) { - /* Currently there are no deletable virtual properties, but - * with force_flag we might attempt to delete one. - */ - DUK_DD(DUK_DDPRINT("delete failed: property found, force flag, but virtual (and implicitly non-configurable)")); - goto fail_virtual; - } - - if (desc.a_idx >= 0) { - DUK_ASSERT(desc.e_idx < 0); - - tv = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, desc.a_idx); - DUK_TVAL_SET_UNUSED_UPDREF(thr, tv); /* side effects */ - goto success; - } else { - DUK_ASSERT(desc.a_idx < 0); - - /* remove hash entry (no decref) */ -#if defined(DUK_USE_HOBJECT_HASH_PART) - if (desc.h_idx >= 0) { - duk_uint32_t *h_base = DUK_HOBJECT_H_GET_BASE(thr->heap, obj); - - DUK_DDD(DUK_DDDPRINT("removing hash entry at h_idx %ld", (long) desc.h_idx)); - DUK_ASSERT(DUK_HOBJECT_GET_HSIZE(obj) > 0); - DUK_ASSERT((duk_uint32_t) desc.h_idx < DUK_HOBJECT_GET_HSIZE(obj)); - h_base[desc.h_idx] = DUK__HASH_DELETED; - } else { - DUK_ASSERT(DUK_HOBJECT_GET_HSIZE(obj) == 0); - } -#else - DUK_ASSERT(DUK_HOBJECT_GET_HSIZE(obj) == 0); -#endif - - /* Remove value. This requires multiple writes so avoid side - * effects via no-refzero macros so that e_idx is not - * invalidated. - */ - DUK_DDD(DUK_DDDPRINT("before removing value, e_idx %ld, key %p, key at slot %p", - (long) desc.e_idx, - (void *) key, - (void *) DUK_HOBJECT_E_GET_KEY(thr->heap, obj, desc.e_idx))); - DUK_DDD(DUK_DDDPRINT("removing value at e_idx %ld", (long) desc.e_idx)); - if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, obj, desc.e_idx)) { - duk_hobject *tmp; - - tmp = DUK_HOBJECT_E_GET_VALUE_GETTER(thr->heap, obj, desc.e_idx); - DUK_HOBJECT_E_SET_VALUE_GETTER(thr->heap, obj, desc.e_idx, NULL); - DUK_UNREF(tmp); - DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, tmp); - - tmp = DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, obj, desc.e_idx); - DUK_HOBJECT_E_SET_VALUE_SETTER(thr->heap, obj, desc.e_idx, NULL); - DUK_UNREF(tmp); - DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, tmp); - } else { - tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, desc.e_idx); - DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ(thr, tv); - } -#if 0 - /* Not strictly necessary because if key == NULL, flag MUST be ignored. */ - DUK_HOBJECT_E_SET_FLAGS(thr->heap, obj, desc.e_idx, 0); -#endif - - /* Remove key. */ - DUK_DDD(DUK_DDDPRINT("before removing key, e_idx %ld, key %p, key at slot %p", - (long) desc.e_idx, - (void *) key, - (void *) DUK_HOBJECT_E_GET_KEY(thr->heap, obj, desc.e_idx))); - DUK_DDD(DUK_DDDPRINT("removing key at e_idx %ld", (long) desc.e_idx)); - DUK_ASSERT(key == DUK_HOBJECT_E_GET_KEY(thr->heap, obj, desc.e_idx)); - DUK_HOBJECT_E_SET_KEY(thr->heap, obj, desc.e_idx, NULL); - DUK_HSTRING_DECREF_NORZ(thr, key); - - /* Trigger refzero side effects only when we're done as a - * finalizer might operate on the object and affect the - * e_idx we're supposed to use. - */ - DUK_REFZERO_CHECK_SLOW(thr); - goto success; - } - - DUK_UNREACHABLE(); - -success: - /* - * Argument exotic [[Delete]] behavior (E5 Section 10.6) is - * a post-check, keeping arguments internal 'map' in sync with - * any successful deletes (note that property does not need to - * exist for delete to 'succeed'). - * - * Delete key from 'map'. Since 'map' only contains array index - * keys, we can use arr_idx for a fast skip. - */ - - DUK_DDD(DUK_DDDPRINT("delete successful, check for arguments exotic behavior")); - - if (arr_idx != DUK__NO_ARRAY_INDEX && DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj)) { - /* Note: only numbered indices are relevant, so arr_idx fast reject - * is good (this is valid unless there are more than 4**32-1 arguments). - */ - - DUK_DDD(DUK_DDDPRINT("delete successful, arguments exotic behavior needed")); - - /* Note: we can reuse 'desc' here */ - (void) duk__check_arguments_map_for_delete(thr, obj, key, &desc); - } - - DUK_DDD(DUK_DDDPRINT("delete successful")); - return 1; - -fail_virtual: /* just use the same "not configurable" error message */ -fail_not_configurable: - DUK_DDD(DUK_DDDPRINT("delete failed: property found, not configurable")); - - if (throw_flag) { - DUK_ERROR_TYPE(thr, DUK_STR_NOT_CONFIGURABLE); - DUK_WO_NORETURN(return 0;); - } - return 0; -} - -/* - * DELPROP: ECMAScript property deletion. - */ - -DUK_INTERNAL duk_bool_t duk_hobject_delprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key, duk_bool_t throw_flag) { - duk_hstring *key = NULL; -#if defined(DUK_USE_ES6_PROXY) - duk_propdesc desc; -#endif - duk_int_t entry_top; - duk_uint32_t arr_idx = DUK__NO_ARRAY_INDEX; - duk_bool_t rc; - - DUK_DDD(DUK_DDDPRINT("delprop: thr=%p, obj=%p, key=%p (obj -> %!T, key -> %!T)", - (void *) thr, - (void *) tv_obj, - (void *) tv_key, - (duk_tval *) tv_obj, - (duk_tval *) tv_key)); - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - DUK_ASSERT(tv_obj != NULL); - DUK_ASSERT(tv_key != NULL); - - DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE); - - /* Storing the entry top is cheaper here to ensure stack is correct at exit, - * as there are several paths out. - */ - entry_top = duk_get_top(thr); - - if (DUK_TVAL_IS_UNDEFINED(tv_obj) || DUK_TVAL_IS_NULL(tv_obj)) { - DUK_DDD(DUK_DDDPRINT("base object is undefined or null -> reject")); - goto fail_invalid_base_uncond; - } - - duk_push_tval(thr, tv_obj); - duk_push_tval(thr, tv_key); - - tv_obj = DUK_GET_TVAL_NEGIDX(thr, -2); - if (DUK_TVAL_IS_OBJECT(tv_obj)) { - duk_hobject *obj = DUK_TVAL_GET_OBJECT(tv_obj); - DUK_ASSERT(obj != NULL); - -#if defined(DUK_USE_ES6_PROXY) - if (DUK_UNLIKELY(DUK_HOBJECT_IS_PROXY(obj))) { - duk_hobject *h_target; - duk_bool_t tmp_bool; - - /* Note: proxy handling must happen before key is string coerced. */ - - if (duk__proxy_check_prop(thr, obj, DUK_STRIDX_DELETE_PROPERTY, tv_key, &h_target)) { - /* -> [ ... obj key trap handler ] */ - DUK_DDD(DUK_DDDPRINT("-> proxy object 'deleteProperty' for key %!T", (duk_tval *) tv_key)); - duk_push_hobject(thr, h_target); /* target */ - duk_dup_m4(thr); /* P */ - duk_call_method(thr, 2 /*nargs*/); - tmp_bool = duk_to_boolean_top_pop(thr); - if (!tmp_bool) { - goto fail_proxy_rejected; /* retval indicates delete failed */ - } - - /* Target object must be checked for a conflicting - * non-configurable property. - */ - tv_key = DUK_GET_TVAL_NEGIDX(thr, -1); - arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key); - DUK_ASSERT(key != NULL); - - if (duk__get_own_propdesc_raw(thr, h_target, key, arr_idx, &desc, 0 /*flags*/)) { /* don't push - value */ - duk_small_int_t desc_reject; - - DUK_DDD(DUK_DDDPRINT("proxy 'deleteProperty': target has matching property %!O, check for " - "conflicting property; desc.flags=0x%08lx, " - "desc.get=%p, desc.set=%p", - (duk_heaphdr *) key, - (unsigned long) desc.flags, - (void *) desc.get, - (void *) desc.set)); - - desc_reject = !(desc.flags & DUK_PROPDESC_FLAG_CONFIGURABLE); - if (desc_reject) { - /* unconditional */ - DUK_ERROR_TYPE(thr, DUK_STR_PROXY_REJECTED); - DUK_WO_NORETURN(return 0;); - } - } - rc = 1; /* success */ - goto done_rc; - } - - obj = h_target; /* resume delete to target */ - } -#endif /* DUK_USE_ES6_PROXY */ - - arr_idx = duk__to_property_key(thr, -1, &key); - DUK_ASSERT(key != NULL); - - rc = duk_hobject_delprop_raw(thr, obj, key, throw_flag ? DUK_DELPROP_FLAG_THROW : 0); - goto done_rc; - } else if (DUK_TVAL_IS_STRING(tv_obj)) { - /* String has .length and array index virtual properties - * which can't be deleted. No need for a symbol check; - * no offending virtual symbols exist. - */ - /* XXX: unnecessary string coercion for array indices, - * intentional to keep small. - */ - duk_hstring *h = DUK_TVAL_GET_STRING(tv_obj); - DUK_ASSERT(h != NULL); - - arr_idx = duk__to_property_key(thr, -1, &key); - DUK_ASSERT(key != NULL); - - if (key == DUK_HTHREAD_STRING_LENGTH(thr)) { - goto fail_not_configurable; - } - - if (arr_idx != DUK__NO_ARRAY_INDEX && arr_idx < DUK_HSTRING_GET_CHARLEN(h)) { - goto fail_not_configurable; - } - } else if (DUK_TVAL_IS_BUFFER(tv_obj)) { - /* XXX: unnecessary string coercion for array indices, - * intentional to keep small; some overlap with string - * handling. - */ - duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv_obj); - DUK_ASSERT(h != NULL); - - arr_idx = duk__to_property_key(thr, -1, &key); - DUK_ASSERT(key != NULL); - - if (key == DUK_HTHREAD_STRING_LENGTH(thr)) { - goto fail_not_configurable; - } - - if (arr_idx != DUK__NO_ARRAY_INDEX && arr_idx < DUK_HBUFFER_GET_SIZE(h)) { - goto fail_not_configurable; - } - } else if (DUK_TVAL_IS_LIGHTFUNC(tv_obj)) { - /* Lightfunc has no virtual properties since Duktape 2.2 - * so success. Still must coerce key for side effects. - */ - - arr_idx = duk__to_property_key(thr, -1, &key); - DUK_ASSERT(key != NULL); - DUK_UNREF(key); - } - - /* non-object base, no offending virtual property */ - rc = 1; - goto done_rc; - -done_rc: - duk_set_top_unsafe(thr, entry_top); - return rc; - -fail_invalid_base_uncond: - /* Note: unconditional throw */ - DUK_ASSERT(duk_get_top(thr) == entry_top); -#if defined(DUK_USE_PARANOID_ERRORS) - DUK_ERROR_TYPE(thr, DUK_STR_INVALID_BASE); -#else - DUK_ERROR_FMT2(thr, - DUK_ERR_TYPE_ERROR, - "cannot delete property %s of %s", - duk_push_string_tval_readable(thr, tv_key), - duk_push_string_tval_readable(thr, tv_obj)); -#endif - DUK_WO_NORETURN(return 0;); - -#if defined(DUK_USE_ES6_PROXY) -fail_proxy_rejected: - if (throw_flag) { - DUK_ERROR_TYPE(thr, DUK_STR_PROXY_REJECTED); - DUK_WO_NORETURN(return 0;); - } - duk_set_top_unsafe(thr, entry_top); - return 0; -#endif - -fail_not_configurable: - if (throw_flag) { - DUK_ERROR_TYPE(thr, DUK_STR_NOT_CONFIGURABLE); - DUK_WO_NORETURN(return 0;); - } - duk_set_top_unsafe(thr, entry_top); - return 0; -} - -/* - * Internal helper to define a property with specific flags, ignoring - * normal semantics such as extensibility, write protection etc. - * Overwrites any existing value and attributes unless caller requests - * that value only be updated if it doesn't already exists. - * - * Does not support: - * - virtual properties (error if write attempted) - * - getter/setter properties (error if write attempted) - * - non-default (!= WEC) attributes for array entries (error if attempted) - * - array abandoning: if array part exists, it is always extended - * - array 'length' updating - * - * Stack: [... in_val] -> [] - * - * Used for e.g. built-in initialization and environment record - * operations. - */ - -DUK_INTERNAL void duk_hobject_define_property_internal(duk_hthread *thr, - duk_hobject *obj, - duk_hstring *key, - duk_small_uint_t flags) { - duk_propdesc desc; - duk_uint32_t arr_idx; - duk_int_t e_idx; - duk_tval *tv1 = NULL; - duk_tval *tv2 = NULL; - duk_small_uint_t propflags = flags & DUK_PROPDESC_FLAGS_MASK; /* mask out flags not actually stored */ - - DUK_DDD(DUK_DDDPRINT("define new property (internal): thr=%p, obj=%!O, key=%!O, flags=0x%02lx, val=%!T", - (void *) thr, - (duk_heaphdr *) obj, - (duk_heaphdr *) key, - (unsigned long) flags, - (duk_tval *) duk_get_tval(thr, -1))); - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - DUK_ASSERT(obj != NULL); - DUK_ASSERT(key != NULL); - DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)); - DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE); - DUK_ASSERT(duk_is_valid_index(thr, -1)); /* contains value */ - - arr_idx = DUK_HSTRING_GET_ARRIDX_SLOW(key); - - if (duk__get_own_propdesc_raw(thr, obj, key, arr_idx, &desc, 0 /*flags*/)) { /* don't push value */ - if (desc.e_idx >= 0) { - if (flags & DUK_PROPDESC_FLAG_NO_OVERWRITE) { - DUK_DDD(DUK_DDDPRINT("property already exists in the entry part -> skip as requested")); - goto pop_exit; - } - DUK_DDD(DUK_DDDPRINT("property already exists in the entry part -> update value and attributes")); - if (DUK_UNLIKELY(DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, obj, desc.e_idx))) { - DUK_D(DUK_DPRINT("existing property is an accessor, not supported")); - goto error_internal; - } - - DUK_HOBJECT_E_SET_FLAGS(thr->heap, obj, desc.e_idx, propflags); - tv1 = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, desc.e_idx); - } else if (desc.a_idx >= 0) { - if (flags & DUK_PROPDESC_FLAG_NO_OVERWRITE) { - DUK_DDD(DUK_DDDPRINT("property already exists in the array part -> skip as requested")); - goto pop_exit; - } - DUK_DDD(DUK_DDDPRINT("property already exists in the array part -> update value (assert attributes)")); - if (propflags != DUK_PROPDESC_FLAGS_WEC) { - DUK_D(DUK_DPRINT("existing property in array part, but propflags not WEC (0x%02lx)", - (unsigned long) propflags)); - goto error_internal; - } - - tv1 = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, desc.a_idx); - } else { - if (flags & DUK_PROPDESC_FLAG_NO_OVERWRITE) { - DUK_DDD(DUK_DDDPRINT("property already exists but is virtual -> skip as requested")); - goto pop_exit; - } - if (key == DUK_HTHREAD_STRING_LENGTH(thr) && DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj)) { - duk_uint32_t new_len; -#if defined(DUK_USE_DEBUG) - duk_uint32_t prev_len; - prev_len = ((duk_harray *) obj)->length; -#endif - new_len = duk__to_new_array_length_checked(thr, DUK_GET_TVAL_NEGIDX(thr, -1)); - ((duk_harray *) obj)->length = new_len; - DUK_DD(DUK_DDPRINT("internal define property for array .length: %ld -> %ld", - (long) prev_len, - (long) ((duk_harray *) obj)->length)); - goto pop_exit; - } - DUK_DD(DUK_DDPRINT("property already exists but is virtual -> failure")); - goto error_virtual; - } - - goto write_value; - } - - if (DUK_HOBJECT_HAS_ARRAY_PART(obj)) { - if (arr_idx != DUK__NO_ARRAY_INDEX) { - DUK_DDD(DUK_DDDPRINT("property does not exist, object has array part -> possibly extend array part and " - "write value (assert attributes)")); - DUK_ASSERT(propflags == DUK_PROPDESC_FLAGS_WEC); - - tv1 = duk__obtain_arridx_slot(thr, arr_idx, obj); - if (tv1 == NULL) { - DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(obj)); - goto write_to_entry_part; - } - - tv1 = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, arr_idx); - goto write_value; - } - } - -write_to_entry_part: - DUK_DDD(DUK_DDDPRINT( - "property does not exist, object belongs in entry part -> allocate new entry and write value and attributes")); - e_idx = duk__hobject_alloc_entry_checked(thr, obj, key); /* increases key refcount */ - DUK_ASSERT(e_idx >= 0); - DUK_HOBJECT_E_SET_FLAGS(thr->heap, obj, e_idx, propflags); - tv1 = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, e_idx); - /* new entry: previous value is garbage; set to undefined to share write_value */ - DUK_TVAL_SET_UNDEFINED(tv1); - goto write_value; - -write_value: - /* tv1 points to value storage */ - - tv2 = duk_require_tval(thr, -1); /* late lookup, avoid side effects */ - DUK_DDD(DUK_DDDPRINT("writing/updating value: %!T -> %!T", (duk_tval *) tv1, (duk_tval *) tv2)); - - DUK_TVAL_SET_TVAL_UPDREF(thr, tv1, tv2); /* side effects */ - goto pop_exit; - -pop_exit: - duk_pop_unsafe(thr); /* remove in_val */ - return; - -error_virtual: /* share error message */ -error_internal: - DUK_ERROR_INTERNAL(thr); - DUK_WO_NORETURN(return;); -} - -/* - * Fast path for defining array indexed values without interning the key. - * This is used by e.g. code for Array prototype and traceback creation so - * must avoid interning. - */ - -DUK_INTERNAL void duk_hobject_define_property_internal_arridx(duk_hthread *thr, - duk_hobject *obj, - duk_uarridx_t arr_idx, - duk_small_uint_t flags) { - duk_hstring *key; - duk_tval *tv1, *tv2; - - DUK_DDD(DUK_DDDPRINT("define new property (internal) arr_idx fast path: thr=%p, obj=%!O, " - "arr_idx=%ld, flags=0x%02lx, val=%!T", - (void *) thr, - obj, - (long) arr_idx, - (unsigned long) flags, - (duk_tval *) duk_get_tval(thr, -1))); - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - DUK_ASSERT(obj != NULL); - DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)); - - if (DUK_HOBJECT_HAS_ARRAY_PART(obj) && arr_idx != DUK__NO_ARRAY_INDEX && flags == DUK_PROPDESC_FLAGS_WEC) { - DUK_ASSERT((flags & DUK_PROPDESC_FLAG_NO_OVERWRITE) == 0); /* covered by comparison */ - - DUK_DDD(DUK_DDDPRINT("define property to array part (property may or may not exist yet)")); - - tv1 = duk__obtain_arridx_slot(thr, arr_idx, obj); - if (tv1 == NULL) { - DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(obj)); - goto write_slow; - } - tv2 = duk_require_tval(thr, -1); - - DUK_TVAL_SET_TVAL_UPDREF(thr, tv1, tv2); /* side effects */ - - duk_pop_unsafe(thr); /* [ ...val ] -> [ ... ] */ - return; - } - -write_slow: - DUK_DDD(DUK_DDDPRINT("define property fast path didn't work, use slow path")); - - key = duk_push_uint_to_hstring(thr, (duk_uint_t) arr_idx); - DUK_ASSERT(key != NULL); - duk_insert(thr, -2); /* [ ... val key ] -> [ ... key val ] */ - - duk_hobject_define_property_internal(thr, obj, key, flags); - - duk_pop_unsafe(thr); /* [ ... key ] -> [ ... ] */ -} - -/* - * Internal helpers for managing object 'length' - */ - -DUK_INTERNAL duk_size_t duk_hobject_get_length(duk_hthread *thr, duk_hobject *obj) { - duk_double_t val; - - DUK_CTX_ASSERT_VALID(thr); - DUK_ASSERT(obj != NULL); - - /* Fast path for Arrays. */ - if (DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj)) { - return ((duk_harray *) obj)->length; - } - - /* Slow path, .length can be e.g. accessor, obj can be a Proxy, etc. */ - duk_push_hobject(thr, obj); - duk_push_hstring_stridx(thr, DUK_STRIDX_LENGTH); - (void) duk_hobject_getprop(thr, DUK_GET_TVAL_NEGIDX(thr, -2), DUK_GET_TVAL_NEGIDX(thr, -1)); - val = duk_to_number_m1(thr); - duk_pop_3_unsafe(thr); - - /* This isn't part of ECMAScript semantics; return a value within - * duk_size_t range, or 0 otherwise. - */ - if (val >= 0.0 && val <= (duk_double_t) DUK_SIZE_MAX) { - return (duk_size_t) val; - } - return 0; -} - -/* - * Fast finalizer check for an object. Walks the prototype chain, checking - * for finalizer presence using DUK_HOBJECT_FLAG_HAVE_FINALIZER which is kept - * in sync with the actual property when setting/removing the finalizer. - */ - -#if defined(DUK_USE_HEAPPTR16) -DUK_INTERNAL duk_bool_t duk_hobject_has_finalizer_fast_raw(duk_heap *heap, duk_hobject *obj) { -#else -DUK_INTERNAL duk_bool_t duk_hobject_has_finalizer_fast_raw(duk_hobject *obj) { -#endif - duk_uint_t sanity; - - DUK_ASSERT(obj != NULL); - - sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY; - do { - if (DUK_UNLIKELY(DUK_HOBJECT_HAS_HAVE_FINALIZER(obj))) { - return 1; - } - if (DUK_UNLIKELY(sanity-- == 0)) { - DUK_D(DUK_DPRINT("prototype loop when checking for finalizer existence; returning false")); - return 0; - } -#if defined(DUK_USE_HEAPPTR16) - DUK_ASSERT(heap != NULL); - obj = DUK_HOBJECT_GET_PROTOTYPE(heap, obj); -#else - obj = DUK_HOBJECT_GET_PROTOTYPE(NULL, obj); /* 'heap' arg ignored */ -#endif - } while (obj != NULL); - - return 0; -} - -/* - * Object.getOwnPropertyDescriptor() (E5 Sections 15.2.3.3, 8.10.4) - * - * [ ... key ] -> [ ... desc/undefined ] - */ - -DUK_INTERNAL void duk_hobject_object_get_own_property_descriptor(duk_hthread *thr, duk_idx_t obj_idx) { - duk_hobject *obj; - duk_hstring *key; - duk_propdesc pd; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - - obj = duk_require_hobject_promote_mask(thr, obj_idx, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER); - key = duk_to_property_key_hstring(thr, -1); - DUK_ASSERT(key != NULL); - - DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE); - - if (!duk_hobject_get_own_propdesc(thr, obj, key, &pd, DUK_GETDESC_FLAG_PUSH_VALUE)) { - duk_push_undefined(thr); - duk_remove_m2(thr); - return; - } - - duk_push_object(thr); - - /* [ ... key value desc ] */ - - if (DUK_PROPDESC_IS_ACCESSOR(&pd)) { - /* If a setter/getter is missing (undefined), the descriptor must - * still have the property present with the value 'undefined'. - */ - if (pd.get) { - duk_push_hobject(thr, pd.get); - } else { - duk_push_undefined(thr); - } - duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_GET); - if (pd.set) { - duk_push_hobject(thr, pd.set); - } else { - duk_push_undefined(thr); - } - duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_SET); - } else { - duk_dup_m2(thr); - duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_VALUE); - duk_push_boolean(thr, DUK_PROPDESC_IS_WRITABLE(&pd)); - duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_WRITABLE); - } - duk_push_boolean(thr, DUK_PROPDESC_IS_ENUMERABLE(&pd)); - duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_ENUMERABLE); - duk_push_boolean(thr, DUK_PROPDESC_IS_CONFIGURABLE(&pd)); - duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_CONFIGURABLE); - - /* [ ... key value desc ] */ - - duk_replace(thr, -3); - duk_pop_unsafe(thr); /* -> [ ... desc ] */ -} - -/* - * NormalizePropertyDescriptor() related helper. - * - * Internal helper which validates and normalizes a property descriptor - * represented as an ECMAScript object (e.g. argument to defineProperty()). - * The output of this conversion is a set of defprop_flags and possibly - * some values pushed on the value stack to (1) ensure borrowed pointers - * remain valid, and (2) avoid unnecessary pops for footprint reasons. - * Caller must manage stack top carefully because the number of values - * pushed depends on the input property descriptor. - * - * The original descriptor object must not be altered in the process. - */ - -/* XXX: very basic optimization -> duk_get_prop_stridx_top */ - -DUK_INTERNAL -void duk_hobject_prepare_property_descriptor(duk_hthread *thr, - duk_idx_t idx_in, - duk_uint_t *out_defprop_flags, - duk_idx_t *out_idx_value, - duk_hobject **out_getter, - duk_hobject **out_setter) { - duk_idx_t idx_value = -1; - duk_hobject *getter = NULL; - duk_hobject *setter = NULL; - duk_bool_t is_data_desc = 0; - duk_bool_t is_acc_desc = 0; - duk_uint_t defprop_flags = 0; - - DUK_ASSERT(out_defprop_flags != NULL); - DUK_ASSERT(out_idx_value != NULL); - DUK_ASSERT(out_getter != NULL); - DUK_ASSERT(out_setter != NULL); - DUK_ASSERT(idx_in <= 0x7fffL); /* short variants would be OK, but not used to avoid shifts */ - - /* Must be an object, otherwise TypeError (E5.1 Section 8.10.5, step 1). */ - idx_in = duk_require_normalize_index(thr, idx_in); - (void) duk_require_hobject(thr, idx_in); - - /* The coercion order must match the ToPropertyDescriptor() algorithm - * so that side effects in coercion happen in the correct order. - * (This order also happens to be compatible with duk_def_prop(), - * although it doesn't matter in practice.) - */ - - if (duk_get_prop_stridx(thr, idx_in, DUK_STRIDX_VALUE)) { - is_data_desc = 1; - defprop_flags |= DUK_DEFPROP_HAVE_VALUE; - idx_value = duk_get_top_index(thr); - } - - if (duk_get_prop_stridx(thr, idx_in, DUK_STRIDX_WRITABLE)) { - is_data_desc = 1; - if (duk_to_boolean_top_pop(thr)) { - defprop_flags |= DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_WRITABLE; - } else { - defprop_flags |= DUK_DEFPROP_HAVE_WRITABLE; - } - } - - if (duk_get_prop_stridx(thr, idx_in, DUK_STRIDX_GET)) { - duk_tval *tv = duk_require_tval(thr, -1); - duk_hobject *h_get; - - if (DUK_TVAL_IS_UNDEFINED(tv)) { - /* undefined is accepted */ - DUK_ASSERT(getter == NULL); - } else { - /* NOTE: lightfuncs are coerced to full functions because - * lightfuncs don't fit into a property value slot. This - * has some side effects, see test-dev-lightfunc-accessor.js. - */ - h_get = duk_get_hobject_promote_lfunc(thr, -1); - if (h_get == NULL || !DUK_HOBJECT_IS_CALLABLE(h_get)) { - goto type_error; - } - getter = h_get; - } - is_acc_desc = 1; - defprop_flags |= DUK_DEFPROP_HAVE_GETTER; - } - - if (duk_get_prop_stridx(thr, idx_in, DUK_STRIDX_SET)) { - duk_tval *tv = duk_require_tval(thr, -1); - duk_hobject *h_set; - - if (DUK_TVAL_IS_UNDEFINED(tv)) { - /* undefined is accepted */ - DUK_ASSERT(setter == NULL); - } else { - /* NOTE: lightfuncs are coerced to full functions because - * lightfuncs don't fit into a property value slot. This - * has some side effects, see test-dev-lightfunc-accessor.js. - */ - h_set = duk_get_hobject_promote_lfunc(thr, -1); - if (h_set == NULL || !DUK_HOBJECT_IS_CALLABLE(h_set)) { - goto type_error; - } - setter = h_set; - } - is_acc_desc = 1; - defprop_flags |= DUK_DEFPROP_HAVE_SETTER; - } - - if (duk_get_prop_stridx(thr, idx_in, DUK_STRIDX_ENUMERABLE)) { - if (duk_to_boolean_top_pop(thr)) { - defprop_flags |= DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_ENUMERABLE; - } else { - defprop_flags |= DUK_DEFPROP_HAVE_ENUMERABLE; - } - } - - if (duk_get_prop_stridx(thr, idx_in, DUK_STRIDX_CONFIGURABLE)) { - if (duk_to_boolean_top_pop(thr)) { - defprop_flags |= DUK_DEFPROP_HAVE_CONFIGURABLE | DUK_DEFPROP_CONFIGURABLE; - } else { - defprop_flags |= DUK_DEFPROP_HAVE_CONFIGURABLE; - } - } - - if (is_data_desc && is_acc_desc) { - goto type_error; - } - - *out_defprop_flags = defprop_flags; - *out_idx_value = idx_value; - *out_getter = getter; - *out_setter = setter; - - /* [ ... [multiple values] ] */ - return; - -type_error: - DUK_ERROR_TYPE(thr, DUK_STR_INVALID_DESCRIPTOR); - DUK_WO_NORETURN(return;); -} - -/* - * Object.defineProperty() related helper (E5 Section 15.2.3.6). - * Also handles ES2015 Reflect.defineProperty(). - * - * Inlines all [[DefineOwnProperty]] exotic behaviors. - * - * Note: ECMAScript compliant [[DefineOwnProperty]](P, Desc, Throw) is not - * implemented directly, but Object.defineProperty() serves its purpose. - * We don't need the [[DefineOwnProperty]] internally and we don't have a - * property descriptor with 'missing values' so it's easier to avoid it - * entirely. - * - * Note: this is only called for actual objects, not primitive values. - * This must support virtual properties for full objects (e.g. Strings) - * but not for plain values (e.g. strings). Lightfuncs, even though - * primitive in a sense, are treated like objects and accepted as target - * values. - */ - -/* XXX: this is a major target for size optimization */ -DUK_INTERNAL -duk_bool_t duk_hobject_define_property_helper(duk_hthread *thr, - duk_uint_t defprop_flags, - duk_hobject *obj, - duk_hstring *key, - duk_idx_t idx_value, - duk_hobject *get, - duk_hobject *set, - duk_bool_t throw_flag) { - duk_uint32_t arr_idx; - duk_tval tv; - duk_bool_t has_enumerable; - duk_bool_t has_configurable; - duk_bool_t has_writable; - duk_bool_t has_value; - duk_bool_t has_get; - duk_bool_t has_set; - duk_bool_t is_enumerable; - duk_bool_t is_configurable; - duk_bool_t is_writable; - duk_bool_t force_flag; - duk_small_uint_t new_flags; - duk_propdesc curr; - duk_uint32_t arridx_new_array_length; /* != 0 => post-update for array 'length' (used when key is an array index) */ - duk_uint32_t arrlen_old_len; - duk_uint32_t arrlen_new_len; - duk_bool_t pending_write_protect; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - DUK_ASSERT(obj != NULL); - DUK_ASSERT(key != NULL); - /* idx_value may be < 0 (no value), set and get may be NULL */ - - DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE); - - /* All the flags fit in 16 bits, so will fit into duk_bool_t. */ - - has_writable = (defprop_flags & DUK_DEFPROP_HAVE_WRITABLE); - has_enumerable = (defprop_flags & DUK_DEFPROP_HAVE_ENUMERABLE); - has_configurable = (defprop_flags & DUK_DEFPROP_HAVE_CONFIGURABLE); - has_value = (defprop_flags & DUK_DEFPROP_HAVE_VALUE); - has_get = (defprop_flags & DUK_DEFPROP_HAVE_GETTER); - has_set = (defprop_flags & DUK_DEFPROP_HAVE_SETTER); - is_writable = (defprop_flags & DUK_DEFPROP_WRITABLE); - is_enumerable = (defprop_flags & DUK_DEFPROP_ENUMERABLE); - is_configurable = (defprop_flags & DUK_DEFPROP_CONFIGURABLE); - force_flag = (defprop_flags & DUK_DEFPROP_FORCE); - - arr_idx = DUK_HSTRING_GET_ARRIDX_SLOW(key); - - arridx_new_array_length = 0; - pending_write_protect = 0; - arrlen_old_len = 0; - arrlen_new_len = 0; - - DUK_DDD(DUK_DDDPRINT("has_enumerable=%ld is_enumerable=%ld " - "has_configurable=%ld is_configurable=%ld " - "has_writable=%ld is_writable=%ld " - "has_value=%ld value=%!T " - "has_get=%ld get=%p=%!O " - "has_set=%ld set=%p=%!O " - "arr_idx=%ld throw_flag=!%ld", - (long) has_enumerable, - (long) is_enumerable, - (long) has_configurable, - (long) is_configurable, - (long) has_writable, - (long) is_writable, - (long) has_value, - (duk_tval *) (idx_value >= 0 ? duk_get_tval(thr, idx_value) : NULL), - (long) has_get, - (void *) get, - (duk_heaphdr *) get, - (long) has_set, - (void *) set, - (duk_heaphdr *) set, - (long) arr_idx, - (long) throw_flag)); - - /* - * Array exotic behaviors can be implemented at this point. The local variables - * are essentially a 'value copy' of the input descriptor (Desc), which is modified - * by the Array [[DefineOwnProperty]] (E5 Section 15.4.5.1). - */ - - if (!DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj)) { - goto skip_array_exotic; - } - - if (key == DUK_HTHREAD_STRING_LENGTH(thr)) { - duk_harray *a; - - /* E5 Section 15.4.5.1, step 3, steps a - i are implemented here, j - n at the end */ - if (!has_value) { - DUK_DDD(DUK_DDDPRINT("exotic array behavior for 'length', but no value in descriptor -> normal behavior")); - goto skip_array_exotic; - } - - DUK_DDD(DUK_DDDPRINT("exotic array behavior for 'length', value present in descriptor -> exotic behavior")); - - /* - * Get old and new length - */ - - a = (duk_harray *) obj; - DUK_HARRAY_ASSERT_VALID(a); - arrlen_old_len = a->length; - - DUK_ASSERT(idx_value >= 0); - arrlen_new_len = duk__to_new_array_length_checked(thr, DUK_GET_TVAL_POSIDX(thr, idx_value)); - duk_push_u32(thr, arrlen_new_len); - duk_replace(thr, idx_value); /* step 3.e: replace 'Desc.[[Value]]' */ - - DUK_DDD(DUK_DDDPRINT("old_len=%ld, new_len=%ld", (long) arrlen_old_len, (long) arrlen_new_len)); - - if (arrlen_new_len >= arrlen_old_len) { - /* standard behavior, step 3.f.i */ - DUK_DDD(DUK_DDDPRINT("new length is same or higher as previous => standard behavior")); - goto skip_array_exotic; - } - DUK_DDD(DUK_DDDPRINT("new length is smaller than previous => exotic post behavior")); - - /* XXX: consolidated algorithm step 15.f -> redundant? */ - if (DUK_HARRAY_LENGTH_NONWRITABLE(a) && !force_flag) { - /* Array .length is always non-configurable; if it's also - * non-writable, don't allow it to be written. - */ - goto fail_not_configurable; - } - - /* steps 3.h and 3.i */ - if (has_writable && !is_writable) { - DUK_DDD(DUK_DDDPRINT("desc writable is false, force it back to true, and flag pending write protect")); - is_writable = 1; - pending_write_protect = 1; - } - - /* remaining actual steps are carried out if standard DefineOwnProperty succeeds */ - } else if (arr_idx != DUK__NO_ARRAY_INDEX) { - /* XXX: any chance of unifying this with the 'length' key handling? */ - - /* E5 Section 15.4.5.1, step 4 */ - duk_uint32_t old_len; - duk_harray *a; - - a = (duk_harray *) obj; - DUK_HARRAY_ASSERT_VALID(a); - - old_len = a->length; - - if (arr_idx >= old_len) { - DUK_DDD(DUK_DDDPRINT("defineProperty requires array length update " - "(arr_idx=%ld, old_len=%ld)", - (long) arr_idx, - (long) old_len)); - - if (DUK_HARRAY_LENGTH_NONWRITABLE(a) && !force_flag) { - /* Array .length is always non-configurable, so - * if it's also non-writable, don't allow a value - * write. With force flag allow writing. - */ - goto fail_not_configurable; - } - - /* actual update happens once write has been completed without - * error below. - */ - DUK_ASSERT(arr_idx != 0xffffffffUL); - arridx_new_array_length = arr_idx + 1; - } else { - DUK_DDD(DUK_DDDPRINT("defineProperty does not require length update " - "(arr_idx=%ld, old_len=%ld) -> standard behavior", - (long) arr_idx, - (long) old_len)); - } - } -skip_array_exotic: - - /* XXX: There is currently no support for writing buffer object - * indexed elements here. Attempt to do so will succeed and - * write a concrete property into the buffer object. This should - * be fixed at some point but because buffers are a custom feature - * anyway, this is relatively unimportant. - */ - - /* - * Actual Object.defineProperty() default algorithm. - */ - - /* - * First check whether property exists; if not, simple case. This covers - * steps 1-4. - */ - - if (!duk__get_own_propdesc_raw(thr, obj, key, arr_idx, &curr, DUK_GETDESC_FLAG_PUSH_VALUE)) { - DUK_DDD(DUK_DDDPRINT("property does not exist")); - - if (!DUK_HOBJECT_HAS_EXTENSIBLE(obj) && !force_flag) { - goto fail_not_extensible; - } - -#if defined(DUK_USE_ROM_OBJECTS) - /* ROM objects are never extensible but force flag may - * allow us to come here anyway. - */ - DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj) || !DUK_HOBJECT_HAS_EXTENSIBLE(obj)); - if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)) { - DUK_D(DUK_DPRINT("attempt to define property on a read-only target object")); - goto fail_not_configurable; - } -#endif - - /* XXX: share final setting code for value and flags? difficult because - * refcount code is different. Share entry allocation? But can't allocate - * until array index checked. - */ - - /* steps 4.a and 4.b are tricky */ - if (has_set || has_get) { - duk_int_t e_idx; - - DUK_DDD(DUK_DDDPRINT("create new accessor property")); - - DUK_ASSERT(has_set || set == NULL); - DUK_ASSERT(has_get || get == NULL); - DUK_ASSERT(!has_value); - DUK_ASSERT(!has_writable); - - new_flags = DUK_PROPDESC_FLAG_ACCESSOR; /* defaults, E5 Section 8.6.1, Table 7 */ - if (has_enumerable && is_enumerable) { - new_flags |= DUK_PROPDESC_FLAG_ENUMERABLE; - } - if (has_configurable && is_configurable) { - new_flags |= DUK_PROPDESC_FLAG_CONFIGURABLE; - } - - if (arr_idx != DUK__NO_ARRAY_INDEX && DUK_HOBJECT_HAS_ARRAY_PART(obj)) { - DUK_DDD(DUK_DDDPRINT("accessor cannot go to array part, abandon array")); - duk__abandon_array_part(thr, obj); - } - - /* write to entry part */ - e_idx = duk__hobject_alloc_entry_checked(thr, obj, key); - DUK_ASSERT(e_idx >= 0); - - DUK_HOBJECT_E_SET_VALUE_GETTER(thr->heap, obj, e_idx, get); - DUK_HOBJECT_E_SET_VALUE_SETTER(thr->heap, obj, e_idx, set); - DUK_HOBJECT_INCREF_ALLOWNULL(thr, get); - DUK_HOBJECT_INCREF_ALLOWNULL(thr, set); - - DUK_HOBJECT_E_SET_FLAGS(thr->heap, obj, e_idx, new_flags); - goto success_exotics; - } else { - duk_int_t e_idx; - duk_tval *tv2; - - DUK_DDD(DUK_DDDPRINT("create new data property")); - - DUK_ASSERT(!has_set); - DUK_ASSERT(!has_get); - - new_flags = 0; /* defaults, E5 Section 8.6.1, Table 7 */ - if (has_writable && is_writable) { - new_flags |= DUK_PROPDESC_FLAG_WRITABLE; - } - if (has_enumerable && is_enumerable) { - new_flags |= DUK_PROPDESC_FLAG_ENUMERABLE; - } - if (has_configurable && is_configurable) { - new_flags |= DUK_PROPDESC_FLAG_CONFIGURABLE; - } - if (has_value) { - duk_tval *tv_tmp = duk_require_tval(thr, idx_value); - DUK_TVAL_SET_TVAL(&tv, tv_tmp); - } else { - DUK_TVAL_SET_UNDEFINED(&tv); /* default value */ - } - - if (arr_idx != DUK__NO_ARRAY_INDEX && DUK_HOBJECT_HAS_ARRAY_PART(obj)) { - if (new_flags == DUK_PROPDESC_FLAGS_WEC) { - DUK_DDD(DUK_DDDPRINT( - "new data property attributes match array defaults, attempt to write to array part")); - tv2 = duk__obtain_arridx_slot(thr, arr_idx, obj); - if (tv2 == NULL) { - DUK_DDD(DUK_DDDPRINT("failed writing to array part, abandoned array")); - } else { - DUK_DDD(DUK_DDDPRINT("success in writing to array part")); - DUK_ASSERT(DUK_HOBJECT_HAS_ARRAY_PART(obj)); - DUK_ASSERT(DUK_TVAL_IS_UNUSED(tv2)); - DUK_TVAL_SET_TVAL(tv2, &tv); - DUK_TVAL_INCREF(thr, tv2); - goto success_exotics; - } - } else { - DUK_DDD(DUK_DDDPRINT("new data property cannot go to array part, abandon array")); - duk__abandon_array_part(thr, obj); - } - /* fall through */ - } - - /* write to entry part */ - e_idx = duk__hobject_alloc_entry_checked(thr, obj, key); - DUK_ASSERT(e_idx >= 0); - tv2 = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, e_idx); - DUK_TVAL_SET_TVAL(tv2, &tv); - DUK_TVAL_INCREF(thr, tv2); - - DUK_HOBJECT_E_SET_FLAGS(thr->heap, obj, e_idx, new_flags); - goto success_exotics; - } - DUK_UNREACHABLE(); - } - - /* we currently assume virtual properties are not configurable (as none of them are) */ - DUK_ASSERT((curr.e_idx >= 0 || curr.a_idx >= 0) || !(curr.flags & DUK_PROPDESC_FLAG_CONFIGURABLE)); - - /* [obj key desc value get set curr_value] */ - - /* - * Property already exists. Steps 5-6 detect whether any changes need - * to be made. - */ - - if (has_enumerable) { - if (is_enumerable) { - if (!(curr.flags & DUK_PROPDESC_FLAG_ENUMERABLE)) { - goto need_check; - } - } else { - if (curr.flags & DUK_PROPDESC_FLAG_ENUMERABLE) { - goto need_check; - } - } - } - if (has_configurable) { - if (is_configurable) { - if (!(curr.flags & DUK_PROPDESC_FLAG_CONFIGURABLE)) { - goto need_check; - } - } else { - if (curr.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) { - goto need_check; - } - } - } - if (has_value) { - duk_tval *tmp1; - duk_tval *tmp2; - - /* attempt to change from accessor to data property */ - if (curr.flags & DUK_PROPDESC_FLAG_ACCESSOR) { - goto need_check; - } - - tmp1 = duk_require_tval(thr, -1); /* curr value */ - tmp2 = duk_require_tval(thr, idx_value); /* new value */ - if (!duk_js_samevalue(tmp1, tmp2)) { - goto need_check; - } - } - if (has_writable) { - /* attempt to change from accessor to data property */ - if (curr.flags & DUK_PROPDESC_FLAG_ACCESSOR) { - goto need_check; - } - - if (is_writable) { - if (!(curr.flags & DUK_PROPDESC_FLAG_WRITABLE)) { - goto need_check; - } - } else { - if (curr.flags & DUK_PROPDESC_FLAG_WRITABLE) { - goto need_check; - } - } - } - if (has_set) { - if (curr.flags & DUK_PROPDESC_FLAG_ACCESSOR) { - if (set != curr.set) { - goto need_check; - } - } else { - goto need_check; - } - } - if (has_get) { - if (curr.flags & DUK_PROPDESC_FLAG_ACCESSOR) { - if (get != curr.get) { - goto need_check; - } - } else { - goto need_check; - } - } - - /* property exists, either 'desc' is empty, or all values - * match (SameValue) - */ - goto success_no_exotics; - -need_check: - - /* - * Some change(s) need to be made. Steps 7-11. - */ - - /* shared checks for all descriptor types */ - if (!(curr.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) && !force_flag) { - if (has_configurable && is_configurable) { - goto fail_not_configurable; - } - if (has_enumerable) { - if (curr.flags & DUK_PROPDESC_FLAG_ENUMERABLE) { - if (!is_enumerable) { - goto fail_not_configurable; - } - } else { - if (is_enumerable) { - goto fail_not_configurable; - } - } - } - } - - /* Virtual properties don't have backing so they can't mostly be - * edited. Some virtual properties are, however, writable: for - * example, virtual index properties of buffer objects and Array - * instance .length. These are not configurable so the checks - * above mostly cover attempts to change them, except when the - * duk_def_prop() call is used with DUK_DEFPROP_FORCE; even in - * that case we can't forcibly change the property attributes - * because they don't have concrete backing. - */ - - /* XXX: for ROM objects too it'd be best if value modify was - * allowed if the value matches SameValue. - */ - /* Reject attempt to change a read-only object. */ -#if defined(DUK_USE_ROM_OBJECTS) - if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)) { - DUK_DD(DUK_DDPRINT("attempt to define property on read-only target object")); - goto fail_not_configurable; - } -#endif - - /* descriptor type specific checks */ - if (has_set || has_get) { - /* IsAccessorDescriptor(desc) == true */ - DUK_ASSERT(!has_writable); - DUK_ASSERT(!has_value); - - if (curr.flags & DUK_PROPDESC_FLAG_ACCESSOR) { - /* curr and desc are accessors */ - if (!(curr.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) && !force_flag) { - if (has_set && set != curr.set) { - goto fail_not_configurable; - } - if (has_get && get != curr.get) { - goto fail_not_configurable; - } - } - } else { - duk_bool_t rc; - duk_tval *tv1; - - /* curr is data, desc is accessor */ - if (!(curr.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) && !force_flag) { - goto fail_not_configurable; - } - - DUK_DDD(DUK_DDDPRINT("convert property to accessor property")); - if (curr.a_idx >= 0) { - DUK_DDD( - DUK_DDDPRINT("property to convert is stored in an array entry, abandon array and re-lookup")); - duk__abandon_array_part(thr, obj); - duk_pop_unsafe(thr); /* remove old value */ - rc = duk__get_own_propdesc_raw(thr, obj, key, arr_idx, &curr, DUK_GETDESC_FLAG_PUSH_VALUE); - DUK_UNREF(rc); - DUK_ASSERT(rc != 0); - DUK_ASSERT(curr.e_idx >= 0 && curr.a_idx < 0); - } - if (curr.e_idx < 0) { - DUK_ASSERT(curr.a_idx < 0 && curr.e_idx < 0); - goto fail_virtual; /* safeguard for virtual property */ - } - - DUK_ASSERT(curr.e_idx >= 0); - DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, obj, curr.e_idx)); - - tv1 = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, curr.e_idx); - DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ(thr, tv1); /* XXX: just decref */ - - DUK_HOBJECT_E_SET_VALUE_GETTER(thr->heap, obj, curr.e_idx, NULL); - DUK_HOBJECT_E_SET_VALUE_SETTER(thr->heap, obj, curr.e_idx, NULL); - DUK_HOBJECT_E_SLOT_CLEAR_WRITABLE(thr->heap, obj, curr.e_idx); - DUK_HOBJECT_E_SLOT_SET_ACCESSOR(thr->heap, obj, curr.e_idx); - - DUK_DDD(DUK_DDDPRINT("flags after data->accessor conversion: 0x%02lx", - (unsigned long) DUK_HOBJECT_E_GET_FLAGS(thr->heap, obj, curr.e_idx))); - /* Update curr.flags; faster than a re-lookup. */ - curr.flags &= ~DUK_PROPDESC_FLAG_WRITABLE; - curr.flags |= DUK_PROPDESC_FLAG_ACCESSOR; - } - } else if (has_value || has_writable) { - /* IsDataDescriptor(desc) == true */ - DUK_ASSERT(!has_set); - DUK_ASSERT(!has_get); - - if (curr.flags & DUK_PROPDESC_FLAG_ACCESSOR) { - duk_hobject *tmp; - - /* curr is accessor, desc is data */ - if (!(curr.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) && !force_flag) { - goto fail_not_configurable; - } - - /* curr is accessor -> cannot be in array part. */ - DUK_ASSERT(curr.a_idx < 0); - if (curr.e_idx < 0) { - goto fail_virtual; /* safeguard; no virtual accessors now */ - } - - DUK_DDD(DUK_DDDPRINT("convert property to data property")); - - DUK_ASSERT(DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, obj, curr.e_idx)); - tmp = DUK_HOBJECT_E_GET_VALUE_GETTER(thr->heap, obj, curr.e_idx); - DUK_UNREF(tmp); - DUK_HOBJECT_E_SET_VALUE_GETTER(thr->heap, obj, curr.e_idx, NULL); - DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, tmp); - tmp = DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, obj, curr.e_idx); - DUK_UNREF(tmp); - DUK_HOBJECT_E_SET_VALUE_SETTER(thr->heap, obj, curr.e_idx, NULL); - DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, tmp); - - DUK_TVAL_SET_UNDEFINED(DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, curr.e_idx)); - DUK_HOBJECT_E_SLOT_CLEAR_WRITABLE(thr->heap, obj, curr.e_idx); - DUK_HOBJECT_E_SLOT_CLEAR_ACCESSOR(thr->heap, obj, curr.e_idx); - - DUK_DDD(DUK_DDDPRINT("flags after accessor->data conversion: 0x%02lx", - (unsigned long) DUK_HOBJECT_E_GET_FLAGS(thr->heap, obj, curr.e_idx))); - - /* Update curr.flags; faster than a re-lookup. */ - curr.flags &= ~(DUK_PROPDESC_FLAG_WRITABLE | DUK_PROPDESC_FLAG_ACCESSOR); - } else { - /* curr and desc are data */ - if (!(curr.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) && !force_flag) { - if (!(curr.flags & DUK_PROPDESC_FLAG_WRITABLE) && has_writable && is_writable) { - goto fail_not_configurable; - } - /* Note: changing from writable to non-writable is OK */ - if (!(curr.flags & DUK_PROPDESC_FLAG_WRITABLE) && has_value) { - duk_tval *tmp1 = duk_require_tval(thr, -1); /* curr value */ - duk_tval *tmp2 = duk_require_tval(thr, idx_value); /* new value */ - if (!duk_js_samevalue(tmp1, tmp2)) { - goto fail_not_configurable; - } - } - } - } - } else { - /* IsGenericDescriptor(desc) == true; this means in practice that 'desc' - * only has [[Enumerable]] or [[Configurable]] flag updates, which are - * allowed at this point. - */ - - DUK_ASSERT(!has_value && !has_writable && !has_get && !has_set); - } - - /* - * Start doing property attributes updates. Steps 12-13. - * - * Start by computing new attribute flags without writing yet. - * Property type conversion is done above if necessary. - */ - - new_flags = curr.flags; - - if (has_enumerable) { - if (is_enumerable) { - new_flags |= DUK_PROPDESC_FLAG_ENUMERABLE; - } else { - new_flags &= ~DUK_PROPDESC_FLAG_ENUMERABLE; - } - } - if (has_configurable) { - if (is_configurable) { - new_flags |= DUK_PROPDESC_FLAG_CONFIGURABLE; - } else { - new_flags &= ~DUK_PROPDESC_FLAG_CONFIGURABLE; - } - } - if (has_writable) { - if (is_writable) { - new_flags |= DUK_PROPDESC_FLAG_WRITABLE; - } else { - new_flags &= ~DUK_PROPDESC_FLAG_WRITABLE; - } - } - - /* XXX: write protect after flag? -> any chance of handling it here? */ - - DUK_DDD(DUK_DDDPRINT("new flags that we want to write: 0x%02lx", (unsigned long) new_flags)); - - /* - * Check whether we need to abandon an array part (if it exists) - */ - - if (curr.a_idx >= 0) { - duk_bool_t rc; - - DUK_ASSERT(curr.e_idx < 0); - - if (new_flags == DUK_PROPDESC_FLAGS_WEC) { - duk_tval *tv1, *tv2; - - DUK_DDD(DUK_DDDPRINT("array index, new property attributes match array defaults, update in-place")); - - DUK_ASSERT(curr.flags == DUK_PROPDESC_FLAGS_WEC); /* must have been, since in array part */ - DUK_ASSERT(!has_set); - DUK_ASSERT(!has_get); - DUK_ASSERT( - idx_value >= - 0); /* must be: if attributes match and we get here the value must differ (otherwise no change) */ - - tv2 = duk_require_tval(thr, idx_value); - tv1 = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, curr.a_idx); - DUK_TVAL_SET_TVAL_UPDREF(thr, tv1, tv2); /* side effects; may invalidate a_idx */ - goto success_exotics; - } - - DUK_DDD( - DUK_DDDPRINT("array index, new property attributes do not match array defaults, abandon array and re-lookup")); - duk__abandon_array_part(thr, obj); - duk_pop_unsafe(thr); /* remove old value */ - rc = duk__get_own_propdesc_raw(thr, obj, key, arr_idx, &curr, DUK_GETDESC_FLAG_PUSH_VALUE); - DUK_UNREF(rc); - DUK_ASSERT(rc != 0); - DUK_ASSERT(curr.e_idx >= 0 && curr.a_idx < 0); - } - - DUK_DDD(DUK_DDDPRINT("updating existing property in entry part")); - - /* Array case is handled comprehensively above: either in entry - * part or a virtual property. - */ - DUK_ASSERT(curr.a_idx < 0); - - DUK_DDD(DUK_DDDPRINT("update existing property attributes")); - if (curr.e_idx >= 0) { - DUK_HOBJECT_E_SET_FLAGS(thr->heap, obj, curr.e_idx, new_flags); - } else { - /* For Array .length the only allowed transition is for .length - * to become non-writable. - */ - if (key == DUK_HTHREAD_STRING_LENGTH(thr) && DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj)) { - duk_harray *a; - a = (duk_harray *) obj; - DUK_DD(DUK_DDPRINT("Object.defineProperty() attribute update for duk_harray .length -> %02lx", - (unsigned long) new_flags)); - DUK_HARRAY_ASSERT_VALID(a); - if ((new_flags & DUK_PROPDESC_FLAGS_EC) != (curr.flags & DUK_PROPDESC_FLAGS_EC)) { - DUK_D(DUK_DPRINT("Object.defineProperty() attempt to change virtual array .length enumerable or " - "configurable attribute, fail")); - goto fail_virtual; - } - if (new_flags & DUK_PROPDESC_FLAG_WRITABLE) { - DUK_HARRAY_SET_LENGTH_WRITABLE(a); - } else { - DUK_HARRAY_SET_LENGTH_NONWRITABLE(a); - } - } - } - - if (has_set) { - duk_hobject *tmp; - - /* Virtual properties are non-configurable but with a 'force' - * flag we might come here so check explicitly for virtual. - */ - if (curr.e_idx < 0) { - goto fail_virtual; - } - - DUK_DDD(DUK_DDDPRINT("update existing property setter")); - DUK_ASSERT(DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, obj, curr.e_idx)); - - tmp = DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, obj, curr.e_idx); - DUK_UNREF(tmp); - DUK_HOBJECT_E_SET_VALUE_SETTER(thr->heap, obj, curr.e_idx, set); - DUK_HOBJECT_INCREF_ALLOWNULL(thr, set); - DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp); /* side effects; may invalidate e_idx */ - } - if (has_get) { - duk_hobject *tmp; - - if (curr.e_idx < 0) { - goto fail_virtual; - } - - DUK_DDD(DUK_DDDPRINT("update existing property getter")); - DUK_ASSERT(DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, obj, curr.e_idx)); - - tmp = DUK_HOBJECT_E_GET_VALUE_GETTER(thr->heap, obj, curr.e_idx); - DUK_UNREF(tmp); - DUK_HOBJECT_E_SET_VALUE_GETTER(thr->heap, obj, curr.e_idx, get); - DUK_HOBJECT_INCREF_ALLOWNULL(thr, get); - DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp); /* side effects; may invalidate e_idx */ - } - if (has_value) { - duk_tval *tv1, *tv2; - - DUK_DDD(DUK_DDDPRINT("update existing property value")); - - if (curr.e_idx >= 0) { - DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, obj, curr.e_idx)); - tv2 = duk_require_tval(thr, idx_value); - tv1 = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, curr.e_idx); - DUK_TVAL_SET_TVAL_UPDREF(thr, tv1, tv2); /* side effects; may invalidate e_idx */ - } else { - DUK_ASSERT(curr.a_idx < 0); /* array part case handled comprehensively previously */ - - DUK_DD(DUK_DDPRINT("Object.defineProperty(), value update for virtual property")); - /* XXX: Uint8Array and other typed array virtual writes not currently - * handled. - */ - if (key == DUK_HTHREAD_STRING_LENGTH(thr) && DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj)) { - duk_harray *a; - a = (duk_harray *) obj; - DUK_DD(DUK_DDPRINT("Object.defineProperty() value update for duk_harray .length -> %ld", - (long) arrlen_new_len)); - DUK_HARRAY_ASSERT_VALID(a); - a->length = arrlen_new_len; - } else { - goto fail_virtual; /* should not happen */ - } - } - } - - /* - * Standard algorithm succeeded without errors, check for exotic post-behaviors. - * - * Arguments exotic behavior in E5 Section 10.6 occurs after the standard - * [[DefineOwnProperty]] has completed successfully. - * - * Array exotic behavior in E5 Section 15.4.5.1 is implemented partly - * prior to the default [[DefineOwnProperty]], but: - * - for an array index key (e.g. "10") the final 'length' update occurs here - * - for 'length' key the element deletion and 'length' update occurs here - */ - -success_exotics: - - /* curr.a_idx or curr.e_idx may have been invalidated by side effects - * above. - */ - - /* [obj key desc value get set curr_value] */ - - if (DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj)) { - duk_harray *a; - - a = (duk_harray *) obj; - DUK_HARRAY_ASSERT_VALID(a); - - if (arridx_new_array_length > 0) { - /* - * Note: zero works as a "no update" marker because the new length - * can never be zero after a new property is written. - */ - - /* E5 Section 15.4.5.1, steps 4.e.i - 4.e.ii */ - - DUK_DDD(DUK_DDDPRINT("defineProperty successful, pending array length update to: %ld", - (long) arridx_new_array_length)); - - a->length = arridx_new_array_length; - } - - if (key == DUK_HTHREAD_STRING_LENGTH(thr) && arrlen_new_len < arrlen_old_len) { - /* - * E5 Section 15.4.5.1, steps 3.k - 3.n. The order at the end combines - * the error case 3.l.iii and the success case 3.m-3.n. - */ - - /* XXX: investigate whether write protect can be handled above, if we - * just update length here while ignoring its protected status - */ - - duk_uint32_t result_len; - duk_bool_t rc; - - DUK_DDD(DUK_DDDPRINT("defineProperty successful, key is 'length', exotic array behavior, " - "doing array element deletion and length update")); - - rc = - duk__handle_put_array_length_smaller(thr, obj, arrlen_old_len, arrlen_new_len, force_flag, &result_len); - - /* update length (curr points to length, and we assume it's still valid) */ - DUK_ASSERT(result_len >= arrlen_new_len && result_len <= arrlen_old_len); - - a->length = result_len; - - if (pending_write_protect) { - DUK_DDD(DUK_DDDPRINT("setting array length non-writable (pending writability update)")); - DUK_HARRAY_SET_LENGTH_NONWRITABLE(a); - } - - /* XXX: shrink array allocation or entries compaction here? */ - if (!rc) { - DUK_DD(DUK_DDPRINT("array length write only partially successful")); - goto fail_not_configurable; - } - } - } else if (arr_idx != DUK__NO_ARRAY_INDEX && DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj)) { - duk_hobject *map; - duk_hobject *varenv; - - DUK_ASSERT(arridx_new_array_length == 0); - DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj)); /* traits are separate; in particular, arguments not an array */ - - map = NULL; - varenv = NULL; - if (!duk__lookup_arguments_map(thr, obj, key, &curr, &map, &varenv)) { - goto success_no_exotics; - } - DUK_ASSERT(map != NULL); - DUK_ASSERT(varenv != NULL); - - /* [obj key desc value get set curr_value varname] */ - - if (has_set || has_get) { - /* = IsAccessorDescriptor(Desc) */ - DUK_DDD(DUK_DDDPRINT("defineProperty successful, key mapped to arguments 'map' " - "changed to an accessor, delete arguments binding")); - - (void) duk_hobject_delprop_raw(thr, map, key, 0); /* ignore result */ - } else { - /* Note: this order matters (final value before deleting map entry must be done) */ - DUK_DDD(DUK_DDDPRINT("defineProperty successful, key mapped to arguments 'map', " - "check for value update / binding deletion")); - - if (has_value) { - duk_hstring *varname; - - DUK_DDD(DUK_DDDPRINT("defineProperty successful, key mapped to arguments 'map', " - "update bound value (variable/argument)")); - - varname = duk_require_hstring(thr, -1); - DUK_ASSERT(varname != NULL); - - DUK_DDD(DUK_DDDPRINT("arguments object automatic putvar for a bound variable; " - "key=%!O, varname=%!O, value=%!T", - (duk_heaphdr *) key, - (duk_heaphdr *) varname, - (duk_tval *) duk_require_tval(thr, idx_value))); - - /* strict flag for putvar comes from our caller (currently: fixed) */ - duk_js_putvar_envrec(thr, varenv, varname, duk_require_tval(thr, idx_value), 1 /*throw_flag*/); - } - if (has_writable && !is_writable) { - DUK_DDD(DUK_DDDPRINT("defineProperty successful, key mapped to arguments 'map', " - "changed to non-writable, delete arguments binding")); - - (void) duk_hobject_delprop_raw(thr, map, key, 0); /* ignore result */ - } - } - - /* 'varname' is in stack in this else branch, leaving an unbalanced stack below, - * but this doesn't matter now. - */ - } - -success_no_exotics: - /* Some code paths use NORZ macros for simplicity, ensure refzero - * handling is completed. - */ - DUK_REFZERO_CHECK_SLOW(thr); - return 1; - -fail_not_extensible: - if (throw_flag) { - DUK_ERROR_TYPE(thr, DUK_STR_NOT_EXTENSIBLE); - DUK_WO_NORETURN(return 0;); - } - return 0; - -fail_virtual: /* just use the same "not configurable" error message" */ -fail_not_configurable: - if (throw_flag) { - DUK_ERROR_TYPE(thr, DUK_STR_NOT_CONFIGURABLE); - DUK_WO_NORETURN(return 0;); - } - return 0; -} - -/* - * Object.prototype.hasOwnProperty() and Object.prototype.propertyIsEnumerable(). - */ - -DUK_INTERNAL duk_bool_t duk_hobject_object_ownprop_helper(duk_hthread *thr, duk_small_uint_t required_desc_flags) { - duk_hstring *h_v; - duk_hobject *h_obj; - duk_propdesc desc; - duk_bool_t ret; - - /* coercion order matters */ - h_v = duk_to_hstring_acceptsymbol(thr, 0); - DUK_ASSERT(h_v != NULL); - - h_obj = duk_push_this_coercible_to_object(thr); - DUK_ASSERT(h_obj != NULL); - - ret = duk_hobject_get_own_propdesc(thr, h_obj, h_v, &desc, 0 /*flags*/); /* don't push value */ - - duk_push_boolean(thr, ret && ((desc.flags & required_desc_flags) == required_desc_flags)); - return 1; -} - -/* - * Object.seal() and Object.freeze() (E5 Sections 15.2.3.8 and 15.2.3.9) - * - * Since the algorithms are similar, a helper provides both functions. - * Freezing is essentially sealing + making plain properties non-writable. - * - * Note: virtual (non-concrete) properties which are non-configurable but - * writable would pose some problems, but such properties do not currently - * exist (all virtual properties are non-configurable and non-writable). - * If they did exist, the non-configurability does NOT prevent them from - * becoming non-writable. However, this change should be recorded somehow - * so that it would turn up (e.g. when getting the property descriptor), - * requiring some additional flags in the object. - */ - -DUK_INTERNAL void duk_hobject_object_seal_freeze_helper(duk_hthread *thr, duk_hobject *obj, duk_bool_t is_freeze) { - duk_uint_fast32_t i; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - DUK_ASSERT(obj != NULL); - - DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE); - -#if defined(DUK_USE_ROM_OBJECTS) - if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)) { - DUK_DD(DUK_DDPRINT("attempt to seal/freeze a readonly object, reject")); - DUK_ERROR_TYPE(thr, DUK_STR_NOT_CONFIGURABLE); - DUK_WO_NORETURN(return;); - } -#endif - - /* - * Abandon array part because all properties must become non-configurable. - * Note that this is now done regardless of whether this is always the case - * (skips check, but performance problem if caller would do this many times - * for the same object; not likely). - */ - - duk__abandon_array_part(thr, obj); - DUK_ASSERT(DUK_HOBJECT_GET_ASIZE(obj) == 0); - - for (i = 0; i < DUK_HOBJECT_GET_ENEXT(obj); i++) { - duk_uint8_t *fp; - - /* since duk__abandon_array_part() causes a resize, there should be no gaps in keys */ - DUK_ASSERT(DUK_HOBJECT_E_GET_KEY(thr->heap, obj, i) != NULL); - - /* avoid multiple computations of flags address; bypasses macros */ - fp = DUK_HOBJECT_E_GET_FLAGS_PTR(thr->heap, obj, i); - if (is_freeze && !((*fp) & DUK_PROPDESC_FLAG_ACCESSOR)) { - *fp &= ~(DUK_PROPDESC_FLAG_WRITABLE | DUK_PROPDESC_FLAG_CONFIGURABLE); - } else { - *fp &= ~DUK_PROPDESC_FLAG_CONFIGURABLE; - } - } - - DUK_HOBJECT_CLEAR_EXTENSIBLE(obj); - - /* no need to compact since we already did that in duk__abandon_array_part() - * (regardless of whether an array part existed or not. - */ - - return; -} - -/* - * Object.isSealed() and Object.isFrozen() (E5 Sections 15.2.3.11, 15.2.3.13) - * - * Since the algorithms are similar, a helper provides both functions. - * Freezing is essentially sealing + making plain properties non-writable. - * - * Note: all virtual (non-concrete) properties are currently non-configurable - * and non-writable (and there are no accessor virtual properties), so they don't - * need to be considered here now. - */ - -DUK_INTERNAL duk_bool_t duk_hobject_object_is_sealed_frozen_helper(duk_hthread *thr, duk_hobject *obj, duk_bool_t is_frozen) { - duk_uint_fast32_t i; - - DUK_ASSERT(obj != NULL); - DUK_UNREF(thr); - - /* Note: no allocation pressure, no need to check refcounts etc */ - - /* must not be extensible */ - if (DUK_HOBJECT_HAS_EXTENSIBLE(obj)) { - return 0; - } - - /* all virtual properties are non-configurable and non-writable */ - - /* entry part must not contain any configurable properties, or - * writable properties (if is_frozen). - */ - for (i = 0; i < DUK_HOBJECT_GET_ENEXT(obj); i++) { - duk_small_uint_t flags; - - if (!DUK_HOBJECT_E_GET_KEY(thr->heap, obj, i)) { - continue; - } - - /* avoid multiple computations of flags address; bypasses macros */ - flags = (duk_small_uint_t) DUK_HOBJECT_E_GET_FLAGS(thr->heap, obj, i); - - if (flags & DUK_PROPDESC_FLAG_CONFIGURABLE) { - return 0; - } - if (is_frozen && !(flags & DUK_PROPDESC_FLAG_ACCESSOR) && (flags & DUK_PROPDESC_FLAG_WRITABLE)) { - return 0; - } - } - - /* array part must not contain any non-unused properties, as they would - * be configurable and writable. - */ - for (i = 0; i < DUK_HOBJECT_GET_ASIZE(obj); i++) { - duk_tval *tv = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, i); - if (!DUK_TVAL_IS_UNUSED(tv)) { - return 0; - } - } - - return 1; -} - -/* - * Object.preventExtensions() and Object.isExtensible() (E5 Sections 15.2.3.10, 15.2.3.13) - * - * Not needed, implemented by macros DUK_HOBJECT_{HAS,CLEAR,SET}_EXTENSIBLE - * and the Object built-in bindings. - */ - -/* automatic undefs */ -#undef DUK__HASH_DELETED -#undef DUK__HASH_UNUSED -#undef DUK__NO_ARRAY_INDEX -#undef DUK__VALSTACK_PROXY_LOOKUP -#undef DUK__VALSTACK_SPACE -/* - * duk_hstring assertion helpers. - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_ASSERTIONS) - -DUK_INTERNAL void duk_hstring_assert_valid(duk_hstring *h) { - DUK_ASSERT(h != NULL); -} - -#endif /* DUK_USE_ASSERTIONS */ -/* - * Misc support functions - */ - -/* #include duk_internal.h -> already included */ - -/* - * duk_hstring charCodeAt, with and without surrogate awareness - */ - -DUK_INTERNAL duk_ucodepoint_t duk_hstring_char_code_at_raw(duk_hthread *thr, - duk_hstring *h, - duk_uint_t pos, - duk_bool_t surrogate_aware) { - duk_uint32_t boff; - const duk_uint8_t *p, *p_start, *p_end; - duk_ucodepoint_t cp1; - duk_ucodepoint_t cp2; - - /* Caller must check character offset to be inside the string. */ - DUK_ASSERT(thr != NULL); - DUK_ASSERT(h != NULL); - DUK_ASSERT_DISABLE(pos >= 0); /* unsigned */ - DUK_ASSERT(pos < (duk_uint_t) DUK_HSTRING_GET_CHARLEN(h)); - - boff = (duk_uint32_t) duk_heap_strcache_offset_char2byte(thr, h, (duk_uint32_t) pos); - DUK_DDD(DUK_DDDPRINT("charCodeAt: pos=%ld -> boff=%ld, str=%!O", (long) pos, (long) boff, (duk_heaphdr *) h)); - DUK_ASSERT_DISABLE(boff >= 0); - DUK_ASSERT(boff < DUK_HSTRING_GET_BYTELEN(h)); - - p_start = DUK_HSTRING_GET_DATA(h); - p_end = p_start + DUK_HSTRING_GET_BYTELEN(h); - p = p_start + boff; - DUK_DDD(DUK_DDDPRINT("p_start=%p, p_end=%p, p=%p", (const void *) p_start, (const void *) p_end, (const void *) p)); - - /* For invalid UTF-8 (never happens for standard ECMAScript strings) - * return U+FFFD replacement character. - */ - if (duk_unicode_decode_xutf8(thr, &p, p_start, p_end, &cp1)) { - if (surrogate_aware && cp1 >= 0xd800UL && cp1 <= 0xdbffUL) { - /* The decode helper is memory safe even if 'cp1' was - * decoded at the end of the string and 'p' is no longer - * within string memory range. - */ - cp2 = 0; /* If call fails, this is left untouched and won't match cp2 check. */ - (void) duk_unicode_decode_xutf8(thr, &p, p_start, p_end, &cp2); - if (cp2 >= 0xdc00UL && cp2 <= 0xdfffUL) { - cp1 = (duk_ucodepoint_t) (((cp1 - 0xd800UL) << 10) + (cp2 - 0xdc00UL) + 0x10000UL); - } - } - } else { - cp1 = DUK_UNICODE_CP_REPLACEMENT_CHARACTER; - } - - return cp1; -} - -/* - * duk_hstring charlen, when lazy charlen disabled - */ - -#if !defined(DUK_USE_HSTRING_LAZY_CLEN) -#if !defined(DUK_USE_HSTRING_CLEN) -#error non-lazy duk_hstring charlen but DUK_USE_HSTRING_CLEN not set -#endif -DUK_INTERNAL void duk_hstring_init_charlen(duk_hstring *h) { - duk_uint32_t clen; - - DUK_ASSERT(h != NULL); - DUK_ASSERT(!DUK_HSTRING_HAS_ASCII(h)); - DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) h)); - - clen = duk_unicode_unvalidated_utf8_length(DUK_HSTRING_GET_DATA(h), DUK_HSTRING_GET_BYTELEN(h)); -#if defined(DUK_USE_STRLEN16) - DUK_ASSERT(clen <= 0xffffUL); /* Bytelength checked during interning. */ - h->clen16 = (duk_uint16_t) clen; -#else - h->clen = (duk_uint32_t) clen; -#endif - if (DUK_LIKELY(clen == DUK_HSTRING_GET_BYTELEN(h))) { - DUK_HSTRING_SET_ASCII(h); - } -} - -DUK_INTERNAL DUK_HOT duk_size_t duk_hstring_get_charlen(duk_hstring *h) { -#if defined(DUK_USE_STRLEN16) - return h->clen16; -#else - return h->clen; -#endif -} -#endif /* !DUK_USE_HSTRING_LAZY_CLEN */ - -/* - * duk_hstring charlen, when lazy charlen enabled - */ - -#if defined(DUK_USE_HSTRING_LAZY_CLEN) -#if defined(DUK_USE_HSTRING_CLEN) -DUK_LOCAL DUK_COLD duk_size_t duk__hstring_get_charlen_slowpath(duk_hstring *h) { - duk_size_t res; - - DUK_ASSERT(h->clen == 0); /* Checked by caller. */ - -#if defined(DUK_USE_ROM_STRINGS) - /* ROM strings have precomputed clen, but if the computed clen is zero - * we can still come here and can't write anything. - */ - if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) h)) { - return 0; - } -#endif - - res = duk_unicode_unvalidated_utf8_length(DUK_HSTRING_GET_DATA(h), DUK_HSTRING_GET_BYTELEN(h)); -#if defined(DUK_USE_STRLEN16) - DUK_ASSERT(res <= 0xffffUL); /* Bytelength checked during interning. */ - h->clen16 = (duk_uint16_t) res; -#else - h->clen = (duk_uint32_t) res; -#endif - if (DUK_LIKELY(res == DUK_HSTRING_GET_BYTELEN(h))) { - DUK_HSTRING_SET_ASCII(h); - } - return res; -} -#else /* DUK_USE_HSTRING_CLEN */ -DUK_LOCAL duk_size_t duk__hstring_get_charlen_slowpath(duk_hstring *h) { - if (DUK_LIKELY(DUK_HSTRING_HAS_ASCII(h))) { - /* Most practical strings will go here. */ - return DUK_HSTRING_GET_BYTELEN(h); - } else { - /* ASCII flag is lazy, so set it here. */ - duk_size_t res; - - /* XXX: here we could use the strcache to speed up the - * computation (matters for 'i < str.length' loops). - */ - - res = duk_unicode_unvalidated_utf8_length(DUK_HSTRING_GET_DATA(h), DUK_HSTRING_GET_BYTELEN(h)); - -#if defined(DUK_USE_ROM_STRINGS) - if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) h)) { - /* For ROM strings, can't write anything; ASCII flag - * is preset so we don't need to update it. - */ - return res; - } -#endif - if (DUK_LIKELY(res == DUK_HSTRING_GET_BYTELEN(h))) { - DUK_HSTRING_SET_ASCII(h); - } - return res; - } -} -#endif /* DUK_USE_HSTRING_CLEN */ - -#if defined(DUK_USE_HSTRING_CLEN) -DUK_INTERNAL DUK_HOT duk_size_t duk_hstring_get_charlen(duk_hstring *h) { -#if defined(DUK_USE_STRLEN16) - if (DUK_LIKELY(h->clen16 != 0)) { - return h->clen16; - } -#else - if (DUK_LIKELY(h->clen != 0)) { - return h->clen; - } -#endif - return duk__hstring_get_charlen_slowpath(h); -} -#else /* DUK_USE_HSTRING_CLEN */ -DUK_INTERNAL DUK_HOT duk_size_t duk_hstring_get_charlen(duk_hstring *h) { - /* Always use slow path. */ - return duk__hstring_get_charlen_slowpath(h); -} -#endif /* DUK_USE_HSTRING_CLEN */ -#endif /* DUK_USE_HSTRING_LAZY_CLEN */ - -/* - * Compare duk_hstring to an ASCII cstring. - */ - -DUK_INTERNAL duk_bool_t duk_hstring_equals_ascii_cstring(duk_hstring *h, const char *cstr) { - duk_size_t len; - - DUK_ASSERT(h != NULL); - DUK_ASSERT(cstr != NULL); - - len = DUK_STRLEN(cstr); - if (len != DUK_HSTRING_GET_BYTELEN(h)) { - return 0; - } - if (duk_memcmp((const void *) cstr, (const void *) DUK_HSTRING_GET_DATA(h), len) == 0) { - return 1; - } - return 0; -} -/* - * duk_hthread allocation and freeing. - */ - -/* #include duk_internal.h -> already included */ - -/* - * Allocate initial stacks for a thread. Note that 'thr' must be reachable - * as a garbage collection may be triggered by the allocation attempts. - * Returns zero (without leaking memory) if init fails. - */ - -DUK_INTERNAL duk_bool_t duk_hthread_init_stacks(duk_heap *heap, duk_hthread *thr) { - duk_size_t alloc_size; - duk_size_t i; - - DUK_ASSERT(heap != NULL); - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->valstack == NULL); - DUK_ASSERT(thr->valstack_end == NULL); - DUK_ASSERT(thr->valstack_alloc_end == NULL); - DUK_ASSERT(thr->valstack_bottom == NULL); - DUK_ASSERT(thr->valstack_top == NULL); - DUK_ASSERT(thr->callstack_curr == NULL); - - /* valstack */ - DUK_ASSERT(DUK_VALSTACK_API_ENTRY_MINIMUM <= DUK_VALSTACK_INITIAL_SIZE); - alloc_size = sizeof(duk_tval) * DUK_VALSTACK_INITIAL_SIZE; - thr->valstack = (duk_tval *) DUK_ALLOC(heap, alloc_size); - if (!thr->valstack) { - goto fail; - } - duk_memzero(thr->valstack, alloc_size); - thr->valstack_end = thr->valstack + DUK_VALSTACK_API_ENTRY_MINIMUM; - thr->valstack_alloc_end = thr->valstack + DUK_VALSTACK_INITIAL_SIZE; - thr->valstack_bottom = thr->valstack; - thr->valstack_top = thr->valstack; - - for (i = 0; i < DUK_VALSTACK_INITIAL_SIZE; i++) { - DUK_TVAL_SET_UNDEFINED(&thr->valstack[i]); - } - - return 1; - -fail: - DUK_FREE(heap, thr->valstack); - DUK_ASSERT(thr->callstack_curr == NULL); - - thr->valstack = NULL; - return 0; -} - -/* For indirect allocs. */ - -DUK_INTERNAL void *duk_hthread_get_valstack_ptr(duk_heap *heap, void *ud) { - duk_hthread *thr = (duk_hthread *) ud; - DUK_UNREF(heap); - return (void *) thr->valstack; -} -/* - * Initialize built-in objects. Current thread must have a valstack - * and initialization errors may longjmp, so a setjmp() catch point - * must exist. - */ - -/* #include duk_internal.h -> already included */ - -/* - * Encoding constants, must match genbuiltins.py - */ - -#define DUK__PROP_FLAGS_BITS 3 -#define DUK__LENGTH_PROP_BITS 3 -#define DUK__NARGS_BITS 3 -#define DUK__PROP_TYPE_BITS 3 - -#define DUK__NARGS_VARARGS_MARKER 0x07 - -#define DUK__PROP_TYPE_DOUBLE 0 -#define DUK__PROP_TYPE_STRING 1 -#define DUK__PROP_TYPE_STRIDX 2 -#define DUK__PROP_TYPE_BUILTIN 3 -#define DUK__PROP_TYPE_UNDEFINED 4 -#define DUK__PROP_TYPE_BOOLEAN_TRUE 5 -#define DUK__PROP_TYPE_BOOLEAN_FALSE 6 -#define DUK__PROP_TYPE_ACCESSOR 7 - -/* - * Create built-in objects by parsing an init bitstream generated - * by genbuiltins.py. - */ - -#if defined(DUK_USE_ROM_OBJECTS) -#if defined(DUK_USE_ROM_GLOBAL_CLONE) || defined(DUK_USE_ROM_GLOBAL_INHERIT) -DUK_LOCAL void duk__duplicate_ram_global_object(duk_hthread *thr) { - duk_hobject *h_global; -#if defined(DUK_USE_ROM_GLOBAL_CLONE) - duk_hobject *h_oldglobal; - duk_uint8_t *props; - duk_size_t alloc_size; -#endif - duk_hobject *h_objenv; - - /* XXX: refactor into internal helper, duk_clone_hobject() */ - -#if defined(DUK_USE_ROM_GLOBAL_INHERIT) - /* Inherit from ROM-based global object: less RAM usage, less transparent. */ - h_global = duk_push_object_helper(thr, - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_FASTREFS | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_GLOBAL), - DUK_BIDX_GLOBAL); - DUK_ASSERT(h_global != NULL); -#elif defined(DUK_USE_ROM_GLOBAL_CLONE) - /* Clone the properties of the ROM-based global object to create a - * fully RAM-based global object. Uses more memory than the inherit - * model but more compliant. - */ - h_global = duk_push_object_helper(thr, - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_FASTREFS | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_GLOBAL), - DUK_BIDX_OBJECT_PROTOTYPE); - DUK_ASSERT(h_global != NULL); - h_oldglobal = thr->builtins[DUK_BIDX_GLOBAL]; - DUK_ASSERT(h_oldglobal != NULL); - - /* Copy the property table verbatim; this handles attributes etc. - * For ROM objects it's not necessary (or possible) to update - * refcounts so leave them as is. - */ - alloc_size = DUK_HOBJECT_P_ALLOC_SIZE(h_oldglobal); - DUK_ASSERT(alloc_size > 0); - props = DUK_ALLOC_CHECKED(thr, alloc_size); - DUK_ASSERT(props != NULL); - DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, h_oldglobal) != NULL); - duk_memcpy((void *) props, (const void *) DUK_HOBJECT_GET_PROPS(thr->heap, h_oldglobal), alloc_size); - - /* XXX: keep property attributes or tweak them here? - * Properties will now be non-configurable even when they're - * normally configurable for the global object. - */ - - DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, h_global) == NULL); - DUK_HOBJECT_SET_PROPS(thr->heap, h_global, props); - DUK_HOBJECT_SET_ESIZE(h_global, DUK_HOBJECT_GET_ESIZE(h_oldglobal)); - DUK_HOBJECT_SET_ENEXT(h_global, DUK_HOBJECT_GET_ENEXT(h_oldglobal)); - DUK_HOBJECT_SET_ASIZE(h_global, DUK_HOBJECT_GET_ASIZE(h_oldglobal)); - DUK_HOBJECT_SET_HSIZE(h_global, DUK_HOBJECT_GET_HSIZE(h_oldglobal)); -#else -#error internal error in config defines -#endif - - duk_hobject_compact_props(thr, h_global); - DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL); - DUK_ASSERT( - !DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE((duk_heaphdr *) thr->builtins[DUK_BIDX_GLOBAL])); /* no need to decref: ROM object */ - thr->builtins[DUK_BIDX_GLOBAL] = h_global; - DUK_HOBJECT_INCREF(thr, h_global); - DUK_D(DUK_DPRINT("duplicated global object: %!O", h_global)); - - /* Create a fresh object environment for the global scope. This is - * needed so that the global scope points to the newly created RAM-based - * global object. - */ - h_objenv = - (duk_hobject *) duk_hobjenv_alloc(thr, - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJENV)); - DUK_ASSERT(h_objenv != NULL); - DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_objenv) == NULL); - duk_push_hobject(thr, h_objenv); - - DUK_ASSERT(h_global != NULL); - ((duk_hobjenv *) h_objenv)->target = h_global; - DUK_HOBJECT_INCREF(thr, h_global); - DUK_ASSERT(((duk_hobjenv *) h_objenv)->has_this == 0); - - DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL_ENV] != NULL); - DUK_ASSERT(!DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE( - (duk_heaphdr *) thr->builtins[DUK_BIDX_GLOBAL_ENV])); /* no need to decref: ROM object */ - thr->builtins[DUK_BIDX_GLOBAL_ENV] = h_objenv; - DUK_HOBJECT_INCREF(thr, h_objenv); - DUK_D(DUK_DPRINT("duplicated global env: %!O", h_objenv)); - - DUK_HOBJENV_ASSERT_VALID((duk_hobjenv *) h_objenv); - - duk_pop_2(thr); /* Pop global object and global env. */ -} -#endif /* DUK_USE_ROM_GLOBAL_CLONE || DUK_USE_ROM_GLOBAL_INHERIT */ - -DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { - /* Setup builtins from ROM objects. All heaps/threads will share - * the same readonly objects. - */ - duk_small_uint_t i; - - for (i = 0; i < DUK_NUM_BUILTINS; i++) { - duk_hobject *h; - h = (duk_hobject *) DUK_LOSE_CONST(duk_rom_builtins_bidx[i]); - DUK_ASSERT(h != NULL); - thr->builtins[i] = h; - } - -#if defined(DUK_USE_ROM_GLOBAL_CLONE) || defined(DUK_USE_ROM_GLOBAL_INHERIT) - /* By default the global object is read-only which is often much - * more of an issue than having read-only built-in objects (like - * RegExp, Date, etc). Use a RAM-based copy of the global object - * and the global environment object for convenience. - */ - duk__duplicate_ram_global_object(thr); -#endif -} -#else /* DUK_USE_ROM_OBJECTS */ -DUK_LOCAL void duk__push_stridx(duk_hthread *thr, duk_bitdecoder_ctx *bd) { - duk_small_uint_t n; - - n = (duk_small_uint_t) duk_bd_decode_varuint(bd); - DUK_ASSERT_DISABLE(n >= 0); /* unsigned */ - DUK_ASSERT(n < DUK_HEAP_NUM_STRINGS); - duk_push_hstring_stridx(thr, n); -} -DUK_LOCAL void duk__push_string(duk_hthread *thr, duk_bitdecoder_ctx *bd) { - /* XXX: built-ins data could provide a maximum length that is - * actually needed; bitpacked max length is now 256 bytes. - */ - duk_uint8_t tmp[DUK_BD_BITPACKED_STRING_MAXLEN]; - duk_small_uint_t len; - - len = duk_bd_decode_bitpacked_string(bd, tmp); - duk_push_lstring(thr, (const char *) tmp, (duk_size_t) len); -} -DUK_LOCAL void duk__push_stridx_or_string(duk_hthread *thr, duk_bitdecoder_ctx *bd) { - duk_small_uint_t n; - - n = (duk_small_uint_t) duk_bd_decode_varuint(bd); - if (n == 0) { - duk__push_string(thr, bd); - } else { - n--; - DUK_ASSERT(n < DUK_HEAP_NUM_STRINGS); - duk_push_hstring_stridx(thr, n); - } -} -DUK_LOCAL void duk__push_double(duk_hthread *thr, duk_bitdecoder_ctx *bd) { - duk_double_union du; - duk_small_uint_t i; - - for (i = 0; i < 8; i++) { - /* Encoding endianness must match target memory layout, - * build scripts and genbuiltins.py must ensure this. - */ - du.uc[i] = (duk_uint8_t) duk_bd_decode(bd, 8); - } - - duk_push_number(thr, du.d); /* push operation normalizes NaNs */ -} - -DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { - duk_bitdecoder_ctx bd_ctx; - duk_bitdecoder_ctx *bd = &bd_ctx; /* convenience */ - duk_hobject *h; - duk_small_uint_t i, j; - - DUK_D(DUK_DPRINT("INITBUILTINS BEGIN: DUK_NUM_BUILTINS=%d, DUK_NUM_BUILTINS_ALL=%d", - (int) DUK_NUM_BUILTINS, - (int) DUK_NUM_ALL_BUILTINS)); - - duk_memzero(&bd_ctx, sizeof(bd_ctx)); - bd->data = (const duk_uint8_t *) duk_builtins_data; - bd->length = (duk_size_t) DUK_BUILTINS_DATA_LENGTH; - - /* - * First create all built-in bare objects on the empty valstack. - * - * Built-ins in the index range [0,DUK_NUM_BUILTINS-1] have value - * stack indices matching their eventual thr->builtins[] index. - * - * Built-ins in the index range [DUK_NUM_BUILTINS,DUK_NUM_ALL_BUILTINS] - * will exist on the value stack during init but won't be placed - * into thr->builtins[]. These are objects referenced in some way - * from thr->builtins[] roots but which don't need to be indexed by - * Duktape through thr->builtins[] (e.g. user custom objects). - * - * Internal prototypes will be incorrect (NULL) at this stage. - */ - - duk_require_stack(thr, DUK_NUM_ALL_BUILTINS); - - DUK_DD(DUK_DDPRINT("create empty built-ins")); - DUK_ASSERT_TOP(thr, 0); - for (i = 0; i < DUK_NUM_ALL_BUILTINS; i++) { - duk_small_uint_t class_num; - duk_small_int_t len = -1; /* must be signed */ - - class_num = (duk_small_uint_t) duk_bd_decode_varuint(bd); - len = (duk_small_int_t) duk_bd_decode_flagged_signed(bd, DUK__LENGTH_PROP_BITS, (duk_int32_t) -1 /*def_value*/); - - if (class_num == DUK_HOBJECT_CLASS_FUNCTION) { - duk_small_uint_t natidx; - duk_small_int_t c_nargs; /* must hold DUK_VARARGS */ - duk_c_function c_func; - duk_int16_t magic; - - DUK_DDD(DUK_DDDPRINT("len=%ld", (long) len)); - DUK_ASSERT(len >= 0); - - natidx = (duk_small_uint_t) duk_bd_decode_varuint(bd); - DUK_ASSERT(natidx != 0); - c_func = duk_bi_native_functions[natidx]; - DUK_ASSERT(c_func != NULL); - - c_nargs = (duk_small_int_t) duk_bd_decode_flagged_signed(bd, DUK__NARGS_BITS, len /*def_value*/); - if (c_nargs == DUK__NARGS_VARARGS_MARKER) { - c_nargs = DUK_VARARGS; - } - - /* XXX: set magic directly here? (it could share the c_nargs arg) */ - (void) duk_push_c_function_builtin(thr, c_func, c_nargs); - h = duk_known_hobject(thr, -1); - - /* Currently all built-in native functions are strict. - * duk_push_c_function() now sets strict flag, so - * assert for it. - */ - DUK_ASSERT(DUK_HOBJECT_HAS_STRICT(h)); - - /* XXX: function properties */ - - duk__push_stridx_or_string(thr, bd); -#if defined(DUK_USE_FUNC_NAME_PROPERTY) - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C); -#else - duk_pop(thr); /* Not very ideal but good enough for now. */ -#endif - - /* Almost all global level Function objects are constructable - * but not all: Function.prototype is a non-constructable, - * callable Function. - */ - if (duk_bd_decode_flag(bd)) { - DUK_ASSERT(DUK_HOBJECT_HAS_CONSTRUCTABLE(h)); - } else { - DUK_HOBJECT_CLEAR_CONSTRUCTABLE(h); - } - - /* Cast converts magic to 16-bit signed value */ - magic = (duk_int16_t) duk_bd_decode_varuint(bd); - ((duk_hnatfunc *) h)->magic = magic; - } else if (class_num == DUK_HOBJECT_CLASS_ARRAY) { - duk_push_array(thr); - } else if (class_num == DUK_HOBJECT_CLASS_OBJENV) { - duk_hobjenv *env; - duk_hobject *global; - - DUK_ASSERT(i == DUK_BIDX_GLOBAL_ENV); - DUK_ASSERT(DUK_BIDX_GLOBAL_ENV > DUK_BIDX_GLOBAL); - - env = duk_hobjenv_alloc(thr, - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJENV)); - DUK_ASSERT(env->target == NULL); - duk_push_hobject(thr, (duk_hobject *) env); - - global = duk_known_hobject(thr, DUK_BIDX_GLOBAL); - DUK_ASSERT(global != NULL); - env->target = global; - DUK_HOBJECT_INCREF(thr, global); - DUK_ASSERT(env->has_this == 0); - - DUK_HOBJENV_ASSERT_VALID(env); - } else { - DUK_ASSERT(class_num != DUK_HOBJECT_CLASS_DECENV); - - (void) duk_push_object_helper(thr, - DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_FLAG_EXTENSIBLE, - -1); /* no prototype or class yet */ - } - - h = duk_known_hobject(thr, -1); - DUK_HOBJECT_SET_CLASS_NUMBER(h, class_num); - - if (i < DUK_NUM_BUILTINS) { - thr->builtins[i] = h; - DUK_HOBJECT_INCREF(thr, &h->hdr); - } - - if (len >= 0) { - /* In ES2015+ built-in function object .length property - * has property attributes C (configurable only): - * http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-standard-built-in-objects - * - * Array.prototype remains an Array instance in ES2015+ - * and its length has attributes W (writable only). - * Because .length is now virtual for duk_harray, it is - * not encoded explicitly in init data. - */ - - DUK_ASSERT(class_num != DUK_HOBJECT_CLASS_ARRAY); /* .length is virtual */ - duk_push_int(thr, len); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_C); - } - - /* enable exotic behaviors last */ - - if (class_num == DUK_HOBJECT_CLASS_ARRAY) { - DUK_ASSERT(DUK_HOBJECT_HAS_EXOTIC_ARRAY(h)); /* set by duk_push_array() */ - } - if (class_num == DUK_HOBJECT_CLASS_STRING) { - DUK_HOBJECT_SET_EXOTIC_STRINGOBJ(h); - } - - /* some assertions */ - - DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(h)); - /* DUK_HOBJECT_FLAG_CONSTRUCTABLE varies */ - DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(h)); - DUK_ASSERT(!DUK_HOBJECT_HAS_COMPFUNC(h)); - /* DUK_HOBJECT_FLAG_NATFUNC varies */ - DUK_ASSERT(!DUK_HOBJECT_IS_THREAD(h)); - DUK_ASSERT(!DUK_HOBJECT_IS_PROXY(h)); - DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(h) || class_num == DUK_HOBJECT_CLASS_ARRAY); - /* DUK_HOBJECT_FLAG_STRICT varies */ - DUK_ASSERT(!DUK_HOBJECT_HAS_NATFUNC(h) || /* all native functions have NEWENV */ - DUK_HOBJECT_HAS_NEWENV(h)); - DUK_ASSERT(!DUK_HOBJECT_HAS_NAMEBINDING(h)); - DUK_ASSERT(!DUK_HOBJECT_HAS_CREATEARGS(h)); - /* DUK_HOBJECT_FLAG_EXOTIC_ARRAY varies */ - /* DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ varies */ - DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(h)); - - DUK_DDD(DUK_DDDPRINT("created built-in %ld, class=%ld, length=%ld", (long) i, (long) class_num, (long) len)); - } - - /* - * Then decode the builtins init data (see genbuiltins.py) to - * init objects. Internal prototypes are set at this stage, - * with thr->builtins[] populated. - */ - - DUK_DD(DUK_DDPRINT("initialize built-in object properties")); - for (i = 0; i < DUK_NUM_ALL_BUILTINS; i++) { - duk_small_uint_t t; - duk_small_uint_t num; - - DUK_DDD(DUK_DDDPRINT("initializing built-in object at index %ld", (long) i)); - h = duk_known_hobject(thr, (duk_idx_t) i); - - t = (duk_small_uint_t) duk_bd_decode_varuint(bd); - if (t > 0) { - t--; - DUK_DDD(DUK_DDDPRINT("set internal prototype: built-in %ld", (long) t)); - DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h, duk_known_hobject(thr, (duk_idx_t) t)); - } else if (DUK_HOBJECT_IS_NATFUNC(h)) { - /* Standard native built-ins cannot inherit from - * %NativeFunctionPrototype%, they are required to - * inherit from Function.prototype directly. - */ - DUK_ASSERT(thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE] != NULL); - DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h, thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]); - } - - t = (duk_small_uint_t) duk_bd_decode_varuint(bd); - if (t > 0) { - /* 'prototype' property for all built-in objects (which have it) has attributes: - * [[Writable]] = false, - * [[Enumerable]] = false, - * [[Configurable]] = false - */ - t--; - DUK_DDD(DUK_DDDPRINT("set external prototype: built-in %ld", (long) t)); - duk_dup(thr, (duk_idx_t) t); - duk_xdef_prop_stridx(thr, (duk_idx_t) i, DUK_STRIDX_PROTOTYPE, DUK_PROPDESC_FLAGS_NONE); - } - - t = (duk_small_uint_t) duk_bd_decode_varuint(bd); - if (t > 0) { - /* 'constructor' property for all built-in objects (which have it) has attributes: - * [[Writable]] = true, - * [[Enumerable]] = false, - * [[Configurable]] = true - */ - t--; - DUK_DDD(DUK_DDDPRINT("set external constructor: built-in %ld", (long) t)); - duk_dup(thr, (duk_idx_t) t); - duk_xdef_prop_stridx(thr, (duk_idx_t) i, DUK_STRIDX_CONSTRUCTOR, DUK_PROPDESC_FLAGS_WC); - } - - /* normal valued properties */ - num = (duk_small_uint_t) duk_bd_decode_varuint(bd); - DUK_DDD(DUK_DDDPRINT("built-in object %ld, %ld normal valued properties", (long) i, (long) num)); - for (j = 0; j < num; j++) { - duk_small_uint_t defprop_flags; - - duk__push_stridx_or_string(thr, bd); - - /* - * Property attribute defaults are defined in E5 Section 15 (first - * few pages); there is a default for all properties and a special - * default for 'length' properties. Variation from the defaults is - * signaled using a single flag bit in the bitstream. - */ - - defprop_flags = (duk_small_uint_t) duk_bd_decode_flagged(bd, - DUK__PROP_FLAGS_BITS, - (duk_uint32_t) DUK_PROPDESC_FLAGS_WC); - defprop_flags |= DUK_DEFPROP_FORCE | DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_HAVE_WRITABLE | - DUK_DEFPROP_HAVE_ENUMERABLE | - DUK_DEFPROP_HAVE_CONFIGURABLE; /* Defaults for data properties. */ - - /* The writable, enumerable, configurable flags in prop_flags - * match both duk_def_prop() and internal property flags. - */ - DUK_ASSERT(DUK_PROPDESC_FLAG_WRITABLE == DUK_DEFPROP_WRITABLE); - DUK_ASSERT(DUK_PROPDESC_FLAG_ENUMERABLE == DUK_DEFPROP_ENUMERABLE); - DUK_ASSERT(DUK_PROPDESC_FLAG_CONFIGURABLE == DUK_DEFPROP_CONFIGURABLE); - - t = (duk_small_uint_t) duk_bd_decode(bd, DUK__PROP_TYPE_BITS); - - DUK_DDD(DUK_DDDPRINT("built-in %ld, normal-valued property %ld, key %!T, flags 0x%02lx, type %ld", - (long) i, - (long) j, - duk_get_tval(thr, -1), - (unsigned long) defprop_flags, - (long) t)); - - switch (t) { - case DUK__PROP_TYPE_DOUBLE: { - duk__push_double(thr, bd); - break; - } - case DUK__PROP_TYPE_STRING: { - duk__push_string(thr, bd); - break; - } - case DUK__PROP_TYPE_STRIDX: { - duk__push_stridx(thr, bd); - break; - } - case DUK__PROP_TYPE_BUILTIN: { - duk_small_uint_t bidx; - - bidx = (duk_small_uint_t) duk_bd_decode_varuint(bd); - duk_dup(thr, (duk_idx_t) bidx); - break; - } - case DUK__PROP_TYPE_UNDEFINED: { - duk_push_undefined(thr); - break; - } - case DUK__PROP_TYPE_BOOLEAN_TRUE: { - duk_push_true(thr); - break; - } - case DUK__PROP_TYPE_BOOLEAN_FALSE: { - duk_push_false(thr); - break; - } - case DUK__PROP_TYPE_ACCESSOR: { - duk_small_uint_t natidx_getter = (duk_small_uint_t) duk_bd_decode_varuint(bd); - duk_small_uint_t natidx_setter = (duk_small_uint_t) duk_bd_decode_varuint(bd); - duk_small_uint_t accessor_magic = (duk_small_uint_t) duk_bd_decode_varuint(bd); - duk_c_function c_func_getter; - duk_c_function c_func_setter; - - DUK_DDD(DUK_DDDPRINT( - "built-in accessor property: objidx=%ld, key=%!T, getteridx=%ld, setteridx=%ld, flags=0x%04lx", - (long) i, - duk_get_tval(thr, -1), - (long) natidx_getter, - (long) natidx_setter, - (unsigned long) defprop_flags)); - - c_func_getter = duk_bi_native_functions[natidx_getter]; - if (c_func_getter != NULL) { - duk_push_c_function_builtin_noconstruct(thr, c_func_getter, 0); /* always 0 args */ - duk_set_magic(thr, -1, (duk_int_t) accessor_magic); - defprop_flags |= DUK_DEFPROP_HAVE_GETTER; - } - c_func_setter = duk_bi_native_functions[natidx_setter]; - if (c_func_setter != NULL) { - duk_push_c_function_builtin_noconstruct(thr, c_func_setter, 1); /* always 1 arg */ - duk_set_magic(thr, -1, (duk_int_t) accessor_magic); - defprop_flags |= DUK_DEFPROP_HAVE_SETTER; - } - - /* Writable flag doesn't make sense for an accessor. */ - DUK_ASSERT((defprop_flags & DUK_PROPDESC_FLAG_WRITABLE) == 0); /* genbuiltins.py ensures */ - - defprop_flags &= ~(DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_HAVE_WRITABLE); - defprop_flags |= DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_HAVE_CONFIGURABLE; - break; - } - default: { - /* exhaustive */ - DUK_UNREACHABLE(); - } - } - - duk_def_prop(thr, (duk_idx_t) i, defprop_flags); - DUK_ASSERT_TOP(thr, DUK_NUM_ALL_BUILTINS); - } - - /* native function properties */ - num = (duk_small_uint_t) duk_bd_decode_varuint(bd); - DUK_DDD(DUK_DDDPRINT("built-in object %ld, %ld function valued properties", (long) i, (long) num)); - for (j = 0; j < num; j++) { - duk_hstring *h_key; - duk_small_uint_t natidx; - duk_int_t c_nargs; /* must hold DUK_VARARGS */ - duk_small_uint_t c_length; - duk_int16_t magic; - duk_c_function c_func; - duk_hnatfunc *h_func; -#if defined(DUK_USE_LIGHTFUNC_BUILTINS) - duk_small_int_t lightfunc_eligible; -#endif - duk_small_uint_t defprop_flags; - - duk__push_stridx_or_string(thr, bd); - h_key = duk_known_hstring(thr, -1); - DUK_UNREF(h_key); - natidx = (duk_small_uint_t) duk_bd_decode_varuint(bd); - - c_length = (duk_small_uint_t) duk_bd_decode(bd, DUK__LENGTH_PROP_BITS); - c_nargs = (duk_int_t) duk_bd_decode_flagged(bd, DUK__NARGS_BITS, (duk_uint32_t) c_length /*def_value*/); - if (c_nargs == DUK__NARGS_VARARGS_MARKER) { - c_nargs = DUK_VARARGS; - } - - c_func = duk_bi_native_functions[natidx]; - - DUK_DDD( - DUK_DDDPRINT("built-in %ld, function-valued property %ld, key %!O, natidx %ld, length %ld, nargs %ld", - (long) i, - (long) j, - (duk_heaphdr *) h_key, - (long) natidx, - (long) c_length, - (c_nargs == DUK_VARARGS ? (long) -1 : (long) c_nargs))); - - /* Cast converts magic to 16-bit signed value */ - magic = (duk_int16_t) duk_bd_decode_varuint(bd); - -#if defined(DUK_USE_LIGHTFUNC_BUILTINS) - lightfunc_eligible = - ((c_nargs >= DUK_LFUNC_NARGS_MIN && c_nargs <= DUK_LFUNC_NARGS_MAX) || (c_nargs == DUK_VARARGS)) && - (c_length <= DUK_LFUNC_LENGTH_MAX) && (magic >= DUK_LFUNC_MAGIC_MIN && magic <= DUK_LFUNC_MAGIC_MAX); - - /* These functions have trouble working as lightfuncs. - * Some of them have specific asserts and some may have - * additional properties (e.g. 'require.id' may be written). - */ - if (c_func == duk_bi_global_object_eval) { - lightfunc_eligible = 0; - } -#if defined(DUK_USE_COROUTINE_SUPPORT) - if (c_func == duk_bi_thread_yield || c_func == duk_bi_thread_resume) { - lightfunc_eligible = 0; - } -#endif - if (c_func == duk_bi_function_prototype_call || c_func == duk_bi_function_prototype_apply || - c_func == duk_bi_reflect_apply || c_func == duk_bi_reflect_construct) { - lightfunc_eligible = 0; - } - - if (lightfunc_eligible) { - duk_tval tv_lfunc; - duk_small_uint_t lf_nargs = - (duk_small_uint_t) (c_nargs == DUK_VARARGS ? DUK_LFUNC_NARGS_VARARGS : c_nargs); - duk_small_uint_t lf_flags = DUK_LFUNC_FLAGS_PACK(magic, c_length, lf_nargs); - DUK_TVAL_SET_LIGHTFUNC(&tv_lfunc, c_func, lf_flags); - duk_push_tval(thr, &tv_lfunc); - DUK_D(DUK_DPRINT("built-in function eligible as light function: i=%d, j=%d c_length=%ld, " - "c_nargs=%ld, magic=%ld -> %!iT", - (int) i, - (int) j, - (long) c_length, - (long) c_nargs, - (long) magic, - duk_get_tval(thr, -1))); - goto lightfunc_skip; - } - - DUK_D(DUK_DPRINT( - "built-in function NOT ELIGIBLE as light function: i=%d, j=%d c_length=%ld, c_nargs=%ld, magic=%ld", - (int) i, - (int) j, - (long) c_length, - (long) c_nargs, - (long) magic)); -#endif /* DUK_USE_LIGHTFUNC_BUILTINS */ - - /* [ (builtin objects) name ] */ - - duk_push_c_function_builtin_noconstruct(thr, c_func, c_nargs); - h_func = duk_known_hnatfunc(thr, -1); - DUK_UNREF(h_func); - - /* XXX: add into init data? */ - - /* Special call handling, not described in init data. */ - if (c_func == duk_bi_global_object_eval || c_func == duk_bi_function_prototype_call || - c_func == duk_bi_function_prototype_apply || c_func == duk_bi_reflect_apply || - c_func == duk_bi_reflect_construct) { - DUK_HOBJECT_SET_SPECIAL_CALL((duk_hobject *) h_func); - } - - /* Currently all built-in native functions are strict. - * This doesn't matter for many functions, but e.g. - * String.prototype.charAt (and other string functions) - * rely on being strict so that their 'this' binding is - * not automatically coerced. - */ - DUK_HOBJECT_SET_STRICT((duk_hobject *) h_func); - - /* No built-in functions are constructable except the top - * level ones (Number, etc). - */ - DUK_ASSERT(!DUK_HOBJECT_HAS_CONSTRUCTABLE((duk_hobject *) h_func)); - - /* XXX: any way to avoid decoding magic bit; there are quite - * many function properties and relatively few with magic values. - */ - h_func->magic = magic; - - /* [ (builtin objects) name func ] */ - - duk_push_uint(thr, c_length); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_C); - - duk_dup_m2(thr); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C); - - /* XXX: other properties of function instances; 'arguments', 'caller'. */ - - DUK_DD(DUK_DDPRINT("built-in object %ld, function property %ld -> %!T", - (long) i, - (long) j, - (duk_tval *) duk_get_tval(thr, -1))); - - /* [ (builtin objects) name func ] */ - - /* - * The default property attributes are correct for all - * function valued properties of built-in objects now. - */ - -#if defined(DUK_USE_LIGHTFUNC_BUILTINS) - lightfunc_skip: -#endif - - defprop_flags = (duk_small_uint_t) duk_bd_decode_flagged(bd, - DUK__PROP_FLAGS_BITS, - (duk_uint32_t) DUK_PROPDESC_FLAGS_WC); - defprop_flags |= DUK_DEFPROP_FORCE | DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_HAVE_WRITABLE | - DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_HAVE_CONFIGURABLE; - DUK_ASSERT(DUK_PROPDESC_FLAG_WRITABLE == DUK_DEFPROP_WRITABLE); - DUK_ASSERT(DUK_PROPDESC_FLAG_ENUMERABLE == DUK_DEFPROP_ENUMERABLE); - DUK_ASSERT(DUK_PROPDESC_FLAG_CONFIGURABLE == DUK_DEFPROP_CONFIGURABLE); - - duk_def_prop(thr, (duk_idx_t) i, defprop_flags); - - /* [ (builtin objects) ] */ - } - } - - /* - * Special post-tweaks, for cases not covered by the init data format. - * - * - Set Date.prototype.toGMTString to Date.prototype.toUTCString. - * toGMTString is required to have the same Function object as - * toUTCString in E5 Section B.2.6. Note that while Smjs respects - * this, V8 does not (the Function objects are distinct). - * - * - Make DoubleError non-extensible. - * - * - Add info about most important effective compile options to Duktape. - * - * - Possibly remove some properties (values or methods) which are not - * desirable with current feature options but are not currently - * conditional in init data. - */ - -#if defined(DUK_USE_DATE_BUILTIN) - duk_get_prop_stridx_short(thr, DUK_BIDX_DATE_PROTOTYPE, DUK_STRIDX_TO_UTC_STRING); - duk_xdef_prop_stridx_short(thr, DUK_BIDX_DATE_PROTOTYPE, DUK_STRIDX_TO_GMT_STRING, DUK_PROPDESC_FLAGS_WC); -#endif - - h = duk_known_hobject(thr, DUK_BIDX_DOUBLE_ERROR); - DUK_HOBJECT_CLEAR_EXTENSIBLE(h); - -#if !defined(DUK_USE_ES6_OBJECT_PROTO_PROPERTY) - DUK_DD(DUK_DDPRINT("delete Object.prototype.__proto__ built-in which is not enabled in features")); - (void) duk_hobject_delprop_raw(thr, - thr->builtins[DUK_BIDX_OBJECT_PROTOTYPE], - DUK_HTHREAD_STRING___PROTO__(thr), - DUK_DELPROP_FLAG_THROW); -#endif - -#if !defined(DUK_USE_ES6_OBJECT_SETPROTOTYPEOF) - DUK_DD(DUK_DDPRINT("delete Object.setPrototypeOf built-in which is not enabled in features")); - (void) duk_hobject_delprop_raw(thr, - thr->builtins[DUK_BIDX_OBJECT_CONSTRUCTOR], - DUK_HTHREAD_STRING_SET_PROTOTYPE_OF(thr), - DUK_DELPROP_FLAG_THROW); -#endif - - /* XXX: relocate */ - duk_push_string(thr, - /* Endianness indicator */ -#if defined(DUK_USE_INTEGER_LE) - "l" -#elif defined(DUK_USE_INTEGER_BE) - "b" -#elif defined(DUK_USE_INTEGER_ME) /* integer mixed endian not really used now */ - "m" -#else - "?" -#endif -#if defined(DUK_USE_DOUBLE_LE) - "l" -#elif defined(DUK_USE_DOUBLE_BE) - "b" -#elif defined(DUK_USE_DOUBLE_ME) - "m" -#else - "?" -#endif - " " - /* Packed or unpacked tval */ -#if defined(DUK_USE_PACKED_TVAL) - "p" -#else - "u" -#endif -#if defined(DUK_USE_FASTINT) - "f" -#endif - " " - /* Low memory/performance options */ -#if defined(DUK_USE_STRTAB_PTRCOMP) - "s" -#endif -#if !defined(DUK_USE_HEAPPTR16) && !defined(DUK_DATAPTR16) && !defined(DUK_FUNCPTR16) - "n" -#endif -#if defined(DUK_USE_HEAPPTR16) - "h" -#endif -#if defined(DUK_USE_DATAPTR16) - "d" -#endif -#if defined(DUK_USE_FUNCPTR16) - "f" -#endif -#if defined(DUK_USE_REFCOUNT16) - "R" -#endif -#if defined(DUK_USE_STRHASH16) - "H" -#endif -#if defined(DUK_USE_STRLEN16) - "S" -#endif -#if defined(DUK_USE_BUFLEN16) - "B" -#endif -#if defined(DUK_USE_OBJSIZES16) - "O" -#endif -#if defined(DUK_USE_LIGHTFUNC_BUILTINS) - "L" -#endif -#if defined(DUK_USE_ROM_STRINGS) || defined(DUK_USE_ROM_OBJECTS) - /* XXX: This won't be shown in practice now - * because this code is not run when builtins - * are in ROM. - */ - "Z" -#endif -#if defined(DUK_USE_LITCACHE_SIZE) - "l" -#endif - " " - /* Object property allocation layout */ -#if defined(DUK_USE_HOBJECT_LAYOUT_1) - "p1" -#elif defined(DUK_USE_HOBJECT_LAYOUT_2) - "p2" -#elif defined(DUK_USE_HOBJECT_LAYOUT_3) - "p3" -#else - "p?" -#endif - " " - /* Alignment guarantee */ -#if (DUK_USE_ALIGN_BY == 4) - "a4" -#elif (DUK_USE_ALIGN_BY == 8) - "a8" -#elif (DUK_USE_ALIGN_BY == 1) - "a1" -#else -#error invalid DUK_USE_ALIGN_BY -#endif - " " - /* Architecture, OS, and compiler strings */ - DUK_USE_ARCH_STRING " " DUK_USE_OS_STRING " " DUK_USE_COMPILER_STRING); - duk_xdef_prop_stridx_short(thr, DUK_BIDX_DUKTAPE, DUK_STRIDX_ENV, DUK_PROPDESC_FLAGS_WC); - - /* - * Since built-ins are not often extended, compact them. - */ - - DUK_DD(DUK_DDPRINT("compact built-ins")); - for (i = 0; i < DUK_NUM_ALL_BUILTINS; i++) { - duk_hobject_compact_props(thr, duk_known_hobject(thr, (duk_idx_t) i)); - } - - DUK_D(DUK_DPRINT("INITBUILTINS END")); - -#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 1) - for (i = 0; i < DUK_NUM_ALL_BUILTINS; i++) { - DUK_DD(DUK_DDPRINT("built-in object %ld after initialization and compacting: %!@iO", - (long) i, - (duk_heaphdr *) duk_require_hobject(thr, (duk_idx_t) i))); - } -#endif - - /* - * Pop built-ins from stack: they are now INCREF'd and - * reachable from the builtins[] array or indirectly - * through builtins[]. - */ - - duk_set_top(thr, 0); - DUK_ASSERT_TOP(thr, 0); -} -#endif /* DUK_USE_ROM_OBJECTS */ - -DUK_INTERNAL void duk_hthread_copy_builtin_objects(duk_hthread *thr_from, duk_hthread *thr_to) { - duk_small_uint_t i; - - for (i = 0; i < DUK_NUM_BUILTINS; i++) { - thr_to->builtins[i] = thr_from->builtins[i]; - DUK_HOBJECT_INCREF_ALLOWNULL(thr_to, thr_to->builtins[i]); /* side effect free */ - } -} - -/* automatic undefs */ -#undef DUK__LENGTH_PROP_BITS -#undef DUK__NARGS_BITS -#undef DUK__NARGS_VARARGS_MARKER -#undef DUK__PROP_FLAGS_BITS -#undef DUK__PROP_TYPE_ACCESSOR -#undef DUK__PROP_TYPE_BITS -#undef DUK__PROP_TYPE_BOOLEAN_FALSE -#undef DUK__PROP_TYPE_BOOLEAN_TRUE -#undef DUK__PROP_TYPE_BUILTIN -#undef DUK__PROP_TYPE_DOUBLE -#undef DUK__PROP_TYPE_STRIDX -#undef DUK__PROP_TYPE_STRING -#undef DUK__PROP_TYPE_UNDEFINED -/* - * Thread support. - */ - -/* #include duk_internal.h -> already included */ - -DUK_INTERNAL void duk_hthread_terminate(duk_hthread *thr) { - DUK_ASSERT(thr != NULL); - - while (thr->callstack_curr != NULL) { - duk_hthread_activation_unwind_norz(thr); - } - - thr->valstack_bottom = thr->valstack; - duk_set_top(thr, 0); /* unwinds valstack, updating refcounts */ - - thr->state = DUK_HTHREAD_STATE_TERMINATED; - - /* Here we could remove references to built-ins, but it may not be - * worth the effort because built-ins are quite likely to be shared - * with another (unterminated) thread, and terminated threads are also - * usually garbage collected quite quickly. - * - * We could also shrink the value stack here, but that also may not - * be worth the effort for the same reason. - */ - - DUK_REFZERO_CHECK_SLOW(thr); -} - -#if defined(DUK_USE_DEBUGGER_SUPPORT) -DUK_INTERNAL duk_uint_fast32_t duk_hthread_get_act_curr_pc(duk_hthread *thr, duk_activation *act) { - duk_instr_t *bcode; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(act != NULL); - DUK_UNREF(thr); - - /* XXX: store 'bcode' pointer to activation for faster lookup? */ - if (act->func && DUK_HOBJECT_IS_COMPFUNC(act->func)) { - bcode = DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, (duk_hcompfunc *) (act->func)); - return (duk_uint_fast32_t) (act->curr_pc - bcode); - } - return 0; -} -#endif /* DUK_USE_DEBUGGER_SUPPORT */ - -DUK_INTERNAL duk_uint_fast32_t duk_hthread_get_act_prev_pc(duk_hthread *thr, duk_activation *act) { - duk_instr_t *bcode; - duk_uint_fast32_t ret; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(act != NULL); - DUK_UNREF(thr); - - if (act->func && DUK_HOBJECT_IS_COMPFUNC(act->func)) { - bcode = DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, (duk_hcompfunc *) (act->func)); - ret = (duk_uint_fast32_t) (act->curr_pc - bcode); - if (ret > 0) { - ret--; - } - return ret; - } - return 0; -} - -/* Write bytecode executor's curr_pc back to topmost activation (if any). */ -DUK_INTERNAL void duk_hthread_sync_currpc(duk_hthread *thr) { - duk_activation *act; - - DUK_ASSERT(thr != NULL); - - if (thr->ptr_curr_pc != NULL) { - /* ptr_curr_pc != NULL only when bytecode executor is active. */ - DUK_ASSERT(thr->callstack_top > 0); - DUK_ASSERT(thr->callstack_curr != NULL); - act = thr->callstack_curr; - DUK_ASSERT(act != NULL); - act->curr_pc = *thr->ptr_curr_pc; - } -} - -DUK_INTERNAL void duk_hthread_sync_and_null_currpc(duk_hthread *thr) { - duk_activation *act; - - DUK_ASSERT(thr != NULL); - - if (thr->ptr_curr_pc != NULL) { - /* ptr_curr_pc != NULL only when bytecode executor is active. */ - DUK_ASSERT(thr->callstack_top > 0); - DUK_ASSERT(thr->callstack_curr != NULL); - act = thr->callstack_curr; - DUK_ASSERT(act != NULL); - act->curr_pc = *thr->ptr_curr_pc; - thr->ptr_curr_pc = NULL; - } -} -/* - * Thread stack (mainly call stack) primitives: allocation of activations, - * unwinding catchers and activations, etc. - * - * Value stack handling is a part of the API implementation. - */ - -/* #include duk_internal.h -> already included */ - -/* Unwind the topmost catcher of the current activation (caller must check that - * both exist) without side effects. - */ -DUK_INTERNAL void duk_hthread_catcher_unwind_norz(duk_hthread *thr, duk_activation *act) { - duk_catcher *cat; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(act != NULL); - DUK_ASSERT(act->cat != NULL); /* caller must check */ - cat = act->cat; - DUK_ASSERT(cat != NULL); - - DUK_DDD(DUK_DDDPRINT("unwinding catch stack entry %p (lexenv check is done)", (void *) cat)); - - if (DUK_CAT_HAS_LEXENV_ACTIVE(cat)) { - duk_hobject *env; - - env = act->lex_env; /* current lex_env of the activation (created for catcher) */ - DUK_ASSERT(env != NULL); /* must be, since env was created when catcher was created */ - act->lex_env = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, env); /* prototype is lex_env before catcher created */ - DUK_HOBJECT_INCREF(thr, act->lex_env); - DUK_HOBJECT_DECREF_NORZ(thr, env); - - /* There is no need to decref anything else than 'env': if 'env' - * becomes unreachable, refzero will handle decref'ing its prototype. - */ - } - - act->cat = cat->parent; - duk_hthread_catcher_free(thr, cat); -} - -/* Same as above, but caller is certain no catcher-related lexenv may exist. */ -DUK_INTERNAL void duk_hthread_catcher_unwind_nolexenv_norz(duk_hthread *thr, duk_activation *act) { - duk_catcher *cat; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(act != NULL); - DUK_ASSERT(act->cat != NULL); /* caller must check */ - cat = act->cat; - DUK_ASSERT(cat != NULL); - - DUK_DDD(DUK_DDDPRINT("unwinding catch stack entry %p (lexenv check is not done)", (void *) cat)); - - DUK_ASSERT(!DUK_CAT_HAS_LEXENV_ACTIVE(cat)); - - act->cat = cat->parent; - duk_hthread_catcher_free(thr, cat); -} - -DUK_LOCAL -#if defined(DUK_USE_CACHE_CATCHER) -DUK_NOINLINE -#endif -duk_catcher *duk__hthread_catcher_alloc_slow(duk_hthread *thr) { - duk_catcher *cat; - - cat = (duk_catcher *) DUK_ALLOC_CHECKED(thr, sizeof(duk_catcher)); - DUK_ASSERT(cat != NULL); - return cat; -} - -#if defined(DUK_USE_CACHE_CATCHER) -DUK_INTERNAL DUK_INLINE duk_catcher *duk_hthread_catcher_alloc(duk_hthread *thr) { - duk_catcher *cat; - - DUK_ASSERT(thr != NULL); - - cat = thr->heap->catcher_free; - if (DUK_LIKELY(cat != NULL)) { - thr->heap->catcher_free = cat->parent; - return cat; - } - - return duk__hthread_catcher_alloc_slow(thr); -} -#else /* DUK_USE_CACHE_CATCHER */ -DUK_INTERNAL duk_catcher *duk_hthread_catcher_alloc(duk_hthread *thr) { - return duk__hthread_catcher_alloc_slow(thr); -} -#endif /* DUK_USE_CACHE_CATCHER */ - -DUK_INTERNAL void duk_hthread_catcher_free(duk_hthread *thr, duk_catcher *cat) { - DUK_ASSERT(thr != NULL); - DUK_ASSERT(cat != NULL); - -#if defined(DUK_USE_CACHE_CATCHER) - /* Unconditional caching for now; freed in mark-and-sweep. */ - cat->parent = thr->heap->catcher_free; - thr->heap->catcher_free = cat; -#else - DUK_FREE_CHECKED(thr, (void *) cat); -#endif -} - -DUK_LOCAL -#if defined(DUK_USE_CACHE_ACTIVATION) -DUK_NOINLINE -#endif -duk_activation *duk__hthread_activation_alloc_slow(duk_hthread *thr) { - duk_activation *act; - - act = (duk_activation *) DUK_ALLOC_CHECKED(thr, sizeof(duk_activation)); - DUK_ASSERT(act != NULL); - return act; -} - -#if defined(DUK_USE_CACHE_ACTIVATION) -DUK_INTERNAL DUK_INLINE duk_activation *duk_hthread_activation_alloc(duk_hthread *thr) { - duk_activation *act; - - DUK_ASSERT(thr != NULL); - - act = thr->heap->activation_free; - if (DUK_LIKELY(act != NULL)) { - thr->heap->activation_free = act->parent; - return act; - } - - return duk__hthread_activation_alloc_slow(thr); -} -#else /* DUK_USE_CACHE_ACTIVATION */ -DUK_INTERNAL duk_activation *duk_hthread_activation_alloc(duk_hthread *thr) { - return duk__hthread_activation_alloc_slow(thr); -} -#endif /* DUK_USE_CACHE_ACTIVATION */ - -DUK_INTERNAL void duk_hthread_activation_free(duk_hthread *thr, duk_activation *act) { - DUK_ASSERT(thr != NULL); - DUK_ASSERT(act != NULL); - -#if defined(DUK_USE_CACHE_ACTIVATION) - /* Unconditional caching for now; freed in mark-and-sweep. */ - act->parent = thr->heap->activation_free; - thr->heap->activation_free = act; -#else - DUK_FREE_CHECKED(thr, (void *) act); -#endif -} - -/* Internal helper: process the unwind for the topmost activation of a thread, - * but leave the duk_activation in place for possible tailcall reuse. - */ -DUK_LOCAL void duk__activation_unwind_nofree_norz(duk_hthread *thr) { -#if defined(DUK_USE_DEBUGGER_SUPPORT) - duk_heap *heap; -#endif - duk_activation *act; - duk_hobject *func; - duk_hobject *tmp; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->callstack_curr != NULL); /* caller must check */ - DUK_ASSERT(thr->callstack_top > 0); - act = thr->callstack_curr; - DUK_ASSERT(act != NULL); - /* With lightfuncs, act 'func' may be NULL. */ - - /* With duk_activation records allocated separately, 'act' is a stable - * pointer and not affected by side effects. - */ - -#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY) - /* - * Restore 'caller' property for non-strict callee functions. - */ - - func = DUK_ACT_GET_FUNC(act); - if (func != NULL && !DUK_HOBJECT_HAS_STRICT(func)) { - duk_tval *tv_caller; - duk_tval tv_tmp; - duk_hobject *h_tmp; - - tv_caller = duk_hobject_find_entry_tval_ptr_stridx(thr->heap, func, DUK_STRIDX_CALLER); - - /* The act->prev_caller should only be set if the entry for 'caller' - * exists (as it is only set in that case, and the property is not - * configurable), but handle all the cases anyway. - */ - - if (tv_caller) { - DUK_TVAL_SET_TVAL(&tv_tmp, tv_caller); - if (act->prev_caller) { - /* Just transfer the refcount from act->prev_caller to tv_caller, - * so no need for a refcount update. This is the expected case. - */ - DUK_TVAL_SET_OBJECT(tv_caller, act->prev_caller); - act->prev_caller = NULL; - } else { - DUK_TVAL_SET_NULL(tv_caller); /* no incref needed */ - DUK_ASSERT(act->prev_caller == NULL); - } - DUK_TVAL_DECREF_NORZ(thr, &tv_tmp); - } else { - h_tmp = act->prev_caller; - if (h_tmp) { - act->prev_caller = NULL; - DUK_HOBJECT_DECREF_NORZ(thr, h_tmp); - } - } - DUK_ASSERT(act->prev_caller == NULL); - } -#endif - - /* - * Unwind debugger state. If we unwind while stepping - * (for any step type), pause execution. This is the - * only place explicitly handling a step out. - */ - -#if defined(DUK_USE_DEBUGGER_SUPPORT) - heap = thr->heap; - if (heap->dbg_pause_act == thr->callstack_curr) { - if (heap->dbg_pause_flags & DUK_PAUSE_FLAG_FUNC_EXIT) { - DUK_D(DUK_DPRINT("PAUSE TRIGGERED by function exit")); - duk_debug_set_paused(heap); - } else { - DUK_D(DUK_DPRINT("unwound past dbg_pause_act, set to NULL")); - heap->dbg_pause_act = NULL; /* avoid stale pointers */ - } - DUK_ASSERT(heap->dbg_pause_act == NULL); - } -#endif - - /* - * Unwind catchers. - * - * Since there are no references in the catcher structure, - * unwinding is quite simple. The only thing we need to - * look out for is popping a possible lexical environment - * established for an active catch clause. - */ - - while (act->cat != NULL) { - duk_hthread_catcher_unwind_norz(thr, act); - } - - /* - * Close environment record(s) if they exist. - * - * Only variable environments are closed. If lex_env != var_env, it - * cannot currently contain any register bound declarations. - * - * Only environments created for a NEWENV function are closed. If an - * environment is created for e.g. an eval call, it must not be closed. - */ - - func = DUK_ACT_GET_FUNC(act); - if (func != NULL && !DUK_HOBJECT_HAS_NEWENV(func)) { - DUK_DDD(DUK_DDDPRINT("skip closing environments, envs not owned by this activation")); - goto skip_env_close; - } - /* func is NULL for lightfunc */ - - /* Catch sites are required to clean up their environments - * in FINALLY part before propagating, so this should - * always hold here. - */ - DUK_ASSERT(act->lex_env == act->var_env); - - /* XXX: Closing the environment record copies values from registers - * into the scope object. It's side effect free as such, but may - * currently run out of memory which causes an error throw. This is - * an actual sandboxing problem for error unwinds, and needs to be - * fixed e.g. by preallocating the scope property slots. - */ - if (act->var_env != NULL) { - DUK_DDD(DUK_DDDPRINT("closing var_env record %p -> %!O", (void *) act->var_env, (duk_heaphdr *) act->var_env)); - duk_js_close_environment_record(thr, act->var_env); - } - -skip_env_close: - - /* - * Update preventcount - */ - - if (act->flags & DUK_ACT_FLAG_PREVENT_YIELD) { - DUK_ASSERT(thr->callstack_preventcount >= 1); - thr->callstack_preventcount--; - } - - /* - * Reference count updates, using NORZ macros so we don't - * need to handle side effects. - * - * duk_activation pointers like act->var_env are intentionally - * left as garbage and not NULLed. Without side effects they - * can't be used when the values are dangling/garbage. - */ - - DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, act->var_env); - DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, act->lex_env); - tmp = DUK_ACT_GET_FUNC(act); - DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, tmp); - DUK_UNREF(tmp); -} - -/* Unwind topmost duk_activation of a thread, caller must ensure that an - * activation exists. The call is side effect free, except that scope - * closure may currently throw an out-of-memory error. - */ -DUK_INTERNAL void duk_hthread_activation_unwind_norz(duk_hthread *thr) { - duk_activation *act; - - duk__activation_unwind_nofree_norz(thr); - - DUK_ASSERT(thr->callstack_curr != NULL); - DUK_ASSERT(thr->callstack_top > 0); - act = thr->callstack_curr; - thr->callstack_curr = act->parent; - thr->callstack_top--; - - /* Ideally we'd restore value stack reserve here to caller's value. - * This doesn't work for current unwind call sites however, because - * the current (unwound) value stack top may be above the reserve. - * Thus value stack reserve is restored by the call sites. - */ - - /* XXX: inline for performance builds? */ - duk_hthread_activation_free(thr, act); - - /* We could clear the book-keeping variables like retval_byteoff for - * the topmost activation, but don't do so now as it's not necessary. - */ -} - -DUK_INTERNAL void duk_hthread_activation_unwind_reuse_norz(duk_hthread *thr) { - duk__activation_unwind_nofree_norz(thr); -} - -/* Get duk_activation for given callstack level or NULL if level is invalid - * or deeper than the call stack. Level -1 refers to current activation, -2 - * to its caller, etc. Starting from Duktape 2.2 finding the activation is - * a linked list scan which gets more expensive the deeper the lookup is. - */ -DUK_INTERNAL duk_activation *duk_hthread_get_activation_for_level(duk_hthread *thr, duk_int_t level) { - duk_activation *act; - - if (level >= 0) { - return NULL; - } - act = thr->callstack_curr; - for (;;) { - if (act == NULL) { - return act; - } - if (level == -1) { - return act; - } - level++; - act = act->parent; - } - /* never here */ -} - -#if defined(DUK_USE_FINALIZER_TORTURE) -DUK_INTERNAL void duk_hthread_valstack_torture_realloc(duk_hthread *thr) { - duk_size_t alloc_size; - duk_tval *new_ptr; - duk_ptrdiff_t alloc_end_off; - duk_ptrdiff_t end_off; - duk_ptrdiff_t bottom_off; - duk_ptrdiff_t top_off; - - if (thr->valstack == NULL) { - DUK_D(DUK_DPRINT("skip valstack torture realloc, valstack is NULL")); - return; - } - - alloc_end_off = (duk_ptrdiff_t) ((duk_uint8_t *) thr->valstack_alloc_end - (duk_uint8_t *) thr->valstack); - end_off = (duk_ptrdiff_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack); - bottom_off = (duk_ptrdiff_t) ((duk_uint8_t *) thr->valstack_bottom - (duk_uint8_t *) thr->valstack); - top_off = (duk_ptrdiff_t) ((duk_uint8_t *) thr->valstack_top - (duk_uint8_t *) thr->valstack); - alloc_size = (duk_size_t) alloc_end_off; - if (alloc_size == 0) { - DUK_D(DUK_DPRINT("skip valstack torture realloc, alloc_size is zero")); - return; - } - - /* Use DUK_ALLOC_RAW() to avoid side effects. */ - new_ptr = (duk_tval *) DUK_ALLOC_RAW(thr->heap, alloc_size); - if (new_ptr != NULL) { - duk_memcpy((void *) new_ptr, (const void *) thr->valstack, alloc_size); - duk_memset((void *) thr->valstack, 0x55, alloc_size); - DUK_FREE_CHECKED(thr, (void *) thr->valstack); - thr->valstack = new_ptr; - thr->valstack_alloc_end = (duk_tval *) ((duk_uint8_t *) new_ptr + alloc_end_off); - thr->valstack_end = (duk_tval *) ((duk_uint8_t *) new_ptr + end_off); - thr->valstack_bottom = (duk_tval *) ((duk_uint8_t *) new_ptr + bottom_off); - thr->valstack_top = (duk_tval *) ((duk_uint8_t *) new_ptr + top_off); - } else { - DUK_D(DUK_DPRINT("failed to realloc valstack for torture, ignore")); - } -} -#endif /* DUK_USE_FINALIZER_TORTURE */ -/* - * Shared helpers for arithmetic operations - */ - -/* #include duk_internal.h -> already included */ - -/* ECMAScript modulus ('%') does not match IEEE 754 "remainder" operation - * (implemented by remainder() in C99) but does seem to match ANSI C fmod(). - * Compare E5 Section 11.5.3 and "man fmod". - */ -DUK_INTERNAL double duk_js_arith_mod(double d1, double d2) { -#if defined(DUK_USE_POW_WORKAROUNDS) - /* Specific fixes to common fmod() implementation issues: - * - test-bug-mingw-math-issues.js - */ - if (DUK_ISINF(d2)) { - if (DUK_ISINF(d1)) { - return DUK_DOUBLE_NAN; - } else { - return d1; - } - } else if (duk_double_equals(d1, 0.0)) { - /* d1 +/-0 is returned as is (preserving sign) except when - * d2 is zero or NaN. - */ - if (duk_double_equals(d2, 0.0) || DUK_ISNAN(d2)) { - return DUK_DOUBLE_NAN; - } else { - return d1; - } - } -#else - /* Some ISO C assumptions. */ - DUK_ASSERT(duk_double_equals(DUK_FMOD(1.0, DUK_DOUBLE_INFINITY), 1.0)); - DUK_ASSERT(duk_double_equals(DUK_FMOD(-1.0, DUK_DOUBLE_INFINITY), -1.0)); - DUK_ASSERT(duk_double_equals(DUK_FMOD(1.0, -DUK_DOUBLE_INFINITY), 1.0)); - DUK_ASSERT(duk_double_equals(DUK_FMOD(-1.0, -DUK_DOUBLE_INFINITY), -1.0)); - DUK_ASSERT(DUK_ISNAN(DUK_FMOD(DUK_DOUBLE_INFINITY, DUK_DOUBLE_INFINITY))); - DUK_ASSERT(DUK_ISNAN(DUK_FMOD(DUK_DOUBLE_INFINITY, -DUK_DOUBLE_INFINITY))); - DUK_ASSERT(DUK_ISNAN(DUK_FMOD(-DUK_DOUBLE_INFINITY, DUK_DOUBLE_INFINITY))); - DUK_ASSERT(DUK_ISNAN(DUK_FMOD(-DUK_DOUBLE_INFINITY, -DUK_DOUBLE_INFINITY))); - DUK_ASSERT(duk_double_equals(DUK_FMOD(0.0, 1.0), 0.0) && DUK_SIGNBIT(DUK_FMOD(0.0, 1.0)) == 0); - DUK_ASSERT(duk_double_equals(DUK_FMOD(-0.0, 1.0), 0.0) && DUK_SIGNBIT(DUK_FMOD(-0.0, 1.0)) != 0); - DUK_ASSERT(duk_double_equals(DUK_FMOD(0.0, DUK_DOUBLE_INFINITY), 0.0) && - DUK_SIGNBIT(DUK_FMOD(0.0, DUK_DOUBLE_INFINITY)) == 0); - DUK_ASSERT(duk_double_equals(DUK_FMOD(-0.0, DUK_DOUBLE_INFINITY), 0.0) && - DUK_SIGNBIT(DUK_FMOD(-0.0, DUK_DOUBLE_INFINITY)) != 0); - DUK_ASSERT(duk_double_equals(DUK_FMOD(0.0, -DUK_DOUBLE_INFINITY), 0.0) && - DUK_SIGNBIT(DUK_FMOD(0.0, DUK_DOUBLE_INFINITY)) == 0); - DUK_ASSERT(duk_double_equals(DUK_FMOD(-0.0, -DUK_DOUBLE_INFINITY), 0.0) && - DUK_SIGNBIT(DUK_FMOD(-0.0, -DUK_DOUBLE_INFINITY)) != 0); - DUK_ASSERT(DUK_ISNAN(DUK_FMOD(0.0, 0.0))); - DUK_ASSERT(DUK_ISNAN(DUK_FMOD(-0.0, 0.0))); - DUK_ASSERT(DUK_ISNAN(DUK_FMOD(0.0, -0.0))); - DUK_ASSERT(DUK_ISNAN(DUK_FMOD(-0.0, -0.0))); - DUK_ASSERT(DUK_ISNAN(DUK_FMOD(0.0, DUK_DOUBLE_NAN))); - DUK_ASSERT(DUK_ISNAN(DUK_FMOD(-0.0, DUK_DOUBLE_NAN))); -#endif - - return (duk_double_t) DUK_FMOD((double) d1, (double) d2); -} - -/* Shared helper for Math.pow() and exponentiation operator. */ -DUK_INTERNAL double duk_js_arith_pow(double x, double y) { - /* The ANSI C pow() semantics differ from ECMAScript. - * - * E.g. when x==1 and y is +/- infinite, the ECMAScript required - * result is NaN, while at least Linux pow() returns 1. - */ - - duk_small_int_t cx, cy, sx; - - DUK_UNREF(cx); - DUK_UNREF(sx); - cy = (duk_small_int_t) DUK_FPCLASSIFY(y); - - if (cy == DUK_FP_NAN) { - goto ret_nan; - } - if (duk_double_equals(DUK_FABS(x), 1.0) && cy == DUK_FP_INFINITE) { - goto ret_nan; - } - -#if defined(DUK_USE_POW_WORKAROUNDS) - /* Specific fixes to common pow() implementation issues: - * - test-bug-netbsd-math-pow.js: NetBSD 6.0 on x86 (at least) - * - test-bug-mingw-math-issues.js - */ - cx = (duk_small_int_t) DUK_FPCLASSIFY(x); - if (cx == DUK_FP_ZERO && y < 0.0) { - sx = (duk_small_int_t) DUK_SIGNBIT(x); - if (sx == 0) { - /* Math.pow(+0,y) should be Infinity when y<0. NetBSD pow() - * returns -Infinity instead when y is <0 and finite. The - * if-clause also catches y == -Infinity (which works even - * without the fix). - */ - return DUK_DOUBLE_INFINITY; - } else { - /* Math.pow(-0,y) where y<0 should be: - * - -Infinity if y<0 and an odd integer - * - Infinity if y<0 but not an odd integer - * NetBSD pow() returns -Infinity for all finite y<0. The - * if-clause also catches y == -Infinity (which works even - * without the fix). - */ - - /* fmod() return value has same sign as input (negative) so - * the result here will be in the range ]-2,0], -1 indicates - * odd. If x is -Infinity, NaN is returned and the odd check - * always concludes "not odd" which results in desired outcome. - */ - double tmp = DUK_FMOD(y, 2); - if (tmp == -1.0) { - return -DUK_DOUBLE_INFINITY; - } else { - /* Not odd, or y == -Infinity */ - return DUK_DOUBLE_INFINITY; - } - } - } else if (cx == DUK_FP_NAN) { - if (duk_double_equals(y, 0.0)) { - /* NaN ** +/- 0 should always be 1, but is NaN on - * at least some Cygwin/MinGW versions. - */ - return 1.0; - } - } -#else - /* Some ISO C assumptions. */ - DUK_ASSERT(duk_double_equals(DUK_POW(DUK_DOUBLE_NAN, 0.0), 1.0)); - DUK_ASSERT(DUK_ISINF(DUK_POW(0.0, -1.0)) && DUK_SIGNBIT(DUK_POW(0.0, -1.0)) == 0); - DUK_ASSERT(DUK_ISINF(DUK_POW(-0.0, -2.0)) && DUK_SIGNBIT(DUK_POW(-0.0, -2.0)) == 0); - DUK_ASSERT(DUK_ISINF(DUK_POW(-0.0, -3.0)) && DUK_SIGNBIT(DUK_POW(-0.0, -3.0)) != 0); -#endif - - return DUK_POW(x, y); - -ret_nan: - return DUK_DOUBLE_NAN; -} -/* - * Call handling. - * - * duk_handle_call_unprotected(): - * - * - Unprotected call to ECMAScript or Duktape/C function, from native - * code or bytecode executor. - * - * - Also handles Ecma-to-Ecma calls which reuses a currently running - * executor instance to avoid native recursion. Call setup is done - * normally, but just before calling the bytecode executor a special - * return code is used to indicate that a calling executor is reused. - * - * - Also handles tailcalls, i.e. reuse of current duk_activation. - * - * - Also handles setup for initial Duktape.Thread.resume(). - * - * duk_handle_safe_call(): - * - * - Protected C call within current activation. - * - * setjmp() and local variables have a nasty interaction, see execution.rst; - * non-volatile locals modified after setjmp() call are not guaranteed to - * keep their value and can cause compiler or compiler version specific - * difficult to replicate issues. - * - * See 'execution.rst'. - */ - -/* #include duk_internal.h -> already included */ - -/* XXX: heap->error_not_allowed for success path too? */ - -/* - * Limit check helpers. - */ - -/* Check native stack space if DUK_USE_NATIVE_STACK_CHECK() defined. */ -DUK_INTERNAL void duk_native_stack_check(duk_hthread *thr) { -#if defined(DUK_USE_NATIVE_STACK_CHECK) - if (DUK_USE_NATIVE_STACK_CHECK() != 0) { - DUK_ERROR_RANGE(thr, DUK_STR_NATIVE_STACK_LIMIT); - } -#else - DUK_UNREF(thr); -#endif -} - -/* Allow headroom for calls during error augmentation (see GH-191). - * We allow space for 10 additional recursions, with one extra - * for, e.g. a print() call at the deepest level, and an extra - * +1 for protected call wrapping. - */ -#define DUK__AUGMENT_CALL_RELAX_COUNT (10 + 2) - -/* Stack space required by call handling entry. */ -#define DUK__CALL_HANDLING_REQUIRE_STACK 8 - -DUK_LOCAL DUK_NOINLINE void duk__call_c_recursion_limit_check_slowpath(duk_hthread *thr) { - /* When augmenting an error, the effective limit is a bit higher. - * Check for it only if the fast path check fails. - */ -#if defined(DUK_USE_AUGMENT_ERROR_THROW) || defined(DUK_USE_AUGMENT_ERROR_CREATE) - if (thr->heap->augmenting_error) { - if (thr->heap->call_recursion_depth < thr->heap->call_recursion_limit + DUK__AUGMENT_CALL_RELAX_COUNT) { - DUK_D(DUK_DPRINT("C recursion limit reached but augmenting error and within relaxed limit")); - return; - } - } -#endif - - DUK_D(DUK_DPRINT("call prevented because C recursion limit reached")); - DUK_ERROR_RANGE(thr, DUK_STR_NATIVE_STACK_LIMIT); - DUK_WO_NORETURN(return;); -} - -DUK_LOCAL DUK_ALWAYS_INLINE void duk__call_c_recursion_limit_check(duk_hthread *thr) { - DUK_ASSERT(thr->heap->call_recursion_depth >= 0); - DUK_ASSERT(thr->heap->call_recursion_depth <= thr->heap->call_recursion_limit); - - duk_native_stack_check(thr); - - /* This check is forcibly inlined because it's very cheap and almost - * always passes. The slow path is forcibly noinline. - */ - if (DUK_LIKELY(thr->heap->call_recursion_depth < thr->heap->call_recursion_limit)) { - return; - } - - duk__call_c_recursion_limit_check_slowpath(thr); -} - -DUK_LOCAL DUK_NOINLINE void duk__call_callstack_limit_check_slowpath(duk_hthread *thr) { - /* When augmenting an error, the effective limit is a bit higher. - * Check for it only if the fast path check fails. - */ -#if defined(DUK_USE_AUGMENT_ERROR_THROW) || defined(DUK_USE_AUGMENT_ERROR_CREATE) - if (thr->heap->augmenting_error) { - if (thr->callstack_top < DUK_USE_CALLSTACK_LIMIT + DUK__AUGMENT_CALL_RELAX_COUNT) { - DUK_D(DUK_DPRINT("call stack limit reached but augmenting error and within relaxed limit")); - return; - } - } -#endif - - /* XXX: error message is a bit misleading: we reached a recursion - * limit which is also essentially the same as a C callstack limit - * (except perhaps with some relaxed threading assumptions). - */ - DUK_D(DUK_DPRINT("call prevented because call stack limit reached")); - DUK_ERROR_RANGE(thr, DUK_STR_CALLSTACK_LIMIT); - DUK_WO_NORETURN(return;); -} - -DUK_LOCAL DUK_ALWAYS_INLINE void duk__call_callstack_limit_check(duk_hthread *thr) { - /* This check is forcibly inlined because it's very cheap and almost - * always passes. The slow path is forcibly noinline. - */ - if (DUK_LIKELY(thr->callstack_top < DUK_USE_CALLSTACK_LIMIT)) { - return; - } - - duk__call_callstack_limit_check_slowpath(thr); -} - -/* - * Interrupt counter fixup (for development only). - */ - -#if defined(DUK_USE_INTERRUPT_COUNTER) && defined(DUK_USE_DEBUG) -DUK_LOCAL void duk__interrupt_fixup(duk_hthread *thr, duk_hthread *entry_curr_thread) { - /* Currently the bytecode executor and executor interrupt - * instruction counts are off because we don't execute the - * interrupt handler when we're about to exit from the initial - * user call into Duktape. - * - * If we were to execute the interrupt handler here, the counts - * would match. You can enable this block manually to check - * that this is the case. - */ - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - -#if defined(DUK_USE_INTERRUPT_DEBUG_FIXUP) - if (entry_curr_thread == NULL) { - thr->interrupt_init = thr->interrupt_init - thr->interrupt_counter; - thr->heap->inst_count_interrupt += thr->interrupt_init; - DUK_DD(DUK_DDPRINT("debug test: updated interrupt count on exit to " - "user code, instruction counts: executor=%ld, interrupt=%ld", - (long) thr->heap->inst_count_exec, - (long) thr->heap->inst_count_interrupt)); - DUK_ASSERT(thr->heap->inst_count_exec == thr->heap->inst_count_interrupt); - } -#else - DUK_UNREF(thr); - DUK_UNREF(entry_curr_thread); -#endif -} -#endif - -/* - * Arguments object creation. - * - * Creating arguments objects involves many small details, see E5 Section - * 10.6 for the specific requirements. Much of the arguments object exotic - * behavior is implemented in duk_hobject_props.c, and is enabled by the - * object flag DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS. - */ - -DUK_LOCAL void duk__create_arguments_object(duk_hthread *thr, duk_hobject *func, duk_hobject *varenv, duk_idx_t idx_args) { - duk_hobject *arg; /* 'arguments' */ - duk_hobject *formals; /* formals for 'func' (may be NULL if func is a C function) */ - duk_idx_t i_arg; - duk_idx_t i_map; - duk_idx_t i_mappednames; - duk_idx_t i_formals; - duk_idx_t i_argbase; - duk_idx_t n_formals; - duk_idx_t idx; - duk_idx_t num_stack_args; - duk_bool_t need_map; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(func != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_NONBOUND_FUNCTION(func)); - DUK_ASSERT(varenv != NULL); - - /* [ ... func this arg1(@idx_args) ... argN envobj ] - * [ arg1(@idx_args) ... argN envobj ] (for tailcalls) - */ - - need_map = 0; - - i_argbase = idx_args; - num_stack_args = duk_get_top(thr) - i_argbase - 1; - DUK_ASSERT(i_argbase >= 0); - DUK_ASSERT(num_stack_args >= 0); - - formals = (duk_hobject *) duk_hobject_get_formals(thr, (duk_hobject *) func); - if (formals) { - n_formals = (duk_idx_t) ((duk_harray *) formals)->length; - duk_push_hobject(thr, formals); - } else { - /* This shouldn't happen without tampering of internal - * properties: if a function accesses 'arguments', _Formals - * is kept. Check for the case anyway in case internal - * properties have been modified manually. - */ - DUK_D(DUK_DPRINT("_Formals is undefined when creating arguments, use n_formals == 0")); - n_formals = 0; - duk_push_undefined(thr); - } - i_formals = duk_require_top_index(thr); - - DUK_ASSERT(n_formals >= 0); - DUK_ASSERT(formals != NULL || n_formals == 0); - - DUK_DDD( - DUK_DDDPRINT("func=%!O, formals=%!O, n_formals=%ld", (duk_heaphdr *) func, (duk_heaphdr *) formals, (long) n_formals)); - - /* [ ... formals ] */ - - /* - * Create required objects: - * - 'arguments' object: array-like, but not an array - * - 'map' object: internal object, tied to 'arguments' (bare) - * - 'mappedNames' object: temporary value used during construction (bare) - */ - - arg = duk_push_object_helper(thr, - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_FLAG_ARRAY_PART | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARGUMENTS), - DUK_BIDX_OBJECT_PROTOTYPE); - DUK_ASSERT(arg != NULL); - (void) duk_push_object_helper(thr, - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_FASTREFS | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT), - -1); /* no prototype */ - (void) duk_push_object_helper(thr, - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_FASTREFS | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT), - -1); /* no prototype */ - i_arg = duk_get_top(thr) - 3; - i_map = i_arg + 1; - i_mappednames = i_arg + 2; - DUK_ASSERT(!duk_is_bare_object(thr, -3)); /* arguments */ - DUK_ASSERT(duk_is_bare_object(thr, -2)); /* map */ - DUK_ASSERT(duk_is_bare_object(thr, -1)); /* mappedNames */ - - /* [ ... formals arguments map mappedNames ] */ - - DUK_DDD(DUK_DDDPRINT("created arguments related objects: " - "arguments at index %ld -> %!O " - "map at index %ld -> %!O " - "mappednames at index %ld -> %!O", - (long) i_arg, - (duk_heaphdr *) duk_get_hobject(thr, i_arg), - (long) i_map, - (duk_heaphdr *) duk_get_hobject(thr, i_map), - (long) i_mappednames, - (duk_heaphdr *) duk_get_hobject(thr, i_mappednames))); - - /* - * Init arguments properties, map, etc. - */ - - duk_push_int(thr, num_stack_args); - duk_xdef_prop_stridx(thr, i_arg, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_WC); - - /* - * Init argument related properties. - */ - - /* step 11 */ - idx = num_stack_args - 1; - while (idx >= 0) { - DUK_DDD( - DUK_DDDPRINT("arg idx %ld, argbase=%ld, argidx=%ld", (long) idx, (long) i_argbase, (long) (i_argbase + idx))); - - DUK_DDD(DUK_DDDPRINT("define arguments[%ld]=arg", (long) idx)); - duk_dup(thr, i_argbase + idx); - duk_xdef_prop_index_wec(thr, i_arg, (duk_uarridx_t) idx); - DUK_DDD(DUK_DDDPRINT("defined arguments[%ld]=arg", (long) idx)); - - /* step 11.c is relevant only if non-strict (checked in 11.c.ii) */ - if (!DUK_HOBJECT_HAS_STRICT(func) && idx < n_formals) { - DUK_ASSERT(formals != NULL); - - DUK_DDD(DUK_DDDPRINT("strict function, index within formals (%ld < %ld)", (long) idx, (long) n_formals)); - - duk_get_prop_index(thr, i_formals, (duk_uarridx_t) idx); - DUK_ASSERT(duk_is_string(thr, -1)); - - duk_dup_top(thr); /* [ ... name name ] */ - - if (!duk_has_prop(thr, i_mappednames)) { - /* steps 11.c.ii.1 - 11.c.ii.4, but our internal book-keeping - * differs from the reference model - */ - - /* [ ... name ] */ - - need_map = 1; - - DUK_DDD( - DUK_DDDPRINT("set mappednames[%s]=%ld", (const char *) duk_get_string(thr, -1), (long) idx)); - duk_dup_top(thr); /* name */ - (void) duk_push_uint_to_hstring(thr, (duk_uint_t) idx); /* index */ - duk_xdef_prop_wec(thr, i_mappednames); /* out of spec, must be configurable */ - - DUK_DDD(DUK_DDDPRINT("set map[%ld]=%s", (long) idx, duk_get_string(thr, -1))); - duk_dup_top(thr); /* name */ - duk_xdef_prop_index_wec(thr, i_map, (duk_uarridx_t) idx); /* out of spec, must be configurable */ - } else { - /* duk_has_prop() popped the second 'name' */ - } - - /* [ ... name ] */ - duk_pop(thr); /* pop 'name' */ - } - - idx--; - } - - DUK_DDD(DUK_DDDPRINT("actual arguments processed")); - - /* step 12 */ - if (need_map) { - DUK_DDD(DUK_DDDPRINT("adding 'map' and 'varenv' to arguments object")); - - /* should never happen for a strict callee */ - DUK_ASSERT(!DUK_HOBJECT_HAS_STRICT(func)); - - duk_dup(thr, i_map); - duk_xdef_prop_stridx(thr, i_arg, DUK_STRIDX_INT_MAP, DUK_PROPDESC_FLAGS_NONE); /* out of spec, don't care */ - - /* The variable environment for magic variable bindings needs to be - * given by the caller and recorded in the arguments object. - * - * See E5 Section 10.6, the creation of setters/getters. - * - * The variable environment also provides access to the callee, so - * an explicit (internal) callee property is not needed. - */ - - duk_push_hobject(thr, varenv); - duk_xdef_prop_stridx(thr, i_arg, DUK_STRIDX_INT_VARENV, DUK_PROPDESC_FLAGS_NONE); /* out of spec, don't care */ - } - - /* steps 13-14 */ - if (DUK_HOBJECT_HAS_STRICT(func)) { - /* Callee/caller are throwers and are not deletable etc. They - * could be implemented as virtual properties, but currently - * there is no support for virtual properties which are accessors - * (only plain virtual properties). This would not be difficult - * to change in duk_hobject_props, but we can make the throwers - * normal, concrete properties just as easily. - * - * Note that the specification requires that the *same* thrower - * built-in object is used here! See E5 Section 10.6 main - * algoritm, step 14, and Section 13.2.3 which describes the - * thrower. See test case test-arguments-throwers.js. - */ - - DUK_DDD(DUK_DDDPRINT("strict function, setting caller/callee to throwers")); - - /* In ES2017 .caller is no longer set at all. */ - duk_xdef_prop_stridx_thrower(thr, i_arg, DUK_STRIDX_CALLEE); - } else { - DUK_DDD(DUK_DDDPRINT("non-strict function, setting callee to actual value")); - duk_push_hobject(thr, func); - duk_xdef_prop_stridx(thr, i_arg, DUK_STRIDX_CALLEE, DUK_PROPDESC_FLAGS_WC); - } - - /* set exotic behavior only after we're done */ - if (need_map) { - /* Exotic behaviors are only enabled for arguments objects - * which have a parameter map (see E5 Section 10.6 main - * algorithm, step 12). - * - * In particular, a non-strict arguments object with no - * mapped formals does *NOT* get exotic behavior, even - * for e.g. "caller" property. This seems counterintuitive - * but seems to be the case. - */ - - /* cannot be strict (never mapped variables) */ - DUK_ASSERT(!DUK_HOBJECT_HAS_STRICT(func)); - - DUK_DDD(DUK_DDDPRINT("enabling exotic behavior for arguments object")); - DUK_HOBJECT_SET_EXOTIC_ARGUMENTS(arg); - } else { - DUK_DDD(DUK_DDDPRINT("not enabling exotic behavior for arguments object")); - } - - DUK_DDD(DUK_DDDPRINT("final arguments related objects: " - "arguments at index %ld -> %!O " - "map at index %ld -> %!O " - "mappednames at index %ld -> %!O", - (long) i_arg, - (duk_heaphdr *) duk_get_hobject(thr, i_arg), - (long) i_map, - (duk_heaphdr *) duk_get_hobject(thr, i_map), - (long) i_mappednames, - (duk_heaphdr *) duk_get_hobject(thr, i_mappednames))); - - /* [ args(n) envobj formals arguments map mappednames ] */ - - duk_pop_2(thr); - duk_remove_m2(thr); - - /* [ args(n) envobj arguments ] */ -} - -/* Helper for creating the arguments object and adding it to the env record - * on top of the value stack. - */ -DUK_LOCAL void duk__handle_createargs_for_call(duk_hthread *thr, duk_hobject *func, duk_hobject *env, duk_idx_t idx_args) { - DUK_DDD(DUK_DDDPRINT("creating arguments object for function call")); - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(func != NULL); - DUK_ASSERT(env != NULL); - DUK_ASSERT(DUK_HOBJECT_HAS_CREATEARGS(func)); - - /* [ ... arg1 ... argN envobj ] */ - - duk__create_arguments_object(thr, func, env, idx_args); - - /* [ ... arg1 ... argN envobj argobj ] */ - - duk_xdef_prop_stridx_short(thr, - -2, - DUK_STRIDX_LC_ARGUMENTS, - DUK_HOBJECT_HAS_STRICT(func) ? DUK_PROPDESC_FLAGS_E : /* strict: non-deletable, non-writable */ - DUK_PROPDESC_FLAGS_WE); /* non-strict: non-deletable, writable */ - /* [ ... arg1 ... argN envobj ] */ -} - -/* - * Helpers for constructor call handling. - * - * There are two [[Construct]] operations in the specification: - * - * - E5 Section 13.2.2: for Function objects - * - E5 Section 15.3.4.5.2: for "bound" Function objects - * - * The chain of bound functions is resolved in Section 15.3.4.5.2, - * with arguments "piling up" until the [[Construct]] internal - * method is called on the final, actual Function object. Note - * that the "prototype" property is looked up *only* from the - * final object, *before* calling the constructor. - * - * Since Duktape 2.2 bound functions are represented with the - * duk_hboundfunc internal type, and bound function chains are - * collapsed when a bound function is created. As a result, the - * direct target of a duk_hboundfunc is always non-bound and the - * this/argument lists have been resolved. - * - * When constructing new Array instances, an unnecessary object is - * created and discarded now: the standard [[Construct]] creates an - * object, and calls the Array constructor. The Array constructor - * returns an Array instance, which is used as the result value for - * the "new" operation; the object created before the Array constructor - * call is discarded. - * - * This would be easy to fix, e.g. by knowing that the Array constructor - * will always create a replacement object and skip creating the fallback - * object in that case. - */ - -/* Update default instance prototype for constructor call. */ -DUK_LOCAL void duk__update_default_instance_proto(duk_hthread *thr, duk_idx_t idx_func) { - duk_hobject *proto; - duk_hobject *fallback; - - DUK_ASSERT(duk_is_constructable(thr, idx_func)); - - duk_get_prop_stridx_short(thr, idx_func, DUK_STRIDX_PROTOTYPE); - proto = duk_get_hobject(thr, -1); - if (proto == NULL) { - DUK_DDD(DUK_DDDPRINT("constructor has no 'prototype' property, or value not an object " - "-> leave standard Object prototype as fallback prototype")); - } else { - DUK_DDD(DUK_DDDPRINT("constructor has 'prototype' property with object value " - "-> set fallback prototype to that value: %!iO", - (duk_heaphdr *) proto)); - /* Original fallback (default instance) is untouched when - * resolving bound functions etc. - */ - fallback = duk_known_hobject(thr, idx_func + 1); - DUK_ASSERT(fallback != NULL); - DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, fallback, proto); - } - duk_pop(thr); -} - -/* Postprocess: return value special handling, error augmentation. */ -DUK_INTERNAL void duk_call_construct_postprocess(duk_hthread *thr, duk_small_uint_t proxy_invariant) { - /* Use either fallback (default instance) or retval depending - * on retval type. Needs to be called before unwind because - * the default instance is read from the current (immutable) - * 'this' binding. - * - * For Proxy 'construct' calls the return value must be an - * Object (we accept object-like values like buffers and - * lightfuncs too). If not, TypeError. - */ - if (duk_check_type_mask(thr, -1, DUK_TYPE_MASK_OBJECT | DUK_TYPE_MASK_BUFFER | DUK_TYPE_MASK_LIGHTFUNC)) { - DUK_DDD(DUK_DDDPRINT("replacement value")); - } else { - if (DUK_UNLIKELY(proxy_invariant != 0U)) { - /* Proxy 'construct' return value invariant violated. */ - DUK_ERROR_TYPE_INVALID_TRAP_RESULT(thr); - DUK_WO_NORETURN(return;); - } - /* XXX: direct value stack access */ - duk_pop(thr); - duk_push_this(thr); - } - -#if defined(DUK_USE_AUGMENT_ERROR_CREATE) - /* Augment created errors upon creation, not when they are thrown or - * rethrown. __FILE__ and __LINE__ are not desirable here; the call - * stack reflects the caller which is correct. Skip topmost, unwound - * activation when creating a traceback. If thr->ptr_curr_pc was != - * NULL we'd need to sync the current PC so that the traceback comes - * out right; however it is always synced here so just assert for it. - */ - DUK_ASSERT(thr->ptr_curr_pc == NULL); - duk_err_augment_error_create(thr, thr, NULL, 0, DUK_AUGMENT_FLAG_NOBLAME_FILELINE | DUK_AUGMENT_FLAG_SKIP_ONE); -#endif -} - -/* - * Helper for handling a bound function when a call is being made. - * - * Assumes that bound function chains have been "collapsed" so that either - * the target is non-bound or there is one bound function that points to a - * nonbound target. - * - * Prepends the bound arguments to the value stack (at idx_func + 2). - * The 'this' binding is also updated if necessary (at idx_func + 1). - * Note that for constructor calls the 'this' binding is never updated by - * [[BoundThis]]. - */ - -DUK_LOCAL void duk__handle_bound_chain_for_call(duk_hthread *thr, duk_idx_t idx_func, duk_bool_t is_constructor_call) { - duk_tval *tv_func; - duk_hobject *func; - duk_idx_t len; - - DUK_ASSERT(thr != NULL); - - /* On entry, item at idx_func is a bound, non-lightweight function, - * but we don't rely on that below. - */ - - DUK_ASSERT(duk_get_top(thr) >= idx_func + 2); - - tv_func = duk_require_tval(thr, idx_func); - DUK_ASSERT(tv_func != NULL); - - if (DUK_TVAL_IS_OBJECT(tv_func)) { - func = DUK_TVAL_GET_OBJECT(tv_func); - - /* XXX: separate helper function, out of fast path? */ - if (DUK_HOBJECT_HAS_BOUNDFUNC(func)) { - duk_hboundfunc *h_bound; - duk_tval *tv_args; - duk_tval *tv_gap; - - h_bound = (duk_hboundfunc *) (void *) func; - tv_args = h_bound->args; - len = h_bound->nargs; - DUK_ASSERT(len == 0 || tv_args != NULL); - - DUK_DDD(DUK_DDDPRINT("bound function encountered, ptr=%p: %!T", - (void *) DUK_TVAL_GET_OBJECT(tv_func), - tv_func)); - - /* [ ... func this arg1 ... argN ] */ - - if (is_constructor_call) { - /* See: tests/ecmascript/test-spec-bound-constructor.js */ - DUK_DDD(DUK_DDDPRINT("constructor call: don't update this binding")); - } else { - /* XXX: duk_replace_tval */ - duk_push_tval(thr, &h_bound->this_binding); - duk_replace(thr, idx_func + 1); /* idx_this = idx_func + 1 */ - } - - /* [ ... func this arg1 ... argN ] */ - - duk_require_stack(thr, len); - - tv_gap = duk_reserve_gap(thr, idx_func + 2, len); - duk_copy_tvals_incref(thr, tv_gap, tv_args, (duk_size_t) len); - - /* [ ... func this arg1 ... argN ] */ - - duk_push_tval(thr, &h_bound->target); - duk_replace(thr, idx_func); /* replace in stack */ - - DUK_DDD(DUK_DDDPRINT("bound function handled, idx_func=%ld, curr func=%!T", - (long) idx_func, - duk_get_tval(thr, idx_func))); - } - } else if (DUK_TVAL_IS_LIGHTFUNC(tv_func)) { - /* Lightweight function: never bound, so terminate. */ - ; - } else { - /* Shouldn't happen, so ugly error is enough. */ - DUK_ERROR_INTERNAL(thr); - DUK_WO_NORETURN(return;); - } - - DUK_ASSERT(duk_get_top(thr) >= idx_func + 2); - - DUK_DDD(DUK_DDDPRINT("final non-bound function is: %!T", duk_get_tval(thr, idx_func))); - -#if defined(DUK_USE_ASSERTIONS) - tv_func = duk_require_tval(thr, idx_func); - DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv_func) || DUK_TVAL_IS_OBJECT(tv_func)); - if (DUK_TVAL_IS_OBJECT(tv_func)) { - func = DUK_TVAL_GET_OBJECT(tv_func); - DUK_ASSERT(func != NULL); - DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func)); - DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(func) || DUK_HOBJECT_HAS_NATFUNC(func) || DUK_HOBJECT_IS_PROXY(func)); - } -#endif -} - -/* - * Helper for inline handling of .call(), .apply(), and .construct(). - */ - -DUK_LOCAL duk_bool_t duk__handle_specialfuncs_for_call(duk_hthread *thr, - duk_idx_t idx_func, - duk_hobject *func, - duk_small_uint_t *call_flags, - duk_bool_t first) { -#if defined(DUK_USE_ASSERTIONS) - duk_c_function natfunc; -#endif - duk_tval *tv_args; - - DUK_ASSERT(func != NULL); - DUK_ASSERT((*call_flags & DUK_CALL_FLAG_CONSTRUCT) == 0); /* Caller. */ - -#if defined(DUK_USE_ASSERTIONS) - natfunc = ((duk_hnatfunc *) func)->func; - DUK_ASSERT(natfunc != NULL); -#endif - - /* On every round of function resolution at least target function and - * 'this' binding are set. We can assume that here, and must guarantee - * it on exit. Value stack reserve is extended for bound function and - * .apply() unpacking so we don't need to extend it here when we need a - * few slots. - */ - DUK_ASSERT(duk_get_top(thr) >= idx_func + 2); - - /* Handle native 'eval' specially. A direct eval check is only made - * for the first resolution attempt; e.g. a bound eval call is -not- - * a direct eval call. - */ - if (DUK_UNLIKELY(((duk_hnatfunc *) func)->magic == 15)) { - /* For now no special handling except for direct eval - * detection. - */ - DUK_ASSERT(((duk_hnatfunc *) func)->func == duk_bi_global_object_eval); - if (first && (*call_flags & DUK_CALL_FLAG_CALLED_AS_EVAL)) { - *call_flags = (*call_flags & ~DUK_CALL_FLAG_CALLED_AS_EVAL) | DUK_CALL_FLAG_DIRECT_EVAL; - } - DUK_ASSERT(duk_get_top(thr) >= idx_func + 2); - return 1; /* stop resolving */ - } - - /* Handle special functions based on the DUK_HOBJECT_FLAG_SPECIAL_CALL - * flag; their magic value is used for switch-case. - * - * NOTE: duk_unpack_array_like() reserves value stack space - * for the result values (unlike most other value stack calls). - */ - switch (((duk_hnatfunc *) func)->magic) { - case 0: { /* 0=Function.prototype.call() */ - /* Value stack: - * idx_func + 0: Function.prototype.call() [removed] - * idx_func + 1: this binding for .call (target function) - * idx_func + 2: 1st argument to .call, desired 'this' binding - * idx_func + 3: 2nd argument to .call, desired 1st argument for ultimate target - * ... - * - * Remove idx_func + 0 to get: - * idx_func + 0: target function - * idx_func + 1: this binding - * idx_func + 2: call arguments - * ... - */ - DUK_ASSERT(natfunc == duk_bi_function_prototype_call); - duk_remove_unsafe(thr, idx_func); - tv_args = thr->valstack_bottom + idx_func + 2; - if (thr->valstack_top < tv_args) { - DUK_ASSERT(tv_args <= thr->valstack_end); - thr->valstack_top = tv_args; /* at least target function and 'this' binding present */ - } - break; - } - case 1: { /* 1=Function.prototype.apply() */ - /* Value stack: - * idx_func + 0: Function.prototype.apply() [removed] - * idx_func + 1: this binding for .apply (target function) - * idx_func + 2: 1st argument to .apply, desired 'this' binding - * idx_func + 3: 2nd argument to .apply, argArray - * [anything after this MUST be ignored] - * - * Remove idx_func + 0 and unpack the argArray to get: - * idx_func + 0: target function - * idx_func + 1: this binding - * idx_func + 2: call arguments - * ... - */ - DUK_ASSERT(natfunc == duk_bi_function_prototype_apply); - duk_remove_unsafe(thr, idx_func); - goto apply_shared; - } -#if defined(DUK_USE_REFLECT_BUILTIN) - case 2: { /* 2=Reflect.apply() */ - /* Value stack: - * idx_func + 0: Reflect.apply() [removed] - * idx_func + 1: this binding for .apply (ignored, usually Reflect) [removed] - * idx_func + 2: 1st argument to .apply, target function - * idx_func + 3: 2nd argument to .apply, desired 'this' binding - * idx_func + 4: 3rd argument to .apply, argArray - * [anything after this MUST be ignored] - * - * Remove idx_func + 0 and idx_func + 1, and unpack the argArray to get: - * idx_func + 0: target function - * idx_func + 1: this binding - * idx_func + 2: call arguments - * ... - */ - DUK_ASSERT(natfunc == duk_bi_reflect_apply); - duk_remove_n_unsafe(thr, idx_func, 2); - goto apply_shared; - } - case 3: { /* 3=Reflect.construct() */ - /* Value stack: - * idx_func + 0: Reflect.construct() [removed] - * idx_func + 1: this binding for .construct (ignored, usually Reflect) [removed] - * idx_func + 2: 1st argument to .construct, target function - * idx_func + 3: 2nd argument to .construct, argArray - * idx_func + 4: 3rd argument to .construct, newTarget - * [anything after this MUST be ignored] - * - * Remove idx_func + 0 and idx_func + 1, unpack the argArray, - * and insert default instance (prototype not yet updated), to get: - * idx_func + 0: target function - * idx_func + 1: this binding (default instance) - * idx_func + 2: constructor call arguments - * ... - * - * Call flags must be updated to reflect the fact that we're - * now dealing with a constructor call, and e.g. the 'this' - * binding cannot be overwritten if the target is bound. - * - * newTarget is checked but not yet passed onwards. - */ - - duk_idx_t top; - - DUK_ASSERT(natfunc == duk_bi_reflect_construct); - *call_flags |= DUK_CALL_FLAG_CONSTRUCT; - duk_remove_n_unsafe(thr, idx_func, 2); - top = duk_get_top(thr); - if (!duk_is_constructable(thr, idx_func)) { - /* Target constructability must be checked before - * unpacking argArray (which may cause side effects). - * Just return; caller will throw the error. - */ - duk_set_top_unsafe(thr, idx_func + 2); /* satisfy asserts */ - break; - } - duk_push_object(thr); - duk_insert(thr, idx_func + 1); /* default instance */ - - /* [ ... func default_instance argArray newTarget? ] */ - - top = duk_get_top(thr); - if (top < idx_func + 3) { - /* argArray is a mandatory argument for Reflect.construct(). */ - DUK_ERROR_TYPE_INVALID_ARGS(thr); - DUK_WO_NORETURN(return 0;); - } - if (top > idx_func + 3) { - if (!duk_strict_equals(thr, idx_func, idx_func + 3)) { - /* XXX: [[Construct]] newTarget currently unsupported */ - DUK_ERROR_UNSUPPORTED(thr); - DUK_WO_NORETURN(return 0;); - } - duk_set_top_unsafe(thr, idx_func + 3); /* remove any args beyond argArray */ - } - DUK_ASSERT(duk_get_top(thr) == idx_func + 3); - DUK_ASSERT(duk_is_valid_index(thr, idx_func + 2)); - (void) duk_unpack_array_like(thr, - idx_func + 2); /* XXX: should also remove target to be symmetric with duk_pack()? */ - duk_remove(thr, idx_func + 2); - DUK_ASSERT(duk_get_top(thr) >= idx_func + 2); - break; - } -#endif /* DUK_USE_REFLECT_BUILTIN */ - default: { - DUK_ASSERT(0); - DUK_UNREACHABLE(); - } - } - - DUK_ASSERT(duk_get_top(thr) >= idx_func + 2); - return 0; /* keep resolving */ - -apply_shared: - tv_args = thr->valstack_bottom + idx_func + 2; - if (thr->valstack_top <= tv_args) { - DUK_ASSERT(tv_args <= thr->valstack_end); - thr->valstack_top = tv_args; /* at least target func and 'this' binding present */ - /* No need to check for argArray. */ - } else { - DUK_ASSERT(duk_get_top(thr) >= idx_func + 3); /* idx_func + 2 covered above */ - if (thr->valstack_top > tv_args + 1) { - duk_set_top_unsafe(thr, idx_func + 3); /* remove any args beyond argArray */ - } - DUK_ASSERT(duk_is_valid_index(thr, idx_func + 2)); - if (!duk_is_callable(thr, idx_func)) { - /* Avoid unpack side effects if the target isn't callable. - * Calling code will throw the actual error. - */ - } else { - (void) duk_unpack_array_like(thr, idx_func + 2); - duk_remove(thr, idx_func + 2); - } - } - DUK_ASSERT(duk_get_top(thr) >= idx_func + 2); - return 0; /* keep resolving */ -} - -/* - * Helper for Proxy handling. - */ - -#if defined(DUK_USE_ES6_PROXY) -DUK_LOCAL void duk__handle_proxy_for_call(duk_hthread *thr, duk_idx_t idx_func, duk_hproxy *h_proxy, duk_small_uint_t *call_flags) { - duk_bool_t rc; - - /* Value stack: - * idx_func + 0: Proxy object - * idx_func + 1: this binding for call - * idx_func + 2: 1st argument for call - * idx_func + 3: 2nd argument for call - * ... - * - * If Proxy doesn't have a trap for the call ('apply' or 'construct'), - * replace Proxy object with target object. - * - * If we're dealing with a normal call and the Proxy has an 'apply' - * trap, manipulate value stack to: - * - * idx_func + 0: trap - * idx_func + 1: Proxy's handler - * idx_func + 2: Proxy's target - * idx_func + 3: this binding for call (from idx_func + 1) - * idx_func + 4: call arguments packed to an array - * - * If we're dealing with a constructor call and the Proxy has a - * 'construct' trap, manipulate value stack to: - * - * idx_func + 0: trap - * idx_func + 1: Proxy's handler - * idx_func + 2: Proxy's target - * idx_func + 3: call arguments packed to an array - * idx_func + 4: newTarget == Proxy object here - * - * As we don't yet have proper newTarget support, the newTarget at - * idx_func + 3 is just the original constructor being called, i.e. - * the Proxy object (not the target). Note that the default instance - * (original 'this' binding) is dropped and ignored. - */ - - duk_push_hobject(thr, h_proxy->handler); - rc = duk_get_prop_stridx_short(thr, -1, (*call_flags & DUK_CALL_FLAG_CONSTRUCT) ? DUK_STRIDX_CONSTRUCT : DUK_STRIDX_APPLY); - if (rc == 0) { - /* Not found, continue to target. If this is a construct - * call, update default instance prototype using the Proxy, - * not the target. - */ - if (*call_flags & DUK_CALL_FLAG_CONSTRUCT) { - if (!(*call_flags & DUK_CALL_FLAG_DEFAULT_INSTANCE_UPDATED)) { - *call_flags |= DUK_CALL_FLAG_DEFAULT_INSTANCE_UPDATED; - duk__update_default_instance_proto(thr, idx_func); - } - } - duk_pop_2(thr); - duk_push_hobject(thr, h_proxy->target); - duk_replace(thr, idx_func); - return; - } - - /* Here we must be careful not to replace idx_func while - * h_proxy is still needed, otherwise h_proxy may become - * dangling. This could be improved e.g. using a - * duk_pack_slice() with a freeform slice. - */ - - /* Here: - * idx_func + 0: Proxy object - * idx_func + 1: this binding for call - * idx_func + 2: 1st argument for call - * idx_func + 3: 2nd argument for call - * ... - * idx_func + N: handler - * idx_func + N + 1: trap - */ - - duk_insert(thr, idx_func + 1); - duk_insert(thr, idx_func + 2); - duk_push_hobject(thr, h_proxy->target); - duk_insert(thr, idx_func + 3); - duk_pack(thr, duk_get_top(thr) - (idx_func + 5)); - DUK_ASSERT(!duk_is_bare_object(thr, -1)); - - /* Here: - * idx_func + 0: Proxy object - * idx_func + 1: trap - * idx_func + 2: Proxy's handler - * idx_func + 3: Proxy's target - * idx_func + 4: this binding for call - * idx_func + 5: arguments array - */ - DUK_ASSERT(duk_get_top(thr) == idx_func + 6); - - if (*call_flags & DUK_CALL_FLAG_CONSTRUCT) { - *call_flags |= DUK_CALL_FLAG_CONSTRUCT_PROXY; /* Enable 'construct' trap return invariant check. */ - *call_flags &= ~(DUK_CALL_FLAG_CONSTRUCT); /* Resume as non-constructor call to the trap. */ - - /* 'apply' args: target, thisArg, argArray - * 'construct' args: target, argArray, newTarget - */ - duk_remove(thr, idx_func + 4); - duk_push_hobject(thr, (duk_hobject *) h_proxy); - } - - /* Finalize value stack layout by removing Proxy reference. */ - duk_remove(thr, idx_func); - h_proxy = NULL; /* invalidated */ - DUK_ASSERT(duk_get_top(thr) == idx_func + 5); -} -#endif /* DUK_USE_ES6_PROXY */ - -/* - * Helper for setting up var_env and lex_env of an activation, - * assuming it does NOT have the DUK_HOBJECT_FLAG_NEWENV flag. - */ - -DUK_LOCAL void duk__handle_oldenv_for_call(duk_hthread *thr, duk_hobject *func, duk_activation *act) { - duk_hcompfunc *f; - duk_hobject *h_lex; - duk_hobject *h_var; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(func != NULL); - DUK_ASSERT(act != NULL); - DUK_ASSERT(!DUK_HOBJECT_HAS_NEWENV(func)); - DUK_ASSERT(!DUK_HOBJECT_HAS_CREATEARGS(func)); - DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(func)); - DUK_UNREF(thr); - - f = (duk_hcompfunc *) func; - h_lex = DUK_HCOMPFUNC_GET_LEXENV(thr->heap, f); - h_var = DUK_HCOMPFUNC_GET_VARENV(thr->heap, f); - DUK_ASSERT(h_lex != NULL); /* Always true for closures (not for templates) */ - DUK_ASSERT(h_var != NULL); - act->lex_env = h_lex; - act->var_env = h_var; - DUK_HOBJECT_INCREF(thr, h_lex); - DUK_HOBJECT_INCREF(thr, h_var); -} - -/* - * Helper for updating callee 'caller' property. - */ - -#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY) -DUK_LOCAL void duk__update_func_caller_prop(duk_hthread *thr, duk_hobject *func) { - duk_tval *tv_caller; - duk_hobject *h_tmp; - duk_activation *act_callee; - duk_activation *act_caller; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(func != NULL); - DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func)); /* bound chain resolved */ - DUK_ASSERT(thr->callstack_top >= 1); - - if (DUK_HOBJECT_HAS_STRICT(func)) { - /* Strict functions don't get their 'caller' updated. */ - return; - } - - DUK_ASSERT(thr->callstack_top > 0); - act_callee = thr->callstack_curr; - DUK_ASSERT(act_callee != NULL); - act_caller = (thr->callstack_top >= 2 ? act_callee->parent : NULL); - - /* XXX: check .caller writability? */ - - /* Backup 'caller' property and update its value. */ - tv_caller = duk_hobject_find_entry_tval_ptr_stridx(thr->heap, func, DUK_STRIDX_CALLER); - if (tv_caller) { - /* If caller is global/eval code, 'caller' should be set to - * 'null'. - * - * XXX: there is no exotic flag to infer this correctly now. - * The NEWENV flag is used now which works as intended for - * everything (global code, non-strict eval code, and functions) - * except strict eval code. Bound functions are never an issue - * because 'func' has been resolved to a non-bound function. - */ - - if (act_caller != NULL) { - /* act_caller->func may be NULL in some finalization cases, - * just treat like we don't know the caller. - */ - if (act_caller->func && !DUK_HOBJECT_HAS_NEWENV(act_caller->func)) { - /* Setting to NULL causes 'caller' to be set to - * 'null' as desired. - */ - act_caller = NULL; - } - } - - if (DUK_TVAL_IS_OBJECT(tv_caller)) { - h_tmp = DUK_TVAL_GET_OBJECT(tv_caller); - DUK_ASSERT(h_tmp != NULL); - act_callee->prev_caller = h_tmp; - - /* Previous value doesn't need refcount changes because its ownership - * is transferred to prev_caller. - */ - - if (act_caller != NULL) { - DUK_ASSERT(act_caller->func != NULL); - DUK_TVAL_SET_OBJECT(tv_caller, act_caller->func); - DUK_TVAL_INCREF(thr, tv_caller); - } else { - DUK_TVAL_SET_NULL(tv_caller); /* no incref */ - } - } else { - /* 'caller' must only take on 'null' or function value */ - DUK_ASSERT(!DUK_TVAL_IS_HEAP_ALLOCATED(tv_caller)); - DUK_ASSERT(act_callee->prev_caller == NULL); - if (act_caller != NULL && act_caller->func) { - /* Tolerate act_caller->func == NULL which happens in - * some finalization cases; treat like unknown caller. - */ - DUK_TVAL_SET_OBJECT(tv_caller, act_caller->func); - DUK_TVAL_INCREF(thr, tv_caller); - } else { - DUK_TVAL_SET_NULL(tv_caller); /* no incref */ - } - } - } -} -#endif /* DUK_USE_NONSTD_FUNC_CALLER_PROPERTY */ - -/* - * Shared helpers for resolving the final, non-bound target function of the - * call and the effective 'this' binding. Resolves bound functions and - * applies .call(), .apply(), and .construct() inline. - * - * Proxy traps are also handled inline so that if the target is a Proxy with - * a 'call' or 'construct' trap, the trap handler is called with a modified - * argument list. - * - * Once the bound function / .call() / .apply() / .construct() sequence has - * been resolved, the value at idx_func + 1 may need coercion described in - * E5 Section 10.4.3. - * - * A call that begins as a non-constructor call may be converted into a - * constructor call during the resolution process if Reflect.construct() - * is invoked. This is handled by updating the caller's call_flags. - * - * For global and eval code (E5 Sections 10.4.1 and 10.4.2), we assume - * that the caller has provided the correct 'this' binding explicitly - * when calling, i.e.: - * - * - global code: this=global object - * - direct eval: this=copy from eval() caller's this binding - * - other eval: this=global object - * - * The 'this' coercion may cause a recursive function call with arbitrary - * side effects, because ToObject() may be called. - */ - -DUK_LOCAL DUK_INLINE void duk__coerce_nonstrict_this_binding(duk_hthread *thr, duk_idx_t idx_this) { - duk_tval *tv_this; - duk_hobject *obj_global; - - tv_this = thr->valstack_bottom + idx_this; - switch (DUK_TVAL_GET_TAG(tv_this)) { - case DUK_TAG_OBJECT: - DUK_DDD(DUK_DDDPRINT("this binding: non-strict, object -> use directly")); - break; - case DUK_TAG_UNDEFINED: - case DUK_TAG_NULL: - DUK_DDD(DUK_DDDPRINT("this binding: non-strict, undefined/null -> use global object")); - obj_global = thr->builtins[DUK_BIDX_GLOBAL]; - /* XXX: avoid this check somehow */ - if (DUK_LIKELY(obj_global != NULL)) { - DUK_ASSERT(!DUK_TVAL_IS_HEAP_ALLOCATED(tv_this)); /* no need to decref previous value */ - DUK_TVAL_SET_OBJECT(tv_this, obj_global); - DUK_HOBJECT_INCREF(thr, obj_global); - } else { - /* This may only happen if built-ins are being "torn down". - * This behavior is out of specification scope. - */ - DUK_D(DUK_DPRINT("this binding: wanted to use global object, but it is NULL -> using undefined instead")); - DUK_ASSERT(!DUK_TVAL_IS_HEAP_ALLOCATED(tv_this)); /* no need to decref previous value */ - DUK_TVAL_SET_UNDEFINED(tv_this); /* nothing to incref */ - } - break; - default: - /* Plain buffers and lightfuncs are object coerced. Lightfuncs - * very rarely come here however, because the call target would - * need to be a non-strict non-lightfunc (lightfuncs are considered - * strict) with an explicit lightfunc 'this' binding. - */ - DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv_this)); - DUK_DDD(DUK_DDDPRINT("this binding: non-strict, not object/undefined/null -> use ToObject(value)")); - duk_to_object(thr, idx_this); /* may have side effects */ - break; - } -} - -DUK_LOCAL DUK_ALWAYS_INLINE duk_bool_t duk__resolve_target_fastpath_check(duk_hthread *thr, - duk_idx_t idx_func, - duk_hobject **out_func, - duk_small_uint_t call_flags) { -#if defined(DUK_USE_PREFER_SIZE) - DUK_UNREF(thr); - DUK_UNREF(idx_func); - DUK_UNREF(out_func); - DUK_UNREF(call_flags); -#else /* DUK_USE_PREFER_SIZE */ - duk_tval *tv_func; - duk_hobject *func; - - if (DUK_UNLIKELY(call_flags & DUK_CALL_FLAG_CONSTRUCT)) { - return 0; - } - - tv_func = DUK_GET_TVAL_POSIDX(thr, idx_func); - DUK_ASSERT(tv_func != NULL); - - if (DUK_LIKELY(DUK_TVAL_IS_OBJECT(tv_func))) { - func = DUK_TVAL_GET_OBJECT(tv_func); - if (DUK_HOBJECT_IS_CALLABLE(func) && !DUK_HOBJECT_HAS_BOUNDFUNC(func) && !DUK_HOBJECT_HAS_SPECIAL_CALL(func)) { - *out_func = func; - - if (DUK_HOBJECT_HAS_STRICT(func)) { - /* Strict function: no 'this' coercion. */ - return 1; - } - - duk__coerce_nonstrict_this_binding(thr, idx_func + 1); - return 1; - } - } else if (DUK_TVAL_IS_LIGHTFUNC(tv_func)) { - *out_func = NULL; - - /* Lightfuncs are considered strict, so 'this' binding is - * used as is. They're never bound, always constructable, - * and never special functions. - */ - return 1; - } -#endif /* DUK_USE_PREFER_SIZE */ - return 0; /* let slow path deal with it */ -} - -DUK_LOCAL duk_hobject *duk__resolve_target_func_and_this_binding(duk_hthread *thr, - duk_idx_t idx_func, - duk_small_uint_t *call_flags) { - duk_tval *tv_func; - duk_hobject *func; - duk_bool_t first; - - DUK_ASSERT(duk_get_top(thr) >= idx_func + 2); - - for (first = 1;; first = 0) { - DUK_ASSERT(duk_get_top(thr) >= idx_func + 2); - - tv_func = DUK_GET_TVAL_POSIDX(thr, idx_func); - DUK_ASSERT(tv_func != NULL); - - DUK_DD(DUK_DDPRINT("target func: %!iT", tv_func)); - - if (DUK_TVAL_IS_OBJECT(tv_func)) { - func = DUK_TVAL_GET_OBJECT(tv_func); - - if (*call_flags & DUK_CALL_FLAG_CONSTRUCT) { - if (DUK_UNLIKELY(!DUK_HOBJECT_HAS_CONSTRUCTABLE(func))) { - goto not_constructable; - } - } else { - if (DUK_UNLIKELY(!DUK_HOBJECT_IS_CALLABLE(func))) { - goto not_callable; - } - } - - if (DUK_LIKELY(!DUK_HOBJECT_HAS_BOUNDFUNC(func) && !DUK_HOBJECT_HAS_SPECIAL_CALL(func) && - !DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(func))) { - /* Common case, so test for using a single bitfield test. - * Break out to handle this coercion etc. - */ - break; - } - - /* XXX: could set specialcall for boundfuncs too, simplify check above */ - - if (DUK_HOBJECT_HAS_BOUNDFUNC(func)) { - DUK_ASSERT(!DUK_HOBJECT_HAS_SPECIAL_CALL(func)); - DUK_ASSERT(!DUK_HOBJECT_IS_NATFUNC(func)); - - /* Callable/constructable flags are the same - * for the bound function and its target, so - * we don't need to check them here, we can - * check them from the target only. - */ - duk__handle_bound_chain_for_call(thr, idx_func, *call_flags & DUK_CALL_FLAG_CONSTRUCT); - - DUK_ASSERT(DUK_TVAL_IS_OBJECT(duk_require_tval(thr, idx_func)) || - DUK_TVAL_IS_LIGHTFUNC(duk_require_tval(thr, idx_func))); - } else { - DUK_ASSERT(DUK_HOBJECT_HAS_SPECIAL_CALL(func)); - -#if defined(DUK_USE_ES6_PROXY) - if (DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(func)) { - /* If no trap, resume processing from Proxy trap. - * If trap exists, helper converts call into a trap - * call; this may change a constructor call into a - * normal (non-constructor) trap call. We must - * continue processing even when a trap is found as - * the trap may be bound. - */ - duk__handle_proxy_for_call(thr, idx_func, (duk_hproxy *) func, call_flags); - } else -#endif - { - DUK_ASSERT(DUK_HOBJECT_IS_NATFUNC(func)); - DUK_ASSERT(DUK_HOBJECT_HAS_CALLABLE(func)); - DUK_ASSERT(!DUK_HOBJECT_HAS_CONSTRUCTABLE(func)); - /* Constructable check already done above. */ - - if (duk__handle_specialfuncs_for_call(thr, idx_func, func, call_flags, first) != 0) { - /* Encountered native eval call, normal call - * context. Break out, handle this coercion etc. - */ - break; - } - } - } - /* Retry loop. */ - } else if (DUK_TVAL_IS_LIGHTFUNC(tv_func)) { - /* Lightfuncs are: - * - Always strict, so no 'this' coercion. - * - Always callable. - * - Always constructable. - * - Never specialfuncs. - */ - func = NULL; - goto finished; - } else { - goto not_callable; - } - } - - DUK_ASSERT(func != NULL); - - if (!DUK_HOBJECT_HAS_STRICT(func)) { - /* Non-strict target needs 'this' coercion. - * This has potential side effects invalidating - * 'tv_func'. - */ - duk__coerce_nonstrict_this_binding(thr, idx_func + 1); - } - if (*call_flags & DUK_CALL_FLAG_CONSTRUCT) { - if (!(*call_flags & DUK_CALL_FLAG_DEFAULT_INSTANCE_UPDATED)) { - *call_flags |= DUK_CALL_FLAG_DEFAULT_INSTANCE_UPDATED; - duk__update_default_instance_proto(thr, idx_func); - } - } - -finished : -#if defined(DUK_USE_ASSERTIONS) -{ - duk_tval *tv_tmp; - - tv_tmp = duk_get_tval(thr, idx_func); - DUK_ASSERT(tv_tmp != NULL); - - DUK_ASSERT((DUK_TVAL_IS_OBJECT(tv_tmp) && DUK_HOBJECT_IS_CALLABLE(DUK_TVAL_GET_OBJECT(tv_tmp))) || - DUK_TVAL_IS_LIGHTFUNC(tv_tmp)); - DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUNDFUNC(func)); - DUK_ASSERT(func == NULL || (DUK_HOBJECT_IS_COMPFUNC(func) || DUK_HOBJECT_IS_NATFUNC(func))); - DUK_ASSERT(func == NULL || (DUK_HOBJECT_HAS_CONSTRUCTABLE(func) || (*call_flags & DUK_CALL_FLAG_CONSTRUCT) == 0)); -} -#endif - - return func; - -not_callable: - DUK_ASSERT(tv_func != NULL); - -#if defined(DUK_USE_VERBOSE_ERRORS) - /* GETPROPC delayed error handling: when target is not callable, - * GETPROPC replaces idx_func+0 with a non-callable wrapper object - * with a hidden Symbol to signify it's to be handled here. If - * found, unwrap the original Error and throw it as is here. The - * hidden Symbol is only checked as an own property, not inherited - * (which would be dangerous). - */ - if (DUK_TVAL_IS_OBJECT(tv_func)) { - duk_tval *tv_wrap = - duk_hobject_find_entry_tval_ptr_stridx(thr->heap, DUK_TVAL_GET_OBJECT(tv_func), DUK_STRIDX_INT_TARGET); - if (tv_wrap != NULL) { - DUK_DD(DUK_DDPRINT("delayed error from GETPROPC: %!T", tv_wrap)); - duk_push_tval(thr, tv_wrap); - (void) duk_throw(thr); - DUK_WO_NORETURN(return NULL;); - } - } -#endif - -#if defined(DUK_USE_VERBOSE_ERRORS) -#if defined(DUK_USE_PARANOID_ERRORS) - DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "%s not callable", duk_get_type_name(thr, idx_func)); -#else - DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "%s not callable", duk_push_string_tval_readable(thr, tv_func)); -#endif -#else - DUK_ERROR_TYPE(thr, DUK_STR_NOT_CALLABLE); -#endif - DUK_WO_NORETURN(return NULL;); - -not_constructable: - /* For now GETPROPC delayed error not needed for constructor calls. */ -#if defined(DUK_USE_VERBOSE_ERRORS) -#if defined(DUK_USE_PARANOID_ERRORS) - DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "%s not constructable", duk_get_type_name(thr, idx_func)); -#else - DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "%s not constructable", duk_push_string_tval_readable(thr, tv_func)); -#endif -#else - DUK_ERROR_TYPE(thr, DUK_STR_NOT_CONSTRUCTABLE); -#endif - DUK_WO_NORETURN(return NULL;); -} - -/* - * Manipulate value stack so that exactly 'num_stack_rets' return - * values are at 'idx_retbase' in every case, assuming there are - * 'rc' return values on top of stack. - * - * This is a bit tricky, because the called C function operates in - * the same activation record and may have e.g. popped the stack - * empty (below idx_retbase). - */ - -DUK_LOCAL void duk__safe_call_adjust_valstack(duk_hthread *thr, - duk_idx_t idx_retbase, - duk_idx_t num_stack_rets, - duk_idx_t num_actual_rets) { - duk_idx_t idx_rcbase; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(idx_retbase >= 0); - DUK_ASSERT(num_stack_rets >= 0); - DUK_ASSERT(num_actual_rets >= 0); - - idx_rcbase = duk_get_top(thr) - num_actual_rets; /* base of known return values */ - if (DUK_UNLIKELY(idx_rcbase < 0)) { - DUK_ERROR_TYPE(thr, DUK_STR_INVALID_CFUNC_RC); - DUK_WO_NORETURN(return;); - } - - DUK_DDD(DUK_DDDPRINT("adjust valstack after func call: " - "num_stack_rets=%ld, num_actual_rets=%ld, stack_top=%ld, idx_retbase=%ld, idx_rcbase=%ld", - (long) num_stack_rets, - (long) num_actual_rets, - (long) duk_get_top(thr), - (long) idx_retbase, - (long) idx_rcbase)); - - DUK_ASSERT(idx_rcbase >= 0); /* caller must check */ - - /* Space for num_stack_rets was reserved before the safe call. - * Because value stack reserve cannot shrink except in call returns, - * the reserve is still in place. Adjust valstack, carefully - * ensuring we don't overstep the reserve. - */ - - /* Match idx_rcbase with idx_retbase so that the return values - * start at the correct index. - */ - if (idx_rcbase > idx_retbase) { - duk_idx_t count = idx_rcbase - idx_retbase; - - DUK_DDD(DUK_DDDPRINT("elements at/after idx_retbase have enough to cover func retvals " - "(idx_retbase=%ld, idx_rcbase=%ld)", - (long) idx_retbase, - (long) idx_rcbase)); - - /* Remove values between irc_rcbase (start of intended return - * values) and idx_retbase to lower return values to idx_retbase. - */ - DUK_ASSERT(count > 0); - duk_remove_n(thr, idx_retbase, count); /* may be NORZ */ - } else { - duk_idx_t count = idx_retbase - idx_rcbase; - - DUK_DDD(DUK_DDDPRINT("not enough elements at/after idx_retbase to cover func retvals " - "(idx_retbase=%ld, idx_rcbase=%ld)", - (long) idx_retbase, - (long) idx_rcbase)); - - /* Insert 'undefined' at idx_rcbase (start of intended return - * values) to lift return values to idx_retbase. - */ - DUK_ASSERT(count >= 0); - DUK_ASSERT(thr->valstack_end - thr->valstack_top >= count); /* reserve cannot shrink */ - duk_insert_undefined_n(thr, idx_rcbase, count); - } - - /* Chop extra retvals away / extend with undefined. */ - duk_set_top_unsafe(thr, idx_retbase + num_stack_rets); -} - -/* - * Activation setup for tailcalls and non-tailcalls. - */ - -#if defined(DUK_USE_TAILCALL) -DUK_LOCAL duk_small_uint_t duk__call_setup_act_attempt_tailcall(duk_hthread *thr, - duk_small_uint_t call_flags, - duk_idx_t idx_func, - duk_hobject *func, - duk_size_t entry_valstack_bottom_byteoff, - duk_size_t entry_valstack_end_byteoff, - duk_idx_t *out_nargs, - duk_idx_t *out_nregs, - duk_size_t *out_vs_min_bytes, - duk_activation **out_act) { - duk_activation *act; - duk_tval *tv1, *tv2; - duk_idx_t idx_args; - duk_small_uint_t flags1, flags2; -#if defined(DUK_USE_DEBUGGER_SUPPORT) - duk_activation *prev_pause_act; -#endif - - DUK_UNREF(entry_valstack_end_byteoff); - - /* Tailcall cannot be flagged to resume calls, and a - * previous frame must exist. - */ - DUK_ASSERT(thr->callstack_top >= 1); - - act = thr->callstack_curr; - DUK_ASSERT(act != NULL); - *out_act = act; - - if (func == NULL || !DUK_HOBJECT_IS_COMPFUNC(func)) { - DUK_DDD(DUK_DDDPRINT("tail call prevented by target not being ecma function")); - return 0; - } - if (act->flags & DUK_ACT_FLAG_PREVENT_YIELD) { - DUK_DDD(DUK_DDDPRINT("tail call prevented by current activation having DUK_ACT_FLAG_PREVENT_YIELD")); - return 0; - } - /* Tailcall is only allowed if current and candidate - * function have identical return value handling. There - * are three possible return value handling cases: - * 1. Normal function call, no special return value handling. - * 2. Constructor call, return value replacement object check. - * 3. Proxy 'construct' trap call, return value invariant check. - */ - flags1 = (duk_small_uint_t) ((act->flags & DUK_ACT_FLAG_CONSTRUCT) ? 1 : 0) -#if defined(DUK_USE_ES6_PROXY) - | (duk_small_uint_t) ((act->flags & DUK_ACT_FLAG_CONSTRUCT_PROXY) ? 2 : 0) -#endif - ; - flags2 = (duk_small_uint_t) ((call_flags & DUK_CALL_FLAG_CONSTRUCT) ? 1 : 0) -#if defined(DUK_USE_ES6_PROXY) - | (duk_small_uint_t) ((call_flags & DUK_CALL_FLAG_CONSTRUCT_PROXY) ? 2 : 0); -#endif - ; - if (flags1 != flags2) { - DUK_DDD(DUK_DDDPRINT("tail call prevented by incompatible return value handling")); - return 0; - } - DUK_ASSERT(((act->flags & DUK_ACT_FLAG_CONSTRUCT) && (call_flags & DUK_CALL_FLAG_CONSTRUCT)) || - (!(act->flags & DUK_ACT_FLAG_CONSTRUCT) && !(call_flags & DUK_CALL_FLAG_CONSTRUCT))); - DUK_ASSERT(((act->flags & DUK_ACT_FLAG_CONSTRUCT_PROXY) && (call_flags & DUK_CALL_FLAG_CONSTRUCT_PROXY)) || - (!(act->flags & DUK_ACT_FLAG_CONSTRUCT_PROXY) && !(call_flags & DUK_CALL_FLAG_CONSTRUCT_PROXY))); - if (DUK_HOBJECT_HAS_NOTAIL(func)) { - /* See: test-bug-tailcall-preventyield-assert.c. */ - DUK_DDD(DUK_DDDPRINT("tail call prevented by function having a notail flag")); - return 0; - } - - /* - * Tailcall handling - * - * Although the callstack entry is reused, we need to explicitly unwind - * the current activation (or simulate an unwind). In particular, the - * current activation must be closed, otherwise something like - * test-bug-reduce-judofyr.js results. Also catchers need to be unwound - * because there may be non-error-catching label entries in valid tail calls. - * - * Special attention is needed for debugger and pause behavior when - * reusing an activation. - * - Disable StepOut processing for the activation unwind because - * we reuse the activation, see: - * https://github.com/svaarala/duktape/issues/1684. - * - Disable line change pause flag permanently if act == dbg_pause_act - * (if set) because it would no longer be relevant, see: - * https://github.com/svaarala/duktape/issues/1726, - * https://github.com/svaarala/duktape/issues/1786. - * - Check for function entry (e.g. StepInto) pause flag here, because - * the executor pause check won't trigger due to shared activation, see: - * https://github.com/svaarala/duktape/issues/1726. - */ - - DUK_DDD(DUK_DDDPRINT("is tail call, reusing activation at callstack top, at index %ld", (long) (thr->callstack_top - 1))); - - DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func)); - DUK_ASSERT(!DUK_HOBJECT_HAS_NATFUNC(func)); - DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(func)); - DUK_ASSERT((act->flags & DUK_ACT_FLAG_PREVENT_YIELD) == 0); - DUK_ASSERT(call_flags & DUK_CALL_FLAG_ALLOW_ECMATOECMA); - - /* Unwind the topmost callstack entry before reusing it. This - * also unwinds the catchers related to the topmost entry. - */ - DUK_ASSERT(thr->callstack_top > 0); - DUK_ASSERT(thr->callstack_curr != NULL); -#if defined(DUK_USE_DEBUGGER_SUPPORT) - if (act == thr->heap->dbg_pause_act) { - thr->heap->dbg_pause_flags &= ~DUK_PAUSE_FLAG_LINE_CHANGE; - } - - prev_pause_act = thr->heap->dbg_pause_act; - thr->heap->dbg_pause_act = NULL; - if (thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_FUNC_ENTRY) { - DUK_D(DUK_DPRINT("PAUSE TRIGGERED by function entry (tailcall)")); - duk_debug_set_paused(thr->heap); - } -#endif - duk_hthread_activation_unwind_reuse_norz(thr); -#if defined(DUK_USE_DEBUGGER_SUPPORT) - thr->heap->dbg_pause_act = prev_pause_act; -#endif - DUK_ASSERT(act == thr->callstack_curr); - - /* XXX: We could restore the caller's value stack reserve - * here, as if we did an actual unwind-and-call. Without - * the restoration, value stack reserve may remain higher - * than would otherwise be possible until we return to a - * non-tailcall. - */ - - /* Then reuse the unwound activation. */ - act->cat = NULL; - act->var_env = NULL; - act->lex_env = NULL; - DUK_ASSERT(func != NULL); - DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(func)); - act->func = func; /* don't want an intermediate exposed state with func == NULL */ -#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY) - act->prev_caller = NULL; -#endif - /* don't want an intermediate exposed state with invalid pc */ - act->curr_pc = DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, (duk_hcompfunc *) func); -#if defined(DUK_USE_DEBUGGER_SUPPORT) - act->prev_line = 0; -#endif - DUK_TVAL_SET_OBJECT(&act->tv_func, func); /* borrowed, no refcount */ - DUK_HOBJECT_INCREF(thr, func); - - act->flags = DUK_ACT_FLAG_TAILCALLED; - if (DUK_HOBJECT_HAS_STRICT(func)) { - act->flags |= DUK_ACT_FLAG_STRICT; - } - if (call_flags & DUK_CALL_FLAG_CONSTRUCT) { - act->flags |= DUK_ACT_FLAG_CONSTRUCT; - } -#if defined(DUK_USE_ES6_PROXY) - if (call_flags & DUK_CALL_FLAG_CONSTRUCT_PROXY) { - act->flags |= DUK_ACT_FLAG_CONSTRUCT_PROXY; - } -#endif - - DUK_ASSERT(DUK_ACT_GET_FUNC(act) == func); /* already updated */ - DUK_ASSERT(act->var_env == NULL); - DUK_ASSERT(act->lex_env == NULL); - act->bottom_byteoff = entry_valstack_bottom_byteoff; /* tail call -> reuse current "frame" */ -#if 0 - /* Topmost activation retval_byteoff is considered garbage, no need to init. */ - act->retval_byteoff = 0; -#endif - /* Filled in when final reserve is known, dummy value doesn't matter - * even in error unwind because reserve_byteoff is only used when - * returning to -this- activation. - */ - act->reserve_byteoff = 0; - - /* - * Manipulate valstack so that args are on the current bottom and the - * previous caller's 'this' binding (which is the value preceding the - * current bottom) is replaced with the new 'this' binding: - * - * [ ... this_old | (crud) func this_new arg1 ... argN ] - * --> [ ... this_new | arg1 ... argN ] - * - * For tail calling to work properly, the valstack bottom must not grow - * here; otherwise crud would accumulate on the valstack. - */ - - tv1 = thr->valstack_bottom - 1; - tv2 = thr->valstack_bottom + idx_func + 1; - DUK_ASSERT(tv1 >= thr->valstack && tv1 < thr->valstack_top); /* tv1 is -below- valstack_bottom */ - DUK_ASSERT(tv2 >= thr->valstack_bottom && tv2 < thr->valstack_top); - DUK_TVAL_SET_TVAL_UPDREF(thr, tv1, tv2); /* side effects */ - - idx_args = idx_func + 2; - duk_remove_n(thr, 0, idx_args); /* may be NORZ */ - - idx_func = 0; - DUK_UNREF(idx_func); /* really 'not applicable' anymore, should not be referenced after this */ - idx_args = 0; - - *out_nargs = ((duk_hcompfunc *) func)->nargs; - *out_nregs = ((duk_hcompfunc *) func)->nregs; - DUK_ASSERT(*out_nregs >= 0); - DUK_ASSERT(*out_nregs >= *out_nargs); - *out_vs_min_bytes = - entry_valstack_bottom_byteoff + sizeof(duk_tval) * ((duk_size_t) *out_nregs + DUK_VALSTACK_INTERNAL_EXTRA); - -#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY) -#if defined(DUK_USE_TAILCALL) -#error incorrect options: tail calls enabled with function caller property -#endif - /* XXX: This doesn't actually work properly for tail calls, so - * tail calls are disabled when DUK_USE_NONSTD_FUNC_CALLER_PROPERTY - * is in use. - */ - duk__update_func_caller_prop(thr, func); -#endif - - /* [ ... this_new | arg1 ... argN ] */ - - return 1; -} -#endif /* DUK_USE_TAILCALL */ - -DUK_LOCAL void duk__call_setup_act_not_tailcall(duk_hthread *thr, - duk_small_uint_t call_flags, - duk_idx_t idx_func, - duk_hobject *func, - duk_size_t entry_valstack_bottom_byteoff, - duk_size_t entry_valstack_end_byteoff, - duk_idx_t *out_nargs, - duk_idx_t *out_nregs, - duk_size_t *out_vs_min_bytes, - duk_activation **out_act) { - duk_activation *act; - duk_activation *new_act; - - DUK_UNREF(entry_valstack_end_byteoff); - - DUK_DDD(DUK_DDDPRINT("not a tail call, pushing a new activation to callstack, to index %ld", (long) (thr->callstack_top))); - - duk__call_callstack_limit_check(thr); - new_act = duk_hthread_activation_alloc(thr); - DUK_ASSERT(new_act != NULL); - - act = thr->callstack_curr; - if (act != NULL) { - /* - * Update return value stack index of current activation (if any). - * - * Although it might seem this is not necessary (bytecode executor - * does this for ECMAScript-to-ECMAScript calls; other calls are - * handled here), this turns out to be necessary for handling yield - * and resume. For them, an ECMAScript-to-native call happens, and - * the ECMAScript call's retval_byteoff must be set for things to work. - */ - - act->retval_byteoff = entry_valstack_bottom_byteoff + (duk_size_t) idx_func * sizeof(duk_tval); - } - - new_act->parent = act; - thr->callstack_curr = new_act; - thr->callstack_top++; - act = new_act; - *out_act = act; - - DUK_ASSERT(thr->valstack_top > thr->valstack_bottom); /* at least effective 'this' */ - DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUNDFUNC(func)); - - act->cat = NULL; - - act->flags = 0; - if (call_flags & DUK_CALL_FLAG_CONSTRUCT) { - act->flags |= DUK_ACT_FLAG_CONSTRUCT; - } -#if defined(DUK_USE_ES6_PROXY) - if (call_flags & DUK_CALL_FLAG_CONSTRUCT_PROXY) { - act->flags |= DUK_ACT_FLAG_CONSTRUCT_PROXY; - } -#endif - if (call_flags & DUK_CALL_FLAG_DIRECT_EVAL) { - act->flags |= DUK_ACT_FLAG_DIRECT_EVAL; - } - - /* start of arguments: idx_func + 2. */ - act->func = func; /* NULL for lightfunc */ - if (DUK_LIKELY(func != NULL)) { - DUK_TVAL_SET_OBJECT(&act->tv_func, func); /* borrowed, no refcount */ - if (DUK_HOBJECT_HAS_STRICT(func)) { - act->flags |= DUK_ACT_FLAG_STRICT; - } - if (DUK_HOBJECT_IS_COMPFUNC(func)) { - *out_nargs = ((duk_hcompfunc *) func)->nargs; - *out_nregs = ((duk_hcompfunc *) func)->nregs; - DUK_ASSERT(*out_nregs >= 0); - DUK_ASSERT(*out_nregs >= *out_nargs); - *out_vs_min_bytes = - entry_valstack_bottom_byteoff + - sizeof(duk_tval) * ((duk_size_t) idx_func + 2U + (duk_size_t) *out_nregs + DUK_VALSTACK_INTERNAL_EXTRA); - } else { - /* True because of call target lookup checks. */ - DUK_ASSERT(DUK_HOBJECT_IS_NATFUNC(func)); - - *out_nargs = ((duk_hnatfunc *) func)->nargs; - *out_nregs = *out_nargs; - if (*out_nargs >= 0) { - *out_vs_min_bytes = - entry_valstack_bottom_byteoff + - sizeof(duk_tval) * ((duk_size_t) idx_func + 2U + (duk_size_t) *out_nregs + - DUK_VALSTACK_API_ENTRY_MINIMUM + DUK_VALSTACK_INTERNAL_EXTRA); - } else { - /* Vararg function. */ - duk_size_t valstack_top_byteoff = - (duk_size_t) ((duk_uint8_t *) thr->valstack_top - ((duk_uint8_t *) thr->valstack)); - *out_vs_min_bytes = valstack_top_byteoff + sizeof(duk_tval) * (DUK_VALSTACK_API_ENTRY_MINIMUM + - DUK_VALSTACK_INTERNAL_EXTRA); - } - } - } else { - duk_small_uint_t lf_flags; - duk_tval *tv_func; - - act->flags |= DUK_ACT_FLAG_STRICT; - - tv_func = DUK_GET_TVAL_POSIDX(thr, idx_func); - DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv_func)); - DUK_TVAL_SET_TVAL(&act->tv_func, tv_func); /* borrowed, no refcount */ - - lf_flags = DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv_func); - *out_nargs = DUK_LFUNC_FLAGS_GET_NARGS(lf_flags); - if (*out_nargs != DUK_LFUNC_NARGS_VARARGS) { - *out_vs_min_bytes = entry_valstack_bottom_byteoff + - sizeof(duk_tval) * ((duk_size_t) idx_func + 2U + (duk_size_t) *out_nargs + - DUK_VALSTACK_API_ENTRY_MINIMUM + DUK_VALSTACK_INTERNAL_EXTRA); - } else { - duk_size_t valstack_top_byteoff = - (duk_size_t) ((duk_uint8_t *) thr->valstack_top - ((duk_uint8_t *) thr->valstack)); - *out_vs_min_bytes = valstack_top_byteoff + - sizeof(duk_tval) * (DUK_VALSTACK_API_ENTRY_MINIMUM + DUK_VALSTACK_INTERNAL_EXTRA); - *out_nargs = -1; /* vararg */ - } - *out_nregs = *out_nargs; - } - - act->var_env = NULL; - act->lex_env = NULL; -#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY) - act->prev_caller = NULL; -#endif - act->curr_pc = NULL; -#if defined(DUK_USE_DEBUGGER_SUPPORT) - act->prev_line = 0; -#endif - act->bottom_byteoff = entry_valstack_bottom_byteoff + sizeof(duk_tval) * ((duk_size_t) idx_func + 2U); -#if 0 - act->retval_byteoff = 0; /* topmost activation retval_byteoff is considered garbage, no need to init */ -#endif - /* Filled in when final reserve is known, dummy value doesn't matter - * even in error unwind because reserve_byteoff is only used when - * returning to -this- activation. - */ - act->reserve_byteoff = 0; /* filled in by caller */ - - /* XXX: Is this INCREF necessary? 'func' is always a borrowed - * reference reachable through the value stack? If changed, stack - * unwind code also needs to be fixed to match. - */ - DUK_HOBJECT_INCREF_ALLOWNULL(thr, func); /* act->func */ - -#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY) - if (func) { - duk__update_func_caller_prop(thr, func); - } -#endif -} - -/* - * Environment setup. - */ - -DUK_LOCAL void duk__call_env_setup(duk_hthread *thr, duk_hobject *func, duk_activation *act, duk_idx_t idx_args) { - duk_hobject *env; - - DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUNDFUNC(func)); /* bound function has already been resolved */ - - if (DUK_LIKELY(func != NULL)) { - if (DUK_LIKELY(DUK_HOBJECT_HAS_NEWENV(func))) { - DUK_STATS_INC(thr->heap, stats_envrec_newenv); - if (DUK_LIKELY(!DUK_HOBJECT_HAS_CREATEARGS(func))) { - /* Use a new environment but there's no 'arguments' object; - * delayed environment initialization. This is the most - * common case. - */ - DUK_ASSERT(act->lex_env == NULL); - DUK_ASSERT(act->var_env == NULL); - } else { - /* Use a new environment and there's an 'arguments' object. - * We need to initialize it right now. - */ - - /* third arg: absolute index (to entire valstack) of bottom_byteoff of new activation */ - env = duk_create_activation_environment_record(thr, func, act->bottom_byteoff); - DUK_ASSERT(env != NULL); - - /* [ ... func this arg1 ... argN envobj ] */ - - DUK_ASSERT(DUK_HOBJECT_HAS_CREATEARGS(func)); - duk__handle_createargs_for_call(thr, func, env, idx_args); - - /* [ ... func this arg1 ... argN envobj ] */ - - act->lex_env = env; - act->var_env = env; - DUK_HOBJECT_INCREF(thr, env); - DUK_HOBJECT_INCREF(thr, env); /* XXX: incref by count (2) directly */ - duk_pop(thr); - } - } else { - /* Use existing env (e.g. for non-strict eval); cannot have - * an own 'arguments' object (but can refer to an existing one). - */ - - DUK_ASSERT(!DUK_HOBJECT_HAS_CREATEARGS(func)); - - DUK_STATS_INC(thr->heap, stats_envrec_oldenv); - duk__handle_oldenv_for_call(thr, func, act); - - DUK_ASSERT(act->lex_env != NULL); - DUK_ASSERT(act->var_env != NULL); - } - } else { - /* Lightfuncs are always native functions and have "newenv". */ - DUK_ASSERT(act->lex_env == NULL); - DUK_ASSERT(act->var_env == NULL); - DUK_STATS_INC(thr->heap, stats_envrec_newenv); - } -} - -/* - * Misc shared helpers. - */ - -/* Check thread state, update current thread. */ -DUK_LOCAL void duk__call_thread_state_update(duk_hthread *thr) { - DUK_ASSERT(thr != NULL); - - if (DUK_LIKELY(thr == thr->heap->curr_thread)) { - if (DUK_UNLIKELY(thr->state != DUK_HTHREAD_STATE_RUNNING)) { - /* Should actually never happen, but check anyway. */ - goto thread_state_error; - } - } else { - DUK_ASSERT(thr->heap->curr_thread == NULL || thr->heap->curr_thread->state == DUK_HTHREAD_STATE_RUNNING); - if (DUK_UNLIKELY(thr->state != DUK_HTHREAD_STATE_INACTIVE)) { - goto thread_state_error; - } - DUK_HEAP_SWITCH_THREAD(thr->heap, thr); - thr->state = DUK_HTHREAD_STATE_RUNNING; - - /* Multiple threads may be simultaneously in the RUNNING - * state, but not in the same "resume chain". - */ - } - DUK_ASSERT(thr->heap->curr_thread == thr); - DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING); - return; - -thread_state_error: - DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "invalid thread state (%ld)", (long) thr->state); - DUK_WO_NORETURN(return;); -} - -/* - * Main unprotected call handler, handles: - * - * - All combinations of native/ECMAScript caller and native/ECMAScript - * target. - * - * - Optimized ECMAScript-to-ECMAScript call where call handling only - * sets up a new duk_activation but reuses an existing bytecode executor - * (the caller) without native recursion. - * - * - Tailcalls, where an activation is reused without increasing call - * stack (duk_activation) depth. - * - * - Setup for an initial Duktape.Thread.resume(). - * - * The call handler doesn't provide any protection guarantees, protected calls - * must be implemented e.g. by wrapping the call in a duk_safe_call(). - * Call setup may fail at any stage, even when the new activation is in - * place; the only guarantee is that the state is consistent for unwinding. - */ - -DUK_LOCAL duk_int_t duk__handle_call_raw(duk_hthread *thr, duk_idx_t idx_func, duk_small_uint_t call_flags) { -#if defined(DUK_USE_ASSERTIONS) - duk_activation *entry_act; - duk_size_t entry_callstack_top; -#endif - duk_size_t entry_valstack_bottom_byteoff; - duk_size_t entry_valstack_end_byteoff; - duk_int_t entry_call_recursion_depth; - duk_hthread *entry_curr_thread; - duk_uint_fast8_t entry_thread_state; - duk_instr_t **entry_ptr_curr_pc; - duk_idx_t idx_args; - duk_idx_t nargs; /* # argument registers target function wants (< 0 => "as is") */ - duk_idx_t nregs; /* # total registers target function wants on entry (< 0 => "as is") */ - duk_size_t vs_min_bytes; /* minimum value stack size (bytes) for handling call */ - duk_hobject *func; /* 'func' on stack (borrowed reference) */ - duk_activation *act; - duk_ret_t rc; - duk_small_uint_t use_tailcall; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - /* Asserts for heap->curr_thread omitted: it may be NULL, 'thr', or - * any other thread (e.g. when heap thread is used to run finalizers). - */ - DUK_CTX_ASSERT_VALID(thr); - DUK_ASSERT(duk_is_valid_index(thr, idx_func)); - DUK_ASSERT(idx_func >= 0); - - DUK_STATS_INC(thr->heap, stats_call_all); - - /* If a tail call: - * - an ECMAScript activation must be on top of the callstack - * - there cannot be any catch stack entries that would catch - * a return - */ -#if defined(DUK_USE_ASSERTIONS) - if (call_flags & DUK_CALL_FLAG_TAILCALL) { - duk_activation *tmp_act; - duk_catcher *tmp_cat; - - DUK_ASSERT(thr->callstack_top >= 1); - DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr))); - - /* No entry in the catch stack which would actually catch a - * throw can refer to the callstack entry being reused. - * There *can* be catch stack entries referring to the current - * callstack entry as long as they don't catch (e.g. label sites). - */ - - tmp_act = thr->callstack_curr; - for (tmp_cat = tmp_act->cat; tmp_cat != NULL; tmp_cat = tmp_cat->parent) { - DUK_ASSERT(DUK_CAT_GET_TYPE(tmp_cat) == DUK_CAT_TYPE_LABEL); /* a non-catching entry */ - } - } -#endif /* DUK_USE_ASSERTIONS */ - - /* - * Store entry state. - */ - -#if defined(DUK_USE_ASSERTIONS) - entry_act = thr->callstack_curr; - entry_callstack_top = thr->callstack_top; -#endif - entry_valstack_bottom_byteoff = (duk_size_t) ((duk_uint8_t *) thr->valstack_bottom - (duk_uint8_t *) thr->valstack); - entry_valstack_end_byteoff = (duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack); - entry_call_recursion_depth = thr->heap->call_recursion_depth; - entry_curr_thread = thr->heap->curr_thread; /* may be NULL if first call */ - entry_thread_state = thr->state; - entry_ptr_curr_pc = thr->ptr_curr_pc; /* may be NULL */ - - /* If thr->ptr_curr_pc is set, sync curr_pc to act->pc. Then NULL - * thr->ptr_curr_pc so that it's not accidentally used with an incorrect - * activation when side effects occur. - */ - duk_hthread_sync_and_null_currpc(thr); - DUK_ASSERT(thr->ptr_curr_pc == NULL); - - DUK_DD(DUK_DDPRINT("duk__handle_call_raw: thr=%p, idx_func=%ld, " - "call_flags=0x%08lx (constructor=%ld), " - "valstack_top=%ld, idx_func=%ld, idx_args=%ld, rec_depth=%ld/%ld, " - "entry_valstack_bottom_byteoff=%ld, entry_valstack_end_byteoff=%ld, " - "entry_call_recursion_depth=%ld, " - "entry_curr_thread=%p, entry_thread_state=%ld", - (void *) thr, - (long) idx_func, - (unsigned long) call_flags, - (long) ((call_flags & DUK_CALL_FLAG_CONSTRUCT) != 0 ? 1 : 0), - (long) duk_get_top(thr), - (long) idx_func, - (long) (idx_func + 2), - (long) thr->heap->call_recursion_depth, - (long) thr->heap->call_recursion_limit, - (long) entry_valstack_bottom_byteoff, - (long) entry_valstack_end_byteoff, - (long) entry_call_recursion_depth, - (void *) entry_curr_thread, - (long) entry_thread_state)); - - /* - * Thread state check and book-keeping. - */ - - duk__call_thread_state_update(thr); - - /* - * Increase call recursion depth as early as possible so that if we - * enter a recursive call for any reason there's a backstop to native - * recursion. This can happen e.g. for almost any property read - * because it may cause a getter call or a Proxy trap (GC and finalizers - * are not an issue because they are not recursive). If we end up - * doing an Ecma-to-Ecma call, revert the increase. (See GH-2032.) - * - * For similar reasons, ensure there is a known value stack spare - * even before we actually prepare the value stack for the target - * function. If this isn't done, early recursion may consume the - * value stack space. - * - * XXX: Should bump yield preventcount early, for the same reason. - */ - - duk__call_c_recursion_limit_check(thr); - thr->heap->call_recursion_depth++; - duk_require_stack(thr, DUK__CALL_HANDLING_REQUIRE_STACK); - - /* - * Resolve final target function; handle bound functions and special - * functions like .call() and .apply(). Also figure out the effective - * 'this' binding, which replaces the current value at idx_func + 1. - */ - - if (DUK_LIKELY(duk__resolve_target_fastpath_check(thr, idx_func, &func, call_flags) != 0U)) { - DUK_DDD(DUK_DDDPRINT("fast path target resolve")); - } else { - DUK_DDD(DUK_DDDPRINT("slow path target resolve")); - func = duk__resolve_target_func_and_this_binding(thr, idx_func, &call_flags); - } - DUK_ASSERT(duk_get_top(thr) - idx_func >= 2); /* at least func and this present */ - - DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUNDFUNC(func)); - DUK_ASSERT(func == NULL || (DUK_HOBJECT_IS_COMPFUNC(func) || DUK_HOBJECT_IS_NATFUNC(func))); - - /* [ ... func this arg1 ... argN ] */ - - /* - * Setup a preliminary activation and figure out nargs/nregs and - * value stack minimum size. - * - * Don't touch valstack_bottom or valstack_top yet so that Duktape API - * calls work normally. - * - * Because 'act' is not zeroed, all fields must be filled in. - */ - - /* Should not be necessary, but initialize to silence warnings. */ - act = NULL; - nargs = 0; - nregs = 0; - vs_min_bytes = 0; - -#if defined(DUK_USE_TAILCALL) - use_tailcall = (call_flags & DUK_CALL_FLAG_TAILCALL); - if (use_tailcall) { - use_tailcall = duk__call_setup_act_attempt_tailcall(thr, - call_flags, - idx_func, - func, - entry_valstack_bottom_byteoff, - entry_valstack_end_byteoff, - &nargs, - &nregs, - &vs_min_bytes, - &act); - } -#else - DUK_ASSERT((call_flags & DUK_CALL_FLAG_TAILCALL) == 0); /* compiler ensures this */ - use_tailcall = 0; -#endif - - if (use_tailcall) { - idx_args = 0; - DUK_STATS_INC(thr->heap, stats_call_tailcall); - } else { - duk__call_setup_act_not_tailcall(thr, - call_flags, - idx_func, - func, - entry_valstack_bottom_byteoff, - entry_valstack_end_byteoff, - &nargs, - &nregs, - &vs_min_bytes, - &act); - idx_args = idx_func + 2; - } - /* After this point idx_func is no longer valid for tailcalls. */ - - DUK_ASSERT(act != NULL); - - /* [ ... func this arg1 ... argN ] */ - - /* - * Grow value stack to required size before env setup. This - * must happen before env setup to handle some corner cases - * correctly, e.g. test-bug-scope-segv-gh2448.js. - */ - - duk_valstack_grow_check_throw(thr, vs_min_bytes); - act->reserve_byteoff = (duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack); - - /* - * Environment record creation and 'arguments' object creation. - * Named function expression name binding is handled by the - * compiler; the compiled function's parent env will contain - * the (immutable) binding already. - * - * This handling is now identical for C and ECMAScript functions. - * C functions always have the 'NEWENV' flag set, so their - * environment record initialization is delayed (which is good). - * - * Delayed creation (on demand) is handled in duk_js_var.c. - */ - - duk__call_env_setup(thr, func, act, idx_args); - - /* [ ... func this arg1 ... argN ] */ - - /* - * Setup value stack: clamp to 'nargs', fill up to 'nregs', - * ensure value stack size matches target requirements, and - * switch value stack bottom. Valstack top is kept. - */ - - if (use_tailcall) { - DUK_ASSERT(nregs >= 0); - DUK_ASSERT(nregs >= nargs); - duk_set_top_and_wipe(thr, nregs, nargs); - } else { - if (nregs >= 0) { - DUK_ASSERT(nregs >= nargs); - duk_set_top_and_wipe(thr, idx_func + 2 + nregs, idx_func + 2 + nargs); - } else { - ; - } - thr->valstack_bottom = thr->valstack_bottom + idx_func + 2; - } - DUK_ASSERT(thr->valstack_bottom >= thr->valstack); - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - DUK_ASSERT(thr->valstack_end >= thr->valstack_top); - - /* - * Make the actual call. For Ecma-to-Ecma calls detect that - * setup is complete, then return with a status code that allows - * the caller to reuse the running executor. - */ - - if (func != NULL && DUK_HOBJECT_IS_COMPFUNC(func)) { - /* - * ECMAScript call. - */ - - DUK_ASSERT(func != NULL); - DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(func)); - act->curr_pc = DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, (duk_hcompfunc *) func); - - if (call_flags & DUK_CALL_FLAG_ALLOW_ECMATOECMA) { - DUK_DD(DUK_DDPRINT("avoid native call, use existing executor")); - DUK_STATS_INC(thr->heap, stats_call_ecmatoecma); - DUK_ASSERT((act->flags & DUK_ACT_FLAG_PREVENT_YIELD) == 0); - DUK_REFZERO_CHECK_FAST(thr); - DUK_ASSERT(thr->ptr_curr_pc == NULL); - thr->heap->call_recursion_depth--; /* No recursion increase for this case. */ - return 1; /* 1=reuse executor */ - } - DUK_ASSERT(use_tailcall == 0); - - /* duk_hthread_activation_unwind_norz() will decrease this on unwind */ - DUK_ASSERT((act->flags & DUK_ACT_FLAG_PREVENT_YIELD) == 0); - act->flags |= DUK_ACT_FLAG_PREVENT_YIELD; - thr->callstack_preventcount++; - - /* [ ... func this | arg1 ... argN ] ('this' must precede new bottom) */ - - /* - * Bytecode executor call. - * - * Execute bytecode, handling any recursive function calls and - * thread resumptions. Returns when execution would return from - * the entry level activation. When the executor returns, a - * single return value is left on the stack top. - * - * The only possible longjmp() is an error (DUK_LJ_TYPE_THROW), - * other types are handled internally by the executor. - */ - - /* thr->ptr_curr_pc is set by bytecode executor early on entry */ - DUK_ASSERT(thr->ptr_curr_pc == NULL); - DUK_DDD(DUK_DDDPRINT("entering bytecode execution")); - duk_js_execute_bytecode(thr); - DUK_DDD(DUK_DDDPRINT("returned from bytecode execution")); - } else { - /* - * Native call. - */ - - DUK_ASSERT(func == NULL || ((duk_hnatfunc *) func)->func != NULL); - DUK_ASSERT(use_tailcall == 0); - - /* [ ... func this | arg1 ... argN ] ('this' must precede new bottom) */ - - /* duk_hthread_activation_unwind_norz() will decrease this on unwind */ - DUK_ASSERT((act->flags & DUK_ACT_FLAG_PREVENT_YIELD) == 0); - act->flags |= DUK_ACT_FLAG_PREVENT_YIELD; - thr->callstack_preventcount++; - - /* For native calls must be NULL so we don't sync back */ - DUK_ASSERT(thr->ptr_curr_pc == NULL); - - /* XXX: native funcptr could come out of call setup. */ - if (func) { - rc = ((duk_hnatfunc *) func)->func(thr); - } else { - duk_tval *tv_func; - duk_c_function funcptr; - - tv_func = &act->tv_func; - DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv_func)); - funcptr = DUK_TVAL_GET_LIGHTFUNC_FUNCPTR(tv_func); - rc = funcptr(thr); - } - - /* Automatic error throwing, retval check. */ - - if (rc == 0) { - DUK_ASSERT(thr->valstack < thr->valstack_end); - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top)); - thr->valstack_top++; - } else if (rc == 1) { - ; - } else if (rc < 0) { - duk_error_throw_from_negative_rc(thr, rc); - DUK_WO_NORETURN(return 0;); - } else { - DUK_ERROR_TYPE(thr, DUK_STR_INVALID_CFUNC_RC); - DUK_WO_NORETURN(return 0;); - } - } - DUK_ASSERT(thr->ptr_curr_pc == NULL); - DUK_ASSERT(use_tailcall == 0); - - /* - * Constructor call post processing. - */ - -#if defined(DUK_USE_ES6_PROXY) - if (call_flags & (DUK_CALL_FLAG_CONSTRUCT | DUK_CALL_FLAG_CONSTRUCT_PROXY)) { - duk_call_construct_postprocess(thr, call_flags & DUK_CALL_FLAG_CONSTRUCT_PROXY); - } -#else - if (call_flags & DUK_CALL_FLAG_CONSTRUCT) { - duk_call_construct_postprocess(thr, 0); - } -#endif - - /* - * Unwind, restore valstack bottom and other book-keeping. - */ - - DUK_ASSERT(thr->callstack_curr != NULL); - DUK_ASSERT(thr->callstack_curr->parent == entry_act); - DUK_ASSERT(thr->callstack_top == entry_callstack_top + 1); - duk_hthread_activation_unwind_norz(thr); - DUK_ASSERT(thr->callstack_curr == entry_act); - DUK_ASSERT(thr->callstack_top == entry_callstack_top); - - thr->valstack_bottom = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + entry_valstack_bottom_byteoff); - /* keep current valstack_top */ - DUK_ASSERT(thr->valstack_bottom >= thr->valstack); - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - DUK_ASSERT(thr->valstack_end >= thr->valstack_top); - DUK_ASSERT(thr->valstack_top - thr->valstack_bottom >= idx_func + 1); - - /* Return value handling. */ - - /* [ ... func this (crud) retval ] */ - - { - duk_tval *tv_ret; - duk_tval *tv_funret; - - tv_ret = thr->valstack_bottom + idx_func; - tv_funret = thr->valstack_top - 1; -#if defined(DUK_USE_FASTINT) - /* Explicit check for fastint downgrade. */ - DUK_TVAL_CHKFAST_INPLACE_FAST(tv_funret); -#endif - DUK_TVAL_SET_TVAL_UPDREF(thr, tv_ret, tv_funret); /* side effects */ - } - - duk_set_top_unsafe(thr, idx_func + 1); - - /* [ ... retval ] */ - - /* Restore caller's value stack reserve (cannot fail). */ - DUK_ASSERT((duk_uint8_t *) thr->valstack + entry_valstack_end_byteoff >= (duk_uint8_t *) thr->valstack_top); - DUK_ASSERT((duk_uint8_t *) thr->valstack + entry_valstack_end_byteoff <= (duk_uint8_t *) thr->valstack_alloc_end); - thr->valstack_end = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + entry_valstack_end_byteoff); - - /* XXX: Trial value stack shrink would be OK here, but we'd need - * to prevent side effects of the potential realloc. - */ - - /* Restore entry thread executor curr_pc stack frame pointer. */ - thr->ptr_curr_pc = entry_ptr_curr_pc; - - DUK_HEAP_SWITCH_THREAD(thr->heap, entry_curr_thread); /* may be NULL */ - thr->state = (duk_uint8_t) entry_thread_state; - - /* Disabled assert: triggered with some torture tests. */ -#if 0 - DUK_ASSERT((thr->state == DUK_HTHREAD_STATE_INACTIVE && thr->heap->curr_thread == NULL) || /* first call */ - (thr->state == DUK_HTHREAD_STATE_INACTIVE && thr->heap->curr_thread != NULL) || /* other call */ - (thr->state == DUK_HTHREAD_STATE_RUNNING && thr->heap->curr_thread == thr)); /* current thread */ -#endif - - thr->heap->call_recursion_depth = entry_call_recursion_depth; - - /* If the debugger is active we need to force an interrupt so that - * debugger breakpoints are rechecked. This is important for function - * calls caused by side effects (e.g. when doing a DUK_OP_GETPROP), see - * GH-303. Only needed for success path, error path always causes a - * breakpoint recheck in the executor. It would be enough to set this - * only when returning to an ECMAScript activation, but setting the flag - * on every return should have no ill effect. - */ -#if defined(DUK_USE_DEBUGGER_SUPPORT) - if (duk_debug_is_attached(thr->heap)) { - DUK_DD(DUK_DDPRINT("returning with debugger enabled, force interrupt")); - DUK_ASSERT(thr->interrupt_counter <= thr->interrupt_init); - thr->interrupt_init -= thr->interrupt_counter; - thr->interrupt_counter = 0; - thr->heap->dbg_force_restart = 1; - } -#endif - -#if defined(DUK_USE_INTERRUPT_COUNTER) && defined(DUK_USE_DEBUG) - duk__interrupt_fixup(thr, entry_curr_thread); -#endif - - /* Restored by success path. */ - DUK_ASSERT(thr->heap->call_recursion_depth == entry_call_recursion_depth); - DUK_ASSERT(thr->ptr_curr_pc == entry_ptr_curr_pc); - DUK_ASSERT_LJSTATE_UNSET(thr->heap); - - DUK_REFZERO_CHECK_FAST(thr); - - return 0; /* 0=call handled inline */ -} - -DUK_INTERNAL duk_int_t duk_handle_call_unprotected_nargs(duk_hthread *thr, duk_idx_t nargs, duk_small_uint_t call_flags) { - duk_idx_t idx_func; - DUK_ASSERT(duk_get_top(thr) >= nargs + 2); - idx_func = duk_get_top(thr) - (nargs + 2); - DUK_ASSERT(idx_func >= 0); - return duk_handle_call_unprotected(thr, idx_func, call_flags); -} - -DUK_INTERNAL duk_int_t duk_handle_call_unprotected(duk_hthread *thr, duk_idx_t idx_func, duk_small_uint_t call_flags) { - DUK_ASSERT(duk_is_valid_index(thr, idx_func)); - DUK_ASSERT(idx_func >= 0); - return duk__handle_call_raw(thr, idx_func, call_flags); -} - -/* - * duk_handle_safe_call(): make a "C protected call" within the - * current activation. - * - * The allowed thread states for making a call are the same as for - * duk_handle_call_protected(). - * - * Even though this call is protected, errors are thrown for insane arguments - * and may result in a fatal error unless there's another protected call which - * catches such errors. - * - * The error handling path should be error free, even for out-of-memory - * errors, to ensure safe sandboxing. (As of Duktape 2.2.0 this is not - * yet the case for environment closing which may run out of memory, see - * XXX notes below.) - */ - -DUK_LOCAL void duk__handle_safe_call_inner(duk_hthread *thr, - duk_safe_call_function func, - void *udata, -#if defined(DUK_USE_ASSERTIONS) - duk_size_t entry_valstack_bottom_byteoff, - duk_size_t entry_callstack_top, -#endif - duk_hthread *entry_curr_thread, - duk_uint_fast8_t entry_thread_state, - duk_idx_t idx_retbase, - duk_idx_t num_stack_rets) { - duk_ret_t rc; - - DUK_ASSERT(thr != NULL); - DUK_CTX_ASSERT_VALID(thr); - - /* - * Thread state check and book-keeping. - */ - - duk__call_thread_state_update(thr); - - /* - * Recursion limit check. - */ - - duk__call_c_recursion_limit_check(thr); - thr->heap->call_recursion_depth++; - - /* - * Make the C call. - */ - - rc = func(thr, udata); - - DUK_DDD(DUK_DDDPRINT("safe_call, func rc=%ld", (long) rc)); - - /* - * Valstack manipulation for results. - */ - - /* we're running inside the caller's activation, so no change in call/catch stack or valstack bottom */ - DUK_ASSERT(thr->callstack_top == entry_callstack_top); - DUK_ASSERT(thr->valstack_bottom >= thr->valstack); - DUK_ASSERT((duk_size_t) ((duk_uint8_t *) thr->valstack_bottom - (duk_uint8_t *) thr->valstack) == - entry_valstack_bottom_byteoff); - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - DUK_ASSERT(thr->valstack_end >= thr->valstack_top); - - if (DUK_UNLIKELY(rc < 0)) { - duk_error_throw_from_negative_rc(thr, rc); - DUK_WO_NORETURN(return;); - } - DUK_ASSERT(rc >= 0); - - duk__safe_call_adjust_valstack(thr, idx_retbase, num_stack_rets, rc); /* throws for insane rc */ - - DUK_HEAP_SWITCH_THREAD(thr->heap, entry_curr_thread); /* may be NULL */ - thr->state = (duk_uint8_t) entry_thread_state; -} - -DUK_LOCAL void duk__handle_safe_call_error(duk_hthread *thr, - duk_activation *entry_act, -#if defined(DUK_USE_ASSERTIONS) - duk_size_t entry_callstack_top, -#endif - duk_hthread *entry_curr_thread, - duk_uint_fast8_t entry_thread_state, - duk_idx_t idx_retbase, - duk_idx_t num_stack_rets, - duk_size_t entry_valstack_bottom_byteoff, - duk_jmpbuf *old_jmpbuf_ptr) { - DUK_ASSERT(thr != NULL); - DUK_CTX_ASSERT_VALID(thr); - - /* - * Error during call. The error value is at heap->lj.value1. - * - * The very first thing we do is restore the previous setjmp catcher. - * This means that any error in error handling will propagate outwards - * instead of causing a setjmp() re-entry above. - */ - - DUK_DDD(DUK_DDDPRINT("error caught during protected duk_handle_safe_call()")); - - /* Other longjmp types are handled by executor before propagating - * the error here. - */ - DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_THROW); - DUK_ASSERT_LJSTATE_SET(thr->heap); - - /* Either pointer may be NULL (at entry), so don't assert. */ - thr->heap->lj.jmpbuf_ptr = old_jmpbuf_ptr; - - /* XXX: callstack unwind may now throw an error when closing - * scopes; this is a sandboxing issue, described in: - * https://github.com/svaarala/duktape/issues/476 - */ - /* XXX: "unwind to" primitive? */ - - DUK_ASSERT(thr->callstack_top >= entry_callstack_top); - while (thr->callstack_curr != entry_act) { - DUK_ASSERT(thr->callstack_curr != NULL); - duk_hthread_activation_unwind_norz(thr); - } - DUK_ASSERT(thr->callstack_top == entry_callstack_top); - - /* Switch active thread before any side effects to avoid a - * dangling curr_thread pointer. - */ - DUK_HEAP_SWITCH_THREAD(thr->heap, entry_curr_thread); /* may be NULL */ - thr->state = (duk_uint8_t) entry_thread_state; - - DUK_ASSERT(thr->heap->curr_thread == entry_curr_thread); - DUK_ASSERT(thr->state == entry_thread_state); - - /* Restore valstack bottom. */ - thr->valstack_bottom = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + entry_valstack_bottom_byteoff); - - /* [ ... | (crud) ] */ - - /* XXX: ensure space in valstack (now relies on internal reserve)? */ - duk_push_tval(thr, &thr->heap->lj.value1); - - /* [ ... | (crud) errobj ] */ - - DUK_ASSERT(duk_get_top(thr) >= 1); /* at least errobj must be on stack */ - - duk__safe_call_adjust_valstack(thr, idx_retbase, num_stack_rets, 1); /* 1 = num actual 'return values' */ - - /* [ ... | ] or [ ... | errobj (M * undefined)] where M = num_stack_rets - 1 */ - - /* Reset longjmp state. */ - thr->heap->lj.type = DUK_LJ_TYPE_UNKNOWN; - thr->heap->lj.iserror = 0; - DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ(thr, &thr->heap->lj.value1); - DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ(thr, &thr->heap->lj.value2); - - /* Error handling complete, remove side effect protections. Caller - * will process pending finalizers. - */ -#if defined(DUK_USE_ASSERTIONS) - DUK_ASSERT(thr->heap->error_not_allowed == 1); - thr->heap->error_not_allowed = 0; -#endif - DUK_ASSERT(thr->heap->pf_prevent_count > 0); - thr->heap->pf_prevent_count--; - DUK_DD(DUK_DDPRINT("safe call error handled, pf_prevent_count updated to %ld", (long) thr->heap->pf_prevent_count)); - - /* thr->ptr_curr_pc is restored by - * duk__handle_safe_call_shared_unwind() which is also used for - * success path. - */ -} - -DUK_LOCAL void duk__handle_safe_call_shared_unwind(duk_hthread *thr, - duk_idx_t idx_retbase, - duk_idx_t num_stack_rets, -#if defined(DUK_USE_ASSERTIONS) - duk_size_t entry_callstack_top, -#endif - duk_int_t entry_call_recursion_depth, - duk_hthread *entry_curr_thread, - duk_instr_t **entry_ptr_curr_pc) { - DUK_ASSERT(thr != NULL); - DUK_CTX_ASSERT_VALID(thr); - DUK_UNREF(idx_retbase); - DUK_UNREF(num_stack_rets); - DUK_UNREF(entry_curr_thread); - - DUK_ASSERT(thr->callstack_top == entry_callstack_top); - - /* Restore entry thread executor curr_pc stack frame pointer. - * XXX: would be enough to do in error path only, should nest - * cleanly in success path. - */ - thr->ptr_curr_pc = entry_ptr_curr_pc; - - thr->heap->call_recursion_depth = entry_call_recursion_depth; - - /* stack discipline consistency check */ - DUK_ASSERT(duk_get_top(thr) == idx_retbase + num_stack_rets); - - /* A debugger forced interrupt check is not needed here, as - * problematic safe calls are not caused by side effects. - */ - -#if defined(DUK_USE_INTERRUPT_COUNTER) && defined(DUK_USE_DEBUG) - duk__interrupt_fixup(thr, entry_curr_thread); -#endif -} - -DUK_INTERNAL duk_int_t duk_handle_safe_call(duk_hthread *thr, - duk_safe_call_function func, - void *udata, - duk_idx_t num_stack_args, - duk_idx_t num_stack_rets) { - duk_activation *entry_act; - duk_size_t entry_valstack_bottom_byteoff; -#if defined(DUK_USE_ASSERTIONS) - duk_size_t entry_valstack_end_byteoff; - duk_size_t entry_callstack_top; - duk_size_t entry_callstack_preventcount; -#endif - duk_int_t entry_call_recursion_depth; - duk_hthread *entry_curr_thread; - duk_uint_fast8_t entry_thread_state; - duk_instr_t **entry_ptr_curr_pc; - duk_jmpbuf *old_jmpbuf_ptr = NULL; - duk_jmpbuf our_jmpbuf; - duk_idx_t idx_retbase; - duk_int_t retval; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(duk_get_top(thr) >= num_stack_args); /* Caller ensures. */ - - DUK_STATS_INC(thr->heap, stats_safecall_all); - - /* Value stack reserve handling: safe call assumes caller has reserved - * space for nrets (assuming optimal unwind processing). Value stack - * reserve is not stored/restored as for normal calls because a safe - * call conceptually happens in the same activation. - */ - - /* Careful with indices like '-x'; if 'x' is zero, it refers to bottom */ - entry_act = thr->callstack_curr; - entry_valstack_bottom_byteoff = (duk_size_t) ((duk_uint8_t *) thr->valstack_bottom - (duk_uint8_t *) thr->valstack); -#if defined(DUK_USE_ASSERTIONS) - entry_valstack_end_byteoff = (duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack); - entry_callstack_top = thr->callstack_top; - entry_callstack_preventcount = thr->callstack_preventcount; -#endif - entry_call_recursion_depth = thr->heap->call_recursion_depth; - entry_curr_thread = thr->heap->curr_thread; /* may be NULL if first call */ - entry_thread_state = thr->state; - entry_ptr_curr_pc = thr->ptr_curr_pc; /* may be NULL */ - idx_retbase = duk_get_top(thr) - num_stack_args; /* not a valid stack index if num_stack_args == 0 */ - DUK_ASSERT(idx_retbase >= 0); - - DUK_ASSERT((duk_idx_t) (thr->valstack_top - thr->valstack_bottom) >= num_stack_args); /* Caller ensures. */ - DUK_ASSERT((duk_idx_t) (thr->valstack_end - (thr->valstack_bottom + idx_retbase)) >= num_stack_rets); /* Caller ensures. */ - - /* Cannot portably debug print a function pointer, hence 'func' not printed! */ - DUK_DD(DUK_DDPRINT("duk_handle_safe_call: thr=%p, num_stack_args=%ld, num_stack_rets=%ld, " - "valstack_top=%ld, idx_retbase=%ld, rec_depth=%ld/%ld, " - "entry_act=%p, entry_valstack_bottom_byteoff=%ld, entry_call_recursion_depth=%ld, " - "entry_curr_thread=%p, entry_thread_state=%ld", - (void *) thr, - (long) num_stack_args, - (long) num_stack_rets, - (long) duk_get_top(thr), - (long) idx_retbase, - (long) thr->heap->call_recursion_depth, - (long) thr->heap->call_recursion_limit, - (void *) entry_act, - (long) entry_valstack_bottom_byteoff, - (long) entry_call_recursion_depth, - (void *) entry_curr_thread, - (long) entry_thread_state)); - - /* Setjmp catchpoint setup. */ - old_jmpbuf_ptr = thr->heap->lj.jmpbuf_ptr; - thr->heap->lj.jmpbuf_ptr = &our_jmpbuf; - - /* Prevent yields for the duration of the safe call. This only - * matters if the executor makes safe calls to functions that - * yield, this doesn't currently happen. - */ - thr->callstack_preventcount++; - -#if defined(DUK_USE_CPP_EXCEPTIONS) - try { -#else - DUK_ASSERT(thr->heap->lj.jmpbuf_ptr == &our_jmpbuf); - if (DUK_SETJMP(our_jmpbuf.jb) == 0) { - /* Success path. */ -#endif - DUK_DDD(DUK_DDDPRINT("safe_call setjmp catchpoint setup complete")); - - duk__handle_safe_call_inner(thr, - func, - udata, -#if defined(DUK_USE_ASSERTIONS) - entry_valstack_bottom_byteoff, - entry_callstack_top, -#endif - entry_curr_thread, - entry_thread_state, - idx_retbase, - num_stack_rets); - - DUK_STATS_INC(thr->heap, stats_safecall_nothrow); - - /* Either pointer may be NULL (at entry), so don't assert */ - thr->heap->lj.jmpbuf_ptr = old_jmpbuf_ptr; - - /* If calls happen inside the safe call, these are restored by - * whatever calls are made. Reserve cannot decrease. - */ - DUK_ASSERT(thr->callstack_curr == entry_act); - DUK_ASSERT((duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack) >= - entry_valstack_end_byteoff); - - retval = DUK_EXEC_SUCCESS; -#if defined(DUK_USE_CPP_EXCEPTIONS) - } catch (duk_internal_exception &exc) { - DUK_UNREF(exc); -#else - } else { - /* Error path. */ -#endif - DUK_ASSERT((duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack) >= - entry_valstack_end_byteoff); - - DUK_STATS_INC(thr->heap, stats_safecall_throw); - - duk__handle_safe_call_error(thr, - entry_act, -#if defined(DUK_USE_ASSERTIONS) - entry_callstack_top, -#endif - entry_curr_thread, - entry_thread_state, - idx_retbase, - num_stack_rets, - entry_valstack_bottom_byteoff, - old_jmpbuf_ptr); - - retval = DUK_EXEC_ERROR; - } -#if defined(DUK_USE_CPP_EXCEPTIONS) - catch (duk_fatal_exception &exc) { - DUK_D(DUK_DPRINT("rethrow duk_fatal_exception")); - DUK_UNREF(exc); - throw; - } catch (std::exception &exc) { - const char *what = exc.what(); - DUK_ASSERT((duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack) >= - entry_valstack_end_byteoff); - DUK_STATS_INC(thr->heap, stats_safecall_throw); - if (!what) { - what = "unknown"; - } - DUK_D(DUK_DPRINT("unexpected c++ std::exception (perhaps thrown by user code)")); - try { - DUK_ERROR_FMT1(thr, - DUK_ERR_TYPE_ERROR, - "caught invalid c++ std::exception '%s' (perhaps thrown by user code)", - what); - DUK_WO_NORETURN(return 0;); - } catch (duk_internal_exception exc) { - DUK_D(DUK_DPRINT("caught api error thrown from unexpected c++ std::exception")); - DUK_UNREF(exc); - duk__handle_safe_call_error(thr, - entry_act, -#if defined(DUK_USE_ASSERTIONS) - entry_callstack_top, -#endif - entry_curr_thread, - entry_thread_state, - idx_retbase, - num_stack_rets, - entry_valstack_bottom_byteoff, - old_jmpbuf_ptr); - retval = DUK_EXEC_ERROR; - } - } catch (...) { - DUK_D(DUK_DPRINT("unexpected c++ exception (perhaps thrown by user code)")); - DUK_ASSERT((duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack) >= - entry_valstack_end_byteoff); - DUK_STATS_INC(thr->heap, stats_safecall_throw); - try { - DUK_ERROR_TYPE(thr, "caught invalid c++ exception (perhaps thrown by user code)"); - DUK_WO_NORETURN(return 0;); - } catch (duk_internal_exception exc) { - DUK_D(DUK_DPRINT("caught api error thrown from unexpected c++ exception")); - DUK_UNREF(exc); - duk__handle_safe_call_error(thr, - entry_act, -#if defined(DUK_USE_ASSERTIONS) - entry_callstack_top, -#endif - entry_curr_thread, - entry_thread_state, - idx_retbase, - num_stack_rets, - entry_valstack_bottom_byteoff, - old_jmpbuf_ptr); - retval = DUK_EXEC_ERROR; - } - } -#endif - - DUK_ASSERT(thr->heap->lj.jmpbuf_ptr == old_jmpbuf_ptr); /* success/error path both do this */ - - DUK_ASSERT_LJSTATE_UNSET(thr->heap); - - DUK_ASSERT((duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack) >= entry_valstack_end_byteoff); - duk__handle_safe_call_shared_unwind(thr, - idx_retbase, - num_stack_rets, -#if defined(DUK_USE_ASSERTIONS) - entry_callstack_top, -#endif - entry_call_recursion_depth, - entry_curr_thread, - entry_ptr_curr_pc); - - /* Restore preventcount. */ - thr->callstack_preventcount--; - DUK_ASSERT(thr->callstack_preventcount == entry_callstack_preventcount); - - /* Final asserts. */ - DUK_ASSERT(thr->callstack_curr == entry_act); - DUK_ASSERT((duk_size_t) ((duk_uint8_t *) thr->valstack_bottom - (duk_uint8_t *) thr->valstack) == - entry_valstack_bottom_byteoff); - DUK_ASSERT((duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack) >= entry_valstack_end_byteoff); - DUK_ASSERT(thr->callstack_top == entry_callstack_top); - DUK_ASSERT(thr->heap->call_recursion_depth == entry_call_recursion_depth); - DUK_ASSERT(thr->heap->curr_thread == entry_curr_thread); - DUK_ASSERT(thr->state == entry_thread_state); - DUK_ASSERT(thr->ptr_curr_pc == entry_ptr_curr_pc); - DUK_ASSERT(duk_get_top(thr) == idx_retbase + num_stack_rets); - DUK_ASSERT_LJSTATE_UNSET(thr->heap); - - /* Pending side effects. */ - DUK_REFZERO_CHECK_FAST(thr); - - return retval; -} - -/* - * Property-based call (foo.noSuch()) error setup: replace target function - * on stack top with a hidden Symbol tagged non-callable wrapper object - * holding the error. The error gets thrown in call handling at the - * proper spot to follow ECMAScript semantics. - */ - -#if defined(DUK_USE_VERBOSE_ERRORS) -DUK_INTERNAL DUK_NOINLINE DUK_COLD void duk_call_setup_propcall_error(duk_hthread *thr, duk_tval *tv_base, duk_tval *tv_key) { - const char *str_targ, *str_key, *str_base; - duk_idx_t entry_top; - - entry_top = duk_get_top(thr); - - /* [ target ] */ - - /* Must stabilize pointers first. tv_targ is already on stack top. */ - duk_push_tval(thr, tv_base); - duk_push_tval(thr, tv_key); - - DUK_GC_TORTURE(thr->heap); - - duk_push_bare_object(thr); - - /* [ target base key {} ] */ - - /* We only push a wrapped error, replacing the call target (at - * idx_func) with the error to ensure side effects come out - * correctly: - * - Property read - * - Call argument evaluation - * - Callability check and error thrown - * - * A hidden Symbol on the wrapper object pushed above is used by - * call handling to figure out the error is to be thrown as is. - * It is CRITICAL that the hidden Symbol can never occur on a - * user visible object that may get thrown. - */ - -#if defined(DUK_USE_PARANOID_ERRORS) - str_targ = duk_get_type_name(thr, -4); - str_key = duk_get_type_name(thr, -2); - str_base = duk_get_type_name(thr, -3); - duk_push_error_object(thr, - DUK_ERR_TYPE_ERROR | DUK_ERRCODE_FLAG_NOBLAME_FILELINE, - "%s not callable (property %s of %s)", - str_targ, - str_key, - str_base); - duk_xdef_prop_stridx(thr, -2, DUK_STRIDX_INT_TARGET, DUK_PROPDESC_FLAGS_NONE); /* Marker property, reuse _Target. */ - /* [ target base key { _Target: error } ] */ - duk_replace(thr, entry_top - 1); -#else - str_targ = duk_push_string_readable(thr, -4); - str_key = duk_push_string_readable(thr, -3); - str_base = duk_push_string_readable(thr, -5); - duk_push_error_object(thr, - DUK_ERR_TYPE_ERROR | DUK_ERRCODE_FLAG_NOBLAME_FILELINE, - "%s not callable (property %s of %s)", - str_targ, - str_key, - str_base); - /* [ target base key {} str_targ str_key str_base error ] */ - duk_xdef_prop_stridx(thr, -5, DUK_STRIDX_INT_TARGET, DUK_PROPDESC_FLAGS_NONE); /* Marker property, reuse _Target. */ - /* [ target base key { _Target: error } str_targ str_key str_base ] */ - duk_swap(thr, -4, entry_top - 1); - /* [ { _Target: error } base key target str_targ str_key str_base ] */ -#endif - - /* [ { _Target: error } */ - duk_set_top(thr, entry_top); - - /* [ { _Target: error } */ - DUK_ASSERT(!duk_is_callable(thr, -1)); /* Critical so that call handling will throw the error. */ -} -#endif /* DUK_USE_VERBOSE_ERRORS */ - -/* automatic undefs */ -#undef DUK__AUGMENT_CALL_RELAX_COUNT -#undef DUK__CALL_HANDLING_REQUIRE_STACK -/* - * ECMAScript compiler. - * - * Parses an input string and generates a function template result. - * Compilation may happen in multiple contexts (global code, eval - * code, function code). - * - * The parser uses a traditional top-down recursive parsing for the - * statement level, and an operator precedence based top-down approach - * for the expression level. The attempt is to minimize the C stack - * depth. Bytecode is generated directly without an intermediate - * representation (tree), at the cost of needing two (and sometimes - * three) passes over each function. - * - * The top-down recursive parser functions are named "duk__parse_XXX". - * - * Recursion limits are in key functions to prevent arbitrary C recursion: - * function body parsing, statement parsing, and expression parsing. - * - * See doc/compiler.rst for discussion on the design. - * - * A few typing notes: - * - * - duk_regconst_t: signed, highest bit set (< 0) means constant, - * some call sites use -1 for "none" (equivalent to constant 0x7fffffff) - * - PC values: duk_int_t, negative values used as markers - */ - -/* #include duk_internal.h -> already included */ - -/* If highest bit of a register number is set, it refers to a constant instead. - * When interpreted as a signed value, this means const values are always - * negative (when interpreted as two's complement). For example DUK__ISREG_TEMP() - * uses this approach to avoid an explicit DUK__ISREG() check (the condition is - * logically "'x' is a register AND 'x' >= temp_first"). - */ -#define DUK__CONST_MARKER DUK_REGCONST_CONST_MARKER -#define DUK__REMOVECONST(x) ((x) & ~DUK__CONST_MARKER) -#define DUK__ISREG(x) ((x) >= 0) -#define DUK__ISCONST(x) ((x) < 0) -#define DUK__ISREG_TEMP(comp_ctx, x) \ - ((duk_int32_t) (x) >= \ - (duk_int32_t) ((comp_ctx)->curr_func.temp_first)) /* Check for x >= temp_first && x >= 0 by comparing as signed. */ -#define DUK__ISREG_NOTTEMP(comp_ctx, x) \ - ((duk_uint32_t) (x) < \ - (duk_uint32_t) ((comp_ctx)->curr_func.temp_first)) /* Check for x >= 0 && x < temp_first by interpreting as unsigned. */ -#define DUK__GETTEMP(comp_ctx) ((comp_ctx)->curr_func.temp_next) -#define DUK__SETTEMP(comp_ctx, x) ((comp_ctx)->curr_func.temp_next = (x)) /* dangerous: must only lower (temp_max not updated) */ -#define DUK__SETTEMP_CHECKMAX(comp_ctx, x) duk__settemp_checkmax((comp_ctx), (x)) -#define DUK__ALLOCTEMP(comp_ctx) duk__alloctemp((comp_ctx)) -#define DUK__ALLOCTEMPS(comp_ctx, count) duk__alloctemps((comp_ctx), (count)) - -/* Init value set size for array and object literals. */ -#define DUK__MAX_ARRAY_INIT_VALUES 20 -#define DUK__MAX_OBJECT_INIT_PAIRS 10 - -/* XXX: hack, remove when const lookup is not O(n) */ -#define DUK__GETCONST_MAX_CONSTS_CHECK 256 - -/* These limits are based on bytecode limits. Max temps is limited - * by duk_hcompfunc nargs/nregs fields being 16 bits. - */ -#define DUK__MAX_CONSTS DUK_BC_BC_MAX -#define DUK__MAX_FUNCS DUK_BC_BC_MAX -#define DUK__MAX_TEMPS 0xffffL - -/* Initial bytecode size allocation. */ -#if defined(DUK_USE_PREFER_SIZE) -#define DUK__BC_INITIAL_INSTS 16 -#else -#define DUK__BC_INITIAL_INSTS 256 -#endif - -#define DUK__RECURSION_INCREASE(comp_ctx, thr) \ - do { \ - DUK_DDD(DUK_DDDPRINT("RECURSION INCREASE: %s:%ld", (const char *) DUK_FILE_MACRO, (long) DUK_LINE_MACRO)); \ - duk__comp_recursion_increase((comp_ctx)); \ - } while (0) - -#define DUK__RECURSION_DECREASE(comp_ctx, thr) \ - do { \ - DUK_DDD(DUK_DDDPRINT("RECURSION DECREASE: %s:%ld", (const char *) DUK_FILE_MACRO, (long) DUK_LINE_MACRO)); \ - duk__comp_recursion_decrease((comp_ctx)); \ - } while (0) - -/* Value stack slot limits: these are quite approximate right now, and - * because they overlap in control flow, some could be eliminated. - */ -#define DUK__COMPILE_ENTRY_SLOTS 8 -#define DUK__FUNCTION_INIT_REQUIRE_SLOTS 16 -#define DUK__FUNCTION_BODY_REQUIRE_SLOTS 16 -#define DUK__PARSE_STATEMENTS_SLOTS 16 -#define DUK__PARSE_EXPR_SLOTS 16 - -/* Temporary structure used to pass a stack allocated region through - * duk_safe_call(). - */ -typedef struct { - duk_small_uint_t flags; - duk_compiler_ctx comp_ctx_alloc; - duk_lexer_point lex_pt_alloc; -} duk__compiler_stkstate; - -/* - * Prototypes - */ - -/* lexing */ -DUK_LOCAL_DECL void duk__advance_helper(duk_compiler_ctx *comp_ctx, duk_small_int_t expect); -DUK_LOCAL_DECL void duk__advance_expect(duk_compiler_ctx *comp_ctx, duk_small_int_t expect); -DUK_LOCAL_DECL void duk__advance(duk_compiler_ctx *ctx); - -/* function helpers */ -DUK_LOCAL_DECL void duk__init_func_valstack_slots(duk_compiler_ctx *comp_ctx); -DUK_LOCAL_DECL void duk__reset_func_for_pass2(duk_compiler_ctx *comp_ctx); -DUK_LOCAL_DECL void duk__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ctx, duk_regconst_t *out_stmt_value_reg); -DUK_LOCAL_DECL void duk__convert_to_func_template(duk_compiler_ctx *comp_ctx); -DUK_LOCAL_DECL duk_int_t duk__cleanup_varmap(duk_compiler_ctx *comp_ctx); - -/* code emission */ -DUK_LOCAL_DECL duk_int_t duk__get_current_pc(duk_compiler_ctx *comp_ctx); -DUK_LOCAL_DECL duk_compiler_instr *duk__get_instr_ptr(duk_compiler_ctx *comp_ctx, duk_int_t pc); -DUK_LOCAL_DECL void duk__emit(duk_compiler_ctx *comp_ctx, duk_instr_t ins); -DUK_LOCAL_DECL void duk__emit_op_only(duk_compiler_ctx *comp_ctx, duk_small_uint_t op); -DUK_LOCAL_DECL void duk__emit_a_b_c(duk_compiler_ctx *comp_ctx, - duk_small_uint_t op_flags, - duk_regconst_t a, - duk_regconst_t b, - duk_regconst_t c); -DUK_LOCAL_DECL void duk__emit_a_b(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t a, duk_regconst_t b); -DUK_LOCAL_DECL void duk__emit_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t b, duk_regconst_t c); -#if 0 /* unused */ -DUK_LOCAL_DECL void duk__emit_a(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t a); -DUK_LOCAL_DECL void duk__emit_b(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t b); -#endif -DUK_LOCAL_DECL void duk__emit_a_bc(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t a, duk_regconst_t bc); -DUK_LOCAL_DECL void duk__emit_bc(duk_compiler_ctx *comp_ctx, duk_small_uint_t op, duk_regconst_t bc); -DUK_LOCAL_DECL void duk__emit_abc(duk_compiler_ctx *comp_ctx, duk_small_uint_t op, duk_regconst_t abc); -DUK_LOCAL_DECL void duk__emit_load_int32(duk_compiler_ctx *comp_ctx, duk_regconst_t reg, duk_int32_t val); -DUK_LOCAL_DECL void duk__emit_load_int32_noshuffle(duk_compiler_ctx *comp_ctx, duk_regconst_t reg, duk_int32_t val); -DUK_LOCAL_DECL void duk__emit_jump(duk_compiler_ctx *comp_ctx, duk_int_t target_pc); -DUK_LOCAL_DECL duk_int_t duk__emit_jump_empty(duk_compiler_ctx *comp_ctx); -DUK_LOCAL_DECL void duk__insert_jump_entry(duk_compiler_ctx *comp_ctx, duk_int_t jump_pc); -DUK_LOCAL_DECL void duk__patch_jump(duk_compiler_ctx *comp_ctx, duk_int_t jump_pc, duk_int_t target_pc); -DUK_LOCAL_DECL void duk__patch_jump_here(duk_compiler_ctx *comp_ctx, duk_int_t jump_pc); -DUK_LOCAL_DECL void duk__patch_trycatch(duk_compiler_ctx *comp_ctx, - duk_int_t ldconst_pc, - duk_int_t trycatch_pc, - duk_regconst_t reg_catch, - duk_regconst_t const_varname, - duk_small_uint_t flags); -DUK_LOCAL_DECL void duk__emit_if_false_skip(duk_compiler_ctx *comp_ctx, duk_regconst_t regconst); -DUK_LOCAL_DECL void duk__emit_if_true_skip(duk_compiler_ctx *comp_ctx, duk_regconst_t regconst); -DUK_LOCAL_DECL void duk__emit_invalid(duk_compiler_ctx *comp_ctx); - -/* ivalue/ispec helpers */ -DUK_LOCAL_DECL void duk__ivalue_regconst(duk_ivalue *x, duk_regconst_t regconst); -DUK_LOCAL_DECL void duk__ivalue_plain_fromstack(duk_compiler_ctx *comp_ctx, duk_ivalue *x); -DUK_LOCAL_DECL void duk__ivalue_var_fromstack(duk_compiler_ctx *comp_ctx, duk_ivalue *x); -DUK_LOCAL_DECL void duk__ivalue_var_hstring(duk_compiler_ctx *comp_ctx, duk_ivalue *x, duk_hstring *h); -DUK_LOCAL_DECL void duk__copy_ispec(duk_compiler_ctx *comp_ctx, duk_ispec *src, duk_ispec *dst); -DUK_LOCAL_DECL void duk__copy_ivalue(duk_compiler_ctx *comp_ctx, duk_ivalue *src, duk_ivalue *dst); -DUK_LOCAL_DECL duk_regconst_t duk__alloctemps(duk_compiler_ctx *comp_ctx, duk_small_int_t num); -DUK_LOCAL_DECL duk_regconst_t duk__alloctemp(duk_compiler_ctx *comp_ctx); -DUK_LOCAL_DECL void duk__settemp_checkmax(duk_compiler_ctx *comp_ctx, duk_regconst_t temp_next); -DUK_LOCAL_DECL duk_regconst_t duk__getconst(duk_compiler_ctx *comp_ctx); -DUK_LOCAL_DECL -duk_regconst_t duk__ispec_toregconst_raw(duk_compiler_ctx *comp_ctx, - duk_ispec *x, - duk_regconst_t forced_reg, - duk_small_uint_t flags); -DUK_LOCAL_DECL void duk__ispec_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ispec *x, duk_regconst_t forced_reg); -DUK_LOCAL_DECL void duk__ivalue_toplain_raw(duk_compiler_ctx *comp_ctx, duk_ivalue *x, duk_regconst_t forced_reg); -DUK_LOCAL_DECL void duk__ivalue_toplain(duk_compiler_ctx *comp_ctx, duk_ivalue *x); -DUK_LOCAL_DECL void duk__ivalue_toplain_ignore(duk_compiler_ctx *comp_ctx, duk_ivalue *x); -DUK_LOCAL_DECL -duk_regconst_t duk__ivalue_toregconst_raw(duk_compiler_ctx *comp_ctx, - duk_ivalue *x, - duk_regconst_t forced_reg, - duk_small_uint_t flags); -DUK_LOCAL_DECL duk_regconst_t duk__ivalue_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *x); -#if 0 /* unused */ -DUK_LOCAL_DECL duk_regconst_t duk__ivalue_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *x); -#endif -DUK_LOCAL_DECL void duk__ivalue_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ivalue *x, duk_int_t forced_reg); -DUK_LOCAL_DECL duk_regconst_t duk__ivalue_toregconst(duk_compiler_ctx *comp_ctx, duk_ivalue *x); -DUK_LOCAL_DECL duk_regconst_t duk__ivalue_totempconst(duk_compiler_ctx *comp_ctx, duk_ivalue *x); - -/* identifier handling */ -DUK_LOCAL_DECL duk_regconst_t duk__lookup_active_register_binding(duk_compiler_ctx *comp_ctx); -DUK_LOCAL_DECL duk_bool_t duk__lookup_lhs(duk_compiler_ctx *ctx, duk_regconst_t *out_reg_varbind, duk_regconst_t *out_rc_varname); - -/* label handling */ -DUK_LOCAL_DECL void duk__add_label(duk_compiler_ctx *comp_ctx, duk_hstring *h_label, duk_int_t pc_label, duk_int_t label_id); -DUK_LOCAL_DECL void duk__update_label_flags(duk_compiler_ctx *comp_ctx, duk_int_t label_id, duk_small_uint_t flags); -DUK_LOCAL_DECL void duk__lookup_active_label(duk_compiler_ctx *comp_ctx, - duk_hstring *h_label, - duk_bool_t is_break, - duk_int_t *out_label_id, - duk_int_t *out_label_catch_depth, - duk_int_t *out_label_pc, - duk_bool_t *out_is_closest); -DUK_LOCAL_DECL void duk__reset_labels_to_length(duk_compiler_ctx *comp_ctx, duk_size_t len); - -/* top-down expression parser */ -DUK_LOCAL_DECL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res); -DUK_LOCAL_DECL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_ivalue *res); -DUK_LOCAL_DECL duk_small_uint_t duk__expr_lbp(duk_compiler_ctx *comp_ctx); -DUK_LOCAL_DECL duk_bool_t duk__expr_is_empty(duk_compiler_ctx *comp_ctx); - -/* exprtop is the top level variant which resets nud/led counts */ -DUK_LOCAL_DECL void duk__expr(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); -DUK_LOCAL_DECL void duk__exprtop(duk_compiler_ctx *ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); - -/* convenience helpers */ -#if 0 /* unused */ -DUK_LOCAL_DECL duk_regconst_t duk__expr_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); -#endif -#if 0 /* unused */ -DUK_LOCAL_DECL duk_regconst_t duk__expr_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); -#endif -DUK_LOCAL_DECL void duk__expr_toforcedreg(duk_compiler_ctx *comp_ctx, - duk_ivalue *res, - duk_small_uint_t rbp_flags, - duk_regconst_t forced_reg); -DUK_LOCAL_DECL duk_regconst_t duk__expr_toregconst(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); -#if 0 /* unused */ -DUK_LOCAL_DECL duk_regconst_t duk__expr_totempconst(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); -#endif -DUK_LOCAL_DECL void duk__expr_toplain(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); -DUK_LOCAL_DECL void duk__expr_toplain_ignore(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); -DUK_LOCAL_DECL duk_regconst_t duk__exprtop_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); -#if 0 /* unused */ -DUK_LOCAL_DECL duk_regconst_t duk__exprtop_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); -#endif -DUK_LOCAL_DECL void duk__exprtop_toforcedreg(duk_compiler_ctx *comp_ctx, - duk_ivalue *res, - duk_small_uint_t rbp_flags, - duk_regconst_t forced_reg); -DUK_LOCAL_DECL duk_regconst_t duk__exprtop_toregconst(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); -#if 0 /* unused */ -DUK_LOCAL_DECL void duk__exprtop_toplain_ignore(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); -#endif - -/* expression parsing helpers */ -DUK_LOCAL_DECL duk_int_t duk__parse_arguments(duk_compiler_ctx *comp_ctx, duk_ivalue *res); -DUK_LOCAL_DECL void duk__nud_array_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *res); -DUK_LOCAL_DECL void duk__nud_object_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *res); - -/* statement parsing */ -DUK_LOCAL_DECL void duk__parse_var_decl(duk_compiler_ctx *comp_ctx, - duk_ivalue *res, - duk_small_uint_t expr_flags, - duk_regconst_t *out_reg_varbind, - duk_regconst_t *out_rc_varname); -DUK_LOCAL_DECL void duk__parse_var_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t expr_flags); -DUK_LOCAL_DECL void duk__parse_for_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site); -DUK_LOCAL_DECL void duk__parse_switch_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site); -DUK_LOCAL_DECL void duk__parse_if_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res); -DUK_LOCAL_DECL void duk__parse_do_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site); -DUK_LOCAL_DECL void duk__parse_while_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site); -DUK_LOCAL_DECL void duk__parse_break_or_continue_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res); -DUK_LOCAL_DECL void duk__parse_return_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res); -DUK_LOCAL_DECL void duk__parse_throw_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res); -DUK_LOCAL_DECL void duk__parse_try_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res); -DUK_LOCAL_DECL void duk__parse_with_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res); -DUK_LOCAL_DECL void duk__parse_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_bool_t allow_source_elem); -DUK_LOCAL_DECL duk_int_t duk__stmt_label_site(duk_compiler_ctx *comp_ctx, duk_int_t label_id); -DUK_LOCAL_DECL void duk__parse_stmts(duk_compiler_ctx *comp_ctx, - duk_bool_t allow_source_elem, - duk_bool_t expect_eof, - duk_bool_t regexp_after); - -DUK_LOCAL_DECL void duk__parse_func_body(duk_compiler_ctx *comp_ctx, - duk_bool_t expect_eof, - duk_bool_t implicit_return_value, - duk_bool_t regexp_after, - duk_small_int_t expect_token); -DUK_LOCAL_DECL void duk__parse_func_formals(duk_compiler_ctx *comp_ctx); -DUK_LOCAL_DECL void duk__parse_func_like_raw(duk_compiler_ctx *comp_ctx, duk_small_uint_t flags); -DUK_LOCAL_DECL duk_int_t duk__parse_func_like_fnum(duk_compiler_ctx *comp_ctx, duk_small_uint_t flags); - -#define DUK__FUNC_FLAG_DECL (1 << 0) /* Parsing a function declaration. */ -#define DUK__FUNC_FLAG_GETSET (1 << 1) /* Parsing an object literal getter/setter. */ -#define DUK__FUNC_FLAG_METDEF (1 << 2) /* Parsing an object literal method definition shorthand. */ -#define DUK__FUNC_FLAG_PUSHNAME_PASS1 (1 << 3) /* Push function name when creating template (first pass only). */ -#define DUK__FUNC_FLAG_USE_PREVTOKEN (1 << 4) /* Use prev_token to start function parsing (workaround for object literal). */ - -/* - * Parser control values for tokens. The token table is ordered by the - * DUK_TOK_XXX defines. - * - * The binding powers are for lbp() use (i.e. for use in led() context). - * Binding powers are positive for typing convenience, and bits at the - * top should be reserved for flags. Binding power step must be higher - * than 1 so that binding power "lbp - 1" can be used for right associative - * operators. Currently a step of 2 is used (which frees one more bit for - * flags). - */ - -/* XXX: actually single step levels would work just fine, clean up */ - -/* binding power "levels" (see doc/compiler.rst) */ -#define DUK__BP_INVALID 0 /* always terminates led() */ -#define DUK__BP_EOF 2 -#define DUK__BP_CLOSING 4 /* token closes expression, e.g. ')', ']' */ -#define DUK__BP_FOR_EXPR DUK__BP_CLOSING /* bp to use when parsing a top level Expression */ -#define DUK__BP_COMMA 6 -#define DUK__BP_ASSIGNMENT 8 -#define DUK__BP_CONDITIONAL 10 -#define DUK__BP_LOR 12 -#define DUK__BP_LAND 14 -#define DUK__BP_BOR 16 -#define DUK__BP_BXOR 18 -#define DUK__BP_BAND 20 -#define DUK__BP_EQUALITY 22 -#define DUK__BP_RELATIONAL 24 -#define DUK__BP_SHIFT 26 -#define DUK__BP_ADDITIVE 28 -#define DUK__BP_MULTIPLICATIVE 30 -#define DUK__BP_EXPONENTIATION 32 -#define DUK__BP_POSTFIX 34 -#define DUK__BP_CALL 36 -#define DUK__BP_MEMBER 38 - -#define DUK__TOKEN_LBP_BP_MASK 0x1f -#define DUK__TOKEN_LBP_FLAG_NO_REGEXP (1 << 5) /* regexp literal must not follow this token */ -#define DUK__TOKEN_LBP_FLAG_TERMINATES (1 << 6) /* terminates expression; e.g. post-increment/-decrement */ -#define DUK__TOKEN_LBP_FLAG_UNUSED (1 << 7) /* unused */ - -#define DUK__TOKEN_LBP_GET_BP(x) ((duk_small_uint_t) (((x) &DUK__TOKEN_LBP_BP_MASK) * 2)) - -#define DUK__MK_LBP(bp) ((bp) >> 1) /* bp is assumed to be even */ -#define DUK__MK_LBP_FLAGS(bp, flags) (((bp) >> 1) | (flags)) - -DUK_LOCAL const duk_uint8_t duk__token_lbp[] = { - DUK__MK_LBP(DUK__BP_EOF), /* DUK_TOK_EOF */ - DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_IDENTIFIER */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_BREAK */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_CASE */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_CATCH */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_CONTINUE */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_DEBUGGER */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_DEFAULT */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_DELETE */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_DO */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_ELSE */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_FINALLY */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_FOR */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_FUNCTION */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_IF */ - DUK__MK_LBP(DUK__BP_RELATIONAL), /* DUK_TOK_IN */ - DUK__MK_LBP(DUK__BP_RELATIONAL), /* DUK_TOK_INSTANCEOF */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_NEW */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_RETURN */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_SWITCH */ - DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_THIS */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_THROW */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_TRY */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_TYPEOF */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_VAR */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_CONST */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_VOID */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_WHILE */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_WITH */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_CLASS */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_ENUM */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_EXPORT */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_EXTENDS */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_IMPORT */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_SUPER */ - DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_NULL */ - DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_TRUE */ - DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_FALSE */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_GET */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_SET */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_IMPLEMENTS */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_INTERFACE */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_LET */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_PACKAGE */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_PRIVATE */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_PROTECTED */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_PUBLIC */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_STATIC */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_YIELD */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_LCURLY */ - DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_RCURLY */ - DUK__MK_LBP(DUK__BP_MEMBER), /* DUK_TOK_LBRACKET */ - DUK__MK_LBP_FLAGS(DUK__BP_CLOSING, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_RBRACKET */ - DUK__MK_LBP(DUK__BP_CALL), /* DUK_TOK_LPAREN */ - DUK__MK_LBP_FLAGS(DUK__BP_CLOSING, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_RPAREN */ - DUK__MK_LBP(DUK__BP_MEMBER), /* DUK_TOK_PERIOD */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_SEMICOLON */ - DUK__MK_LBP(DUK__BP_COMMA), /* DUK_TOK_COMMA */ - DUK__MK_LBP(DUK__BP_RELATIONAL), /* DUK_TOK_LT */ - DUK__MK_LBP(DUK__BP_RELATIONAL), /* DUK_TOK_GT */ - DUK__MK_LBP(DUK__BP_RELATIONAL), /* DUK_TOK_LE */ - DUK__MK_LBP(DUK__BP_RELATIONAL), /* DUK_TOK_GE */ - DUK__MK_LBP(DUK__BP_EQUALITY), /* DUK_TOK_EQ */ - DUK__MK_LBP(DUK__BP_EQUALITY), /* DUK_TOK_NEQ */ - DUK__MK_LBP(DUK__BP_EQUALITY), /* DUK_TOK_SEQ */ - DUK__MK_LBP(DUK__BP_EQUALITY), /* DUK_TOK_SNEQ */ - DUK__MK_LBP(DUK__BP_ADDITIVE), /* DUK_TOK_ADD */ - DUK__MK_LBP(DUK__BP_ADDITIVE), /* DUK_TOK_SUB */ - DUK__MK_LBP(DUK__BP_MULTIPLICATIVE), /* DUK_TOK_MUL */ - DUK__MK_LBP(DUK__BP_MULTIPLICATIVE), /* DUK_TOK_DIV */ - DUK__MK_LBP(DUK__BP_MULTIPLICATIVE), /* DUK_TOK_MOD */ - DUK__MK_LBP(DUK__BP_EXPONENTIATION), /* DUK_TOK_EXP */ - DUK__MK_LBP_FLAGS(DUK__BP_POSTFIX, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_INCREMENT */ - DUK__MK_LBP_FLAGS(DUK__BP_POSTFIX, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_DECREMENT */ - DUK__MK_LBP(DUK__BP_SHIFT), /* DUK_TOK_ALSHIFT */ - DUK__MK_LBP(DUK__BP_SHIFT), /* DUK_TOK_ARSHIFT */ - DUK__MK_LBP(DUK__BP_SHIFT), /* DUK_TOK_RSHIFT */ - DUK__MK_LBP(DUK__BP_BAND), /* DUK_TOK_BAND */ - DUK__MK_LBP(DUK__BP_BOR), /* DUK_TOK_BOR */ - DUK__MK_LBP(DUK__BP_BXOR), /* DUK_TOK_BXOR */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_LNOT */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_BNOT */ - DUK__MK_LBP(DUK__BP_LAND), /* DUK_TOK_LAND */ - DUK__MK_LBP(DUK__BP_LOR), /* DUK_TOK_LOR */ - DUK__MK_LBP(DUK__BP_CONDITIONAL), /* DUK_TOK_QUESTION */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_COLON */ - DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_EQUALSIGN */ - DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_ADD_EQ */ - DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_SUB_EQ */ - DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_MUL_EQ */ - DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_DIV_EQ */ - DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_MOD_EQ */ - DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_EXP_EQ */ - DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_ALSHIFT_EQ */ - DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_ARSHIFT_EQ */ - DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_RSHIFT_EQ */ - DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_BAND_EQ */ - DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_BOR_EQ */ - DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_BXOR_EQ */ - DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_NUMBER */ - DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_STRING */ - DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_REGEXP */ -}; - -/* - * Misc helpers - */ - -DUK_LOCAL void duk__comp_recursion_increase(duk_compiler_ctx *comp_ctx) { - DUK_ASSERT(comp_ctx != NULL); - DUK_ASSERT(comp_ctx->recursion_depth >= 0); - if (comp_ctx->recursion_depth >= comp_ctx->recursion_limit) { - DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_COMPILER_RECURSION_LIMIT); - DUK_WO_NORETURN(return;); - } - comp_ctx->recursion_depth++; -} - -DUK_LOCAL void duk__comp_recursion_decrease(duk_compiler_ctx *comp_ctx) { - DUK_ASSERT(comp_ctx != NULL); - DUK_ASSERT(comp_ctx->recursion_depth > 0); - comp_ctx->recursion_depth--; -} - -DUK_LOCAL duk_bool_t duk__hstring_is_eval_or_arguments(duk_compiler_ctx *comp_ctx, duk_hstring *h) { - DUK_UNREF(comp_ctx); - DUK_ASSERT(h != NULL); - return DUK_HSTRING_HAS_EVAL_OR_ARGUMENTS(h); -} - -DUK_LOCAL duk_bool_t duk__hstring_is_eval_or_arguments_in_strict_mode(duk_compiler_ctx *comp_ctx, duk_hstring *h) { - DUK_ASSERT(h != NULL); - return (comp_ctx->curr_func.is_strict && DUK_HSTRING_HAS_EVAL_OR_ARGUMENTS(h)); -} - -/* - * Parser duk__advance() token eating functions - */ - -/* XXX: valstack handling is awkward. Add a valstack helper which - * avoids dup():ing; valstack_copy(src, dst)? - */ - -DUK_LOCAL void duk__advance_helper(duk_compiler_ctx *comp_ctx, duk_small_int_t expect) { - duk_hthread *thr = comp_ctx->thr; - duk_bool_t regexp; - - DUK_ASSERT_DISABLE(comp_ctx->curr_token.t >= 0); /* unsigned */ - DUK_ASSERT(comp_ctx->curr_token.t <= DUK_TOK_MAXVAL); /* MAXVAL is inclusive */ - - /* - * Use current token to decide whether a RegExp can follow. - * - * We can use either 't' or 't_nores'; the latter would not - * recognize keywords. Some keywords can be followed by a - * RegExp (e.g. "return"), so using 't' is better. This is - * not trivial, see doc/compiler.rst. - */ - - regexp = 1; - if (duk__token_lbp[comp_ctx->curr_token.t] & DUK__TOKEN_LBP_FLAG_NO_REGEXP) { - regexp = 0; - } - if (comp_ctx->curr_func.reject_regexp_in_adv) { - comp_ctx->curr_func.reject_regexp_in_adv = 0; - regexp = 0; - } - if (comp_ctx->curr_func.allow_regexp_in_adv) { - comp_ctx->curr_func.allow_regexp_in_adv = 0; - regexp = 1; - } - - if (expect >= 0 && comp_ctx->curr_token.t != (duk_small_uint_t) expect) { - DUK_D(DUK_DPRINT("parse error: expect=%ld, got=%ld", (long) expect, (long) comp_ctx->curr_token.t)); - DUK_ERROR_SYNTAX(thr, DUK_STR_PARSE_ERROR); - DUK_WO_NORETURN(return;); - } - - /* make current token the previous; need to fiddle with valstack "backing store" */ - duk_memcpy(&comp_ctx->prev_token, &comp_ctx->curr_token, sizeof(duk_token)); - duk_copy(thr, comp_ctx->tok11_idx, comp_ctx->tok21_idx); - duk_copy(thr, comp_ctx->tok12_idx, comp_ctx->tok22_idx); - - /* parse new token */ - duk_lexer_parse_js_input_element(&comp_ctx->lex, &comp_ctx->curr_token, comp_ctx->curr_func.is_strict, regexp); - - DUK_DDD(DUK_DDDPRINT("advance: curr: tok=%ld/%ld,%ld,term=%ld,%!T,%!T " - "prev: tok=%ld/%ld,%ld,term=%ld,%!T,%!T", - (long) comp_ctx->curr_token.t, - (long) comp_ctx->curr_token.t_nores, - (long) comp_ctx->curr_token.start_line, - (long) comp_ctx->curr_token.lineterm, - (duk_tval *) duk_get_tval(thr, comp_ctx->tok11_idx), - (duk_tval *) duk_get_tval(thr, comp_ctx->tok12_idx), - (long) comp_ctx->prev_token.t, - (long) comp_ctx->prev_token.t_nores, - (long) comp_ctx->prev_token.start_line, - (long) comp_ctx->prev_token.lineterm, - (duk_tval *) duk_get_tval(thr, comp_ctx->tok21_idx), - (duk_tval *) duk_get_tval(thr, comp_ctx->tok22_idx))); -} - -/* advance, expecting current token to be a specific token; parse next token in regexp context */ -DUK_LOCAL void duk__advance_expect(duk_compiler_ctx *comp_ctx, duk_small_int_t expect) { - duk__advance_helper(comp_ctx, expect); -} - -/* advance, whatever the current token is; parse next token in regexp context */ -DUK_LOCAL void duk__advance(duk_compiler_ctx *comp_ctx) { - duk__advance_helper(comp_ctx, -1); -} - -/* - * Helpers for duk_compiler_func. - */ - -/* init function state: inits valstack allocations */ -DUK_LOCAL void duk__init_func_valstack_slots(duk_compiler_ctx *comp_ctx) { - duk_compiler_func *func = &comp_ctx->curr_func; - duk_hthread *thr = comp_ctx->thr; - duk_idx_t entry_top; - - entry_top = duk_get_top(thr); - - duk_memzero(func, sizeof(*func)); /* intentional overlap with earlier memzero */ -#if defined(DUK_USE_EXPLICIT_NULL_INIT) - func->h_name = NULL; - func->h_consts = NULL; - func->h_funcs = NULL; - func->h_decls = NULL; - func->h_labelnames = NULL; - func->h_labelinfos = NULL; - func->h_argnames = NULL; - func->h_varmap = NULL; -#endif - - duk_require_stack(thr, DUK__FUNCTION_INIT_REQUIRE_SLOTS); - - DUK_BW_INIT_PUSHBUF(thr, &func->bw_code, DUK__BC_INITIAL_INSTS * sizeof(duk_compiler_instr)); - /* code_idx = entry_top + 0 */ - - duk_push_bare_array(thr); - func->consts_idx = entry_top + 1; - func->h_consts = DUK_GET_HOBJECT_POSIDX(thr, entry_top + 1); - DUK_ASSERT(func->h_consts != NULL); - - duk_push_bare_array(thr); - func->funcs_idx = entry_top + 2; - func->h_funcs = DUK_GET_HOBJECT_POSIDX(thr, entry_top + 2); - DUK_ASSERT(func->h_funcs != NULL); - DUK_ASSERT(func->fnum_next == 0); - - duk_push_bare_array(thr); - func->decls_idx = entry_top + 3; - func->h_decls = DUK_GET_HOBJECT_POSIDX(thr, entry_top + 3); - DUK_ASSERT(func->h_decls != NULL); - - duk_push_bare_array(thr); - func->labelnames_idx = entry_top + 4; - func->h_labelnames = DUK_GET_HOBJECT_POSIDX(thr, entry_top + 4); - DUK_ASSERT(func->h_labelnames != NULL); - - duk_push_dynamic_buffer(thr, 0); - func->labelinfos_idx = entry_top + 5; - func->h_labelinfos = (duk_hbuffer_dynamic *) duk_known_hbuffer(thr, entry_top + 5); - DUK_ASSERT(func->h_labelinfos != NULL); - DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(func->h_labelinfos) && !DUK_HBUFFER_HAS_EXTERNAL(func->h_labelinfos)); - - duk_push_bare_array(thr); - func->argnames_idx = entry_top + 6; - func->h_argnames = DUK_GET_HOBJECT_POSIDX(thr, entry_top + 6); - DUK_ASSERT(func->h_argnames != NULL); - - duk_push_bare_object(thr); - func->varmap_idx = entry_top + 7; - func->h_varmap = DUK_GET_HOBJECT_POSIDX(thr, entry_top + 7); - DUK_ASSERT(func->h_varmap != NULL); -} - -/* reset function state (prepare for pass 2) */ -DUK_LOCAL void duk__reset_func_for_pass2(duk_compiler_ctx *comp_ctx) { - duk_compiler_func *func = &comp_ctx->curr_func; - duk_hthread *thr = comp_ctx->thr; - - /* reset bytecode buffer but keep current size; pass 2 will - * require same amount or more. - */ - DUK_BW_RESET_SIZE(thr, &func->bw_code); - - duk_set_length(thr, func->consts_idx, 0); - /* keep func->h_funcs; inner functions are not reparsed to avoid O(depth^2) parsing */ - func->fnum_next = 0; - /* duk_set_length(thr, func->funcs_idx, 0); */ - duk_set_length(thr, func->labelnames_idx, 0); - duk_hbuffer_reset(thr, func->h_labelinfos); - /* keep func->h_argnames; it is fixed for all passes */ - - /* truncated in case pass 3 needed */ - duk_push_bare_object(thr); - duk_replace(thr, func->varmap_idx); - func->h_varmap = DUK_GET_HOBJECT_POSIDX(thr, func->varmap_idx); - DUK_ASSERT(func->h_varmap != NULL); -} - -/* cleanup varmap from any null entries, compact it, etc; returns number - * of final entries after cleanup. - */ -DUK_LOCAL duk_int_t duk__cleanup_varmap(duk_compiler_ctx *comp_ctx) { - duk_hthread *thr = comp_ctx->thr; - duk_hobject *h_varmap; - duk_hstring *h_key; - duk_tval *tv; - duk_uint32_t i, e_next; - duk_int_t ret; - - /* [ ... varmap ] */ - - h_varmap = DUK_GET_HOBJECT_NEGIDX(thr, -1); - DUK_ASSERT(h_varmap != NULL); - - ret = 0; - e_next = DUK_HOBJECT_GET_ENEXT(h_varmap); - for (i = 0; i < e_next; i++) { - h_key = DUK_HOBJECT_E_GET_KEY(thr->heap, h_varmap, i); - if (!h_key) { - continue; - } - - DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, h_varmap, i)); - - /* The entries can either be register numbers or 'null' values. - * Thus, no need to DECREF them and get side effects. DECREF'ing - * the keys (strings) can cause memory to be freed but no side - * effects as strings don't have finalizers. This is why we can - * rely on the object properties not changing from underneath us. - */ - - tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, h_varmap, i); - if (!DUK_TVAL_IS_NUMBER(tv)) { - DUK_ASSERT(!DUK_TVAL_IS_HEAP_ALLOCATED(tv)); - DUK_HOBJECT_E_SET_KEY(thr->heap, h_varmap, i, NULL); - DUK_HSTRING_DECREF(thr, h_key); - /* when key is NULL, value is garbage so no need to set */ - } else { - ret++; - } - } - - duk_compact_m1(thr); - - return ret; -} - -/* Convert duk_compiler_func into a function template, leaving the result - * on top of stack. - */ -/* XXX: awkward and bloated asm -- use faster internal accesses */ -DUK_LOCAL void duk__convert_to_func_template(duk_compiler_ctx *comp_ctx) { - duk_compiler_func *func = &comp_ctx->curr_func; - duk_hthread *thr = comp_ctx->thr; - duk_hcompfunc *h_res; - duk_hbuffer_fixed *h_data; - duk_size_t consts_count; - duk_size_t funcs_count; - duk_size_t code_count; - duk_size_t code_size; - duk_size_t data_size; - duk_size_t i; - duk_tval *p_const; - duk_hobject **p_func; - duk_instr_t *p_instr; - duk_compiler_instr *q_instr; - duk_tval *tv; - duk_bool_t keep_varmap; - duk_bool_t keep_formals; -#if !defined(DUK_USE_DEBUGGER_SUPPORT) - duk_size_t formals_length; -#endif - - DUK_DDD(DUK_DDDPRINT("converting duk_compiler_func to function/template")); - - /* - * Push result object and init its flags - */ - - /* Valstack should suffice here, required on function valstack init */ - - h_res = duk_push_hcompfunc(thr); - DUK_ASSERT(h_res != NULL); - DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) h_res) == thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]); - DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, (duk_hobject *) h_res, NULL); /* Function templates are "bare objects". */ - - if (func->is_function) { - DUK_DDD(DUK_DDDPRINT("function -> set NEWENV")); - DUK_HOBJECT_SET_NEWENV((duk_hobject *) h_res); - - if (!func->is_arguments_shadowed) { - /* arguments object would be accessible; note that shadowing - * bindings are arguments or function declarations, neither - * of which are deletable, so this is safe. - */ - - if (func->id_access_arguments || func->may_direct_eval) { - DUK_DDD(DUK_DDDPRINT("function may access 'arguments' object directly or " - "indirectly -> set CREATEARGS")); - DUK_HOBJECT_SET_CREATEARGS((duk_hobject *) h_res); - } - } - } else if (func->is_eval && func->is_strict) { - DUK_DDD(DUK_DDDPRINT("strict eval code -> set NEWENV")); - DUK_HOBJECT_SET_NEWENV((duk_hobject *) h_res); - } else { - /* non-strict eval: env is caller's env or global env (direct vs. indirect call) - * global code: env is is global env - */ - DUK_DDD(DUK_DDDPRINT("non-strict eval code or global code -> no NEWENV")); - DUK_ASSERT(!DUK_HOBJECT_HAS_NEWENV((duk_hobject *) h_res)); - } - -#if defined(DUK_USE_FUNC_NAME_PROPERTY) - if (func->is_function && func->is_namebinding && func->h_name != NULL) { - /* Object literal set/get functions have a name (property - * name) but must not have a lexical name binding, see - * test-bug-getset-func-name.js. - */ - DUK_DDD(DUK_DDDPRINT("function expression with a name -> set NAMEBINDING")); - DUK_HOBJECT_SET_NAMEBINDING((duk_hobject *) h_res); - } -#endif - - if (func->is_strict) { - DUK_DDD(DUK_DDDPRINT("function is strict -> set STRICT")); - DUK_HOBJECT_SET_STRICT((duk_hobject *) h_res); - } - - if (func->is_notail) { - DUK_DDD(DUK_DDDPRINT("function is notail -> set NOTAIL")); - DUK_HOBJECT_SET_NOTAIL((duk_hobject *) h_res); - } - - if (func->is_constructable) { - DUK_DDD(DUK_DDDPRINT("function is constructable -> set CONSTRUCTABLE")); - DUK_HOBJECT_SET_CONSTRUCTABLE((duk_hobject *) h_res); - } - - /* - * Build function fixed size 'data' buffer, which contains bytecode, - * constants, and inner function references. - * - * During the building phase 'data' is reachable but incomplete. - * Only incref's occur during building (no refzero or GC happens), - * so the building process is atomic. - */ - - consts_count = duk_hobject_get_length(thr, func->h_consts); - funcs_count = duk_hobject_get_length(thr, func->h_funcs) / 3; - code_count = DUK_BW_GET_SIZE(thr, &func->bw_code) / sizeof(duk_compiler_instr); - code_size = code_count * sizeof(duk_instr_t); - - data_size = consts_count * sizeof(duk_tval) + funcs_count * sizeof(duk_hobject *) + code_size; - - DUK_DDD(DUK_DDDPRINT("consts_count=%ld, funcs_count=%ld, code_size=%ld -> " - "data_size=%ld*%ld + %ld*%ld + %ld = %ld", - (long) consts_count, - (long) funcs_count, - (long) code_size, - (long) consts_count, - (long) sizeof(duk_tval), - (long) funcs_count, - (long) sizeof(duk_hobject *), - (long) code_size, - (long) data_size)); - - duk_push_fixed_buffer_nozero(thr, data_size); - h_data = (duk_hbuffer_fixed *) (void *) duk_known_hbuffer(thr, -1); - - DUK_HCOMPFUNC_SET_DATA(thr->heap, h_res, (duk_hbuffer *) h_data); - DUK_HEAPHDR_INCREF(thr, h_data); - - p_const = (duk_tval *) (void *) DUK_HBUFFER_FIXED_GET_DATA_PTR(thr->heap, h_data); - for (i = 0; i < consts_count; i++) { - DUK_ASSERT(i <= DUK_UARRIDX_MAX); /* const limits */ - tv = duk_hobject_find_array_entry_tval_ptr(thr->heap, func->h_consts, (duk_uarridx_t) i); - DUK_ASSERT(tv != NULL); - DUK_TVAL_SET_TVAL(p_const, tv); - p_const++; - DUK_TVAL_INCREF(thr, tv); /* may be a string constant */ - - DUK_DDD(DUK_DDDPRINT("constant: %!T", (duk_tval *) tv)); - } - - p_func = (duk_hobject **) p_const; - DUK_HCOMPFUNC_SET_FUNCS(thr->heap, h_res, p_func); - for (i = 0; i < funcs_count; i++) { - duk_hobject *h; - DUK_ASSERT(i * 3 <= DUK_UARRIDX_MAX); /* func limits */ - tv = duk_hobject_find_array_entry_tval_ptr(thr->heap, func->h_funcs, (duk_uarridx_t) (i * 3)); - DUK_ASSERT(tv != NULL); - DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv)); - h = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(h)); - *p_func++ = h; - DUK_HOBJECT_INCREF(thr, h); - - DUK_DDD(DUK_DDDPRINT("inner function: %p -> %!iO", (void *) h, (duk_heaphdr *) h)); - } - - p_instr = (duk_instr_t *) p_func; - DUK_HCOMPFUNC_SET_BYTECODE(thr->heap, h_res, p_instr); - - /* copy bytecode instructions one at a time */ - q_instr = (duk_compiler_instr *) (void *) DUK_BW_GET_BASEPTR(thr, &func->bw_code); - for (i = 0; i < code_count; i++) { - p_instr[i] = q_instr[i].ins; - } - /* Note: 'q_instr' is still used below */ - - DUK_ASSERT((duk_uint8_t *) (p_instr + code_count) == DUK_HBUFFER_FIXED_GET_DATA_PTR(thr->heap, h_data) + data_size); - - duk_pop(thr); /* 'data' (and everything in it) is reachable through h_res now */ - - /* - * Init non-property result fields - * - * 'nregs' controls how large a register frame is allocated. - * - * 'nargs' controls how many formal arguments are written to registers: - * r0, ... r(nargs-1). The remaining registers are initialized to - * undefined. - */ - - DUK_ASSERT(func->temp_max >= 0); - h_res->nregs = (duk_uint16_t) func->temp_max; - h_res->nargs = (duk_uint16_t) duk_hobject_get_length(thr, func->h_argnames); - DUK_ASSERT(h_res->nregs >= h_res->nargs); /* pass2 allocation handles this */ -#if defined(DUK_USE_DEBUGGER_SUPPORT) - h_res->start_line = (duk_uint32_t) func->min_line; - h_res->end_line = (duk_uint32_t) func->max_line; -#endif - - /* - * Init object properties - * - * Properties should be added in decreasing order of access frequency. - * (Not very critical for function templates.) - */ - - DUK_DDD(DUK_DDDPRINT("init function properties")); - - /* [ ... res ] */ - - /* _Varmap: omitted if function is guaranteed not to do a slow path - * identifier access that might be caught by locally declared variables. - * The varmap can also be omitted if it turns out empty of actual - * register mappings after a cleanup. When debugging is enabled, we - * always need the varmap to be able to lookup variables at any point. - */ - -#if defined(DUK_USE_DEBUGGER_SUPPORT) - DUK_DD(DUK_DDPRINT("keeping _Varmap because debugger support is enabled")); - keep_varmap = 1; -#else - if (func->id_access_slow_own || /* directly uses slow accesses that may match own variables */ - func->id_access_arguments || /* accesses 'arguments' directly */ - func->may_direct_eval || /* may indirectly slow access through a direct eval */ - funcs_count > - 0) { /* has inner functions which may slow access (XXX: this can be optimized by looking at the inner functions) */ - DUK_DD(DUK_DDPRINT("keeping _Varmap because of direct eval, slow path access that may match local variables, or " - "presence of inner functions")); - keep_varmap = 1; - } else { - DUK_DD(DUK_DDPRINT("dropping _Varmap")); - keep_varmap = 0; - } -#endif - - if (keep_varmap) { - duk_int_t num_used; - duk_dup(thr, func->varmap_idx); - num_used = duk__cleanup_varmap(comp_ctx); - DUK_DDD(DUK_DDDPRINT("cleaned up varmap: %!T (num_used=%ld)", (duk_tval *) duk_get_tval(thr, -1), (long) num_used)); - - if (num_used > 0) { - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VARMAP, DUK_PROPDESC_FLAGS_NONE); - } else { - DUK_DD(DUK_DDPRINT("varmap is empty after cleanup -> no need to add")); - duk_pop(thr); - } - } - - /* _Formals: omitted if function is guaranteed not to need a (non-strict) - * arguments object, and _Formals.length matches nargs exactly. - * - * Non-arrow functions can't see an outer function's 'argument' binding - * (because they have their own), but arrow functions can. When arrow - * functions are added, this condition would need to be added: - * inner_arrow_funcs_count > 0 inner arrow functions may access 'arguments' - */ -#if defined(DUK_USE_DEBUGGER_SUPPORT) - DUK_DD(DUK_DDPRINT("keeping _Formals because debugger support is enabled")); - keep_formals = 1; -#else - formals_length = duk_get_length(thr, func->argnames_idx); - if (formals_length != (duk_size_t) h_res->nargs) { - /* Nargs not enough for closure .length: keep _Formals regardless - * of its length. Shouldn't happen in practice at the moment. - */ - DUK_DD(DUK_DDPRINT("keeping _Formals because _Formals.length != nargs")); - keep_formals = 1; - } else if ((func->id_access_arguments || func->may_direct_eval) && (formals_length > 0)) { - /* Direct eval (may access 'arguments') or accesses 'arguments' - * explicitly: keep _Formals unless it is zero length. - */ - DUK_DD(DUK_DDPRINT( - "keeping _Formals because of direct eval or explicit access to 'arguments', and _Formals.length != 0")); - keep_formals = 1; - } else { - DUK_DD(DUK_DDPRINT("omitting _Formals, nargs matches _Formals.length, so no properties added")); - keep_formals = 0; - } -#endif - - if (keep_formals) { - duk_dup(thr, func->argnames_idx); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_FORMALS, DUK_PROPDESC_FLAGS_NONE); - } - - /* name */ -#if defined(DUK_USE_FUNC_NAME_PROPERTY) - if (func->h_name) { - duk_push_hstring(thr, func->h_name); - DUK_DD(DUK_DDPRINT("setting function template .name to %!T", duk_get_tval(thr, -1))); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE); - } -#endif /* DUK_USE_FUNC_NAME_PROPERTY */ - - /* _Source */ -#if defined(DUK_USE_NONSTD_FUNC_SOURCE_PROPERTY) - if (0) { - /* XXX: Currently function source code is not stored, as it is not - * required by the standard. Source code should not be stored by - * default (user should enable it explicitly), and the source should - * probably be compressed with a trivial text compressor; average - * compression of 20-30% is quite easy to achieve even with a trivial - * compressor (RLE + backwards lookup). - * - * Debugging needs source code to be useful: sometimes input code is - * not found in files as it may be generated and then eval()'d, given - * by dynamic C code, etc. - * - * Other issues: - * - * - Need tokenizer indices for start and end to substring - * - Always normalize function declaration part? - * - If we keep _Formals, only need to store body - */ - - /* - * For global or eval code this is straightforward. For functions - * created with the Function constructor we only get the source for - * the body and must manufacture the "function ..." part. - * - * For instance, for constructed functions (v8): - * - * > a = new Function("foo", "bar", "print(foo)"); - * [Function] - * > a.toString() - * 'function anonymous(foo,bar) {\nprint(foo)\n}' - * - * Similarly for e.g. getters (v8): - * - * > x = { get a(foo,bar) { print(foo); } } - * { a: [Getter] } - * > Object.getOwnPropertyDescriptor(x, 'a').get.toString() - * 'function a(foo,bar) { print(foo); }' - */ - -#if 0 - duk_push_literal(thr, "XXX"); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_SOURCE, DUK_PROPDESC_FLAGS_NONE); -#endif - } -#endif /* DUK_USE_NONSTD_FUNC_SOURCE_PROPERTY */ - - /* _Pc2line */ -#if defined(DUK_USE_PC2LINE) - if (1) { - /* - * Size-optimized pc->line mapping. - */ - - DUK_ASSERT(code_count <= DUK_COMPILER_MAX_BYTECODE_LENGTH); - duk_hobject_pc2line_pack(thr, q_instr, (duk_uint_fast32_t) code_count); /* -> pushes fixed buffer */ - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_PC2LINE, DUK_PROPDESC_FLAGS_NONE); - - /* XXX: if assertions enabled, walk through all valid PCs - * and check line mapping. - */ - } -#endif /* DUK_USE_PC2LINE */ - - /* fileName */ -#if defined(DUK_USE_FUNC_FILENAME_PROPERTY) - if (comp_ctx->h_filename) { - /* - * Source filename (or equivalent), for identifying thrown errors. - */ - - duk_push_hstring(thr, comp_ctx->h_filename); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_NONE); - } -#endif - - DUK_DD(DUK_DDPRINT("converted function: %!ixT", (duk_tval *) duk_get_tval(thr, -1))); - - /* - * Compact the function template. - */ - - duk_compact_m1(thr); - - /* - * Debug dumping - */ - -#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2) - { - duk_hcompfunc *h; - duk_instr_t *p, *p_start, *p_end; - - h = (duk_hcompfunc *) duk_get_hobject(thr, -1); - p_start = (duk_instr_t *) DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, h); - p_end = (duk_instr_t *) DUK_HCOMPFUNC_GET_CODE_END(thr->heap, h); - - p = p_start; - while (p < p_end) { - DUK_DDD(DUK_DDDPRINT("BC %04ld: %!I ; 0x%08lx op=%ld (%!X) a=%ld b=%ld c=%ld", - (long) (p - p_start), - (duk_instr_t) (*p), - (unsigned long) (*p), - (long) DUK_DEC_OP(*p), - (long) DUK_DEC_OP(*p), - (long) DUK_DEC_A(*p), - (long) DUK_DEC_B(*p), - (long) DUK_DEC_C(*p))); - p++; - } - } -#endif -} - -/* - * Code emission helpers - * - * Some emission helpers understand the range of target and source reg/const - * values and automatically emit shuffling code if necessary. This is the - * case when the slot in question (A, B, C) is used in the standard way and - * for opcodes the emission helpers explicitly understand (like DUK_OP_MPUTOBJ). - * - * The standard way is that: - * - slot A is a target register - * - slot B is a source register/constant - * - slot C is a source register/constant - * - * If a slot is used in a non-standard way the caller must indicate this - * somehow. If a slot is used as a target instead of a source (or vice - * versa), this can be indicated with a flag to trigger proper shuffling - * (e.g. DUK__EMIT_FLAG_B_IS_TARGET). If the value in the slot is not - * register/const related at all, the caller must ensure that the raw value - * fits into the corresponding slot so as to not trigger shuffling. The - * caller must set a "no shuffle" flag to ensure compilation fails if - * shuffling were to be triggered because of an internal error. - * - * For slots B and C the raw slot size is 9 bits but one bit is reserved for - * the reg/const indicator. To use the full 9-bit range for a raw value, - * shuffling must be disabled with the DUK__EMIT_FLAG_NO_SHUFFLE_{B,C} flag. - * Shuffling is only done for A, B, and C slots, not the larger BC or ABC slots. - * - * There is call handling specific understanding in the A-B-C emitter to - * convert call setup and call instructions into indirect ones if necessary. - */ - -/* Code emission flags, passed in the 'opcode' field. Opcode + flags - * fit into 16 bits for now, so use duk_small_uint_t. - */ -#define DUK__EMIT_FLAG_NO_SHUFFLE_A (1 << 8) -#define DUK__EMIT_FLAG_NO_SHUFFLE_B (1 << 9) -#define DUK__EMIT_FLAG_NO_SHUFFLE_C (1 << 10) -#define DUK__EMIT_FLAG_A_IS_SOURCE (1 << 11) /* slot A is a source (default: target) */ -#define DUK__EMIT_FLAG_B_IS_TARGET (1 << 12) /* slot B is a target (default: source) */ -#define DUK__EMIT_FLAG_C_IS_TARGET (1 << 13) /* slot C is a target (default: source) */ -#define DUK__EMIT_FLAG_BC_REGCONST (1 << 14) /* slots B and C are reg/const */ -#define DUK__EMIT_FLAG_RESERVE_JUMPSLOT (1 << 15) /* reserve a jumpslot after instr before target spilling, used for NEXTENUM */ - -/* XXX: macro smaller than call? */ -DUK_LOCAL duk_int_t duk__get_current_pc(duk_compiler_ctx *comp_ctx) { - duk_compiler_func *func; - func = &comp_ctx->curr_func; - return (duk_int_t) (DUK_BW_GET_SIZE(comp_ctx->thr, &func->bw_code) / sizeof(duk_compiler_instr)); -} - -DUK_LOCAL duk_compiler_instr *duk__get_instr_ptr(duk_compiler_ctx *comp_ctx, duk_int_t pc) { - DUK_ASSERT(pc >= 0); - DUK_ASSERT((duk_size_t) pc < - (duk_size_t) (DUK_BW_GET_SIZE(comp_ctx->thr, &comp_ctx->curr_func.bw_code) / sizeof(duk_compiler_instr))); - return ((duk_compiler_instr *) (void *) DUK_BW_GET_BASEPTR(comp_ctx->thr, &comp_ctx->curr_func.bw_code)) + pc; -} - -/* emit instruction; could return PC but that's not needed in the majority - * of cases. - */ -DUK_LOCAL void duk__emit(duk_compiler_ctx *comp_ctx, duk_instr_t ins) { -#if defined(DUK_USE_PC2LINE) - duk_int_t line; -#endif - duk_compiler_instr *instr; - - DUK_DDD(DUK_DDDPRINT("duk__emit: 0x%08lx curr_token.start_line=%ld prev_token.start_line=%ld pc=%ld --> %!I", - (unsigned long) ins, - (long) comp_ctx->curr_token.start_line, - (long) comp_ctx->prev_token.start_line, - (long) duk__get_current_pc(comp_ctx), - (duk_instr_t) ins)); - - instr = (duk_compiler_instr *) (void *) DUK_BW_ENSURE_GETPTR(comp_ctx->thr, - &comp_ctx->curr_func.bw_code, - sizeof(duk_compiler_instr)); - DUK_BW_ADD_PTR(comp_ctx->thr, &comp_ctx->curr_func.bw_code, sizeof(duk_compiler_instr)); - -#if defined(DUK_USE_PC2LINE) - /* The line number tracking is a bit inconsistent right now, which - * affects debugger accuracy. Mostly call sites emit opcodes when - * they have parsed a token (say a terminating semicolon) and called - * duk__advance(). In this case the line number of the previous - * token is the most accurate one (except in prologue where - * prev_token.start_line is 0). This is probably not 100% correct - * right now. - */ - /* approximation, close enough */ - line = comp_ctx->prev_token.start_line; - if (line == 0) { - line = comp_ctx->curr_token.start_line; - } -#endif - - instr->ins = ins; -#if defined(DUK_USE_PC2LINE) - instr->line = (duk_uint32_t) line; -#endif -#if defined(DUK_USE_DEBUGGER_SUPPORT) - if (line < comp_ctx->curr_func.min_line) { - comp_ctx->curr_func.min_line = line; - } - if (line > comp_ctx->curr_func.max_line) { - comp_ctx->curr_func.max_line = line; - } -#endif - - /* Limit checks for bytecode byte size and line number. */ - if (DUK_UNLIKELY(DUK_BW_GET_SIZE(comp_ctx->thr, &comp_ctx->curr_func.bw_code) > DUK_USE_ESBC_MAX_BYTES)) { - goto fail_bc_limit; - } -#if defined(DUK_USE_PC2LINE) && defined(DUK_USE_ESBC_LIMITS) -#if defined(DUK_USE_BUFLEN16) - /* Buffer length is bounded to 0xffff automatically, avoid compile warning. */ - if (DUK_UNLIKELY(line > DUK_USE_ESBC_MAX_LINENUMBER)) { - goto fail_bc_limit; - } -#else - if (DUK_UNLIKELY(line > DUK_USE_ESBC_MAX_LINENUMBER)) { - goto fail_bc_limit; - } -#endif -#endif - - return; - -fail_bc_limit: - DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_BYTECODE_LIMIT); - DUK_WO_NORETURN(return;); -} - -/* Update function min/max line from current token. Needed to improve - * function line range information for debugging, so that e.g. opening - * curly brace is covered by line range even when no opcodes are emitted - * for the line containing the brace. - */ -DUK_LOCAL void duk__update_lineinfo_currtoken(duk_compiler_ctx *comp_ctx) { -#if defined(DUK_USE_DEBUGGER_SUPPORT) - duk_int_t line; - - line = comp_ctx->curr_token.start_line; - if (line == 0) { - return; - } - if (line < comp_ctx->curr_func.min_line) { - comp_ctx->curr_func.min_line = line; - } - if (line > comp_ctx->curr_func.max_line) { - comp_ctx->curr_func.max_line = line; - } -#else - DUK_UNREF(comp_ctx); -#endif -} - -DUK_LOCAL void duk__emit_op_only(duk_compiler_ctx *comp_ctx, duk_small_uint_t op) { - duk__emit(comp_ctx, DUK_ENC_OP_ABC(op, 0)); -} - -/* Important main primitive. */ -DUK_LOCAL void duk__emit_a_b_c(duk_compiler_ctx *comp_ctx, - duk_small_uint_t op_flags, - duk_regconst_t a, - duk_regconst_t b, - duk_regconst_t c) { - duk_instr_t ins = 0; - duk_int_t a_out = -1; - duk_int_t b_out = -1; - duk_int_t c_out = -1; - duk_int_t tmp; - duk_small_uint_t op = op_flags & 0xffU; - - DUK_DDD(DUK_DDDPRINT("emit: op_flags=%04lx, a=%ld, b=%ld, c=%ld", (unsigned long) op_flags, (long) a, (long) b, (long) c)); - - /* We could rely on max temp/const checks: if they don't exceed BC - * limit, nothing here can either (just asserts would be enough). - * Currently we check for the limits, which provides additional - * protection against creating invalid bytecode due to compiler - * bugs. - */ - - DUK_ASSERT_DISABLE((op_flags & 0xff) >= DUK_BC_OP_MIN); /* unsigned */ - DUK_ASSERT((op_flags & 0xff) <= DUK_BC_OP_MAX); - DUK_ASSERT(DUK__ISREG(a)); - DUK_ASSERT(b != -1); /* Not 'none'. */ - DUK_ASSERT(c != -1); /* Not 'none'. */ - - /* Input shuffling happens before the actual operation, while output - * shuffling happens afterwards. Output shuffling decisions are still - * made at the same time to reduce branch clutter; output shuffle decisions - * are recorded into X_out variables. - */ - - /* Slot A: currently no support for reg/const. */ - -#if defined(DUK_USE_SHUFFLE_TORTURE) - if (a <= DUK_BC_A_MAX && (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_A)) { -#else - if (a <= DUK_BC_A_MAX) { -#endif - ; - } else if (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_A) { - DUK_D(DUK_DPRINT("out of regs: 'a' (reg) needs shuffling but shuffle prohibited, a: %ld", (long) a)); - goto error_outofregs; - } else if (a <= DUK_BC_BC_MAX) { - comp_ctx->curr_func.needs_shuffle = 1; - tmp = comp_ctx->curr_func.shuffle1; - if (op_flags & DUK__EMIT_FLAG_A_IS_SOURCE) { - duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_LDREG, tmp, a)); - } else { - /* Output shuffle needed after main operation */ - a_out = a; - - /* The DUK_OP_CSVAR output shuffle assumes shuffle registers are - * consecutive. - */ - DUK_ASSERT((comp_ctx->curr_func.shuffle1 == 0 && comp_ctx->curr_func.shuffle2 == 0) || - (comp_ctx->curr_func.shuffle2 == comp_ctx->curr_func.shuffle1 + 1)); - if (op == DUK_OP_CSVAR) { - /* For CSVAR the limit is one smaller because output shuffle - * must be able to express 'a + 1' in BC. - */ - if (a + 1 > DUK_BC_BC_MAX) { - goto error_outofregs; - } - } - } - a = tmp; - } else { - DUK_D(DUK_DPRINT("out of regs: 'a' (reg) needs shuffling but does not fit into BC, a: %ld", (long) a)); - goto error_outofregs; - } - - /* Slot B: reg/const support, mapped to bit 0 of opcode. */ - - if ((b & DUK__CONST_MARKER) != 0) { - DUK_ASSERT((op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_B) == 0); - DUK_ASSERT((op_flags & DUK__EMIT_FLAG_B_IS_TARGET) == 0); - b = b & ~DUK__CONST_MARKER; -#if defined(DUK_USE_SHUFFLE_TORTURE) - if (0) { -#else - if (b <= 0xff) { -#endif - if (op_flags & DUK__EMIT_FLAG_BC_REGCONST) { - /* Opcode follows B/C reg/const convention. */ - DUK_ASSERT((op & 0x01) == 0); - ins |= DUK_ENC_OP_A_B_C(0x01, 0, 0, 0); /* const flag for B */ - } else { - DUK_D(DUK_DPRINT("B is const, opcode is not B/C reg/const: %x", op_flags)); - } - } else if (b <= DUK_BC_BC_MAX) { - comp_ctx->curr_func.needs_shuffle = 1; - tmp = comp_ctx->curr_func.shuffle2; - duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_LDCONST, tmp, b)); - b = tmp; - } else { - DUK_D(DUK_DPRINT("out of regs: 'b' (const) needs shuffling but does not fit into BC, b: %ld", (long) b)); - goto error_outofregs; - } - } else { -#if defined(DUK_USE_SHUFFLE_TORTURE) - if (b <= 0xff && (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_B)) { -#else - if (b <= 0xff) { -#endif - ; - } else if (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_B) { - if (b > DUK_BC_B_MAX) { - /* Note: 0xff != DUK_BC_B_MAX */ - DUK_D( - DUK_DPRINT("out of regs: 'b' (reg) needs shuffling but shuffle prohibited, b: %ld", (long) b)); - goto error_outofregs; - } - } else if (b <= DUK_BC_BC_MAX) { - comp_ctx->curr_func.needs_shuffle = 1; - tmp = comp_ctx->curr_func.shuffle2; - if (op_flags & DUK__EMIT_FLAG_B_IS_TARGET) { - /* Output shuffle needed after main operation */ - b_out = b; - } - if (!(op_flags & DUK__EMIT_FLAG_B_IS_TARGET)) { - if (op == DUK_OP_MPUTOBJ || op == DUK_OP_MPUTARR) { - /* Special handling for MPUTOBJ/MPUTARR shuffling. - * For each, slot B identifies the first register of a range - * of registers, so normal shuffling won't work. Instead, - * an indirect version of the opcode is used. - */ - DUK_ASSERT((op_flags & DUK__EMIT_FLAG_B_IS_TARGET) == 0); - duk__emit_load_int32_noshuffle(comp_ctx, tmp, b); - DUK_ASSERT(DUK_OP_MPUTOBJI == DUK_OP_MPUTOBJ + 1); - DUK_ASSERT(DUK_OP_MPUTARRI == DUK_OP_MPUTARR + 1); - op_flags++; /* indirect opcode follows direct */ - } else { - duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_LDREG, tmp, b)); - } - } - b = tmp; - } else { - DUK_D(DUK_DPRINT("out of regs: 'b' (reg) needs shuffling but does not fit into BC, b: %ld", (long) b)); - goto error_outofregs; - } - } - - /* Slot C: reg/const support, mapped to bit 1 of opcode. */ - - if ((c & DUK__CONST_MARKER) != 0) { - DUK_ASSERT((op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_C) == 0); - DUK_ASSERT((op_flags & DUK__EMIT_FLAG_C_IS_TARGET) == 0); - c = c & ~DUK__CONST_MARKER; -#if defined(DUK_USE_SHUFFLE_TORTURE) - if (0) { -#else - if (c <= 0xff) { -#endif - if (op_flags & DUK__EMIT_FLAG_BC_REGCONST) { - /* Opcode follows B/C reg/const convention. */ - DUK_ASSERT((op & 0x02) == 0); - ins |= DUK_ENC_OP_A_B_C(0x02, 0, 0, 0); /* const flag for C */ - } else { - DUK_D(DUK_DPRINT("C is const, opcode is not B/C reg/const: %x", op_flags)); - } - } else if (c <= DUK_BC_BC_MAX) { - comp_ctx->curr_func.needs_shuffle = 1; - tmp = comp_ctx->curr_func.shuffle3; - duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_LDCONST, tmp, c)); - c = tmp; - } else { - DUK_D(DUK_DPRINT("out of regs: 'c' (const) needs shuffling but does not fit into BC, c: %ld", (long) c)); - goto error_outofregs; - } - } else { -#if defined(DUK_USE_SHUFFLE_TORTURE) - if (c <= 0xff && (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_C)) { -#else - if (c <= 0xff) { -#endif - ; - } else if (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_C) { - if (c > DUK_BC_C_MAX) { - /* Note: 0xff != DUK_BC_C_MAX */ - DUK_D( - DUK_DPRINT("out of regs: 'c' (reg) needs shuffling but shuffle prohibited, c: %ld", (long) c)); - goto error_outofregs; - } - } else if (c <= DUK_BC_BC_MAX) { - comp_ctx->curr_func.needs_shuffle = 1; - tmp = comp_ctx->curr_func.shuffle3; - if (op_flags & DUK__EMIT_FLAG_C_IS_TARGET) { - /* Output shuffle needed after main operation */ - c_out = c; - } else { - duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_LDREG, tmp, c)); - } - c = tmp; - } else { - DUK_D(DUK_DPRINT("out of regs: 'c' (reg) needs shuffling but does not fit into BC, c: %ld", (long) c)); - goto error_outofregs; - } - } - - /* Main operation */ - - DUK_ASSERT(a >= DUK_BC_A_MIN); - DUK_ASSERT(a <= DUK_BC_A_MAX); - DUK_ASSERT(b >= DUK_BC_B_MIN); - DUK_ASSERT(b <= DUK_BC_B_MAX); - DUK_ASSERT(c >= DUK_BC_C_MIN); - DUK_ASSERT(c <= DUK_BC_C_MAX); - - ins |= DUK_ENC_OP_A_B_C(op_flags & 0xff, a, b, c); - duk__emit(comp_ctx, ins); - - /* NEXTENUM needs a jump slot right after the main instruction. - * When the JUMP is taken, output spilling is not needed so this - * workaround is possible. The jump slot PC is exceptionally - * plumbed through comp_ctx to minimize call sites. - */ - if (op_flags & DUK__EMIT_FLAG_RESERVE_JUMPSLOT) { - comp_ctx->emit_jumpslot_pc = duk__get_current_pc(comp_ctx); - duk__emit_abc(comp_ctx, DUK_OP_JUMP, 0); - } - - /* Output shuffling: only one output register is realistically possible. - * - * (Zero would normally be an OK marker value: if the target register - * was zero, it would never be shuffled. But with DUK_USE_SHUFFLE_TORTURE - * this is no longer true, so use -1 as a marker instead.) - */ - - if (a_out >= 0) { - DUK_ASSERT(b_out < 0); - DUK_ASSERT(c_out < 0); - duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_STREG, a, a_out)); - - if (op == DUK_OP_CSVAR) { - /* Special handling for CSVAR shuffling. The variable lookup - * results in a pair in successive - * registers so use two shuffle registers and two output - * loads. (In practice this is dead code because temp/const - * limit is reached first.) - */ - duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_STREG, a + 1, a_out + 1)); - } - } else if (b_out >= 0) { - DUK_ASSERT(a_out < 0); - DUK_ASSERT(c_out < 0); - duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_STREG, b, b_out)); - } else if (c_out >= 0) { - DUK_ASSERT(b_out < 0); - DUK_ASSERT(c_out < 0); - duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_STREG, c, c_out)); - } - - return; - -error_outofregs: - DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_REG_LIMIT); - DUK_WO_NORETURN(return;); -} - -/* For many of the helpers below it'd be technically correct to add - * "no shuffle" flags for parameters passed in as zero. For example, - * duk__emit_a_b() should call duk__emit_a_b_c() with C set to 0, and - * DUK__EMIT_FLAG_NO_SHUFFLE_C added to op_flags. However, since the - * C value is 0, it'll never get shuffled so adding the flag is just - * unnecessary additional code. This is unfortunately not true for - * "shuffle torture" mode which needs special handling. - */ - -DUK_LOCAL void duk__emit_a_b(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t a, duk_regconst_t b) { -#if defined(DUK_USE_SHUFFLE_TORTURE) - op_flags |= DUK__EMIT_FLAG_NO_SHUFFLE_C; -#endif - duk__emit_a_b_c(comp_ctx, op_flags, a, b, 0); -} - -DUK_LOCAL void duk__emit_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t b, duk_regconst_t c) { -#if defined(DUK_USE_SHUFFLE_TORTURE) - op_flags |= DUK__EMIT_FLAG_NO_SHUFFLE_A; -#endif - duk__emit_a_b_c(comp_ctx, op_flags, 0, b, c); -} - -#if 0 /* unused */ -DUK_LOCAL void duk__emit_a(duk_compiler_ctx *comp_ctx, int op_flags, int a) { -#if defined(DUK_USE_SHUFFLE_TORTURE) - op_flags |= DUK__EMIT_FLAG_NO_SHUFFLE_B | DUK__EMIT_FLAG_NO_SHUFFLE_C; -#endif - duk__emit_a_b_c(comp_ctx, op_flags, a, 0, 0); -} -#endif - -#if 0 /* unused */ -DUK_LOCAL void duk__emit_b(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t b) { -#if defined(DUK_USE_SHUFFLE_TORTURE) - op_flags |= DUK__EMIT_FLAG_NO_SHUFFLE_A | DUK__EMIT_FLAG_NO_SHUFFLE_C; -#endif - duk__emit_a_b_c(comp_ctx, op_flags, 0, b, 0); -} -#endif - -DUK_LOCAL void duk__emit_a_bc(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t a, duk_regconst_t bc) { - duk_instr_t ins; - duk_int_t tmp; - - /* allow caller to give a const number with the DUK__CONST_MARKER */ - DUK_ASSERT(bc != -1); /* Not 'none'. */ - bc = bc & (~DUK__CONST_MARKER); - - DUK_ASSERT_DISABLE((op_flags & 0xff) >= DUK_BC_OP_MIN); /* unsigned */ - DUK_ASSERT((op_flags & 0xff) <= DUK_BC_OP_MAX); - DUK_ASSERT(bc >= DUK_BC_BC_MIN); - DUK_ASSERT(bc <= DUK_BC_BC_MAX); - DUK_ASSERT((bc & DUK__CONST_MARKER) == 0); - - if (bc <= DUK_BC_BC_MAX) { - ; - } else { - /* No BC shuffling now. */ - goto error_outofregs; - } - -#if defined(DUK_USE_SHUFFLE_TORTURE) - if (a <= DUK_BC_A_MAX && (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_A)) { -#else - if (a <= DUK_BC_A_MAX) { -#endif - ins = DUK_ENC_OP_A_BC(op_flags & 0xff, a, bc); - duk__emit(comp_ctx, ins); - } else if (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_A) { - goto error_outofregs; - } else if ((op_flags & 0xf0U) == DUK_OP_CALL0) { - comp_ctx->curr_func.needs_shuffle = 1; - tmp = comp_ctx->curr_func.shuffle1; - duk__emit_load_int32_noshuffle(comp_ctx, tmp, a); - op_flags |= DUK_BC_CALL_FLAG_INDIRECT; - ins = DUK_ENC_OP_A_BC(op_flags & 0xff, tmp, bc); - duk__emit(comp_ctx, ins); - } else if (a <= DUK_BC_BC_MAX) { - comp_ctx->curr_func.needs_shuffle = 1; - tmp = comp_ctx->curr_func.shuffle1; - ins = DUK_ENC_OP_A_BC(op_flags & 0xff, tmp, bc); - if (op_flags & DUK__EMIT_FLAG_A_IS_SOURCE) { - duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_LDREG, tmp, a)); - duk__emit(comp_ctx, ins); - } else { - duk__emit(comp_ctx, ins); - duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_STREG, tmp, a)); - } - } else { - goto error_outofregs; - } - return; - -error_outofregs: - DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_REG_LIMIT); - DUK_WO_NORETURN(return;); -} - -DUK_LOCAL void duk__emit_bc(duk_compiler_ctx *comp_ctx, duk_small_uint_t op, duk_regconst_t bc) { -#if defined(DUK_USE_SHUFFLE_TORTURE) - op |= DUK__EMIT_FLAG_NO_SHUFFLE_A; -#endif - duk__emit_a_bc(comp_ctx, op, 0, bc); -} - -DUK_LOCAL void duk__emit_abc(duk_compiler_ctx *comp_ctx, duk_small_uint_t op, duk_regconst_t abc) { - duk_instr_t ins; - - DUK_ASSERT_DISABLE(op >= DUK_BC_OP_MIN); /* unsigned */ - DUK_ASSERT(op <= DUK_BC_OP_MAX); - DUK_ASSERT_DISABLE(abc >= DUK_BC_ABC_MIN); /* unsigned */ - DUK_ASSERT(abc <= DUK_BC_ABC_MAX); - DUK_ASSERT((abc & DUK__CONST_MARKER) == 0); - DUK_ASSERT(abc != -1); /* Not 'none'. */ - - if (abc <= DUK_BC_ABC_MAX) { - ; - } else { - goto error_outofregs; - } - ins = DUK_ENC_OP_ABC(op, abc); - DUK_DDD(DUK_DDDPRINT("duk__emit_abc: 0x%08lx line=%ld pc=%ld op=%ld (%!X) abc=%ld (%!I)", - (unsigned long) ins, - (long) comp_ctx->curr_token.start_line, - (long) duk__get_current_pc(comp_ctx), - (long) op, - (long) op, - (long) abc, - (duk_instr_t) ins)); - duk__emit(comp_ctx, ins); - return; - -error_outofregs: - DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_REG_LIMIT); - DUK_WO_NORETURN(return;); -} - -DUK_LOCAL void duk__emit_load_int32_raw(duk_compiler_ctx *comp_ctx, - duk_regconst_t reg, - duk_int32_t val, - duk_small_uint_t op_flags) { - /* XXX: Shuffling support could be implemented here so that LDINT+LDINTX - * would only shuffle once (instead of twice). The current code works - * though, and has a smaller compiler footprint. - */ - - if ((val >= (duk_int32_t) DUK_BC_BC_MIN - (duk_int32_t) DUK_BC_LDINT_BIAS) && - (val <= (duk_int32_t) DUK_BC_BC_MAX - (duk_int32_t) DUK_BC_LDINT_BIAS)) { - DUK_DDD(DUK_DDDPRINT("emit LDINT to reg %ld for %ld", (long) reg, (long) val)); - duk__emit_a_bc(comp_ctx, DUK_OP_LDINT | op_flags, reg, (duk_regconst_t) (val + (duk_int32_t) DUK_BC_LDINT_BIAS)); - } else { - duk_int32_t hi = val >> DUK_BC_LDINTX_SHIFT; - duk_int32_t lo = val & ((((duk_int32_t) 1) << DUK_BC_LDINTX_SHIFT) - 1); - DUK_ASSERT(lo >= 0); - DUK_DDD(DUK_DDDPRINT("emit LDINT+LDINTX to reg %ld for %ld -> hi %ld, lo %ld", - (long) reg, - (long) val, - (long) hi, - (long) lo)); - duk__emit_a_bc(comp_ctx, DUK_OP_LDINT | op_flags, reg, (duk_regconst_t) (hi + (duk_int32_t) DUK_BC_LDINT_BIAS)); - duk__emit_a_bc(comp_ctx, DUK_OP_LDINTX | op_flags, reg, (duk_regconst_t) lo); - } -} - -DUK_LOCAL void duk__emit_load_int32(duk_compiler_ctx *comp_ctx, duk_regconst_t reg, duk_int32_t val) { - duk__emit_load_int32_raw(comp_ctx, reg, val, 0 /*op_flags*/); -} - -#if defined(DUK_USE_SHUFFLE_TORTURE) -/* Used by duk__emit*() calls so that we don't shuffle the loadints that - * are needed to handle indirect opcodes. - */ -DUK_LOCAL void duk__emit_load_int32_noshuffle(duk_compiler_ctx *comp_ctx, duk_regconst_t reg, duk_int32_t val) { - duk__emit_load_int32_raw(comp_ctx, reg, val, DUK__EMIT_FLAG_NO_SHUFFLE_A /*op_flags*/); -} -#else -DUK_LOCAL void duk__emit_load_int32_noshuffle(duk_compiler_ctx *comp_ctx, duk_regconst_t reg, duk_int32_t val) { - /* When torture not enabled, can just use the same helper because - * 'reg' won't get spilled. - */ - DUK_ASSERT(reg <= DUK_BC_A_MAX); - duk__emit_load_int32(comp_ctx, reg, val); -} -#endif - -DUK_LOCAL void duk__emit_jump(duk_compiler_ctx *comp_ctx, duk_int_t target_pc) { - duk_int_t curr_pc; - duk_int_t offset; - - curr_pc = (duk_int_t) (DUK_BW_GET_SIZE(comp_ctx->thr, &comp_ctx->curr_func.bw_code) / sizeof(duk_compiler_instr)); - offset = (duk_int_t) target_pc - (duk_int_t) curr_pc - 1; - DUK_ASSERT(offset + DUK_BC_JUMP_BIAS >= DUK_BC_ABC_MIN); - DUK_ASSERT(offset + DUK_BC_JUMP_BIAS <= DUK_BC_ABC_MAX); - duk__emit_abc(comp_ctx, DUK_OP_JUMP, (duk_regconst_t) (offset + DUK_BC_JUMP_BIAS)); -} - -DUK_LOCAL duk_int_t duk__emit_jump_empty(duk_compiler_ctx *comp_ctx) { - duk_int_t ret; - - ret = duk__get_current_pc(comp_ctx); /* useful for patching jumps later */ - duk__emit_op_only(comp_ctx, DUK_OP_JUMP); - return ret; -} - -/* Insert an empty jump in the middle of code emitted earlier. This is - * currently needed for compiling for-in. - */ -DUK_LOCAL void duk__insert_jump_entry(duk_compiler_ctx *comp_ctx, duk_int_t jump_pc) { -#if defined(DUK_USE_PC2LINE) - duk_int_t line; -#endif - duk_compiler_instr *instr; - duk_size_t offset; - - DUK_ASSERT(jump_pc >= 0); - offset = (duk_size_t) jump_pc * sizeof(duk_compiler_instr); - instr = (duk_compiler_instr *) (void *) - DUK_BW_INSERT_ENSURE_AREA(comp_ctx->thr, &comp_ctx->curr_func.bw_code, offset, sizeof(duk_compiler_instr)); - -#if defined(DUK_USE_PC2LINE) - line = comp_ctx->curr_token.start_line; /* approximation, close enough */ -#endif - instr->ins = DUK_ENC_OP_ABC(DUK_OP_JUMP, 0); -#if defined(DUK_USE_PC2LINE) - instr->line = (duk_uint32_t) line; -#endif - - DUK_BW_ADD_PTR(comp_ctx->thr, &comp_ctx->curr_func.bw_code, sizeof(duk_compiler_instr)); - if (DUK_UNLIKELY(DUK_BW_GET_SIZE(comp_ctx->thr, &comp_ctx->curr_func.bw_code) > DUK_USE_ESBC_MAX_BYTES)) { - goto fail_bc_limit; - } - return; - -fail_bc_limit: - DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_BYTECODE_LIMIT); - DUK_WO_NORETURN(return;); -} - -/* Does not assume that jump_pc contains a DUK_OP_JUMP previously; this is intentional - * to allow e.g. an INVALID opcode be overwritten with a JUMP (label management uses this). - */ -DUK_LOCAL void duk__patch_jump(duk_compiler_ctx *comp_ctx, duk_int_t jump_pc, duk_int_t target_pc) { - duk_compiler_instr *instr; - duk_int_t offset; - - /* allow negative PCs, behave as a no-op */ - if (jump_pc < 0) { - DUK_DDD( - DUK_DDDPRINT("duk__patch_jump(): nop call, jump_pc=%ld (<0), target_pc=%ld", (long) jump_pc, (long) target_pc)); - return; - } - DUK_ASSERT(jump_pc >= 0); - - /* XXX: range assert */ - instr = duk__get_instr_ptr(comp_ctx, jump_pc); - DUK_ASSERT(instr != NULL); - - /* XXX: range assert */ - offset = target_pc - jump_pc - 1; - - instr->ins = DUK_ENC_OP_ABC(DUK_OP_JUMP, offset + DUK_BC_JUMP_BIAS); - DUK_DDD(DUK_DDDPRINT("duk__patch_jump(): jump_pc=%ld, target_pc=%ld, offset=%ld", - (long) jump_pc, - (long) target_pc, - (long) offset)); -} - -DUK_LOCAL void duk__patch_jump_here(duk_compiler_ctx *comp_ctx, duk_int_t jump_pc) { - duk__patch_jump(comp_ctx, jump_pc, duk__get_current_pc(comp_ctx)); -} - -DUK_LOCAL void duk__patch_trycatch(duk_compiler_ctx *comp_ctx, - duk_int_t ldconst_pc, - duk_int_t trycatch_pc, - duk_regconst_t reg_catch, - duk_regconst_t const_varname, - duk_small_uint_t flags) { - duk_compiler_instr *instr; - - DUK_ASSERT(DUK__ISREG(reg_catch)); - - instr = duk__get_instr_ptr(comp_ctx, ldconst_pc); - DUK_ASSERT(DUK_DEC_OP(instr->ins) == DUK_OP_LDCONST); - DUK_ASSERT(instr != NULL); - if (const_varname & DUK__CONST_MARKER) { - /* Have a catch variable. */ - const_varname = const_varname & (~DUK__CONST_MARKER); - if (reg_catch > DUK_BC_BC_MAX || const_varname > DUK_BC_BC_MAX) { - /* Catch attempts to use out-of-range reg/const. Without this - * check Duktape 0.12.0 could generate invalid code which caused - * an assert failure on execution. This error is triggered e.g. - * for functions with a lot of constants and a try-catch statement. - * Shuffling or opcode semantics change is needed to fix the issue. - * See: test-bug-trycatch-many-constants.js. - */ - DUK_D(DUK_DPRINT("failed to patch trycatch: flags=%ld, reg_catch=%ld, const_varname=%ld (0x%08lx)", - (long) flags, - (long) reg_catch, - (long) const_varname, - (long) const_varname)); - DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_REG_LIMIT); - DUK_WO_NORETURN(return;); - } - instr->ins |= DUK_ENC_OP_A_BC(0, 0, const_varname); - } else { - /* No catch variable, e.g. a try-finally; replace LDCONST with - * NOP to avoid a bogus LDCONST. - */ - instr->ins = DUK_ENC_OP(DUK_OP_NOP); - } - - instr = duk__get_instr_ptr(comp_ctx, trycatch_pc); - DUK_ASSERT(instr != NULL); - DUK_ASSERT_DISABLE(flags >= DUK_BC_A_MIN); - DUK_ASSERT(flags <= DUK_BC_A_MAX); - instr->ins = DUK_ENC_OP_A_BC(DUK_OP_TRYCATCH, flags, reg_catch); -} - -DUK_LOCAL void duk__emit_if_false_skip(duk_compiler_ctx *comp_ctx, duk_regconst_t regconst) { - duk_small_uint_t op; - - op = DUK__ISREG(regconst) ? DUK_OP_IFFALSE_R : DUK_OP_IFFALSE_C; - duk__emit_bc(comp_ctx, op, regconst); /* helper will remove const flag */ -} - -DUK_LOCAL void duk__emit_if_true_skip(duk_compiler_ctx *comp_ctx, duk_regconst_t regconst) { - duk_small_uint_t op; - - op = DUK__ISREG(regconst) ? DUK_OP_IFTRUE_R : DUK_OP_IFTRUE_C; - duk__emit_bc(comp_ctx, op, regconst); /* helper will remove const flag */ -} - -DUK_LOCAL void duk__emit_invalid(duk_compiler_ctx *comp_ctx) { - duk__emit_op_only(comp_ctx, DUK_OP_INVALID); -} - -/* - * Peephole optimizer for finished bytecode. - * - * Does not remove opcodes; currently only straightens out unconditional - * jump chains which are generated by several control structures. - */ - -DUK_LOCAL void duk__peephole_optimize_bytecode(duk_compiler_ctx *comp_ctx) { - duk_compiler_instr *bc; - duk_small_uint_t iter; - duk_int_t i, n; - duk_int_t count_opt; - - bc = (duk_compiler_instr *) (void *) DUK_BW_GET_BASEPTR(comp_ctx->thr, &comp_ctx->curr_func.bw_code); -#if defined(DUK_USE_BUFLEN16) - /* No need to assert, buffer size maximum is 0xffff. */ -#else - DUK_ASSERT((duk_size_t) DUK_BW_GET_SIZE(comp_ctx->thr, &comp_ctx->curr_func.bw_code) / sizeof(duk_compiler_instr) <= - (duk_size_t) DUK_INT_MAX); /* bytecode limits */ -#endif - n = (duk_int_t) (DUK_BW_GET_SIZE(comp_ctx->thr, &comp_ctx->curr_func.bw_code) / sizeof(duk_compiler_instr)); - - for (iter = 0; iter < DUK_COMPILER_PEEPHOLE_MAXITER; iter++) { - count_opt = 0; - - for (i = 0; i < n; i++) { - duk_instr_t ins; - duk_int_t target_pc1; - duk_int_t target_pc2; - - ins = bc[i].ins; - if (DUK_DEC_OP(ins) != DUK_OP_JUMP) { - continue; - } - - target_pc1 = i + 1 + (duk_int_t) DUK_DEC_ABC(ins) - (duk_int_t) DUK_BC_JUMP_BIAS; - DUK_DDD(DUK_DDDPRINT("consider jump at pc %ld; target_pc=%ld", (long) i, (long) target_pc1)); - DUK_ASSERT(target_pc1 >= 0); - DUK_ASSERT(target_pc1 < n); - - /* Note: if target_pc1 == i, we'll optimize a jump to itself. - * This does not need to be checked for explicitly; the case - * is rare and max iter breaks us out. - */ - - ins = bc[target_pc1].ins; - if (DUK_DEC_OP(ins) != DUK_OP_JUMP) { - continue; - } - - target_pc2 = target_pc1 + 1 + (duk_int_t) DUK_DEC_ABC(ins) - (duk_int_t) DUK_BC_JUMP_BIAS; - - DUK_DDD(DUK_DDDPRINT("optimizing jump at pc %ld; old target is %ld -> new target is %ld", - (long) i, - (long) target_pc1, - (long) target_pc2)); - - bc[i].ins = DUK_ENC_OP_ABC(DUK_OP_JUMP, target_pc2 - (i + 1) + DUK_BC_JUMP_BIAS); - - count_opt++; - } - - DUK_DD(DUK_DDPRINT("optimized %ld jumps on peephole round %ld", (long) count_opt, (long) (iter + 1))); - - if (count_opt == 0) { - break; - } - } -} - -/* - * Intermediate value helpers - */ - -/* Flags for intermediate value coercions. A flag for using a forced reg - * is not needed, the forced_reg argument suffices and generates better - * code (it is checked as it is used). - */ -/* XXX: DUK__IVAL_FLAG_REQUIRE_SHORT is passed but not currently implemented - * by ispec/ivalue operations. - */ -#define DUK__IVAL_FLAG_ALLOW_CONST (1 << 0) /* allow a constant to be returned */ -#define DUK__IVAL_FLAG_REQUIRE_TEMP (1 << 1) /* require a (mutable) temporary as a result (or a const if allowed) */ -#define DUK__IVAL_FLAG_REQUIRE_SHORT (1 << 2) /* require a short (8-bit) reg/const which fits into bytecode B/C slot */ - -/* XXX: some code might benefit from DUK__SETTEMP_IFTEMP(thr,x) */ - -#if 0 /* enable manually for dumping */ -#define DUK__DUMP_ISPEC(compctx, ispec) \ - do { \ - duk__dump_ispec((compctx), (ispec)); \ - } while (0) -#define DUK__DUMP_IVALUE(compctx, ivalue) \ - do { \ - duk__dump_ivalue((compctx), (ivalue)); \ - } while (0) - -DUK_LOCAL void duk__dump_ispec(duk_compiler_ctx *comp_ctx, duk_ispec *x) { - DUK_D(DUK_DPRINT("ispec dump: t=%ld regconst=0x%08lx, valstack_idx=%ld, value=%!T", - (long) x->t, (unsigned long) x->regconst, (long) x->valstack_idx, - duk_get_tval(comp_ctx->thr, x->valstack_idx))); -} -DUK_LOCAL void duk__dump_ivalue(duk_compiler_ctx *comp_ctx, duk_ivalue *x) { - DUK_D(DUK_DPRINT("ivalue dump: t=%ld op=%ld " - "x1={t=%ld regconst=0x%08lx valstack_idx=%ld value=%!T} " - "x2={t=%ld regconst=0x%08lx valstack_idx=%ld value=%!T}", - (long) x->t, (long) x->op, - (long) x->x1.t, (unsigned long) x->x1.regconst, (long) x->x1.valstack_idx, - duk_get_tval(comp_ctx->thr, x->x1.valstack_idx), - (long) x->x2.t, (unsigned long) x->x2.regconst, (long) x->x2.valstack_idx, - duk_get_tval(comp_ctx->thr, x->x2.valstack_idx))); -} -#else -#define DUK__DUMP_ISPEC(comp_ctx, x) \ - do { \ - } while (0) -#define DUK__DUMP_IVALUE(comp_ctx, x) \ - do { \ - } while (0) -#endif - -DUK_LOCAL void duk__ivalue_regconst(duk_ivalue *x, duk_regconst_t regconst) { - x->t = DUK_IVAL_PLAIN; - x->x1.t = DUK_ISPEC_REGCONST; - x->x1.regconst = regconst; -} - -DUK_LOCAL void duk__ivalue_plain_fromstack(duk_compiler_ctx *comp_ctx, duk_ivalue *x) { - x->t = DUK_IVAL_PLAIN; - x->x1.t = DUK_ISPEC_VALUE; - duk_replace(comp_ctx->thr, x->x1.valstack_idx); -} - -DUK_LOCAL void duk__ivalue_var_fromstack(duk_compiler_ctx *comp_ctx, duk_ivalue *x) { - x->t = DUK_IVAL_VAR; - x->x1.t = DUK_ISPEC_VALUE; - duk_replace(comp_ctx->thr, x->x1.valstack_idx); -} - -DUK_LOCAL_DECL void duk__ivalue_var_hstring(duk_compiler_ctx *comp_ctx, duk_ivalue *x, duk_hstring *h) { - DUK_ASSERT(h != NULL); - duk_push_hstring(comp_ctx->thr, h); - duk__ivalue_var_fromstack(comp_ctx, x); -} - -DUK_LOCAL void duk__copy_ispec(duk_compiler_ctx *comp_ctx, duk_ispec *src, duk_ispec *dst) { - dst->t = src->t; - dst->regconst = src->regconst; - duk_copy(comp_ctx->thr, src->valstack_idx, dst->valstack_idx); -} - -DUK_LOCAL void duk__copy_ivalue(duk_compiler_ctx *comp_ctx, duk_ivalue *src, duk_ivalue *dst) { - dst->t = src->t; - dst->op = src->op; - dst->x1.t = src->x1.t; - dst->x1.regconst = src->x1.regconst; - dst->x2.t = src->x2.t; - dst->x2.regconst = src->x2.regconst; - duk_copy(comp_ctx->thr, src->x1.valstack_idx, dst->x1.valstack_idx); - duk_copy(comp_ctx->thr, src->x2.valstack_idx, dst->x2.valstack_idx); -} - -DUK_LOCAL duk_regconst_t duk__alloctemps(duk_compiler_ctx *comp_ctx, duk_small_int_t num) { - duk_regconst_t res; - - res = comp_ctx->curr_func.temp_next; - comp_ctx->curr_func.temp_next += num; - - if (comp_ctx->curr_func.temp_next > DUK__MAX_TEMPS) { /* == DUK__MAX_TEMPS is OK */ - DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_TEMP_LIMIT); - DUK_WO_NORETURN(return 0;); - } - - /* maintain highest 'used' temporary, needed to figure out nregs of function */ - if (comp_ctx->curr_func.temp_next > comp_ctx->curr_func.temp_max) { - comp_ctx->curr_func.temp_max = comp_ctx->curr_func.temp_next; - } - - return res; -} - -DUK_LOCAL duk_regconst_t duk__alloctemp(duk_compiler_ctx *comp_ctx) { - return duk__alloctemps(comp_ctx, 1); -} - -DUK_LOCAL void duk__settemp_checkmax(duk_compiler_ctx *comp_ctx, duk_regconst_t temp_next) { - comp_ctx->curr_func.temp_next = temp_next; - if (temp_next > comp_ctx->curr_func.temp_max) { - comp_ctx->curr_func.temp_max = temp_next; - } -} - -/* get const for value at valstack top */ -DUK_LOCAL duk_regconst_t duk__getconst(duk_compiler_ctx *comp_ctx) { - duk_hthread *thr = comp_ctx->thr; - duk_compiler_func *f = &comp_ctx->curr_func; - duk_tval *tv1; - duk_int_t i, n, n_check; - - n = (duk_int_t) duk_get_length(thr, f->consts_idx); - - tv1 = DUK_GET_TVAL_NEGIDX(thr, -1); - DUK_ASSERT(tv1 != NULL); - -#if defined(DUK_USE_FASTINT) - /* Explicit check for fastint downgrade. */ - DUK_TVAL_CHKFAST_INPLACE_SLOW(tv1); -#endif - - /* Sanity workaround for handling functions with a large number of - * constants at least somewhat reasonably. Otherwise checking whether - * we already have the constant would grow very slow (as it is O(N^2)). - */ - n_check = (n > DUK__GETCONST_MAX_CONSTS_CHECK ? DUK__GETCONST_MAX_CONSTS_CHECK : n); - for (i = 0; i < n_check; i++) { - duk_tval *tv2 = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, f->h_consts, i); - - /* Strict equality is NOT enough, because we cannot use the same - * constant for e.g. +0 and -0. - */ - if (duk_js_samevalue(tv1, tv2)) { - DUK_DDD(DUK_DDDPRINT("reused existing constant for %!T -> const index %ld", (duk_tval *) tv1, (long) i)); - duk_pop(thr); - return (duk_regconst_t) i | (duk_regconst_t) DUK__CONST_MARKER; - } - } - - if (n > DUK__MAX_CONSTS) { - DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_CONST_LIMIT); - DUK_WO_NORETURN(return 0;); - } - - DUK_DDD(DUK_DDDPRINT("allocating new constant for %!T -> const index %ld", (duk_tval *) tv1, (long) n)); - (void) duk_put_prop_index(thr, f->consts_idx, (duk_uarridx_t) n); /* invalidates tv1, tv2 */ - return (duk_regconst_t) n | (duk_regconst_t) DUK__CONST_MARKER; -} - -DUK_LOCAL duk_bool_t duk__const_needs_refcount(duk_compiler_ctx *comp_ctx, duk_regconst_t rc) { -#if defined(DUK_USE_REFERENCE_COUNTING) - duk_compiler_func *f = &comp_ctx->curr_func; - duk_bool_t ret; - - DUK_ASSERT((rc & DUK__CONST_MARKER) == 0); /* caller removes const marker */ - (void) duk_get_prop_index(comp_ctx->thr, f->consts_idx, (duk_uarridx_t) rc); - ret = !duk_is_number(comp_ctx->thr, -1); /* now only number/string, so conservative check */ - duk_pop(comp_ctx->thr); - return ret; -#else - DUK_UNREF(comp_ctx); - DUK_UNREF(rc); - DUK_ASSERT((rc & DUK__CONST_MARKER) == 0); /* caller removes const marker */ - return 0; -#endif -} - -/* Get the value represented by an duk_ispec to a register or constant. - * The caller can control the result by indicating whether or not: - * - * (1) a constant is allowed (sometimes the caller needs the result to - * be in a register) - * - * (2) a temporary register is required (usually when caller requires - * the register to be safely mutable; normally either a bound - * register or a temporary register are both OK) - * - * (3) a forced register target needs to be used - * - * Bytecode may be emitted to generate the necessary value. The return - * value is either a register or a constant. - */ - -DUK_LOCAL -duk_regconst_t duk__ispec_toregconst_raw(duk_compiler_ctx *comp_ctx, - duk_ispec *x, - duk_regconst_t forced_reg, - duk_small_uint_t flags) { - duk_hthread *thr = comp_ctx->thr; - - DUK_DDD(DUK_DDDPRINT("duk__ispec_toregconst_raw(): x={%ld:%ld:%!T}, " - "forced_reg=%ld, flags 0x%08lx: allow_const=%ld require_temp=%ld require_short=%ld", - (long) x->t, - (long) x->regconst, - (duk_tval *) duk_get_tval(thr, x->valstack_idx), - (long) forced_reg, - (unsigned long) flags, - (long) ((flags & DUK__IVAL_FLAG_ALLOW_CONST) ? 1 : 0), - (long) ((flags & DUK__IVAL_FLAG_REQUIRE_TEMP) ? 1 : 0), - (long) ((flags & DUK__IVAL_FLAG_REQUIRE_SHORT) ? 1 : 0))); - - switch (x->t) { - case DUK_ISPEC_VALUE: { - duk_tval *tv; - - tv = DUK_GET_TVAL_POSIDX(thr, x->valstack_idx); - DUK_ASSERT(tv != NULL); - - switch (DUK_TVAL_GET_TAG(tv)) { - case DUK_TAG_UNDEFINED: { - /* Note: although there is no 'undefined' literal, undefined - * values can occur during compilation as a result of e.g. - * the 'void' operator. - */ - duk_regconst_t dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx)); - duk__emit_bc(comp_ctx, DUK_OP_LDUNDEF, dest); - return dest; - } - case DUK_TAG_NULL: { - duk_regconst_t dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx)); - duk__emit_bc(comp_ctx, DUK_OP_LDNULL, dest); - return dest; - } - case DUK_TAG_BOOLEAN: { - duk_regconst_t dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx)); - duk__emit_bc(comp_ctx, (DUK_TVAL_GET_BOOLEAN(tv) ? DUK_OP_LDTRUE : DUK_OP_LDFALSE), dest); - return dest; - } - case DUK_TAG_POINTER: { - DUK_UNREACHABLE(); - break; - } - case DUK_TAG_STRING: { - duk_hstring *h; - duk_regconst_t dest; - duk_regconst_t constidx; - - h = DUK_TVAL_GET_STRING(tv); - DUK_UNREF(h); - DUK_ASSERT(h != NULL); - -#if 0 /* XXX: to be implemented? */ - /* Use special opcodes to load short strings */ - if (DUK_HSTRING_GET_BYTELEN(h) <= 2) { - /* Encode into a single opcode (18 bits can encode 1-2 bytes + length indicator) */ - } else if (DUK_HSTRING_GET_BYTELEN(h) <= 6) { - /* Encode into a double constant (53 bits can encode 6*8 = 48 bits + 3-bit length */ - } -#endif - duk_dup(thr, x->valstack_idx); - constidx = duk__getconst(comp_ctx); - - if (flags & DUK__IVAL_FLAG_ALLOW_CONST) { - return constidx; - } - - dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx)); - duk__emit_a_bc(comp_ctx, DUK_OP_LDCONST, dest, constidx); - return dest; - } - case DUK_TAG_OBJECT: { - DUK_UNREACHABLE(); - break; - } - case DUK_TAG_BUFFER: { - DUK_UNREACHABLE(); - break; - } - case DUK_TAG_LIGHTFUNC: { - DUK_UNREACHABLE(); - break; - } -#if defined(DUK_USE_FASTINT) - case DUK_TAG_FASTINT: -#endif - default: { - /* number */ - duk_regconst_t dest; - duk_regconst_t constidx; - duk_double_t dval; - duk_int32_t ival; - - DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv)); - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); - dval = DUK_TVAL_GET_NUMBER(tv); - - if (!(flags & DUK__IVAL_FLAG_ALLOW_CONST)) { - /* A number can be loaded either through a constant, using - * LDINT, or using LDINT+LDINTX. LDINT is always a size win, - * LDINT+LDINTX is not if the constant is used multiple times. - * Currently always prefer LDINT+LDINTX over a double constant. - */ - - if (duk_is_whole_get_int32_nonegzero(dval, &ival)) { - dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx)); - duk__emit_load_int32(comp_ctx, dest, ival); - return dest; - } - } - - duk_dup(thr, x->valstack_idx); - constidx = duk__getconst(comp_ctx); - - if (flags & DUK__IVAL_FLAG_ALLOW_CONST) { - return constidx; - } else { - dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx)); - duk__emit_a_bc(comp_ctx, DUK_OP_LDCONST, dest, constidx); - return dest; - } - } - } /* end switch */ - goto fail_internal; /* never here */ - } - case DUK_ISPEC_REGCONST: { - if (forced_reg >= 0) { - if (DUK__ISCONST(x->regconst)) { - duk__emit_a_bc(comp_ctx, DUK_OP_LDCONST, forced_reg, x->regconst); - } else if (x->regconst != forced_reg) { - duk__emit_a_bc(comp_ctx, DUK_OP_LDREG, forced_reg, x->regconst); - } else { - ; /* already in correct reg */ - } - return forced_reg; - } - - DUK_ASSERT(forced_reg < 0); - if (DUK__ISCONST(x->regconst)) { - if (!(flags & DUK__IVAL_FLAG_ALLOW_CONST)) { - duk_regconst_t dest = DUK__ALLOCTEMP(comp_ctx); - duk__emit_a_bc(comp_ctx, DUK_OP_LDCONST, dest, x->regconst); - return dest; - } - return x->regconst; - } - - DUK_ASSERT(forced_reg < 0 && !DUK__ISCONST(x->regconst)); - if ((flags & DUK__IVAL_FLAG_REQUIRE_TEMP) && !DUK__ISREG_TEMP(comp_ctx, x->regconst)) { - duk_regconst_t dest = DUK__ALLOCTEMP(comp_ctx); - duk__emit_a_bc(comp_ctx, DUK_OP_LDREG, dest, x->regconst); - return dest; - } - return x->regconst; - } - default: { - break; /* never here */ - } - } - -fail_internal: - DUK_ERROR_INTERNAL(thr); - DUK_WO_NORETURN(return 0;); -} - -DUK_LOCAL void duk__ispec_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ispec *x, duk_regconst_t forced_reg) { - DUK_ASSERT(forced_reg >= 0); - (void) duk__ispec_toregconst_raw(comp_ctx, x, forced_reg, 0 /*flags*/); -} - -/* Coerce an duk_ivalue to a 'plain' value by generating the necessary - * arithmetic operations, property access, or variable access bytecode. - * The duk_ivalue argument ('x') is converted into a plain value as a - * side effect. - */ -DUK_LOCAL void duk__ivalue_toplain_raw(duk_compiler_ctx *comp_ctx, duk_ivalue *x, duk_regconst_t forced_reg) { - duk_hthread *thr = comp_ctx->thr; - - DUK_DDD(DUK_DDDPRINT("duk__ivalue_toplain_raw(): x={t=%ld,op=%ld,x1={%ld:%ld:%!T},x2={%ld:%ld:%!T}}, " - "forced_reg=%ld", - (long) x->t, - (long) x->op, - (long) x->x1.t, - (long) x->x1.regconst, - (duk_tval *) duk_get_tval(thr, x->x1.valstack_idx), - (long) x->x2.t, - (long) x->x2.regconst, - (duk_tval *) duk_get_tval(thr, x->x2.valstack_idx), - (long) forced_reg)); - - switch (x->t) { - case DUK_IVAL_PLAIN: { - return; - } - /* XXX: support unary arithmetic ivalues (useful?) */ - case DUK_IVAL_ARITH: { - duk_regconst_t arg1; - duk_regconst_t arg2; - duk_regconst_t dest; - duk_tval *tv1; - duk_tval *tv2; - - DUK_DDD(DUK_DDDPRINT("arith to plain conversion")); - - /* inline arithmetic check for constant values */ - /* XXX: use the exactly same arithmetic function here as in executor */ - if (x->x1.t == DUK_ISPEC_VALUE && x->x2.t == DUK_ISPEC_VALUE && x->t == DUK_IVAL_ARITH) { - tv1 = DUK_GET_TVAL_POSIDX(thr, x->x1.valstack_idx); - tv2 = DUK_GET_TVAL_POSIDX(thr, x->x2.valstack_idx); - DUK_ASSERT(tv1 != NULL); - DUK_ASSERT(tv2 != NULL); - - DUK_DDD(DUK_DDDPRINT("arith: tv1=%!T, tv2=%!T", (duk_tval *) tv1, (duk_tval *) tv2)); - - if (DUK_TVAL_IS_NUMBER(tv1) && DUK_TVAL_IS_NUMBER(tv2)) { - duk_double_t d1 = DUK_TVAL_GET_NUMBER(tv1); - duk_double_t d2 = DUK_TVAL_GET_NUMBER(tv2); - duk_double_t d3; - duk_bool_t accept_fold = 1; - - DUK_DDD(DUK_DDDPRINT("arith inline check: d1=%lf, d2=%lf, op=%ld", - (double) d1, - (double) d2, - (long) x->op)); - switch (x->op) { - case DUK_OP_ADD: { - d3 = d1 + d2; - break; - } - case DUK_OP_SUB: { - d3 = d1 - d2; - break; - } - case DUK_OP_MUL: { - d3 = d1 * d2; - break; - } - case DUK_OP_DIV: { - /* Division-by-zero is undefined - * behavior, so rely on a helper. - */ - d3 = duk_double_div(d1, d2); - break; - } - case DUK_OP_EXP: { - d3 = (duk_double_t) duk_js_arith_pow((double) d1, (double) d2); - break; - } - default: { - d3 = 0.0; /* Won't be used, but silence MSVC /W4 warning. */ - accept_fold = 0; - break; - } - } - - if (accept_fold) { - duk_double_union du; - du.d = d3; - DUK_DBLUNION_NORMALIZE_NAN_CHECK(&du); - d3 = du.d; - - x->t = DUK_IVAL_PLAIN; - DUK_ASSERT(x->x1.t == DUK_ISPEC_VALUE); - DUK_TVAL_SET_NUMBER(tv1, d3); /* old value is number: no refcount */ - return; - } - } else if (x->op == DUK_OP_ADD && DUK_TVAL_IS_STRING(tv1) && DUK_TVAL_IS_STRING(tv2)) { - /* Inline string concatenation. No need to check for - * symbols, as all inputs are valid ECMAScript strings. - */ - duk_dup(thr, x->x1.valstack_idx); - duk_dup(thr, x->x2.valstack_idx); - duk_concat(thr, 2); - duk_replace(thr, x->x1.valstack_idx); - x->t = DUK_IVAL_PLAIN; - DUK_ASSERT(x->x1.t == DUK_ISPEC_VALUE); - return; - } - } - - arg1 = duk__ispec_toregconst_raw(comp_ctx, - &x->x1, - -1, - DUK__IVAL_FLAG_ALLOW_CONST | DUK__IVAL_FLAG_REQUIRE_SHORT /*flags*/); - arg2 = duk__ispec_toregconst_raw(comp_ctx, - &x->x2, - -1, - DUK__IVAL_FLAG_ALLOW_CONST | DUK__IVAL_FLAG_REQUIRE_SHORT /*flags*/); - - /* If forced reg, use it as destination. Otherwise try to - * use either coerced ispec if it is a temporary. - */ - if (forced_reg >= 0) { - dest = forced_reg; - } else if (DUK__ISREG_TEMP(comp_ctx, arg1)) { - dest = arg1; - } else if (DUK__ISREG_TEMP(comp_ctx, arg2)) { - dest = arg2; - } else { - dest = DUK__ALLOCTEMP(comp_ctx); - } - - DUK_ASSERT(DUK__ISREG(dest)); - duk__emit_a_b_c(comp_ctx, x->op | DUK__EMIT_FLAG_BC_REGCONST, dest, arg1, arg2); - - duk__ivalue_regconst(x, dest); - return; - } - case DUK_IVAL_PROP: { - /* XXX: very similar to DUK_IVAL_ARITH - merge? */ - duk_regconst_t arg1; - duk_regconst_t arg2; - duk_regconst_t dest; - - /* Need a short reg/const, does not have to be a mutable temp. */ - arg1 = duk__ispec_toregconst_raw(comp_ctx, - &x->x1, - -1, - DUK__IVAL_FLAG_ALLOW_CONST | DUK__IVAL_FLAG_REQUIRE_SHORT /*flags*/); - arg2 = duk__ispec_toregconst_raw(comp_ctx, - &x->x2, - -1, - DUK__IVAL_FLAG_ALLOW_CONST | DUK__IVAL_FLAG_REQUIRE_SHORT /*flags*/); - - /* Pick a destination register. If either base value or key - * happens to be a temp value, reuse it as the destination. - * - * XXX: The temp must be a "mutable" one, i.e. such that no - * other expression is using it anymore. Here this should be - * the case because the value of a property access expression - * is neither the base nor the key, but the lookup result. - */ - - if (forced_reg >= 0) { - dest = forced_reg; - } else if (DUK__ISREG_TEMP(comp_ctx, arg1)) { - dest = arg1; - } else if (DUK__ISREG_TEMP(comp_ctx, arg2)) { - dest = arg2; - } else { - dest = DUK__ALLOCTEMP(comp_ctx); - } - - duk__emit_a_b_c(comp_ctx, DUK_OP_GETPROP | DUK__EMIT_FLAG_BC_REGCONST, dest, arg1, arg2); - - duk__ivalue_regconst(x, dest); - return; - } - case DUK_IVAL_VAR: { - /* x1 must be a string */ - duk_regconst_t dest; - duk_regconst_t reg_varbind; - duk_regconst_t rc_varname; - - DUK_ASSERT(x->x1.t == DUK_ISPEC_VALUE); - - duk_dup(thr, x->x1.valstack_idx); - if (duk__lookup_lhs(comp_ctx, ®_varbind, &rc_varname)) { - duk__ivalue_regconst(x, reg_varbind); - } else { - dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx)); - duk__emit_a_bc(comp_ctx, DUK_OP_GETVAR, dest, rc_varname); - duk__ivalue_regconst(x, dest); - } - return; - } - case DUK_IVAL_NONE: - default: { - DUK_D(DUK_DPRINT("invalid ivalue type: %ld", (long) x->t)); - break; - } - } - - DUK_ERROR_INTERNAL(thr); - DUK_WO_NORETURN(return;); -} - -/* evaluate to plain value, no forced register (temp/bound reg both ok) */ -DUK_LOCAL void duk__ivalue_toplain(duk_compiler_ctx *comp_ctx, duk_ivalue *x) { - duk__ivalue_toplain_raw(comp_ctx, x, -1 /*forced_reg*/); -} - -/* evaluate to final form (e.g. coerce GETPROP to code), throw away temp */ -DUK_LOCAL void duk__ivalue_toplain_ignore(duk_compiler_ctx *comp_ctx, duk_ivalue *x) { - duk_regconst_t temp; - - /* If duk__ivalue_toplain_raw() allocates a temp, forget it and - * restore next temp state. - */ - temp = DUK__GETTEMP(comp_ctx); - duk__ivalue_toplain_raw(comp_ctx, x, -1 /*forced_reg*/); - DUK__SETTEMP(comp_ctx, temp); -} - -/* Coerce an duk_ivalue to a register or constant; result register may - * be a temp or a bound register. - * - * The duk_ivalue argument ('x') is converted into a regconst as a - * side effect. - */ -DUK_LOCAL -duk_regconst_t duk__ivalue_toregconst_raw(duk_compiler_ctx *comp_ctx, - duk_ivalue *x, - duk_regconst_t forced_reg, - duk_small_uint_t flags) { - duk_hthread *thr = comp_ctx->thr; - duk_regconst_t reg; - DUK_UNREF(thr); - - DUK_DDD(DUK_DDDPRINT("duk__ivalue_toregconst_raw(): x={t=%ld,op=%ld,x1={%ld:%ld:%!T},x2={%ld:%ld:%!T}}, " - "forced_reg=%ld, flags 0x%08lx: allow_const=%ld require_temp=%ld require_short=%ld", - (long) x->t, - (long) x->op, - (long) x->x1.t, - (long) x->x1.regconst, - (duk_tval *) duk_get_tval(thr, x->x1.valstack_idx), - (long) x->x2.t, - (long) x->x2.regconst, - (duk_tval *) duk_get_tval(thr, x->x2.valstack_idx), - (long) forced_reg, - (unsigned long) flags, - (long) ((flags & DUK__IVAL_FLAG_ALLOW_CONST) ? 1 : 0), - (long) ((flags & DUK__IVAL_FLAG_REQUIRE_TEMP) ? 1 : 0), - (long) ((flags & DUK__IVAL_FLAG_REQUIRE_SHORT) ? 1 : 0))); - - /* first coerce to a plain value */ - duk__ivalue_toplain_raw(comp_ctx, x, forced_reg); - DUK_ASSERT(x->t == DUK_IVAL_PLAIN); - - /* then to a register */ - reg = duk__ispec_toregconst_raw(comp_ctx, &x->x1, forced_reg, flags); - duk__ivalue_regconst(x, reg); - - return reg; -} - -DUK_LOCAL duk_regconst_t duk__ivalue_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *x) { - return duk__ivalue_toregconst_raw(comp_ctx, x, -1, 0 /*flags*/); -} - -#if 0 /* unused */ -DUK_LOCAL duk_regconst_t duk__ivalue_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *x) { - return duk__ivalue_toregconst_raw(comp_ctx, x, -1, DUK__IVAL_FLAG_REQUIRE_TEMP /*flags*/); -} -#endif - -DUK_LOCAL void duk__ivalue_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ivalue *x, duk_int_t forced_reg) { - DUK_ASSERT(forced_reg >= 0); - (void) duk__ivalue_toregconst_raw(comp_ctx, x, forced_reg, 0 /*flags*/); -} - -DUK_LOCAL duk_regconst_t duk__ivalue_toregconst(duk_compiler_ctx *comp_ctx, duk_ivalue *x) { - return duk__ivalue_toregconst_raw(comp_ctx, x, -1, DUK__IVAL_FLAG_ALLOW_CONST /*flags*/); -} - -DUK_LOCAL duk_regconst_t duk__ivalue_totempconst(duk_compiler_ctx *comp_ctx, duk_ivalue *x) { - return duk__ivalue_toregconst_raw(comp_ctx, x, -1, DUK__IVAL_FLAG_ALLOW_CONST | DUK__IVAL_FLAG_REQUIRE_TEMP /*flags*/); -} - -/* The issues below can be solved with better flags */ - -/* XXX: many operations actually want toforcedtemp() -- brand new temp? */ -/* XXX: need a toplain_ignore() which will only coerce a value to a temp - * register if it might have a side effect. Side-effect free values do not - * need to be coerced. - */ - -/* - * Identifier handling - */ - -DUK_LOCAL duk_regconst_t duk__lookup_active_register_binding(duk_compiler_ctx *comp_ctx) { - duk_hthread *thr = comp_ctx->thr; - duk_hstring *h_varname; - duk_regconst_t ret; - - DUK_DDD(DUK_DDDPRINT("resolving identifier reference to '%!T'", (duk_tval *) duk_get_tval(thr, -1))); - - /* - * Special name handling - */ - - h_varname = duk_known_hstring(thr, -1); - - if (h_varname == DUK_HTHREAD_STRING_LC_ARGUMENTS(thr)) { - DUK_DDD(DUK_DDDPRINT("flagging function as accessing 'arguments'")); - comp_ctx->curr_func.id_access_arguments = 1; - } - - /* - * Inside one or more 'with' statements fall back to slow path always. - * (See e.g. test-stmt-with.js.) - */ - - if (comp_ctx->curr_func.with_depth > 0) { - DUK_DDD(DUK_DDDPRINT("identifier lookup inside a 'with' -> fall back to slow path")); - goto slow_path_own; - } - - /* - * Any catch bindings ("catch (e)") also affect identifier binding. - * - * Currently, the varmap is modified for the duration of the catch - * clause to ensure any identifier accesses with the catch variable - * name will use slow path. - */ - - duk_get_prop(thr, comp_ctx->curr_func.varmap_idx); - if (duk_is_number(thr, -1)) { - ret = duk_to_int(thr, -1); - duk_pop(thr); - } else { - duk_pop(thr); - if (comp_ctx->curr_func.catch_depth > 0 || comp_ctx->curr_func.with_depth > 0) { - DUK_DDD(DUK_DDDPRINT("slow path access from inside a try-catch or with needs _Varmap")); - goto slow_path_own; - } else { - /* In this case we're doing a variable lookup that doesn't - * match our own variables, so _Varmap won't be needed at - * run time. - */ - DUK_DDD(DUK_DDDPRINT("slow path access outside of try-catch and with, no need for _Varmap")); - goto slow_path_notown; - } - } - - DUK_DDD(DUK_DDDPRINT("identifier lookup -> reg %ld", (long) ret)); - return ret; - -slow_path_notown: - DUK_DDD(DUK_DDDPRINT("identifier lookup -> slow path, not own variable")); - - comp_ctx->curr_func.id_access_slow = 1; - return (duk_regconst_t) -1; - -slow_path_own: - DUK_DDD(DUK_DDDPRINT("identifier lookup -> slow path, may be own variable")); - - comp_ctx->curr_func.id_access_slow = 1; - comp_ctx->curr_func.id_access_slow_own = 1; - return (duk_regconst_t) -1; -} - -/* Lookup an identifier name in the current varmap, indicating whether the - * identifier is register-bound and if not, allocating a constant for the - * identifier name. Returns 1 if register-bound, 0 otherwise. Caller can - * also check (out_reg_varbind >= 0) to check whether or not identifier is - * register bound. The caller must NOT use out_rc_varname at all unless - * return code is 0 or out_reg_varbind is < 0; this is becuase out_rc_varname - * is unsigned and doesn't have a "unused" / none value. - */ -DUK_LOCAL duk_bool_t duk__lookup_lhs(duk_compiler_ctx *comp_ctx, duk_regconst_t *out_reg_varbind, duk_regconst_t *out_rc_varname) { - duk_hthread *thr = comp_ctx->thr; - duk_regconst_t reg_varbind; - duk_regconst_t rc_varname; - - /* [ ... varname ] */ - - duk_dup_top(thr); - reg_varbind = duk__lookup_active_register_binding(comp_ctx); - - if (reg_varbind >= 0) { - *out_reg_varbind = reg_varbind; - *out_rc_varname = 0; /* duk_regconst_t is unsigned, so use 0 as dummy value (ignored by caller) */ - duk_pop(thr); - return 1; - } else { - rc_varname = duk__getconst(comp_ctx); - *out_reg_varbind = -1; - *out_rc_varname = rc_varname; - return 0; - } -} - -/* - * Label handling - * - * Labels are initially added with flags prohibiting both break and continue. - * When the statement type is finally uncovered (after potentially multiple - * labels), all the labels are updated to allow/prohibit break and continue. - */ - -DUK_LOCAL void duk__add_label(duk_compiler_ctx *comp_ctx, duk_hstring *h_label, duk_int_t pc_label, duk_int_t label_id) { - duk_hthread *thr = comp_ctx->thr; - duk_size_t n; - duk_size_t new_size; - duk_uint8_t *p; - duk_labelinfo *li_start, *li; - - /* Duplicate (shadowing) labels are not allowed, except for the empty - * labels (which are used as default labels for switch and iteration - * statements). - * - * We could also allow shadowing of non-empty pending labels without any - * other issues than breaking the required label shadowing requirements - * of the E5 specification, see Section 12.12. - */ - - p = (duk_uint8_t *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, comp_ctx->curr_func.h_labelinfos); - li_start = (duk_labelinfo *) (void *) p; - li = (duk_labelinfo *) (void *) (p + DUK_HBUFFER_GET_SIZE(comp_ctx->curr_func.h_labelinfos)); - n = (duk_size_t) (li - li_start); - - while (li > li_start) { - li--; - - if (li->h_label == h_label && h_label != DUK_HTHREAD_STRING_EMPTY_STRING(thr)) { - DUK_ERROR_SYNTAX(thr, DUK_STR_DUPLICATE_LABEL); - DUK_WO_NORETURN(return;); - } - } - - duk_push_hstring(thr, h_label); - DUK_ASSERT(n <= DUK_UARRIDX_MAX); /* label limits */ - (void) duk_put_prop_index(thr, comp_ctx->curr_func.labelnames_idx, (duk_uarridx_t) n); - - new_size = (n + 1) * sizeof(duk_labelinfo); - duk_hbuffer_resize(thr, comp_ctx->curr_func.h_labelinfos, new_size); - /* XXX: slack handling, slow now */ - - /* relookup after possible realloc */ - p = (duk_uint8_t *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, comp_ctx->curr_func.h_labelinfos); - li_start = (duk_labelinfo *) (void *) p; - DUK_UNREF(li_start); /* silence scan-build warning */ - li = (duk_labelinfo *) (void *) (p + DUK_HBUFFER_GET_SIZE(comp_ctx->curr_func.h_labelinfos)); - li--; - - /* Labels can be used for iteration statements but also for other statements, - * in particular a label can be used for a block statement. All cases of a - * named label accept a 'break' so that flag is set here. Iteration statements - * also allow 'continue', so that flag is updated when we figure out the - * statement type. - */ - - li->flags = DUK_LABEL_FLAG_ALLOW_BREAK; - li->label_id = label_id; - li->h_label = h_label; - li->catch_depth = comp_ctx->curr_func.catch_depth; /* catch depth from current func */ - li->pc_label = pc_label; - - DUK_DDD(DUK_DDDPRINT("registered label: flags=0x%08lx, id=%ld, name=%!O, catch_depth=%ld, pc_label=%ld", - (unsigned long) li->flags, - (long) li->label_id, - (duk_heaphdr *) li->h_label, - (long) li->catch_depth, - (long) li->pc_label)); -} - -/* Update all labels with matching label_id. */ -DUK_LOCAL void duk__update_label_flags(duk_compiler_ctx *comp_ctx, duk_int_t label_id, duk_small_uint_t flags) { - duk_uint8_t *p; - duk_labelinfo *li_start, *li; - - p = (duk_uint8_t *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(comp_ctx->thr->heap, comp_ctx->curr_func.h_labelinfos); - li_start = (duk_labelinfo *) (void *) p; - li = (duk_labelinfo *) (void *) (p + DUK_HBUFFER_GET_SIZE(comp_ctx->curr_func.h_labelinfos)); - - /* Match labels starting from latest; once label_id no longer matches, we can - * safely exit without checking the rest of the labels (only the topmost labels - * are ever updated). - */ - while (li > li_start) { - li--; - - if (li->label_id != label_id) { - break; - } - - DUK_DDD(DUK_DDDPRINT("updating (overwriting) label flags for li=%p, label_id=%ld, flags=%ld", - (void *) li, - (long) label_id, - (long) flags)); - - li->flags = flags; - } -} - -/* Lookup active label information. Break/continue distinction is necessary to handle switch - * statement related labels correctly: a switch will only catch a 'break', not a 'continue'. - * - * An explicit label cannot appear multiple times in the active set, but empty labels (unlabelled - * iteration and switch statements) can. A break will match the closest unlabelled or labelled - * statement. A continue will match the closest unlabelled or labelled iteration statement. It is - * a syntax error if a continue matches a labelled switch statement; because an explicit label cannot - * be duplicated, the continue cannot match any valid label outside the switch. - * - * A side effect of these rules is that a LABEL statement related to a switch should never actually - * catch a continue abrupt completion at run-time. Hence an INVALID opcode can be placed in the - * continue slot of the switch's LABEL statement. - */ - -/* XXX: awkward, especially the bunch of separate output values -> output struct? */ -DUK_LOCAL void duk__lookup_active_label(duk_compiler_ctx *comp_ctx, - duk_hstring *h_label, - duk_bool_t is_break, - duk_int_t *out_label_id, - duk_int_t *out_label_catch_depth, - duk_int_t *out_label_pc, - duk_bool_t *out_is_closest) { - duk_hthread *thr = comp_ctx->thr; - duk_uint8_t *p; - duk_labelinfo *li_start, *li_end, *li; - duk_bool_t match = 0; - - DUK_DDD(DUK_DDDPRINT("looking up active label: label='%!O', is_break=%ld", (duk_heaphdr *) h_label, (long) is_break)); - - DUK_UNREF(thr); - - p = (duk_uint8_t *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, comp_ctx->curr_func.h_labelinfos); - li_start = (duk_labelinfo *) (void *) p; - li_end = (duk_labelinfo *) (void *) (p + DUK_HBUFFER_GET_SIZE(comp_ctx->curr_func.h_labelinfos)); - li = li_end; - - /* Match labels starting from latest label because there can be duplicate empty - * labels in the label set. - */ - while (li > li_start) { - li--; - - if (li->h_label != h_label) { - DUK_DDD(DUK_DDDPRINT("labelinfo[%ld] ->'%!O' != %!O", - (long) (li - li_start), - (duk_heaphdr *) li->h_label, - (duk_heaphdr *) h_label)); - continue; - } - - DUK_DDD(DUK_DDDPRINT("labelinfo[%ld] -> '%!O' label name matches (still need to check type)", - (long) (li - li_start), - (duk_heaphdr *) h_label)); - - /* currently all labels accept a break, so no explicit check for it now */ - DUK_ASSERT(li->flags & DUK_LABEL_FLAG_ALLOW_BREAK); - - if (is_break) { - /* break matches always */ - match = 1; - break; - } else if (li->flags & DUK_LABEL_FLAG_ALLOW_CONTINUE) { - /* iteration statements allow continue */ - match = 1; - break; - } else { - /* continue matched this label -- we can only continue if this is the empty - * label, for which duplication is allowed, and thus there is hope of - * finding a match deeper in the label stack. - */ - if (h_label != DUK_HTHREAD_STRING_EMPTY_STRING(thr)) { - DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_LABEL); - DUK_WO_NORETURN(return;); - } else { - DUK_DDD(DUK_DDDPRINT("continue matched an empty label which does not " - "allow a continue -> continue lookup deeper in label stack")); - } - } - } - /* XXX: match flag is awkward, rework */ - if (!match) { - DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_LABEL); - DUK_WO_NORETURN(return;); - } - - DUK_DDD(DUK_DDDPRINT("label match: %!O -> label_id %ld, catch_depth=%ld, pc_label=%ld", - (duk_heaphdr *) h_label, - (long) li->label_id, - (long) li->catch_depth, - (long) li->pc_label)); - - *out_label_id = li->label_id; - *out_label_catch_depth = li->catch_depth; - *out_label_pc = li->pc_label; - *out_is_closest = (li == li_end - 1); -} - -DUK_LOCAL void duk__reset_labels_to_length(duk_compiler_ctx *comp_ctx, duk_size_t len) { - duk_hthread *thr = comp_ctx->thr; - - duk_set_length(thr, comp_ctx->curr_func.labelnames_idx, len); - duk_hbuffer_resize(thr, comp_ctx->curr_func.h_labelinfos, sizeof(duk_labelinfo) * len); -} - -/* - * Expression parsing: duk__expr_nud(), duk__expr_led(), duk__expr_lbp(), and helpers. - * - * - duk__expr_nud(): ("null denotation"): process prev_token as a "start" of an expression (e.g. literal) - * - duk__expr_led(): ("left denotation"): process prev_token in the "middle" of an expression (e.g. operator) - * - duk__expr_lbp(): ("left-binding power"): return left-binding power of curr_token - */ - -/* object literal key tracking flags */ -#define DUK__OBJ_LIT_KEY_PLAIN (1 << 0) /* key encountered as a plain property */ -#define DUK__OBJ_LIT_KEY_GET (1 << 1) /* key encountered as a getter */ -#define DUK__OBJ_LIT_KEY_SET (1 << 2) /* key encountered as a setter */ - -DUK_LOCAL void duk__nud_array_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { - duk_hthread *thr = comp_ctx->thr; - duk_regconst_t reg_obj; /* result reg */ - duk_regconst_t reg_temp; /* temp reg */ - duk_regconst_t temp_start; /* temp reg value for start of loop */ - duk_small_uint_t max_init_values; /* max # of values initialized in one MPUTARR set */ - duk_small_uint_t num_values; /* number of values in current MPUTARR set */ - duk_uarridx_t curr_idx; /* current (next) array index */ - duk_uarridx_t start_idx; /* start array index of current MPUTARR set */ - duk_uarridx_t init_idx; /* last array index explicitly initialized, +1 */ - duk_bool_t require_comma; /* next loop requires a comma */ -#if !defined(DUK_USE_PREFER_SIZE) - duk_int_t pc_newarr; - duk_compiler_instr *instr; -#endif - - /* DUK_TOK_LBRACKET already eaten, current token is right after that */ - DUK_ASSERT(comp_ctx->prev_token.t == DUK_TOK_LBRACKET); - - max_init_values = DUK__MAX_ARRAY_INIT_VALUES; /* XXX: depend on available temps? */ - - reg_obj = DUK__ALLOCTEMP(comp_ctx); -#if !defined(DUK_USE_PREFER_SIZE) - pc_newarr = duk__get_current_pc(comp_ctx); -#endif - duk__emit_bc(comp_ctx, DUK_OP_NEWARR, reg_obj); /* XXX: patch initial size hint afterwards? */ - temp_start = DUK__GETTEMP(comp_ctx); - - /* - * Emit initializers in sets of maximum max_init_values. - * Corner cases such as single value initializers do not have - * special handling now. - * - * Elided elements must not be emitted as 'undefined' values, - * because such values would be enumerable (which is incorrect). - * Also note that trailing elisions must be reflected in the - * length of the final array but cause no elements to be actually - * inserted. - */ - - curr_idx = 0; - init_idx = 0; /* tracks maximum initialized index + 1 */ - start_idx = 0; - require_comma = 0; - - for (;;) { - num_values = 0; - DUK__SETTEMP(comp_ctx, temp_start); - - if (comp_ctx->curr_token.t == DUK_TOK_RBRACKET) { - break; - } - - for (;;) { - if (comp_ctx->curr_token.t == DUK_TOK_RBRACKET) { - /* the outer loop will recheck and exit */ - break; - } - - /* comma check */ - if (require_comma) { - if (comp_ctx->curr_token.t == DUK_TOK_COMMA) { - /* comma after a value, expected */ - duk__advance(comp_ctx); - require_comma = 0; - continue; - } else { - goto syntax_error; - } - } else { - if (comp_ctx->curr_token.t == DUK_TOK_COMMA) { - /* elision - flush */ - curr_idx++; - duk__advance(comp_ctx); - /* if num_values > 0, MPUTARR emitted by outer loop after break */ - break; - } - } - /* else an array initializer element */ - - /* initial index */ - if (num_values == 0) { - start_idx = curr_idx; - reg_temp = DUK__ALLOCTEMP(comp_ctx); - duk__emit_load_int32(comp_ctx, reg_temp, (duk_int32_t) start_idx); - } - - reg_temp = DUK__ALLOCTEMP(comp_ctx); /* alloc temp just in case, to update max temp */ - DUK__SETTEMP(comp_ctx, reg_temp); - duk__expr_toforcedreg(comp_ctx, res, DUK__BP_COMMA /*rbp_flags*/, reg_temp /*forced_reg*/); - DUK__SETTEMP(comp_ctx, reg_temp + 1); - - num_values++; - curr_idx++; - require_comma = 1; - - if (num_values >= max_init_values) { - /* MPUTARR emitted by outer loop */ - break; - } - } - - if (num_values > 0) { - /* - A is a source register (it's not a write target, but used - * to identify the target object) but can be shuffled. - * - B cannot be shuffled normally because it identifies a range - * of registers, the emitter has special handling for this - * (the "no shuffle" flag must not be set). - * - C is a non-register number and cannot be shuffled, but - * never needs to be. - */ - duk__emit_a_b_c(comp_ctx, - DUK_OP_MPUTARR | DUK__EMIT_FLAG_NO_SHUFFLE_C | DUK__EMIT_FLAG_A_IS_SOURCE, - reg_obj, - temp_start, - (duk_regconst_t) (num_values + 1)); - init_idx = start_idx + num_values; - - /* num_values and temp_start reset at top of outer loop */ - } - } - - /* Update initil size for NEWARR, doesn't need to be exact and is - * capped at A field limit. - */ -#if !defined(DUK_USE_PREFER_SIZE) - instr = duk__get_instr_ptr(comp_ctx, pc_newarr); - instr->ins |= DUK_ENC_OP_A(0, curr_idx > DUK_BC_A_MAX ? DUK_BC_A_MAX : curr_idx); -#endif - - DUK_ASSERT(comp_ctx->curr_token.t == DUK_TOK_RBRACKET); - duk__advance(comp_ctx); - - DUK_DDD(DUK_DDDPRINT("array literal done, curridx=%ld, initidx=%ld", (long) curr_idx, (long) init_idx)); - - /* trailing elisions? */ - if (curr_idx > init_idx) { - /* yes, must set array length explicitly */ - DUK_DDD(DUK_DDDPRINT("array literal has trailing elisions which affect its length")); - reg_temp = DUK__ALLOCTEMP(comp_ctx); - duk__emit_load_int32(comp_ctx, reg_temp, (duk_int_t) curr_idx); - duk__emit_a_bc(comp_ctx, DUK_OP_SETALEN | DUK__EMIT_FLAG_A_IS_SOURCE, reg_obj, reg_temp); - } - - DUK__SETTEMP(comp_ctx, temp_start); - - duk__ivalue_regconst(res, reg_obj); - return; - -syntax_error: - DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_ARRAY_LITERAL); - DUK_WO_NORETURN(return;); -} - -typedef struct { - duk_regconst_t reg_obj; - duk_regconst_t temp_start; - duk_small_uint_t num_pairs; - duk_small_uint_t num_total_pairs; -} duk__objlit_state; - -DUK_LOCAL void duk__objlit_flush_keys(duk_compiler_ctx *comp_ctx, duk__objlit_state *st) { - if (st->num_pairs > 0) { - /* - A is a source register (it's not a write target, but used - * to identify the target object) but can be shuffled. - * - B cannot be shuffled normally because it identifies a range - * of registers, the emitter has special handling for this - * (the "no shuffle" flag must not be set). - * - C is a non-register number and cannot be shuffled, but - * never needs to be. - */ - DUK_ASSERT(st->num_pairs > 0); - duk__emit_a_b_c(comp_ctx, - DUK_OP_MPUTOBJ | DUK__EMIT_FLAG_NO_SHUFFLE_C | DUK__EMIT_FLAG_A_IS_SOURCE, - st->reg_obj, - st->temp_start, - (duk_regconst_t) (st->num_pairs * 2)); - st->num_total_pairs += st->num_pairs; - st->num_pairs = 0; - } - DUK__SETTEMP(comp_ctx, st->temp_start); -} - -DUK_LOCAL duk_bool_t duk__objlit_load_key(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_token *tok, duk_regconst_t reg_temp) { - if (tok->t_nores == DUK_TOK_IDENTIFIER || tok->t_nores == DUK_TOK_STRING) { - /* same handling for identifiers and strings */ - DUK_ASSERT(tok->str1 != NULL); - duk_push_hstring(comp_ctx->thr, tok->str1); - } else if (tok->t == DUK_TOK_NUMBER) { - /* numbers can be loaded as numbers and coerced on the fly */ - duk_push_number(comp_ctx->thr, tok->num); - } else { - return 1; /* error */ - } - - duk__ivalue_plain_fromstack(comp_ctx, res); - DUK__SETTEMP(comp_ctx, reg_temp + 1); - duk__ivalue_toforcedreg(comp_ctx, res, reg_temp); - DUK__SETTEMP(comp_ctx, reg_temp + 1); - return 0; -} - -DUK_LOCAL void duk__nud_object_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { - duk_hthread *thr = comp_ctx->thr; - duk__objlit_state st; - duk_regconst_t reg_temp; /* temp reg */ - duk_small_uint_t max_init_pairs; /* max # of key-value pairs initialized in one MPUTOBJ set */ - duk_bool_t first; /* first value: comma must not precede the value */ - duk_bool_t is_set, is_get; /* temps */ -#if !defined(DUK_USE_PREFER_SIZE) - duk_int_t pc_newobj; - duk_compiler_instr *instr; -#endif - - DUK_ASSERT(comp_ctx->prev_token.t == DUK_TOK_LCURLY); - - max_init_pairs = DUK__MAX_OBJECT_INIT_PAIRS; /* XXX: depend on available temps? */ - - st.reg_obj = DUK__ALLOCTEMP(comp_ctx); /* target object */ - st.temp_start = DUK__GETTEMP(comp_ctx); /* start of MPUTOBJ argument list */ - st.num_pairs = 0; /* number of key/value pairs emitted for current MPUTOBJ set */ - st.num_total_pairs = 0; /* number of key/value pairs emitted overall */ - -#if !defined(DUK_USE_PREFER_SIZE) - pc_newobj = duk__get_current_pc(comp_ctx); -#endif - duk__emit_bc(comp_ctx, DUK_OP_NEWOBJ, st.reg_obj); - - /* - * Emit initializers in sets of maximum max_init_pairs keys. - * Setter/getter is handled separately and terminates the - * current set of initializer values. Corner cases such as - * single value initializers do not have special handling now. - */ - - first = 1; - for (;;) { - /* - * ES5 and ES2015+ provide a lot of different PropertyDefinition - * formats, see http://www.ecma-international.org/ecma-262/6.0/#sec-object-initializer. - * - * PropertyName can be IdentifierName (includes reserved words), a string - * literal, or a number literal. Note that IdentifierName allows 'get' and - * 'set' too, so we need to look ahead to the next token to distinguish: - * - * { get : 1 } - * - * and - * - * { get foo() { return 1 } } - * { get get() { return 1 } } // 'get' as getter propertyname - * - * Finally, a trailing comma is allowed. - * - * Key name is coerced to string at compile time (and ends up as a - * a string constant) even for numeric keys (e.g. "{1:'foo'}"). - * These could be emitted using e.g. LDINT, but that seems hardly - * worth the effort and would increase code size. - */ - - DUK_DDD(DUK_DDDPRINT("object literal loop, curr_token->t = %ld", (long) comp_ctx->curr_token.t)); - - if (comp_ctx->curr_token.t == DUK_TOK_RCURLY) { - break; - } - - if (first) { - first = 0; - } else { - if (comp_ctx->curr_token.t != DUK_TOK_COMMA) { - goto syntax_error; - } - duk__advance(comp_ctx); - if (comp_ctx->curr_token.t == DUK_TOK_RCURLY) { - /* trailing comma followed by rcurly */ - break; - } - } - - /* Advance to get one step of lookup. */ - duk__advance(comp_ctx); - - /* Flush current MPUTOBJ if enough many pairs gathered. */ - if (st.num_pairs >= max_init_pairs) { - duk__objlit_flush_keys(comp_ctx, &st); - DUK_ASSERT(st.num_pairs == 0); - } - - /* Reset temp register state and reserve reg_temp and - * reg_temp + 1 for handling the current property. - */ - DUK__SETTEMP(comp_ctx, st.temp_start + 2 * (duk_regconst_t) st.num_pairs); - reg_temp = DUK__ALLOCTEMPS(comp_ctx, 2); - - /* NOTE: "get" and "set" are not officially ReservedWords and the lexer - * currently treats them always like ordinary identifiers (DUK_TOK_GET - * and DUK_TOK_SET are unused). They need to be detected based on the - * identifier string content. - */ - - is_get = (comp_ctx->prev_token.t == DUK_TOK_IDENTIFIER && comp_ctx->prev_token.str1 == DUK_HTHREAD_STRING_GET(thr)); - is_set = (comp_ctx->prev_token.t == DUK_TOK_IDENTIFIER && comp_ctx->prev_token.str1 == DUK_HTHREAD_STRING_SET(thr)); - if ((is_get || is_set) && comp_ctx->curr_token.t != DUK_TOK_COLON) { - /* getter/setter */ - duk_int_t fnum; - - duk__objlit_flush_keys(comp_ctx, &st); - DUK_ASSERT(DUK__GETTEMP(comp_ctx) == - st.temp_start); /* 2 regs are guaranteed to be allocated w.r.t. temp_max */ - reg_temp = DUK__ALLOCTEMPS(comp_ctx, 2); - - if (duk__objlit_load_key(comp_ctx, res, &comp_ctx->curr_token, reg_temp) != 0) { - goto syntax_error; - } - - /* curr_token = get/set name */ - fnum = duk__parse_func_like_fnum(comp_ctx, DUK__FUNC_FLAG_GETSET); - - duk__emit_a_bc(comp_ctx, DUK_OP_CLOSURE, st.temp_start + 1, (duk_regconst_t) fnum); - - /* Slot C is used in a non-standard fashion (range of regs), - * emitter code has special handling for it (must not set the - * "no shuffle" flag). - */ - duk__emit_a_bc(comp_ctx, - (is_get ? DUK_OP_INITGET : DUK_OP_INITSET) | DUK__EMIT_FLAG_A_IS_SOURCE, - st.reg_obj, - st.temp_start); /* temp_start+0 = key, temp_start+1 = closure */ - - DUK_ASSERT(st.num_pairs == 0); /* temp state is reset on next loop */ -#if defined(DUK_USE_ES6) - } else if (comp_ctx->prev_token.t == DUK_TOK_IDENTIFIER && - (comp_ctx->curr_token.t == DUK_TOK_COMMA || comp_ctx->curr_token.t == DUK_TOK_RCURLY)) { - duk_bool_t load_rc; - - load_rc = duk__objlit_load_key(comp_ctx, res, &comp_ctx->prev_token, reg_temp); - DUK_UNREF(load_rc); - DUK_ASSERT(load_rc == 0); /* always succeeds because token is identifier */ - - duk__ivalue_var_hstring(comp_ctx, res, comp_ctx->prev_token.str1); - DUK_ASSERT(DUK__GETTEMP(comp_ctx) == reg_temp + 1); - duk__ivalue_toforcedreg(comp_ctx, res, reg_temp + 1); - - st.num_pairs++; - } else if ((comp_ctx->prev_token.t == DUK_TOK_IDENTIFIER || comp_ctx->prev_token.t == DUK_TOK_STRING || - comp_ctx->prev_token.t == DUK_TOK_NUMBER) && - comp_ctx->curr_token.t == DUK_TOK_LPAREN) { - duk_int_t fnum; - - /* Parsing-wise there's a small hickup here: the token parsing - * state is one step too advanced for the function parse helper - * compared to other cases. The current solution is an extra - * flag to indicate whether function parsing should use the - * current or the previous token to starting parsing from. - */ - - if (duk__objlit_load_key(comp_ctx, res, &comp_ctx->prev_token, reg_temp) != 0) { - goto syntax_error; - } - - fnum = duk__parse_func_like_fnum(comp_ctx, DUK__FUNC_FLAG_USE_PREVTOKEN | DUK__FUNC_FLAG_METDEF); - - duk__emit_a_bc(comp_ctx, DUK_OP_CLOSURE, reg_temp + 1, (duk_regconst_t) fnum); - - st.num_pairs++; -#endif /* DUK_USE_ES6 */ - } else { -#if defined(DUK_USE_ES6) - if (comp_ctx->prev_token.t == DUK_TOK_LBRACKET) { - /* ES2015 computed property name. Executor ToPropertyKey() - * coerces the key at runtime. - */ - DUK__SETTEMP(comp_ctx, reg_temp); - duk__expr_toforcedreg(comp_ctx, res, DUK__BP_FOR_EXPR, reg_temp); - duk__advance_expect(comp_ctx, DUK_TOK_RBRACKET); - - /* XXX: If next token is '(' we're dealing with - * the method shorthand with a computed name, - * e.g. { [Symbol.for('foo')](a,b) {} }. This - * form is not yet supported and causes a - * SyntaxError on the DUK_TOK_COLON check below. - */ - } else -#endif /* DUK_USE_ES6 */ - { - if (duk__objlit_load_key(comp_ctx, res, &comp_ctx->prev_token, reg_temp) != 0) { - goto syntax_error; - } - } - - duk__advance_expect(comp_ctx, DUK_TOK_COLON); - - DUK__SETTEMP(comp_ctx, reg_temp + 1); - duk__expr_toforcedreg(comp_ctx, res, DUK__BP_COMMA /*rbp_flags*/, reg_temp + 1 /*forced_reg*/); - - st.num_pairs++; - } - } /* property loop */ - - /* Flush remaining properties. */ - duk__objlit_flush_keys(comp_ctx, &st); - DUK_ASSERT(st.num_pairs == 0); - DUK_ASSERT(DUK__GETTEMP(comp_ctx) == st.temp_start); - - /* Update initial size for NEWOBJ. The init size doesn't need to be - * exact as the purpose is just to avoid object resizes in common - * cases. The size is capped to field A limit, and will be too high - * if the object literal contains duplicate keys (this is harmless but - * increases memory traffic if the object is compacted later on). - */ -#if !defined(DUK_USE_PREFER_SIZE) - instr = duk__get_instr_ptr(comp_ctx, pc_newobj); - instr->ins |= DUK_ENC_OP_A(0, st.num_total_pairs > DUK_BC_A_MAX ? DUK_BC_A_MAX : st.num_total_pairs); -#endif - - DUK_ASSERT(comp_ctx->curr_token.t == DUK_TOK_RCURLY); - duk__advance(comp_ctx); /* No RegExp after object literal. */ - - duk__ivalue_regconst(res, st.reg_obj); - return; - -syntax_error: - DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_OBJECT_LITERAL); - DUK_WO_NORETURN(return;); -} - -/* Parse argument list. Arguments are written to temps starting from - * "next temp". Returns number of arguments parsed. Expects left paren - * to be already eaten, and eats the right paren before returning. - */ -DUK_LOCAL duk_int_t duk__parse_arguments(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { - duk_int_t nargs = 0; - duk_regconst_t reg_temp; - - /* Note: expect that caller has already eaten the left paren */ - - DUK_DDD(DUK_DDDPRINT("start parsing arguments, prev_token.t=%ld, curr_token.t=%ld", - (long) comp_ctx->prev_token.t, - (long) comp_ctx->curr_token.t)); - - for (;;) { - if (comp_ctx->curr_token.t == DUK_TOK_RPAREN) { - break; - } - if (nargs > 0) { - duk__advance_expect(comp_ctx, DUK_TOK_COMMA); - } - - /* We want the argument expression value to go to "next temp" - * without additional moves. That should almost always be the - * case, but we double check after expression parsing. - * - * This is not the cleanest possible approach. - */ - - reg_temp = DUK__ALLOCTEMP(comp_ctx); /* bump up "allocated" reg count, just in case */ - DUK__SETTEMP(comp_ctx, reg_temp); - - /* binding power must be high enough to NOT allow comma expressions directly */ - duk__expr_toforcedreg(comp_ctx, - res, - DUK__BP_COMMA /*rbp_flags*/, - reg_temp); /* always allow 'in', coerce to 'tr' just in case */ - - DUK__SETTEMP(comp_ctx, reg_temp + 1); - nargs++; - - DUK_DDD(DUK_DDDPRINT("argument #%ld written into reg %ld", (long) nargs, (long) reg_temp)); - } - - /* eat the right paren */ - duk__advance_expect(comp_ctx, DUK_TOK_RPAREN); /* RegExp mode does not matter. */ - - DUK_DDD(DUK_DDDPRINT("end parsing arguments")); - - return nargs; -} - -DUK_LOCAL duk_bool_t duk__expr_is_empty(duk_compiler_ctx *comp_ctx) { - /* empty expressions can be detected conveniently with nud/led counts */ - return (comp_ctx->curr_func.nud_count == 0) && (comp_ctx->curr_func.led_count == 0); -} - -DUK_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { - duk_hthread *thr = comp_ctx->thr; - duk_token *tk; - duk_regconst_t temp_at_entry; - duk_small_uint_t tok; - duk_uint32_t args; /* temp variable to pass constants and flags to shared code */ - - /* - * ctx->prev_token token to process with duk__expr_nud() - * ctx->curr_token updated by caller - * - * Note: the token in the switch below has already been eaten. - */ - - temp_at_entry = DUK__GETTEMP(comp_ctx); - - comp_ctx->curr_func.nud_count++; - - tk = &comp_ctx->prev_token; - tok = tk->t; - res->t = DUK_IVAL_NONE; - - DUK_DDD(DUK_DDDPRINT("duk__expr_nud(), prev_token.t=%ld, allow_in=%ld, paren_level=%ld", - (long) tk->t, - (long) comp_ctx->curr_func.allow_in, - (long) comp_ctx->curr_func.paren_level)); - - switch (tok) { - /* PRIMARY EXPRESSIONS */ - - case DUK_TOK_THIS: { - duk_regconst_t reg_temp; - reg_temp = DUK__ALLOCTEMP(comp_ctx); - duk__emit_bc(comp_ctx, DUK_OP_LDTHIS, reg_temp); - duk__ivalue_regconst(res, reg_temp); - return; - } - case DUK_TOK_IDENTIFIER: { - duk__ivalue_var_hstring(comp_ctx, res, tk->str1); - return; - } - case DUK_TOK_NULL: { - duk_push_null(thr); - goto plain_value; - } - case DUK_TOK_TRUE: { - duk_push_true(thr); - goto plain_value; - } - case DUK_TOK_FALSE: { - duk_push_false(thr); - goto plain_value; - } - case DUK_TOK_NUMBER: { - duk_push_number(thr, tk->num); - goto plain_value; - } - case DUK_TOK_STRING: { - DUK_ASSERT(tk->str1 != NULL); - duk_push_hstring(thr, tk->str1); - goto plain_value; - } - case DUK_TOK_REGEXP: { -#if defined(DUK_USE_REGEXP_SUPPORT) - duk_regconst_t reg_temp; - duk_regconst_t rc_re_bytecode; /* const */ - duk_regconst_t rc_re_source; /* const */ - - DUK_ASSERT(tk->str1 != NULL); - DUK_ASSERT(tk->str2 != NULL); - - DUK_DDD(DUK_DDDPRINT("emitting regexp op, str1=%!O, str2=%!O", (duk_heaphdr *) tk->str1, (duk_heaphdr *) tk->str2)); - - reg_temp = DUK__ALLOCTEMP(comp_ctx); - duk_push_hstring(thr, tk->str1); - duk_push_hstring(thr, tk->str2); - - /* [ ... pattern flags ] */ - - duk_regexp_compile(thr); - - /* [ ... escaped_source bytecode ] */ - - rc_re_bytecode = duk__getconst(comp_ctx); - rc_re_source = duk__getconst(comp_ctx); - - duk__emit_a_b_c(comp_ctx, - DUK_OP_REGEXP | DUK__EMIT_FLAG_BC_REGCONST, - reg_temp /*a*/, - rc_re_bytecode /*b*/, - rc_re_source /*c*/); - - duk__ivalue_regconst(res, reg_temp); - return; -#else /* DUK_USE_REGEXP_SUPPORT */ - goto syntax_error; -#endif /* DUK_USE_REGEXP_SUPPORT */ - } - case DUK_TOK_LBRACKET: { - DUK_DDD(DUK_DDDPRINT("parsing array literal")); - duk__nud_array_literal(comp_ctx, res); - return; - } - case DUK_TOK_LCURLY: { - DUK_DDD(DUK_DDDPRINT("parsing object literal")); - duk__nud_object_literal(comp_ctx, res); - return; - } - case DUK_TOK_LPAREN: { - duk_bool_t prev_allow_in; - - comp_ctx->curr_func.paren_level++; - prev_allow_in = comp_ctx->curr_func.allow_in; - comp_ctx->curr_func.allow_in = 1; /* reset 'allow_in' for parenthesized expression */ - - duk__expr(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/); /* Expression, terminates at a ')' */ - - duk__advance_expect(comp_ctx, DUK_TOK_RPAREN); /* No RegExp after parenthesized expression. */ - comp_ctx->curr_func.allow_in = prev_allow_in; - comp_ctx->curr_func.paren_level--; - return; - } - - /* MEMBER/NEW/CALL EXPRESSIONS */ - - case DUK_TOK_NEW: { - /* - * Parsing an expression starting with 'new' is tricky because - * there are multiple possible productions deriving from - * LeftHandSideExpression which begin with 'new'. - * - * We currently resort to one-token lookahead to distinguish the - * cases. Hopefully this is correct. The binding power must be - * such that parsing ends at an LPAREN (CallExpression) but not at - * a PERIOD or LBRACKET (MemberExpression). - * - * See doc/compiler.rst for discussion on the parsing approach, - * and testcases/test-dev-new.js for a bunch of documented tests. - */ - - duk_regconst_t reg_target; - duk_int_t nargs; - - DUK_DDD(DUK_DDDPRINT("begin parsing new expression")); - - reg_target = DUK__ALLOCTEMPS(comp_ctx, 2); - -#if defined(DUK_USE_ES6) - if (comp_ctx->curr_token.t == DUK_TOK_PERIOD) { - /* new.target */ - DUK_DDD(DUK_DDDPRINT("new.target")); - duk__advance(comp_ctx); - if (comp_ctx->curr_token.t_nores != DUK_TOK_IDENTIFIER || - !duk_hstring_equals_ascii_cstring(comp_ctx->curr_token.str1, "target")) { - goto syntax_error_newtarget; - } - if (comp_ctx->curr_func.is_global) { - goto syntax_error_newtarget; - } - duk__advance(comp_ctx); - duk__emit_bc(comp_ctx, DUK_OP_NEWTARGET, reg_target); - duk__ivalue_regconst(res, reg_target); - return; - } -#endif /* DUK_USE_ES6 */ - - duk__expr_toforcedreg(comp_ctx, res, DUK__BP_CALL /*rbp_flags*/, reg_target /*forced_reg*/); - duk__emit_bc(comp_ctx, DUK_OP_NEWOBJ, reg_target + 1); /* default instance */ - DUK__SETTEMP(comp_ctx, reg_target + 2); - - /* XXX: 'new obj.noSuch()' doesn't use GETPROPC now which - * makes the error message worse than for obj.noSuch(). - */ - - if (comp_ctx->curr_token.t == DUK_TOK_LPAREN) { - /* 'new' MemberExpression Arguments */ - DUK_DDD(DUK_DDDPRINT("new expression has argument list")); - duk__advance(comp_ctx); - nargs = duk__parse_arguments(comp_ctx, res); /* parse args starting from "next temp", reg_target + 1 */ - /* right paren eaten */ - } else { - /* 'new' MemberExpression */ - DUK_DDD(DUK_DDDPRINT("new expression has no argument list")); - nargs = 0; - } - - duk__emit_a_bc(comp_ctx, DUK_OP_CALL0 | DUK_BC_CALL_FLAG_CONSTRUCT, nargs /*num_args*/, reg_target /*target*/); - - DUK_DDD(DUK_DDDPRINT("end parsing new expression")); - - duk__ivalue_regconst(res, reg_target); - return; - } - - /* FUNCTION EXPRESSIONS */ - - case DUK_TOK_FUNCTION: { - /* Function expression. Note that any statement beginning with 'function' - * is handled by the statement parser as a function declaration, or a - * non-standard function expression/statement (or a SyntaxError). We only - * handle actual function expressions (occurring inside an expression) here. - * - * O(depth^2) parse count for inner functions is handled by recording a - * lexer offset on the first compilation pass, so that the function can - * be efficiently skipped on the second pass. This is encapsulated into - * duk__parse_func_like_fnum(). - */ - - duk_regconst_t reg_temp; - duk_int_t fnum; - - reg_temp = DUK__ALLOCTEMP(comp_ctx); - - /* curr_token follows 'function' */ - fnum = duk__parse_func_like_fnum(comp_ctx, 0 /*flags*/); - DUK_DDD(DUK_DDDPRINT("parsed inner function -> fnum %ld", (long) fnum)); - - duk__emit_a_bc(comp_ctx, DUK_OP_CLOSURE, reg_temp /*a*/, (duk_regconst_t) fnum /*bc*/); - - duk__ivalue_regconst(res, reg_temp); - return; - } - - /* UNARY EXPRESSIONS */ - - case DUK_TOK_DELETE: { - /* Delete semantics are a bit tricky. The description in E5 specification - * is kind of confusing, because it distinguishes between resolvability of - * a reference (which is only known at runtime) seemingly at compile time - * (= SyntaxError throwing). - */ - duk__expr(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/); /* UnaryExpression */ - if (res->t == DUK_IVAL_VAR) { - /* not allowed in strict mode, regardless of whether resolves; - * in non-strict mode DELVAR handles both non-resolving and - * resolving cases (the specification description is a bit confusing). - */ - - duk_regconst_t reg_temp; - duk_regconst_t reg_varbind; - duk_regconst_t rc_varname; - - if (comp_ctx->curr_func.is_strict) { - DUK_ERROR_SYNTAX(thr, DUK_STR_CANNOT_DELETE_IDENTIFIER); - DUK_WO_NORETURN(return;); - } - - DUK__SETTEMP(comp_ctx, temp_at_entry); - reg_temp = DUK__ALLOCTEMP(comp_ctx); - - duk_dup(thr, res->x1.valstack_idx); - if (duk__lookup_lhs(comp_ctx, ®_varbind, &rc_varname)) { - /* register bound variables are non-configurable -> always false */ - duk__emit_bc(comp_ctx, DUK_OP_LDFALSE, reg_temp); - } else { - duk_dup(thr, res->x1.valstack_idx); - rc_varname = duk__getconst(comp_ctx); - duk__emit_a_bc(comp_ctx, DUK_OP_DELVAR, reg_temp, rc_varname); - } - duk__ivalue_regconst(res, reg_temp); - } else if (res->t == DUK_IVAL_PROP) { - duk_regconst_t reg_temp; - duk_regconst_t reg_obj; - duk_regconst_t rc_key; - - DUK__SETTEMP(comp_ctx, temp_at_entry); - reg_temp = DUK__ALLOCTEMP(comp_ctx); - reg_obj = - duk__ispec_toregconst_raw(comp_ctx, &res->x1, -1 /*forced_reg*/, 0 /*flags*/); /* don't allow const */ - rc_key = - duk__ispec_toregconst_raw(comp_ctx, &res->x2, -1 /*forced_reg*/, DUK__IVAL_FLAG_ALLOW_CONST /*flags*/); - duk__emit_a_b_c(comp_ctx, DUK_OP_DELPROP | DUK__EMIT_FLAG_BC_REGCONST, reg_temp, reg_obj, rc_key); - - duk__ivalue_regconst(res, reg_temp); - } else { - /* non-Reference deletion is always 'true', even in strict mode */ - duk_push_true(thr); - goto plain_value; - } - return; - } - case DUK_TOK_VOID: { - duk__expr_toplain_ignore(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/); /* UnaryExpression */ - duk_push_undefined(thr); - goto plain_value; - } - case DUK_TOK_TYPEOF: { - /* 'typeof' must handle unresolvable references without throwing - * a ReferenceError (E5 Section 11.4.3). Register mapped values - * will never be unresolvable so special handling is only required - * when an identifier is a "slow path" one. - */ - duk__expr(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/); /* UnaryExpression */ - - if (res->t == DUK_IVAL_VAR) { - duk_regconst_t reg_varbind; - duk_regconst_t rc_varname; - duk_regconst_t reg_temp; - - duk_dup(thr, res->x1.valstack_idx); - if (!duk__lookup_lhs(comp_ctx, ®_varbind, &rc_varname)) { - DUK_DDD(DUK_DDDPRINT("typeof for an identifier name which could not be resolved " - "at compile time, need to use special run-time handling")); - reg_temp = DUK__ALLOCTEMP(comp_ctx); - duk__emit_a_bc(comp_ctx, DUK_OP_TYPEOFID, reg_temp, rc_varname); - duk__ivalue_regconst(res, reg_temp); - return; - } - } - - args = DUK_OP_TYPEOF; - goto unary; - } - case DUK_TOK_INCREMENT: { - args = (DUK_OP_PREINCP << 8) + DUK_OP_PREINCR; - goto preincdec; - } - case DUK_TOK_DECREMENT: { - args = (DUK_OP_PREDECP << 8) + DUK_OP_PREDECR; - goto preincdec; - } - case DUK_TOK_ADD: { - /* unary plus */ - duk__expr(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/); /* UnaryExpression */ - if (res->t == DUK_IVAL_PLAIN && res->x1.t == DUK_ISPEC_VALUE && duk_is_number(thr, res->x1.valstack_idx)) { - /* unary plus of a number is identity */ - return; - } - args = DUK_OP_UNP; - goto unary; - } - case DUK_TOK_SUB: { - /* unary minus */ - duk__expr(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/); /* UnaryExpression */ - if (res->t == DUK_IVAL_PLAIN && res->x1.t == DUK_ISPEC_VALUE && duk_is_number(thr, res->x1.valstack_idx)) { - /* this optimization is important to handle negative literals - * (which are not directly provided by the lexical grammar) - */ - duk_tval *tv_num; - duk_double_union du; - - tv_num = DUK_GET_TVAL_POSIDX(thr, res->x1.valstack_idx); - DUK_ASSERT(tv_num != NULL); - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_num)); - du.d = DUK_TVAL_GET_NUMBER(tv_num); - du.d = -du.d; - DUK_DBLUNION_NORMALIZE_NAN_CHECK(&du); - DUK_TVAL_SET_NUMBER(tv_num, du.d); - return; - } - args = DUK_OP_UNM; - goto unary; - } - case DUK_TOK_BNOT: { - duk__expr(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/); /* UnaryExpression */ - args = DUK_OP_BNOT; - goto unary; - } - case DUK_TOK_LNOT: { - duk__expr(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/); /* UnaryExpression */ - if (res->t == DUK_IVAL_PLAIN && res->x1.t == DUK_ISPEC_VALUE) { - /* Very minimal inlining to handle common idioms '!0' and '!1', - * and also boolean arguments like '!false' and '!true'. - */ - duk_tval *tv_val; - - tv_val = DUK_GET_TVAL_POSIDX(thr, res->x1.valstack_idx); - DUK_ASSERT(tv_val != NULL); - if (DUK_TVAL_IS_NUMBER(tv_val)) { - duk_double_t d; - d = DUK_TVAL_GET_NUMBER(tv_val); - if (duk_double_equals(d, 0.0)) { - /* Matches both +0 and -0 on purpose. */ - DUK_DDD(DUK_DDDPRINT("inlined lnot: !0 -> true")); - DUK_TVAL_SET_BOOLEAN_TRUE(tv_val); - return; - } else if (duk_double_equals(d, 1.0)) { - DUK_DDD(DUK_DDDPRINT("inlined lnot: !1 -> false")); - DUK_TVAL_SET_BOOLEAN_FALSE(tv_val); - return; - } - } else if (DUK_TVAL_IS_BOOLEAN(tv_val)) { - duk_small_uint_t v; - v = DUK_TVAL_GET_BOOLEAN(tv_val); - DUK_DDD(DUK_DDDPRINT("inlined lnot boolean: %ld", (long) v)); - DUK_ASSERT(v == 0 || v == 1); - DUK_TVAL_SET_BOOLEAN(tv_val, v ^ 0x01); - return; - } - } - args = DUK_OP_LNOT; - goto unary; - } - - } /* end switch */ - - DUK_ERROR_SYNTAX(thr, DUK_STR_PARSE_ERROR); - DUK_WO_NORETURN(return;); - -unary : { - /* Unary opcodes use just the 'BC' register source because it - * matches current shuffle limits, and maps cleanly to 16 high - * bits of the opcode. - */ - - duk_regconst_t reg_src, reg_res; - - reg_src = duk__ivalue_toregconst_raw(comp_ctx, res, -1 /*forced_reg*/, 0 /*flags*/); - if (DUK__ISREG_TEMP(comp_ctx, reg_src)) { - reg_res = reg_src; - } else { - reg_res = DUK__ALLOCTEMP(comp_ctx); - } - duk__emit_a_bc(comp_ctx, args, reg_res, reg_src); - duk__ivalue_regconst(res, reg_res); - return; -} - -preincdec : { - /* preincrement and predecrement */ - duk_regconst_t reg_res; - duk_small_uint_t args_op1 = args & 0xff; /* DUK_OP_PREINCR/DUK_OP_PREDECR */ - duk_small_uint_t args_op2 = args >> 8; /* DUK_OP_PREINCP_RR/DUK_OP_PREDECP_RR */ - - /* Specific assumptions for opcode numbering. */ - DUK_ASSERT(DUK_OP_PREINCR + 4 == DUK_OP_PREINCV); - DUK_ASSERT(DUK_OP_PREDECR + 4 == DUK_OP_PREDECV); - - reg_res = DUK__ALLOCTEMP(comp_ctx); - - duk__expr(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/); /* UnaryExpression */ - if (res->t == DUK_IVAL_VAR) { - duk_hstring *h_varname; - duk_regconst_t reg_varbind; - duk_regconst_t rc_varname; - - h_varname = duk_known_hstring(thr, res->x1.valstack_idx); - - if (duk__hstring_is_eval_or_arguments_in_strict_mode(comp_ctx, h_varname)) { - goto syntax_error; - } - - duk_dup(thr, res->x1.valstack_idx); - if (duk__lookup_lhs(comp_ctx, ®_varbind, &rc_varname)) { - duk__emit_a_bc(comp_ctx, - args_op1, /* e.g. DUK_OP_PREINCR */ - reg_res, - reg_varbind); - } else { - duk__emit_a_bc(comp_ctx, - args_op1 + 4, /* e.g. DUK_OP_PREINCV */ - reg_res, - rc_varname); - } - - DUK_DDD(DUK_DDDPRINT("preincdec to '%!O' -> reg_varbind=%ld, rc_varname=%ld", - (duk_heaphdr *) h_varname, - (long) reg_varbind, - (long) rc_varname)); - } else if (res->t == DUK_IVAL_PROP) { - duk_regconst_t reg_obj; /* allocate to reg only (not const) */ - duk_regconst_t rc_key; - reg_obj = duk__ispec_toregconst_raw(comp_ctx, &res->x1, -1 /*forced_reg*/, 0 /*flags*/); /* don't allow const */ - rc_key = duk__ispec_toregconst_raw(comp_ctx, &res->x2, -1 /*forced_reg*/, DUK__IVAL_FLAG_ALLOW_CONST /*flags*/); - duk__emit_a_b_c(comp_ctx, - args_op2 | DUK__EMIT_FLAG_BC_REGCONST, /* e.g. DUK_OP_PREINCP */ - reg_res, - reg_obj, - rc_key); - } else { - /* Technically return value is not needed because INVLHS will - * unconditially throw a ReferenceError. Coercion is necessary - * for proper semantics (consider ToNumber() called for an object). - * Use DUK_OP_UNP with a dummy register to get ToNumber(). - */ - - duk__ivalue_toforcedreg(comp_ctx, res, reg_res); - duk__emit_bc(comp_ctx, DUK_OP_UNP, reg_res); /* for side effects, result ignored */ - duk__emit_op_only(comp_ctx, DUK_OP_INVLHS); - } - DUK__SETTEMP(comp_ctx, reg_res + 1); - duk__ivalue_regconst(res, reg_res); - return; -} - -plain_value : { - /* Stack top contains plain value */ - duk__ivalue_plain_fromstack(comp_ctx, res); - return; -} - -#if defined(DUK_USE_ES6) -syntax_error_newtarget: - DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_NEWTARGET); - DUK_WO_NORETURN(return;); -#endif - -syntax_error: - DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_EXPRESSION); - DUK_WO_NORETURN(return;); -} - -/* XXX: add flag to indicate whether caller cares about return value; this - * affects e.g. handling of assignment expressions. This change needs API - * changes elsewhere too. - */ -DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_ivalue *res) { - duk_hthread *thr = comp_ctx->thr; - duk_token *tk; - duk_small_uint_t tok; - duk_uint32_t args; /* temp variable to pass constants and flags to shared code */ - - /* - * ctx->prev_token token to process with duk__expr_led() - * ctx->curr_token updated by caller - */ - - comp_ctx->curr_func.led_count++; - - /* The token in the switch has already been eaten here */ - tk = &comp_ctx->prev_token; - tok = tk->t; - - DUK_DDD(DUK_DDDPRINT("duk__expr_led(), prev_token.t=%ld, allow_in=%ld, paren_level=%ld", - (long) tk->t, - (long) comp_ctx->curr_func.allow_in, - (long) comp_ctx->curr_func.paren_level)); - - /* XXX: default priority for infix operators is duk__expr_lbp(tok) -> get it here? */ - - switch (tok) { - /* PRIMARY EXPRESSIONS */ - - case DUK_TOK_PERIOD: { - /* Property access expressions are critical for correct LHS ordering, - * see comments in duk__expr()! - * - * A conservative approach would be to use duk__ivalue_totempconst() - * for 'left'. However, allowing a reg-bound variable seems safe here - * and is nice because "foo.bar" is a common expression. If the ivalue - * is used in an expression a GETPROP will occur before any changes to - * the base value can occur. If the ivalue is used as an assignment - * LHS, the assignment code will ensure the base value is safe from - * RHS mutation. - */ - - /* XXX: This now coerces an identifier into a GETVAR to a temp, which - * causes an extra LDREG in call setup. It's sufficient to coerce to a - * unary ivalue? - */ - duk__ivalue_toplain(comp_ctx, left); - - /* NB: must accept reserved words as property name */ - if (comp_ctx->curr_token.t_nores != DUK_TOK_IDENTIFIER) { - DUK_ERROR_SYNTAX(thr, DUK_STR_EXPECTED_IDENTIFIER); - DUK_WO_NORETURN(return;); - } - - res->t = DUK_IVAL_PROP; - duk__copy_ispec(comp_ctx, &left->x1, &res->x1); /* left.x1 -> res.x1 */ - DUK_ASSERT(comp_ctx->curr_token.str1 != NULL); - duk_push_hstring(thr, comp_ctx->curr_token.str1); - duk_replace(thr, res->x2.valstack_idx); - res->x2.t = DUK_ISPEC_VALUE; - - /* special RegExp literal handling after IdentifierName */ - comp_ctx->curr_func.reject_regexp_in_adv = 1; - - duk__advance(comp_ctx); - return; - } - case DUK_TOK_LBRACKET: { - /* Property access expressions are critical for correct LHS ordering, - * see comments in duk__expr()! - */ - - /* XXX: optimize temp reg use */ - /* XXX: similar coercion issue as in DUK_TOK_PERIOD */ - /* XXX: coerce to regs? it might be better for enumeration use, where the - * same PROP ivalue is used multiple times. Or perhaps coerce PROP further - * there? - */ - /* XXX: for simple cases like x['y'] an unnecessary LDREG is - * emitted for the base value; could avoid it if we knew that - * the key expression is safe (e.g. just a single literal). - */ - - /* The 'left' value must not be a register bound variable - * because it may be mutated during the rest of the expression - * and E5.1 Section 11.2.1 specifies the order of evaluation - * so that the base value is evaluated first. - * See: test-bug-nested-prop-mutate.js. - */ - duk__ivalue_totempconst(comp_ctx, left); - duk__expr_toplain(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/); /* Expression, ']' terminates */ - duk__advance_expect(comp_ctx, DUK_TOK_RBRACKET); - - res->t = DUK_IVAL_PROP; - duk__copy_ispec(comp_ctx, &res->x1, &res->x2); /* res.x1 -> res.x2 */ - duk__copy_ispec(comp_ctx, &left->x1, &res->x1); /* left.x1 -> res.x1 */ - return; - } - case DUK_TOK_LPAREN: { - /* function call */ - duk_regconst_t reg_cs = DUK__ALLOCTEMPS(comp_ctx, 2); - duk_int_t nargs; - duk_small_uint_t call_op = DUK_OP_CALL0; - - /* XXX: attempt to get the call result to "next temp" whenever - * possible to avoid unnecessary register shuffles. - */ - - /* - * Setup call: target and 'this' binding. Three cases: - * - * 1. Identifier base (e.g. "foo()") - * 2. Property base (e.g. "foo.bar()") - * 3. Register base (e.g. "foo()()"; i.e. when a return value is a function) - */ - - if (left->t == DUK_IVAL_VAR) { - duk_hstring *h_varname; - duk_regconst_t reg_varbind; - duk_regconst_t rc_varname; - - DUK_DDD(DUK_DDDPRINT("function call with identifier base")); - - h_varname = duk_known_hstring(thr, left->x1.valstack_idx); - if (h_varname == DUK_HTHREAD_STRING_EVAL(thr)) { - /* Potential direct eval call detected, flag the CALL - * so that a run-time "direct eval" check is made and - * special behavior may be triggered. Note that this - * does not prevent 'eval' from being register bound. - */ - DUK_DDD(DUK_DDDPRINT("function call with identifier 'eval' " - "-> using EVALCALL, marking function " - "as may_direct_eval")); - call_op |= DUK_BC_CALL_FLAG_CALLED_AS_EVAL; - comp_ctx->curr_func.may_direct_eval = 1; - } - - duk_dup(thr, left->x1.valstack_idx); - if (duk__lookup_lhs(comp_ctx, ®_varbind, &rc_varname)) { - duk__emit_a_bc(comp_ctx, DUK_OP_CSREG | DUK__EMIT_FLAG_A_IS_SOURCE, reg_varbind, reg_cs + 0); - } else { - /* XXX: expand target register or constant field to - * reduce shuffling. - */ - DUK_ASSERT(DUK__ISCONST(rc_varname)); - duk__emit_a_b(comp_ctx, DUK_OP_CSVAR | DUK__EMIT_FLAG_BC_REGCONST, reg_cs + 0, rc_varname); - } - } else if (left->t == DUK_IVAL_PROP) { - /* Call through a property lookup, E5 Section 11.2.3, step 6.a.i, - * E5 Section 10.4.3. There used to be a separate CSPROP opcode - * but a typical call setup took 3 opcodes (e.g. LDREG, LDCONST, - * CSPROP) and the same can be achieved with ordinary loads. - */ -#if defined(DUK_USE_VERBOSE_ERRORS) - duk_regconst_t reg_key; -#endif - - DUK_DDD(DUK_DDDPRINT("function call with property base")); - - /* XXX: For Math.sin() this generates: LDCONST + LDREG + - * GETPROPC + call. The LDREG is unnecessary because LDCONST - * could be loaded directly into reg_cs + 1. This doesn't - * happen now because a variable cannot be in left->x1 of a - * DUK_IVAL_PROP. We could notice that left->x1 is a temp - * and reuse, but it would still be in the wrong position - * (reg_cs + 0 rather than reg_cs + 1). - */ - duk__ispec_toforcedreg(comp_ctx, &left->x1, reg_cs + 1); /* base */ -#if defined(DUK_USE_VERBOSE_ERRORS) - reg_key = duk__ispec_toregconst_raw(comp_ctx, &left->x2, -1, DUK__IVAL_FLAG_ALLOW_CONST /*flags*/); - duk__emit_a_b_c(comp_ctx, DUK_OP_GETPROPC | DUK__EMIT_FLAG_BC_REGCONST, reg_cs + 0, reg_cs + 1, reg_key); -#else - duk__ivalue_toforcedreg(comp_ctx, left, reg_cs + 0); /* base[key] */ -#endif - } else { - DUK_DDD(DUK_DDDPRINT("function call with register base")); - - duk__ivalue_toforcedreg(comp_ctx, left, reg_cs + 0); -#if 0 - duk__emit_a_bc(comp_ctx, - DUK_OP_CSREG | DUK__EMIT_FLAG_A_IS_SOURCE, - reg_cs + 0, - reg_cs + 0); /* in-place setup */ -#endif - /* Because of in-place setup, REGCS is equivalent to - * just this LDUNDEF. - */ - duk__emit_bc(comp_ctx, DUK_OP_LDUNDEF, reg_cs + 1); - } - - DUK__SETTEMP(comp_ctx, reg_cs + 2); - nargs = duk__parse_arguments(comp_ctx, res); /* parse args starting from "next temp" */ - - /* Tailcalls are handled by back-patching the already emitted opcode - * later in return statement parser. - */ - - duk__emit_a_bc(comp_ctx, call_op, (duk_regconst_t) nargs /*numargs*/, reg_cs /*basereg*/); - DUK__SETTEMP(comp_ctx, reg_cs + 1); /* result in csreg */ - - duk__ivalue_regconst(res, reg_cs); - return; - } - - /* POSTFIX EXPRESSION */ - - case DUK_TOK_INCREMENT: { - args = (DUK_OP_POSTINCP_RR << 16) + (DUK_OP_POSTINCR << 8) + 0; - goto postincdec; - } - case DUK_TOK_DECREMENT: { - args = (DUK_OP_POSTDECP_RR << 16) + (DUK_OP_POSTDECR << 8) + 0; - goto postincdec; - } - - /* EXPONENTIATION EXPRESSION */ - -#if defined(DUK_USE_ES7_EXP_OPERATOR) - case DUK_TOK_EXP: { - args = (DUK_OP_EXP << 8) + DUK__BP_EXPONENTIATION - 1; /* UnaryExpression */ - goto binary; - } -#endif - - /* MULTIPLICATIVE EXPRESSION */ - - case DUK_TOK_MUL: { - args = (DUK_OP_MUL << 8) + DUK__BP_MULTIPLICATIVE; /* ExponentiationExpression */ - goto binary; - } - case DUK_TOK_DIV: { - args = (DUK_OP_DIV << 8) + DUK__BP_MULTIPLICATIVE; /* ExponentiationExpression */ - goto binary; - } - case DUK_TOK_MOD: { - args = (DUK_OP_MOD << 8) + DUK__BP_MULTIPLICATIVE; /* ExponentiationExpression */ - goto binary; - } - - /* ADDITIVE EXPRESSION */ - - case DUK_TOK_ADD: { - args = (DUK_OP_ADD << 8) + DUK__BP_ADDITIVE; /* MultiplicativeExpression */ - goto binary; - } - case DUK_TOK_SUB: { - args = (DUK_OP_SUB << 8) + DUK__BP_ADDITIVE; /* MultiplicativeExpression */ - goto binary; - } - - /* SHIFT EXPRESSION */ - - case DUK_TOK_ALSHIFT: { - /* << */ - args = (DUK_OP_BASL << 8) + DUK__BP_SHIFT; - goto binary; - } - case DUK_TOK_ARSHIFT: { - /* >> */ - args = (DUK_OP_BASR << 8) + DUK__BP_SHIFT; - goto binary; - } - case DUK_TOK_RSHIFT: { - /* >>> */ - args = (DUK_OP_BLSR << 8) + DUK__BP_SHIFT; - goto binary; - } - - /* RELATIONAL EXPRESSION */ - - case DUK_TOK_LT: { - /* < */ - args = (DUK_OP_LT << 8) + DUK__BP_RELATIONAL; - goto binary; - } - case DUK_TOK_GT: { - args = (DUK_OP_GT << 8) + DUK__BP_RELATIONAL; - goto binary; - } - case DUK_TOK_LE: { - args = (DUK_OP_LE << 8) + DUK__BP_RELATIONAL; - goto binary; - } - case DUK_TOK_GE: { - args = (DUK_OP_GE << 8) + DUK__BP_RELATIONAL; - goto binary; - } - case DUK_TOK_INSTANCEOF: { - args = (DUK_OP_INSTOF << 8) + DUK__BP_RELATIONAL; - goto binary; - } - case DUK_TOK_IN: { - args = (DUK_OP_IN << 8) + DUK__BP_RELATIONAL; - goto binary; - } - - /* EQUALITY EXPRESSION */ - - case DUK_TOK_EQ: { - args = (DUK_OP_EQ << 8) + DUK__BP_EQUALITY; - goto binary; - } - case DUK_TOK_NEQ: { - args = (DUK_OP_NEQ << 8) + DUK__BP_EQUALITY; - goto binary; - } - case DUK_TOK_SEQ: { - args = (DUK_OP_SEQ << 8) + DUK__BP_EQUALITY; - goto binary; - } - case DUK_TOK_SNEQ: { - args = (DUK_OP_SNEQ << 8) + DUK__BP_EQUALITY; - goto binary; - } - - /* BITWISE EXPRESSIONS */ - - case DUK_TOK_BAND: { - args = (DUK_OP_BAND << 8) + DUK__BP_BAND; - goto binary; - } - case DUK_TOK_BXOR: { - args = (DUK_OP_BXOR << 8) + DUK__BP_BXOR; - goto binary; - } - case DUK_TOK_BOR: { - args = (DUK_OP_BOR << 8) + DUK__BP_BOR; - goto binary; - } - - /* LOGICAL EXPRESSIONS */ - - case DUK_TOK_LAND: { - /* syntactically left-associative but parsed as right-associative */ - args = (1 << 8) + DUK__BP_LAND - 1; - goto binary_logical; - } - case DUK_TOK_LOR: { - /* syntactically left-associative but parsed as right-associative */ - args = (0 << 8) + DUK__BP_LOR - 1; - goto binary_logical; - } - - /* CONDITIONAL EXPRESSION */ - - case DUK_TOK_QUESTION: { - /* XXX: common reg allocation need is to reuse a sub-expression's temp reg, - * but only if it really is a temp. Nothing fancy here now. - */ - duk_regconst_t reg_temp; - duk_int_t pc_jump1; - duk_int_t pc_jump2; - - reg_temp = DUK__ALLOCTEMP(comp_ctx); - duk__ivalue_toforcedreg(comp_ctx, left, reg_temp); - duk__emit_if_true_skip(comp_ctx, reg_temp); - pc_jump1 = duk__emit_jump_empty(comp_ctx); /* jump to false */ - duk__expr_toforcedreg(comp_ctx, - res, - DUK__BP_COMMA /*rbp_flags*/, - reg_temp /*forced_reg*/); /* AssignmentExpression */ - duk__advance_expect(comp_ctx, DUK_TOK_COLON); - pc_jump2 = duk__emit_jump_empty(comp_ctx); /* jump to end */ - duk__patch_jump_here(comp_ctx, pc_jump1); - duk__expr_toforcedreg(comp_ctx, - res, - DUK__BP_COMMA /*rbp_flags*/, - reg_temp /*forced_reg*/); /* AssignmentExpression */ - duk__patch_jump_here(comp_ctx, pc_jump2); - - DUK__SETTEMP(comp_ctx, reg_temp + 1); - duk__ivalue_regconst(res, reg_temp); - return; - } - - /* ASSIGNMENT EXPRESSION */ - - case DUK_TOK_EQUALSIGN: { - /* - * Assignments are right associative, allows e.g. - * a = 5; - * a += b = 9; // same as a += (b = 9) - * -> expression value 14, a = 14, b = 9 - * - * Right associativiness is reflected in the BP for recursion, - * "-1" ensures assignment operations are allowed. - * - * XXX: just use DUK__BP_COMMA (i.e. no need for 2-step bp levels)? - */ - args = (DUK_OP_NONE << 8) + DUK__BP_ASSIGNMENT - 1; /* DUK_OP_NONE marks a 'plain' assignment */ - goto assign; - } - case DUK_TOK_ADD_EQ: { - /* right associative */ - args = (DUK_OP_ADD << 8) + DUK__BP_ASSIGNMENT - 1; - goto assign; - } - case DUK_TOK_SUB_EQ: { - /* right associative */ - args = (DUK_OP_SUB << 8) + DUK__BP_ASSIGNMENT - 1; - goto assign; - } - case DUK_TOK_MUL_EQ: { - /* right associative */ - args = (DUK_OP_MUL << 8) + DUK__BP_ASSIGNMENT - 1; - goto assign; - } - case DUK_TOK_DIV_EQ: { - /* right associative */ - args = (DUK_OP_DIV << 8) + DUK__BP_ASSIGNMENT - 1; - goto assign; - } - case DUK_TOK_MOD_EQ: { - /* right associative */ - args = (DUK_OP_MOD << 8) + DUK__BP_ASSIGNMENT - 1; - goto assign; - } -#if defined(DUK_USE_ES7_EXP_OPERATOR) - case DUK_TOK_EXP_EQ: { - /* right associative */ - args = (DUK_OP_EXP << 8) + DUK__BP_ASSIGNMENT - 1; - goto assign; - } -#endif - case DUK_TOK_ALSHIFT_EQ: { - /* right associative */ - args = (DUK_OP_BASL << 8) + DUK__BP_ASSIGNMENT - 1; - goto assign; - } - case DUK_TOK_ARSHIFT_EQ: { - /* right associative */ - args = (DUK_OP_BASR << 8) + DUK__BP_ASSIGNMENT - 1; - goto assign; - } - case DUK_TOK_RSHIFT_EQ: { - /* right associative */ - args = (DUK_OP_BLSR << 8) + DUK__BP_ASSIGNMENT - 1; - goto assign; - } - case DUK_TOK_BAND_EQ: { - /* right associative */ - args = (DUK_OP_BAND << 8) + DUK__BP_ASSIGNMENT - 1; - goto assign; - } - case DUK_TOK_BOR_EQ: { - /* right associative */ - args = (DUK_OP_BOR << 8) + DUK__BP_ASSIGNMENT - 1; - goto assign; - } - case DUK_TOK_BXOR_EQ: { - /* right associative */ - args = (DUK_OP_BXOR << 8) + DUK__BP_ASSIGNMENT - 1; - goto assign; - } - - /* COMMA */ - - case DUK_TOK_COMMA: { - /* right associative */ - - duk__ivalue_toplain_ignore(comp_ctx, left); /* need side effects, not value */ - duk__expr_toplain(comp_ctx, res, DUK__BP_COMMA - 1 /*rbp_flags*/); - - /* return 'res' (of right part) as our result */ - return; - } - - default: { - break; - } - } - - DUK_D(DUK_DPRINT("parse error: unexpected token: %ld", (long) tok)); - DUK_ERROR_SYNTAX(thr, DUK_STR_PARSE_ERROR); - DUK_WO_NORETURN(return;); - -#if 0 - /* XXX: shared handling for 'duk__expr_lhs'? */ - if (comp_ctx->curr_func.paren_level == 0 && XXX) { - comp_ctx->curr_func.duk__expr_lhs = 0; - } -#endif - -binary: - /* - * Shared handling of binary operations - * - * args = (opcode << 8) + rbp - */ - { - duk__ivalue_toplain(comp_ctx, left); - duk__expr_toplain(comp_ctx, res, args & 0xff /*rbp_flags*/); - - /* combine left->x1 and res->x1 (right->x1, really) -> (left->x1 OP res->x1) */ - DUK_ASSERT(left->t == DUK_IVAL_PLAIN); - DUK_ASSERT(res->t == DUK_IVAL_PLAIN); - - res->t = DUK_IVAL_ARITH; - res->op = (args >> 8) & 0xff; - - res->x2.t = res->x1.t; - res->x2.regconst = res->x1.regconst; - duk_copy(thr, res->x1.valstack_idx, res->x2.valstack_idx); - - res->x1.t = left->x1.t; - res->x1.regconst = left->x1.regconst; - duk_copy(thr, left->x1.valstack_idx, res->x1.valstack_idx); - - DUK_DDD(DUK_DDDPRINT("binary op, res: t=%ld, x1.t=%ld, x1.regconst=0x%08lx, x2.t=%ld, x2.regconst=0x%08lx", - (long) res->t, - (long) res->x1.t, - (unsigned long) res->x1.regconst, - (long) res->x2.t, - (unsigned long) res->x2.regconst)); - return; - } - -binary_logical: - /* - * Shared handling for logical AND and logical OR. - * - * args = (truthval << 8) + rbp - * - * Truthval determines when to skip right-hand-side. - * For logical AND truthval=1, for logical OR truthval=0. - * - * See doc/compiler.rst for discussion on compiling logical - * AND and OR expressions. The approach here is very simplistic, - * generating extra jumps and multiple evaluations of truth values, - * but generates code on-the-fly with only local back-patching. - * - * Both logical AND and OR are syntactically left-associated. - * However, logical ANDs are compiled as right associative - * expressions, i.e. "A && B && C" as "A && (B && C)", to allow - * skip jumps to skip over the entire tail. Similarly for logical OR. - */ - - { - duk_regconst_t reg_temp; - duk_int_t pc_jump; - duk_small_uint_t args_truthval = args >> 8; - duk_small_uint_t args_rbp = args & 0xff; - - /* XXX: unoptimal use of temps, resetting */ - - reg_temp = DUK__ALLOCTEMP(comp_ctx); - - duk__ivalue_toforcedreg(comp_ctx, left, reg_temp); - DUK_ASSERT(DUK__ISREG(reg_temp)); - duk__emit_bc(comp_ctx, - (args_truthval ? DUK_OP_IFTRUE_R : DUK_OP_IFFALSE_R), - reg_temp); /* skip jump conditionally */ - pc_jump = duk__emit_jump_empty(comp_ctx); - duk__expr_toforcedreg(comp_ctx, res, args_rbp /*rbp_flags*/, reg_temp /*forced_reg*/); - duk__patch_jump_here(comp_ctx, pc_jump); - - duk__ivalue_regconst(res, reg_temp); - return; - } - -assign: - /* - * Shared assignment expression handling - * - * args = (opcode << 8) + rbp - * - * If 'opcode' is DUK_OP_NONE, plain assignment without arithmetic. - * Syntactically valid left-hand-side forms which are not accepted as - * left-hand-side values (e.g. as in "f() = 1") must NOT cause a - * SyntaxError, but rather a run-time ReferenceError. - * - * When evaluating X = Y, the LHS (X) is conceptually evaluated - * to a temporary first. The RHS is then evaluated. Finally, the - * is applied to the initial value of RHS (not the value after - * RHS evaluation), and written to X. Doing so concretely generates - * inefficient code so we'd like to avoid the temporary when possible. - * See: https://github.com/svaarala/duktape/pull/992. - * - * The expression value (final LHS value, written to RHS) is - * conceptually copied into a fresh temporary so that it won't - * change even if the LHS/RHS values change in outer expressions. - * For example, it'd be generally incorrect for the expression value - * to be the RHS register binding, unless there's a guarantee that it - * won't change during further expression evaluation. Using the - * temporary concretely produces inefficient bytecode, so we try to - * avoid the extra temporary for some known-to-be-safe cases. - * Currently the only safe case we detect is a "top level assignment", - * for example "x = y + z;", where the assignment expression value is - * ignored. - * See: test-dev-assign-expr.js and test-bug-assign-mutate-gh381.js. - */ - - { - duk_small_uint_t args_op = args >> 8; - duk_small_uint_t args_rbp = args & 0xff; - duk_bool_t toplevel_assign; - - /* XXX: here we need to know if 'left' is left-hand-side compatible. - * That information is no longer available from current expr parsing - * state; it would need to be carried into the 'left' ivalue or by - * some other means. - */ - - /* A top-level assignment is e.g. "x = y;". For these it's safe - * to use the RHS as-is as the expression value, even if the RHS - * is a reg-bound identifier. The RHS ('res') is right associative - * so it has consumed all other assignment level operations; the - * only relevant lower binding power construct is comma operator - * which will ignore the expression value provided here. Usually - * the top level assignment expression value is ignored, but it - * is relevant for e.g. eval code. - */ - toplevel_assign = (comp_ctx->curr_func.nud_count == 1 && /* one token before */ - comp_ctx->curr_func.led_count == 1); /* one operator (= assign) */ - DUK_DDD(DUK_DDDPRINT("assignment: nud_count=%ld, led_count=%ld, toplevel_assign=%ld", - (long) comp_ctx->curr_func.nud_count, - (long) comp_ctx->curr_func.led_count, - (long) toplevel_assign)); - - if (left->t == DUK_IVAL_VAR) { - duk_hstring *h_varname; - duk_regconst_t reg_varbind; - duk_regconst_t rc_varname; - - DUK_ASSERT(left->x1.t == DUK_ISPEC_VALUE); /* LHS is already side effect free */ - - h_varname = duk_known_hstring(thr, left->x1.valstack_idx); - if (duk__hstring_is_eval_or_arguments_in_strict_mode(comp_ctx, h_varname)) { - /* E5 Section 11.13.1 (and others for other assignments), step 4. */ - goto syntax_error_lvalue; - } - duk_dup(thr, left->x1.valstack_idx); - (void) duk__lookup_lhs(comp_ctx, ®_varbind, &rc_varname); - - if (args_op == DUK_OP_NONE) { - duk__expr(comp_ctx, res, args_rbp /*rbp_flags*/); - if (toplevel_assign) { - /* Any 'res' will do. */ - DUK_DDD(DUK_DDDPRINT("plain assignment, toplevel assign, use as is")); - } else { - /* 'res' must be a plain ivalue, and not register-bound variable. */ - DUK_DDD(DUK_DDDPRINT( - "plain assignment, not toplevel assign, ensure not a reg-bound identifier")); - if (res->t != DUK_IVAL_PLAIN || - (res->x1.t == DUK_ISPEC_REGCONST && DUK__ISREG_NOTTEMP(comp_ctx, res->x1.regconst))) { - duk__ivalue_totempconst(comp_ctx, res); - } - } - } else { - /* For X = Y we need to evaluate the pre-op - * value of X before evaluating the RHS: the RHS - * can change X, but when we do we must use - * the pre-op value. - */ - duk_regconst_t reg_temp; - - reg_temp = DUK__ALLOCTEMP(comp_ctx); - - if (reg_varbind >= 0) { - duk_regconst_t reg_res; - duk_regconst_t reg_src; - duk_int_t pc_temp_load; - duk_int_t pc_before_rhs; - duk_int_t pc_after_rhs; - - if (toplevel_assign) { - /* 'reg_varbind' is the operation result and can also - * become the expression value for top level assignments - * such as: "var x; x += y;". - */ - DUK_DD(DUK_DDPRINT("= expression is top level, write directly to reg_varbind")); - reg_res = reg_varbind; - } else { - /* Not safe to use 'reg_varbind' as assignment expression - * value, so go through a temp. - */ - DUK_DD(DUK_DDPRINT("= expression is not top level, write to reg_temp")); - reg_res = reg_temp; /* reg_res should be smallest possible */ - reg_temp = DUK__ALLOCTEMP(comp_ctx); - } - - /* Try to optimize X = Y for reg-bound - * variables. Detect side-effect free RHS - * narrowly by seeing whether it emits code. - * If not, rewind the code emitter and overwrite - * the unnecessary temp reg load. - */ - - pc_temp_load = duk__get_current_pc(comp_ctx); - duk__emit_a_bc(comp_ctx, DUK_OP_LDREG, reg_temp, reg_varbind); - - pc_before_rhs = duk__get_current_pc(comp_ctx); - duk__expr_toregconst(comp_ctx, res, args_rbp /*rbp_flags*/); - DUK_ASSERT(res->t == DUK_IVAL_PLAIN && res->x1.t == DUK_ISPEC_REGCONST); - pc_after_rhs = duk__get_current_pc(comp_ctx); - - DUK_DD(DUK_DDPRINT("pc_temp_load=%ld, pc_before_rhs=%ld, pc_after_rhs=%ld", - (long) pc_temp_load, - (long) pc_before_rhs, - (long) pc_after_rhs)); - - if (pc_after_rhs == pc_before_rhs) { - /* Note: if the reg_temp load generated shuffling - * instructions, we may need to rewind more than - * one instruction, so use explicit PC computation. - */ - DUK_DD(DUK_DDPRINT("rhs is side effect free, rewind and avoid unnecessary temp for " - "reg-based =")); - DUK_BW_ADD_PTR(comp_ctx->thr, - &comp_ctx->curr_func.bw_code, - (pc_temp_load - pc_before_rhs) * - (duk_int_t) sizeof(duk_compiler_instr)); - reg_src = reg_varbind; - } else { - DUK_DD(DUK_DDPRINT("rhs evaluation emitted code, not sure if rhs is side effect " - "free; use temp reg for LHS")); - reg_src = reg_temp; - } - - duk__emit_a_b_c(comp_ctx, - args_op | DUK__EMIT_FLAG_BC_REGCONST, - reg_res, - reg_src, - res->x1.regconst); - - res->x1.regconst = reg_res; - - /* Ensure compact use of temps. */ - if (DUK__ISREG_TEMP(comp_ctx, reg_res)) { - DUK__SETTEMP(comp_ctx, reg_res + 1); - } - } else { - /* When LHS is not register bound, always go through a - * temporary. No optimization for top level assignment. - */ - - duk__emit_a_bc(comp_ctx, DUK_OP_GETVAR, reg_temp, rc_varname); - - duk__expr_toregconst(comp_ctx, res, args_rbp /*rbp_flags*/); - DUK_ASSERT(res->t == DUK_IVAL_PLAIN && res->x1.t == DUK_ISPEC_REGCONST); - - duk__emit_a_b_c(comp_ctx, - args_op | DUK__EMIT_FLAG_BC_REGCONST, - reg_temp, - reg_temp, - res->x1.regconst); - res->x1.regconst = reg_temp; - } - - DUK_ASSERT(res->t == DUK_IVAL_PLAIN && res->x1.t == DUK_ISPEC_REGCONST); - } - - /* At this point 'res' holds the potential expression value. - * It can be basically any ivalue here, including a reg-bound - * identifier (if code above deems it safe) or a unary/binary - * operation. Operations must be resolved to a side effect free - * plain value, and the side effects must happen exactly once. - */ - - if (reg_varbind >= 0) { - if (res->t != DUK_IVAL_PLAIN) { - /* Resolve 'res' directly into the LHS binding, and use - * that as the expression value if safe. If not safe, - * resolve to a temp/const and copy to LHS. - */ - if (toplevel_assign) { - duk__ivalue_toforcedreg(comp_ctx, res, (duk_int_t) reg_varbind); - } else { - duk__ivalue_totempconst(comp_ctx, res); - duk__copy_ivalue(comp_ctx, res, left); /* use 'left' as a temp */ - duk__ivalue_toforcedreg(comp_ctx, left, (duk_int_t) reg_varbind); - } - } else { - /* Use 'res' as the expression value (it's side effect - * free and may be a plain value, a register, or a - * constant) and write it to the LHS binding too. - */ - duk__copy_ivalue(comp_ctx, res, left); /* use 'left' as a temp */ - duk__ivalue_toforcedreg(comp_ctx, left, (duk_int_t) reg_varbind); - } - } else { - /* Only a reg fits into 'A' so coerce 'res' into a register - * for PUTVAR. - * - * XXX: here the current A/B/C split is suboptimal: we could - * just use 9 bits for reg_res (and support constants) and 17 - * instead of 18 bits for the varname const index. - */ - - duk__ivalue_toreg(comp_ctx, res); - duk__emit_a_bc(comp_ctx, DUK_OP_PUTVAR | DUK__EMIT_FLAG_A_IS_SOURCE, res->x1.regconst, rc_varname); - } - - /* 'res' contains expression value */ - } else if (left->t == DUK_IVAL_PROP) { - /* E5 Section 11.13.1 (and others) step 4 never matches for prop writes -> no check */ - duk_regconst_t reg_obj; - duk_regconst_t rc_key; - duk_regconst_t rc_res; - duk_regconst_t reg_temp; - - /* Property access expressions ('a[b]') are critical to correct - * LHS evaluation ordering, see test-dev-assign-eval-order*.js. - * We must make sure that the LHS target slot (base object and - * key) don't change during RHS evaluation. The only concrete - * problem is a register reference to a variable-bound register - * (i.e., non-temp). Require temp regs for both key and base. - * - * Don't allow a constant for the object (even for a number - * etc), as it goes into the 'A' field of the opcode. - */ - - reg_obj = duk__ispec_toregconst_raw(comp_ctx, - &left->x1, - -1 /*forced_reg*/, - DUK__IVAL_FLAG_REQUIRE_TEMP /*flags*/); - - rc_key = duk__ispec_toregconst_raw(comp_ctx, - &left->x2, - -1 /*forced_reg*/, - DUK__IVAL_FLAG_REQUIRE_TEMP | DUK__IVAL_FLAG_ALLOW_CONST /*flags*/); - - /* Evaluate RHS only when LHS is safe. */ - - if (args_op == DUK_OP_NONE) { - duk__expr_toregconst(comp_ctx, res, args_rbp /*rbp_flags*/); - DUK_ASSERT(res->t == DUK_IVAL_PLAIN && res->x1.t == DUK_ISPEC_REGCONST); - rc_res = res->x1.regconst; - } else { - reg_temp = DUK__ALLOCTEMP(comp_ctx); - duk__emit_a_b_c(comp_ctx, DUK_OP_GETPROP | DUK__EMIT_FLAG_BC_REGCONST, reg_temp, reg_obj, rc_key); - - duk__expr_toregconst(comp_ctx, res, args_rbp /*rbp_flags*/); - DUK_ASSERT(res->t == DUK_IVAL_PLAIN && res->x1.t == DUK_ISPEC_REGCONST); - - duk__emit_a_b_c(comp_ctx, - args_op | DUK__EMIT_FLAG_BC_REGCONST, - reg_temp, - reg_temp, - res->x1.regconst); - rc_res = reg_temp; - } - - duk__emit_a_b_c(comp_ctx, - DUK_OP_PUTPROP | DUK__EMIT_FLAG_A_IS_SOURCE | DUK__EMIT_FLAG_BC_REGCONST, - reg_obj, - rc_key, - rc_res); - - duk__ivalue_regconst(res, rc_res); - } else { - /* No support for lvalues returned from new or function call expressions. - * However, these must NOT cause compile-time SyntaxErrors, but run-time - * ReferenceErrors. Both left and right sides of the assignment must be - * evaluated before throwing a ReferenceError. For instance: - * - * f() = g(); - * - * must result in f() being evaluated, then g() being evaluated, and - * finally, a ReferenceError being thrown. See E5 Section 11.13.1. - */ - - duk_regconst_t rc_res; - - /* First evaluate LHS fully to ensure all side effects are out. */ - duk__ivalue_toplain_ignore(comp_ctx, left); - - /* Then evaluate RHS fully (its value becomes the expression value too). - * Technically we'd need the side effect safety check here too, but because - * we always throw using INVLHS the result doesn't matter. - */ - rc_res = duk__expr_toregconst(comp_ctx, res, args_rbp /*rbp_flags*/); - - duk__emit_op_only(comp_ctx, DUK_OP_INVLHS); - - duk__ivalue_regconst(res, rc_res); - } - - return; - } - -postincdec : { - /* - * Post-increment/decrement will return the original value as its - * result value. However, even that value will be coerced using - * ToNumber() which is quite awkward. Specific bytecode opcodes - * are used to handle these semantics. - * - * Note that post increment/decrement has a "no LineTerminator here" - * restriction. This is handled by duk__expr_lbp(), which forcibly terminates - * the previous expression if a LineTerminator occurs before '++'/'--'. - */ - - duk_regconst_t reg_res; - duk_small_uint_t args_op1 = (args >> 8) & 0xff; /* DUK_OP_POSTINCR/DUK_OP_POSTDECR */ - duk_small_uint_t args_op2 = args >> 16; /* DUK_OP_POSTINCP_RR/DUK_OP_POSTDECP_RR */ - - /* Specific assumptions for opcode numbering. */ - DUK_ASSERT(DUK_OP_POSTINCR + 4 == DUK_OP_POSTINCV); - DUK_ASSERT(DUK_OP_POSTDECR + 4 == DUK_OP_POSTDECV); - - reg_res = DUK__ALLOCTEMP(comp_ctx); - - if (left->t == DUK_IVAL_VAR) { - duk_hstring *h_varname; - duk_regconst_t reg_varbind; - duk_regconst_t rc_varname; - - h_varname = duk_known_hstring(thr, left->x1.valstack_idx); - - if (duk__hstring_is_eval_or_arguments_in_strict_mode(comp_ctx, h_varname)) { - goto syntax_error; - } - - duk_dup(thr, left->x1.valstack_idx); - if (duk__lookup_lhs(comp_ctx, ®_varbind, &rc_varname)) { - duk__emit_a_bc(comp_ctx, - args_op1, /* e.g. DUK_OP_POSTINCR */ - reg_res, - reg_varbind); - } else { - duk__emit_a_bc(comp_ctx, - args_op1 + 4, /* e.g. DUK_OP_POSTINCV */ - reg_res, - rc_varname); - } - - DUK_DDD(DUK_DDDPRINT("postincdec to '%!O' -> reg_varbind=%ld, rc_varname=%ld", - (duk_heaphdr *) h_varname, - (long) reg_varbind, - (long) rc_varname)); - } else if (left->t == DUK_IVAL_PROP) { - duk_regconst_t reg_obj; /* allocate to reg only (not const) */ - duk_regconst_t rc_key; - - reg_obj = duk__ispec_toregconst_raw(comp_ctx, &left->x1, -1 /*forced_reg*/, 0 /*flags*/); /* don't allow const */ - rc_key = duk__ispec_toregconst_raw(comp_ctx, &left->x2, -1 /*forced_reg*/, DUK__IVAL_FLAG_ALLOW_CONST /*flags*/); - duk__emit_a_b_c(comp_ctx, - args_op2 | DUK__EMIT_FLAG_BC_REGCONST, /* e.g. DUK_OP_POSTINCP */ - reg_res, - reg_obj, - rc_key); - } else { - /* Technically return value is not needed because INVLHS will - * unconditially throw a ReferenceError. Coercion is necessary - * for proper semantics (consider ToNumber() called for an object). - * Use DUK_OP_UNP with a dummy register to get ToNumber(). - */ - duk__ivalue_toforcedreg(comp_ctx, left, reg_res); - duk__emit_bc(comp_ctx, DUK_OP_UNP, reg_res); /* for side effects, result ignored */ - duk__emit_op_only(comp_ctx, DUK_OP_INVLHS); - } - - DUK__SETTEMP(comp_ctx, reg_res + 1); - duk__ivalue_regconst(res, reg_res); - return; -} - -syntax_error: - DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_EXPRESSION); - DUK_WO_NORETURN(return;); - -syntax_error_lvalue: - DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_LVALUE); - DUK_WO_NORETURN(return;); -} - -DUK_LOCAL duk_small_uint_t duk__expr_lbp(duk_compiler_ctx *comp_ctx) { - duk_small_uint_t tok = comp_ctx->curr_token.t; - - DUK_ASSERT_DISABLE(tok >= DUK_TOK_MINVAL); /* unsigned */ - DUK_ASSERT(tok <= DUK_TOK_MAXVAL); - DUK_ASSERT(sizeof(duk__token_lbp) == DUK_TOK_MAXVAL + 1); - - /* XXX: integrate support for this into led() instead? - * Similar issue as post-increment/post-decrement. - */ - - /* prevent duk__expr_led() by using a binding power less than anything valid */ - if (tok == DUK_TOK_IN && !comp_ctx->curr_func.allow_in) { - return 0; - } - - if ((tok == DUK_TOK_DECREMENT || tok == DUK_TOK_INCREMENT) && (comp_ctx->curr_token.lineterm)) { - /* '++' or '--' in a post-increment/decrement position, - * and a LineTerminator occurs between the operator and - * the preceding expression. Force the previous expr - * to terminate, in effect treating e.g. "a,b\n++" as - * "a,b;++" (= SyntaxError). - */ - return 0; - } - - return DUK__TOKEN_LBP_GET_BP(duk__token_lbp[tok]); /* format is bit packed */ -} - -/* - * Expression parsing. - * - * Upon entry to 'expr' and its variants, 'curr_tok' is assumed to be the - * first token of the expression. Upon exit, 'curr_tok' will be the first - * token not part of the expression (e.g. semicolon terminating an expression - * statement). - */ - -#define DUK__EXPR_RBP_MASK 0xff -#define DUK__EXPR_FLAG_REJECT_IN (1 << 8) /* reject 'in' token (used for for-in) */ -#define DUK__EXPR_FLAG_ALLOW_EMPTY (1 << 9) /* allow empty expression */ -#define DUK__EXPR_FLAG_REQUIRE_INIT (1 << 10) /* require initializer for var/const */ - -/* main expression parser function */ -DUK_LOCAL void duk__expr(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) { - duk_hthread *thr = comp_ctx->thr; - duk_ivalue tmp_alloc; /* 'res' is used for "left", and 'tmp' for "right" */ - duk_ivalue *tmp = &tmp_alloc; - duk_small_uint_t rbp; - - DUK__RECURSION_INCREASE(comp_ctx, thr); - - duk_require_stack(thr, DUK__PARSE_EXPR_SLOTS); - - /* filter out flags from exprtop rbp_flags here to save space */ - rbp = rbp_flags & DUK__EXPR_RBP_MASK; - - DUK_DDD(DUK_DDDPRINT("duk__expr(), rbp_flags=%ld, rbp=%ld, allow_in=%ld, paren_level=%ld", - (long) rbp_flags, - (long) rbp, - (long) comp_ctx->curr_func.allow_in, - (long) comp_ctx->curr_func.paren_level)); - - duk_memzero(&tmp_alloc, sizeof(tmp_alloc)); - tmp->x1.valstack_idx = duk_get_top(thr); - tmp->x2.valstack_idx = tmp->x1.valstack_idx + 1; - duk_push_undefined(thr); - duk_push_undefined(thr); - - /* XXX: where to release temp regs in intermediate expressions? - * e.g. 1+2+3 -> don't inflate temp register count when parsing this. - * that particular expression temp regs can be forced here. - */ - - /* XXX: increase ctx->expr_tokens here for every consumed token - * (this would be a nice statistic)? - */ - - if (comp_ctx->curr_token.t == DUK_TOK_SEMICOLON || comp_ctx->curr_token.t == DUK_TOK_RPAREN) { - /* XXX: possibly incorrect handling of empty expression */ - DUK_DDD(DUK_DDDPRINT("empty expression")); - if (!(rbp_flags & DUK__EXPR_FLAG_ALLOW_EMPTY)) { - DUK_ERROR_SYNTAX(thr, DUK_STR_EMPTY_EXPR_NOT_ALLOWED); - DUK_WO_NORETURN(return;); - } - duk_push_undefined(thr); - duk__ivalue_plain_fromstack(comp_ctx, res); - goto cleanup; - } - - duk__advance(comp_ctx); - duk__expr_nud(comp_ctx, res); /* reuse 'res' as 'left' */ - while (rbp < duk__expr_lbp(comp_ctx)) { - duk__advance(comp_ctx); - duk__expr_led(comp_ctx, res, tmp); - duk__copy_ivalue(comp_ctx, tmp, res); /* tmp -> res */ - } - -cleanup: - /* final result is already in 'res' */ - - duk_pop_2(thr); - - DUK__RECURSION_DECREASE(comp_ctx, thr); -} - -DUK_LOCAL void duk__exprtop(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) { - duk_hthread *thr = comp_ctx->thr; - - /* Note: these variables must reside in 'curr_func' instead of the global - * context: when parsing function expressions, expression parsing is nested. - */ - comp_ctx->curr_func.nud_count = 0; - comp_ctx->curr_func.led_count = 0; - comp_ctx->curr_func.paren_level = 0; - comp_ctx->curr_func.expr_lhs = 1; - comp_ctx->curr_func.allow_in = (rbp_flags & DUK__EXPR_FLAG_REJECT_IN ? 0 : 1); - - duk__expr(comp_ctx, res, rbp_flags); - - if (!(rbp_flags & DUK__EXPR_FLAG_ALLOW_EMPTY) && duk__expr_is_empty(comp_ctx)) { - DUK_ERROR_SYNTAX(thr, DUK_STR_EMPTY_EXPR_NOT_ALLOWED); - DUK_WO_NORETURN(return;); - } -} - -/* A bunch of helpers (for size optimization) that combine duk__expr()/duk__exprtop() - * and result conversions. - * - * Each helper needs at least 2-3 calls to make it worth while to wrap. - */ - -#if 0 /* unused */ -DUK_LOCAL duk_regconst_t duk__expr_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) { - duk__expr(comp_ctx, res, rbp_flags); - return duk__ivalue_toreg(comp_ctx, res); -} -#endif - -#if 0 /* unused */ -DUK_LOCAL duk_regconst_t duk__expr_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) { - duk__expr(comp_ctx, res, rbp_flags); - return duk__ivalue_totemp(comp_ctx, res); -} -#endif - -DUK_LOCAL void duk__expr_toforcedreg(duk_compiler_ctx *comp_ctx, - duk_ivalue *res, - duk_small_uint_t rbp_flags, - duk_regconst_t forced_reg) { - DUK_ASSERT(forced_reg >= 0); - duk__expr(comp_ctx, res, rbp_flags); - duk__ivalue_toforcedreg(comp_ctx, res, forced_reg); -} - -DUK_LOCAL duk_regconst_t duk__expr_toregconst(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) { - duk__expr(comp_ctx, res, rbp_flags); - return duk__ivalue_toregconst(comp_ctx, res); -} - -#if 0 /* unused */ -DUK_LOCAL duk_regconst_t duk__expr_totempconst(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) { - duk__expr(comp_ctx, res, rbp_flags); - return duk__ivalue_totempconst(comp_ctx, res); -} -#endif - -DUK_LOCAL void duk__expr_toplain(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) { - duk__expr(comp_ctx, res, rbp_flags); - duk__ivalue_toplain(comp_ctx, res); -} - -DUK_LOCAL void duk__expr_toplain_ignore(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) { - duk__expr(comp_ctx, res, rbp_flags); - duk__ivalue_toplain_ignore(comp_ctx, res); -} - -DUK_LOCAL duk_regconst_t duk__exprtop_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) { - duk__exprtop(comp_ctx, res, rbp_flags); - return duk__ivalue_toreg(comp_ctx, res); -} - -#if 0 /* unused */ -DUK_LOCAL duk_regconst_t duk__exprtop_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) { - duk__exprtop(comp_ctx, res, rbp_flags); - return duk__ivalue_totemp(comp_ctx, res); -} -#endif - -DUK_LOCAL void duk__exprtop_toforcedreg(duk_compiler_ctx *comp_ctx, - duk_ivalue *res, - duk_small_uint_t rbp_flags, - duk_regconst_t forced_reg) { - DUK_ASSERT(forced_reg >= 0); - duk__exprtop(comp_ctx, res, rbp_flags); - duk__ivalue_toforcedreg(comp_ctx, res, forced_reg); -} - -DUK_LOCAL duk_regconst_t duk__exprtop_toregconst(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) { - duk__exprtop(comp_ctx, res, rbp_flags); - return duk__ivalue_toregconst(comp_ctx, res); -} - -#if 0 /* unused */ -DUK_LOCAL void duk__exprtop_toplain_ignore(duk_compiler_ctx *comp_ctx, duk_ivalue *res, int rbp_flags) { - duk__exprtop(comp_ctx, res, rbp_flags); - duk__ivalue_toplain_ignore(comp_ctx, res); -} -#endif - -/* - * Parse an individual source element (top level statement) or a statement. - * - * Handles labeled statements automatically (peeling away labels before - * parsing an expression that follows the label(s)). - * - * Upon entry, 'curr_tok' contains the first token of the statement (parsed - * in "allow regexp literal" mode). Upon exit, 'curr_tok' contains the first - * token following the statement (if the statement has a terminator, this is - * the token after the terminator). - */ - -#define DUK__HAS_VAL (1 << 0) /* stmt has non-empty value */ -#define DUK__HAS_TERM (1 << 1) /* stmt has explicit/implicit semicolon terminator */ -#define DUK__ALLOW_AUTO_SEMI_ALWAYS (1 << 2) /* allow automatic semicolon even without lineterm (compatibility) */ -#define DUK__STILL_PROLOGUE (1 << 3) /* statement does not terminate directive prologue */ -#define DUK__IS_TERMINAL (1 << 4) /* statement is guaranteed to be terminal (control doesn't flow to next statement) */ - -/* Parse a single variable declaration (e.g. "i" or "i=10"). A leading 'var' - * has already been eaten. These is no return value in 'res', it is used only - * as a temporary. - * - * When called from 'for-in' statement parser, the initializer expression must - * not allow the 'in' token. The caller supply additional expression parsing - * flags (like DUK__EXPR_FLAG_REJECT_IN) in 'expr_flags'. - * - * Finally, out_rc_varname and out_reg_varbind are updated to reflect where - * the identifier is bound: - * - * If register bound: out_reg_varbind >= 0, out_rc_varname == 0 (ignore) - * If not register bound: out_reg_varbind < 0, out_rc_varname >= 0 - * - * These allow the caller to use the variable for further assignment, e.g. - * as is done in 'for-in' parsing. - */ - -DUK_LOCAL void duk__parse_var_decl(duk_compiler_ctx *comp_ctx, - duk_ivalue *res, - duk_small_uint_t expr_flags, - duk_regconst_t *out_reg_varbind, - duk_regconst_t *out_rc_varname) { - duk_hthread *thr = comp_ctx->thr; - duk_hstring *h_varname; - duk_regconst_t reg_varbind; - duk_regconst_t rc_varname; - - /* assume 'var' has been eaten */ - - /* Note: Identifier rejects reserved words */ - if (comp_ctx->curr_token.t != DUK_TOK_IDENTIFIER) { - goto syntax_error; - } - h_varname = comp_ctx->curr_token.str1; - - DUK_ASSERT(h_varname != NULL); - - /* strict mode restrictions (E5 Section 12.2.1) */ - if (duk__hstring_is_eval_or_arguments_in_strict_mode(comp_ctx, h_varname)) { - goto syntax_error; - } - - /* register declarations in first pass */ - if (comp_ctx->curr_func.in_scanning) { - duk_uarridx_t n; - DUK_DDD(DUK_DDDPRINT("register variable declaration %!O in pass 1", (duk_heaphdr *) h_varname)); - n = (duk_uarridx_t) duk_get_length(thr, comp_ctx->curr_func.decls_idx); - duk_push_hstring(thr, h_varname); - duk_put_prop_index(thr, comp_ctx->curr_func.decls_idx, n); - duk_push_int(thr, DUK_DECL_TYPE_VAR + (0 << 8)); - duk_put_prop_index(thr, comp_ctx->curr_func.decls_idx, n + 1); - } - - duk_push_hstring(thr, h_varname); /* push before advancing to keep reachable */ - - /* register binding lookup is based on varmap (even in first pass) */ - duk_dup_top(thr); - (void) duk__lookup_lhs(comp_ctx, ®_varbind, &rc_varname); - - duk__advance(comp_ctx); /* eat identifier */ - - if (comp_ctx->curr_token.t == DUK_TOK_EQUALSIGN) { - duk__advance(comp_ctx); - - DUK_DDD(DUK_DDDPRINT("vardecl, assign to '%!O' -> reg_varbind=%ld, rc_varname=%ld", - (duk_heaphdr *) h_varname, - (long) reg_varbind, - (long) rc_varname)); - - duk__exprtop(comp_ctx, res, DUK__BP_COMMA | expr_flags /*rbp_flags*/); /* AssignmentExpression */ - - if (reg_varbind >= 0) { - duk__ivalue_toforcedreg(comp_ctx, res, reg_varbind); - } else { - duk_regconst_t reg_val; - reg_val = duk__ivalue_toreg(comp_ctx, res); - duk__emit_a_bc(comp_ctx, DUK_OP_PUTVAR | DUK__EMIT_FLAG_A_IS_SOURCE, reg_val, rc_varname); - } - } else { - if (expr_flags & DUK__EXPR_FLAG_REQUIRE_INIT) { - /* Used for minimal 'const': initializer required. */ - goto syntax_error; - } - } - - duk_pop(thr); /* pop varname */ - - *out_rc_varname = rc_varname; - *out_reg_varbind = reg_varbind; - - return; - -syntax_error: - DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_VAR_DECLARATION); - DUK_WO_NORETURN(return;); -} - -DUK_LOCAL void duk__parse_var_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t expr_flags) { - duk_regconst_t reg_varbind; - duk_regconst_t rc_varname; - - duk__advance(comp_ctx); /* eat 'var' */ - - for (;;) { - /* rc_varname and reg_varbind are ignored here */ - duk__parse_var_decl(comp_ctx, res, 0 | expr_flags, ®_varbind, &rc_varname); - - if (comp_ctx->curr_token.t != DUK_TOK_COMMA) { - break; - } - duk__advance(comp_ctx); - } -} - -DUK_LOCAL void duk__parse_for_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site) { - duk_hthread *thr = comp_ctx->thr; - duk_int_t pc_v34_lhs; /* start variant 3/4 left-hand-side code (L1 in doc/compiler.rst example) */ - duk_regconst_t temp_reset; /* knock back "next temp" to this whenever possible */ - duk_regconst_t reg_temps; /* preallocated temporaries (2) for variants 3 and 4 */ - - DUK_DDD(DUK_DDDPRINT("start parsing a for/for-in statement")); - - /* Two temporaries are preallocated here for variants 3 and 4 which need - * registers which are never clobbered by expressions in the loop - * (concretely: for the enumerator object and the next enumerated value). - * Variants 1 and 2 "release" these temps. - */ - - reg_temps = DUK__ALLOCTEMPS(comp_ctx, 2); - - temp_reset = DUK__GETTEMP(comp_ctx); - - /* - * For/for-in main variants are: - * - * 1. for (ExpressionNoIn_opt; Expression_opt; Expression_opt) Statement - * 2. for (var VariableDeclarationNoIn; Expression_opt; Expression_opt) Statement - * 3. for (LeftHandSideExpression in Expression) Statement - * 4. for (var VariableDeclarationNoIn in Expression) Statement - * - * Parsing these without arbitrary lookahead or backtracking is relatively - * tricky but we manage to do so for now. - * - * See doc/compiler.rst for a detailed discussion of control flow - * issues, evaluation order issues, etc. - */ - - duk__advance(comp_ctx); /* eat 'for' */ - duk__advance_expect(comp_ctx, DUK_TOK_LPAREN); - - DUK_DDD(DUK_DDDPRINT("detecting for/for-in loop variant, pc=%ld", (long) duk__get_current_pc(comp_ctx))); - - /* a label site has been emitted by duk__parse_stmt() automatically - * (it will also emit the ENDLABEL). - */ - - if (comp_ctx->curr_token.t == DUK_TOK_VAR) { - /* - * Variant 2 or 4 - */ - - duk_regconst_t reg_varbind; /* variable binding register if register-bound (otherwise < 0) */ - duk_regconst_t rc_varname; /* variable name reg/const, if variable not register-bound */ - - duk__advance(comp_ctx); /* eat 'var' */ - duk__parse_var_decl(comp_ctx, res, DUK__EXPR_FLAG_REJECT_IN, ®_varbind, &rc_varname); - DUK__SETTEMP(comp_ctx, temp_reset); - - if (comp_ctx->curr_token.t == DUK_TOK_IN) { - /* - * Variant 4 - */ - - DUK_DDD(DUK_DDDPRINT("detected for variant 4: for (var VariableDeclarationNoIn in Expression) Statement")); - pc_v34_lhs = duk__get_current_pc(comp_ctx); /* jump is inserted here */ - if (reg_varbind >= 0) { - duk__emit_a_bc(comp_ctx, DUK_OP_LDREG, reg_varbind, reg_temps + 0); - } else { - duk__emit_a_bc(comp_ctx, DUK_OP_PUTVAR | DUK__EMIT_FLAG_A_IS_SOURCE, reg_temps + 0, rc_varname); - } - goto parse_3_or_4; - } else { - /* - * Variant 2 - */ - - DUK_DDD(DUK_DDDPRINT( - "detected for variant 2: for (var VariableDeclarationNoIn; Expression_opt; Expression_opt) Statement")); - for (;;) { - /* more initializers */ - if (comp_ctx->curr_token.t != DUK_TOK_COMMA) { - break; - } - DUK_DDD(DUK_DDDPRINT("variant 2 has another variable initializer")); - - duk__advance(comp_ctx); /* eat comma */ - duk__parse_var_decl(comp_ctx, res, DUK__EXPR_FLAG_REJECT_IN, ®_varbind, &rc_varname); - } - goto parse_1_or_2; - } - } else { - /* - * Variant 1 or 3 - */ - - pc_v34_lhs = duk__get_current_pc(comp_ctx); /* jump is inserted here (variant 3) */ - - /* Note that duk__exprtop() here can clobber any reg above current temp_next, - * so any loop variables (e.g. enumerator) must be "preallocated". - */ - - /* don't coerce yet to a plain value (variant 3 needs special handling) */ - duk__exprtop(comp_ctx, - res, - DUK__BP_FOR_EXPR | DUK__EXPR_FLAG_REJECT_IN | - DUK__EXPR_FLAG_ALLOW_EMPTY /*rbp_flags*/); /* Expression */ - if (comp_ctx->curr_token.t == DUK_TOK_IN) { - /* - * Variant 3 - */ - - /* XXX: need to determine LHS type, and check that it is LHS compatible */ - DUK_DDD(DUK_DDDPRINT("detected for variant 3: for (LeftHandSideExpression in Expression) Statement")); - if (duk__expr_is_empty(comp_ctx)) { - goto syntax_error; /* LeftHandSideExpression does not allow empty expression */ - } - - if (res->t == DUK_IVAL_VAR) { - duk_regconst_t reg_varbind; - duk_regconst_t rc_varname; - - duk_dup(thr, res->x1.valstack_idx); - if (duk__lookup_lhs(comp_ctx, ®_varbind, &rc_varname)) { - duk__emit_a_bc(comp_ctx, DUK_OP_LDREG, reg_varbind, reg_temps + 0); - } else { - duk__emit_a_bc(comp_ctx, - DUK_OP_PUTVAR | DUK__EMIT_FLAG_A_IS_SOURCE, - reg_temps + 0, - rc_varname); - } - } else if (res->t == DUK_IVAL_PROP) { - /* Don't allow a constant for the object (even for a number etc), as - * it goes into the 'A' field of the opcode. - */ - duk_regconst_t reg_obj; - duk_regconst_t rc_key; - reg_obj = duk__ispec_toregconst_raw(comp_ctx, - &res->x1, - -1 /*forced_reg*/, - 0 /*flags*/); /* don't allow const */ - rc_key = duk__ispec_toregconst_raw(comp_ctx, - &res->x2, - -1 /*forced_reg*/, - DUK__IVAL_FLAG_ALLOW_CONST /*flags*/); - duk__emit_a_b_c(comp_ctx, - DUK_OP_PUTPROP | DUK__EMIT_FLAG_A_IS_SOURCE | DUK__EMIT_FLAG_BC_REGCONST, - reg_obj, - rc_key, - reg_temps + 0); - } else { - duk__ivalue_toplain_ignore(comp_ctx, res); /* just in case */ - duk__emit_op_only(comp_ctx, DUK_OP_INVLHS); - } - goto parse_3_or_4; - } else { - /* - * Variant 1 - */ - - DUK_DDD(DUK_DDDPRINT( - "detected for variant 1: for (ExpressionNoIn_opt; Expression_opt; Expression_opt) Statement")); - duk__ivalue_toplain_ignore(comp_ctx, res); - goto parse_1_or_2; - } - } - -parse_1_or_2: - /* - * Parse variant 1 or 2. The first part expression (which differs - * in the variants) has already been parsed and its code emitted. - * - * reg_temps + 0: unused - * reg_temps + 1: unused - */ - { - duk_regconst_t rc_cond; - duk_int_t pc_l1, pc_l2, pc_l3, pc_l4; - duk_int_t pc_jumpto_l3, pc_jumpto_l4; - duk_bool_t expr_c_empty; - - DUK_DDD(DUK_DDDPRINT("shared code for parsing variants 1 and 2")); - - /* "release" preallocated temps since we won't need them */ - temp_reset = reg_temps + 0; - DUK__SETTEMP(comp_ctx, temp_reset); - - duk__advance_expect(comp_ctx, DUK_TOK_SEMICOLON); - - pc_l1 = duk__get_current_pc(comp_ctx); - duk__exprtop(comp_ctx, res, DUK__BP_FOR_EXPR | DUK__EXPR_FLAG_ALLOW_EMPTY /*rbp_flags*/); /* Expression_opt */ - if (duk__expr_is_empty(comp_ctx)) { - /* no need to coerce */ - pc_jumpto_l3 = duk__emit_jump_empty(comp_ctx); /* to body */ - pc_jumpto_l4 = -1; /* omitted */ - } else { - rc_cond = duk__ivalue_toregconst(comp_ctx, res); - duk__emit_if_false_skip(comp_ctx, rc_cond); - pc_jumpto_l3 = duk__emit_jump_empty(comp_ctx); /* to body */ - pc_jumpto_l4 = duk__emit_jump_empty(comp_ctx); /* to exit */ - } - DUK__SETTEMP(comp_ctx, temp_reset); - - duk__advance_expect(comp_ctx, DUK_TOK_SEMICOLON); - - pc_l2 = duk__get_current_pc(comp_ctx); - duk__exprtop(comp_ctx, res, DUK__BP_FOR_EXPR | DUK__EXPR_FLAG_ALLOW_EMPTY /*rbp_flags*/); /* Expression_opt */ - if (duk__expr_is_empty(comp_ctx)) { - /* no need to coerce */ - expr_c_empty = 1; - /* JUMP L1 omitted */ - } else { - duk__ivalue_toplain_ignore(comp_ctx, res); - expr_c_empty = 0; - duk__emit_jump(comp_ctx, pc_l1); - } - DUK__SETTEMP(comp_ctx, temp_reset); - - comp_ctx->curr_func.allow_regexp_in_adv = 1; - duk__advance_expect(comp_ctx, DUK_TOK_RPAREN); /* Allow RegExp as part of next stmt. */ - - pc_l3 = duk__get_current_pc(comp_ctx); - duk__parse_stmt(comp_ctx, res, 0 /*allow_source_elem*/); - if (expr_c_empty) { - duk__emit_jump(comp_ctx, pc_l1); - } else { - duk__emit_jump(comp_ctx, pc_l2); - } - /* temp reset is not necessary after duk__parse_stmt(), which already does it */ - - pc_l4 = duk__get_current_pc(comp_ctx); - - DUK_DDD(DUK_DDDPRINT("patching jumps: jumpto_l3: %ld->%ld, jumpto_l4: %ld->%ld, " - "break: %ld->%ld, continue: %ld->%ld", - (long) pc_jumpto_l3, - (long) pc_l3, - (long) pc_jumpto_l4, - (long) pc_l4, - (long) (pc_label_site + 1), - (long) pc_l4, - (long) (pc_label_site + 2), - (long) pc_l2)); - - duk__patch_jump(comp_ctx, pc_jumpto_l3, pc_l3); - duk__patch_jump(comp_ctx, pc_jumpto_l4, pc_l4); - duk__patch_jump(comp_ctx, pc_label_site + 1, pc_l4); /* break jump */ - duk__patch_jump(comp_ctx, pc_label_site + 2, expr_c_empty ? pc_l1 : pc_l2); /* continue jump */ - } - goto finished; - -parse_3_or_4: - /* - * Parse variant 3 or 4. - * - * For variant 3 (e.g. "for (A in C) D;") the code for A (except the - * final property/variable write) has already been emitted. The first - * instruction of that code is at pc_v34_lhs; a JUMP needs to be inserted - * there to satisfy control flow needs. - * - * For variant 4, if the variable declaration had an initializer - * (e.g. "for (var A = B in C) D;") the code for the assignment - * (B) has already been emitted. - * - * Variables set before entering here: - * - * pc_v34_lhs: insert a "JUMP L2" here (see doc/compiler.rst example). - * reg_temps + 0: iteration target value (written to LHS) - * reg_temps + 1: enumerator object - */ - { - duk_int_t pc_l1, pc_l2, pc_l3, pc_l4, pc_l5; - duk_int_t pc_jumpto_l2, pc_jumpto_l3, pc_jumpto_l4, pc_jumpto_l5; - duk_regconst_t reg_target; - - DUK_DDD(DUK_DDDPRINT("shared code for parsing variants 3 and 4, pc_v34_lhs=%ld", (long) pc_v34_lhs)); - - DUK__SETTEMP(comp_ctx, temp_reset); - - /* First we need to insert a jump in the middle of previously - * emitted code to get the control flow right. No jumps can - * cross the position where the jump is inserted. See doc/compiler.rst - * for discussion on the intricacies of control flow and side effects - * for variants 3 and 4. - */ - - duk__insert_jump_entry(comp_ctx, pc_v34_lhs); - pc_jumpto_l2 = pc_v34_lhs; /* inserted jump */ - pc_l1 = pc_v34_lhs + 1; /* +1, right after inserted jump */ - - /* The code for writing reg_temps + 0 to the left hand side has already - * been emitted. - */ - - pc_jumpto_l3 = duk__emit_jump_empty(comp_ctx); /* -> loop body */ - - duk__advance(comp_ctx); /* eat 'in' */ - - /* Parse enumeration target and initialize enumerator. For 'null' and 'undefined', - * INITENUM will creates a 'null' enumerator which works like an empty enumerator - * (E5 Section 12.6.4, step 3). Note that INITENUM requires the value to be in a - * register (constant not allowed). - */ - - pc_l2 = duk__get_current_pc(comp_ctx); - reg_target = duk__exprtop_toreg(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/); /* Expression */ - duk__emit_b_c(comp_ctx, DUK_OP_INITENUM | DUK__EMIT_FLAG_B_IS_TARGET, reg_temps + 1, reg_target); - pc_jumpto_l4 = duk__emit_jump_empty(comp_ctx); - DUK__SETTEMP(comp_ctx, temp_reset); - - comp_ctx->curr_func.allow_regexp_in_adv = 1; - duk__advance_expect(comp_ctx, DUK_TOK_RPAREN); /* Allow RegExp as part of next stmt. */ - - pc_l3 = duk__get_current_pc(comp_ctx); - duk__parse_stmt(comp_ctx, res, 0 /*allow_source_elem*/); - /* temp reset is not necessary after duk__parse_stmt(), which already does it */ - - /* NEXTENUM needs a jump slot right after the main opcode. - * We need the code emitter to reserve the slot: if there's - * target shuffling, the target shuffle opcodes must happen - * after the jump slot (for NEXTENUM the shuffle opcodes are - * not needed if the enum is finished). - */ - pc_l4 = duk__get_current_pc(comp_ctx); - duk__emit_b_c(comp_ctx, - DUK_OP_NEXTENUM | DUK__EMIT_FLAG_B_IS_TARGET | DUK__EMIT_FLAG_RESERVE_JUMPSLOT, - reg_temps + 0, - reg_temps + 1); - pc_jumpto_l5 = comp_ctx->emit_jumpslot_pc; /* NEXTENUM jump slot: executed when enum finished */ - duk__emit_jump(comp_ctx, pc_l1); /* jump to next loop, using reg_v34_iter as iterated value */ - - pc_l5 = duk__get_current_pc(comp_ctx); - - /* XXX: since the enumerator may be a memory expensive object, - * perhaps clear it explicitly here? If so, break jump must - * go through this clearing operation. - */ - - DUK_DDD(DUK_DDDPRINT("patching jumps: jumpto_l2: %ld->%ld, jumpto_l3: %ld->%ld, " - "jumpto_l4: %ld->%ld, jumpto_l5: %ld->%ld, " - "break: %ld->%ld, continue: %ld->%ld", - (long) pc_jumpto_l2, - (long) pc_l2, - (long) pc_jumpto_l3, - (long) pc_l3, - (long) pc_jumpto_l4, - (long) pc_l4, - (long) pc_jumpto_l5, - (long) pc_l5, - (long) (pc_label_site + 1), - (long) pc_l5, - (long) (pc_label_site + 2), - (long) pc_l4)); - - duk__patch_jump(comp_ctx, pc_jumpto_l2, pc_l2); - duk__patch_jump(comp_ctx, pc_jumpto_l3, pc_l3); - duk__patch_jump(comp_ctx, pc_jumpto_l4, pc_l4); - duk__patch_jump(comp_ctx, pc_jumpto_l5, pc_l5); - duk__patch_jump(comp_ctx, pc_label_site + 1, pc_l5); /* break jump */ - duk__patch_jump(comp_ctx, pc_label_site + 2, pc_l4); /* continue jump */ - } - goto finished; - -finished: - DUK_DDD(DUK_DDDPRINT("end parsing a for/for-in statement")); - return; - -syntax_error: - DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_FOR); - DUK_WO_NORETURN(return;); -} - -DUK_LOCAL void duk__parse_switch_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site) { - duk_hthread *thr = comp_ctx->thr; - duk_regconst_t temp_at_loop; - duk_regconst_t rc_switch; /* reg/const for switch value */ - duk_regconst_t rc_case; /* reg/const for case value */ - duk_regconst_t reg_temp; /* general temp register */ - duk_int_t pc_prevcase = -1; - duk_int_t pc_prevstmt = -1; - duk_int_t pc_default = -1; /* -1 == not set, -2 == pending (next statement list) */ - - /* Note: negative pc values are ignored when patching jumps, so no explicit checks needed */ - - /* - * Switch is pretty complicated because of several conflicting concerns: - * - * - Want to generate code without an intermediate representation, - * i.e., in one go - * - * - Case selectors are expressions, not values, and may thus e.g. throw - * exceptions (which causes evaluation order concerns) - * - * - Evaluation semantics of case selectors and default clause need to be - * carefully implemented to provide correct behavior even with case value - * side effects - * - * - Fall through case and default clauses; avoiding dead JUMPs if case - * ends with an unconditional jump (a break or a continue) - * - * - The same case value may occur multiple times, but evaluation rules - * only process the first match before switching to a "propagation" mode - * where case values are no longer evaluated - * - * See E5 Section 12.11. Also see doc/compiler.rst for compilation - * discussion. - */ - - duk__advance(comp_ctx); - duk__advance_expect(comp_ctx, DUK_TOK_LPAREN); - rc_switch = duk__exprtop_toregconst(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/); - duk__advance_expect(comp_ctx, DUK_TOK_RPAREN); /* RegExp mode does not matter. */ - duk__advance_expect(comp_ctx, DUK_TOK_LCURLY); - - DUK_DDD(DUK_DDDPRINT("switch value in register %ld", (long) rc_switch)); - - temp_at_loop = DUK__GETTEMP(comp_ctx); - - for (;;) { - duk_int_t num_stmts; - duk_small_uint_t tok; - - /* sufficient for keeping temp reg numbers in check */ - DUK__SETTEMP(comp_ctx, temp_at_loop); - - if (comp_ctx->curr_token.t == DUK_TOK_RCURLY) { - break; - } - - /* - * Parse a case or default clause. - */ - - if (comp_ctx->curr_token.t == DUK_TOK_CASE) { - /* - * Case clause. - * - * Note: cannot use reg_case as a temp register (for SEQ target) - * because it may be a constant. - */ - - duk__patch_jump_here(comp_ctx, pc_prevcase); /* chain jumps for case - * evaluation and checking - */ - - duk__advance(comp_ctx); - rc_case = duk__exprtop_toregconst(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/); - duk__advance_expect(comp_ctx, DUK_TOK_COLON); - - reg_temp = DUK__ALLOCTEMP(comp_ctx); - duk__emit_a_b_c(comp_ctx, DUK_OP_SEQ | DUK__EMIT_FLAG_BC_REGCONST, reg_temp, rc_switch, rc_case); - duk__emit_if_true_skip(comp_ctx, reg_temp); - - /* jump to next case clause */ - pc_prevcase = duk__emit_jump_empty(comp_ctx); /* no match, next case */ - - /* statements go here (if any) on next loop */ - } else if (comp_ctx->curr_token.t == DUK_TOK_DEFAULT) { - /* - * Default clause. - */ - - if (pc_default >= 0) { - goto syntax_error; - } - duk__advance(comp_ctx); - duk__advance_expect(comp_ctx, DUK_TOK_COLON); - - /* Fix for https://github.com/svaarala/duktape/issues/155: - * If 'default' is first clause (detected by pc_prevcase < 0) - * we need to ensure we stay in the matching chain. - */ - if (pc_prevcase < 0) { - DUK_DD(DUK_DDPRINT("default clause is first, emit prevcase jump")); - pc_prevcase = duk__emit_jump_empty(comp_ctx); - } - - /* default clause matches next statement list (if any) */ - pc_default = -2; - } else { - /* Code is not accepted before the first case/default clause */ - goto syntax_error; - } - - /* - * Parse code after the clause. Possible terminators are - * 'case', 'default', and '}'. - * - * Note that there may be no code at all, not even an empty statement, - * between case clauses. This must be handled just like an empty statement - * (omitting seemingly pointless JUMPs), to avoid situations like - * test-bug-case-fallthrough.js. - */ - - num_stmts = 0; - if (pc_default == -2) { - pc_default = duk__get_current_pc(comp_ctx); - } - - /* Note: this is correct even for default clause statements: - * they participate in 'fall-through' behavior even if the - * default clause is in the middle. - */ - duk__patch_jump_here(comp_ctx, pc_prevstmt); /* chain jumps for 'fall-through' - * after a case matches. - */ - - for (;;) { - tok = comp_ctx->curr_token.t; - if (tok == DUK_TOK_CASE || tok == DUK_TOK_DEFAULT || tok == DUK_TOK_RCURLY) { - break; - } - num_stmts++; - duk__parse_stmt(comp_ctx, res, 0 /*allow_source_elem*/); - } - - /* fall-through jump to next code of next case (backpatched) */ - pc_prevstmt = duk__emit_jump_empty(comp_ctx); - - /* XXX: would be nice to omit this jump when the jump is not - * reachable, at least in the obvious cases (such as the case - * ending with a 'break'. - * - * Perhaps duk__parse_stmt() could provide some info on whether - * the statement is a "dead end"? - * - * If implemented, just set pc_prevstmt to -1 when not needed. - */ - } - - DUK_ASSERT(comp_ctx->curr_token.t == DUK_TOK_RCURLY); - comp_ctx->curr_func.allow_regexp_in_adv = 1; - duk__advance(comp_ctx); /* Allow RegExp as part of next stmt. */ - - /* default case control flow patchup; note that if pc_prevcase < 0 - * (i.e. no case clauses), control enters default case automatically. - */ - if (pc_default >= 0) { - /* default case exists: go there if no case matches */ - duk__patch_jump(comp_ctx, pc_prevcase, pc_default); - } else { - /* default case does not exist, or no statements present - * after default case: finish case evaluation - */ - duk__patch_jump_here(comp_ctx, pc_prevcase); - } - - /* fall-through control flow patchup; note that pc_prevstmt may be - * < 0 (i.e. no case clauses), in which case this is a no-op. - */ - duk__patch_jump_here(comp_ctx, pc_prevstmt); - - /* continue jump not patched, an INVALID opcode remains there */ - duk__patch_jump_here(comp_ctx, pc_label_site + 1); /* break jump */ - - /* Note: 'fast' breaks will jump to pc_label_site + 1, which will - * then jump here. The double jump will be eliminated by a - * peephole pass, resulting in an optimal jump here. The label - * site jumps will remain in bytecode and will waste code size. - */ - - return; - -syntax_error: - DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_SWITCH); - DUK_WO_NORETURN(return;); -} - -DUK_LOCAL void duk__parse_if_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { - duk_regconst_t temp_reset; - duk_regconst_t rc_cond; - duk_int_t pc_jump_false; - - DUK_DDD(DUK_DDDPRINT("begin parsing if statement")); - - temp_reset = DUK__GETTEMP(comp_ctx); - - duk__advance(comp_ctx); /* eat 'if' */ - duk__advance_expect(comp_ctx, DUK_TOK_LPAREN); - - rc_cond = duk__exprtop_toregconst(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/); - duk__emit_if_true_skip(comp_ctx, rc_cond); - pc_jump_false = duk__emit_jump_empty(comp_ctx); /* jump to end or else part */ - DUK__SETTEMP(comp_ctx, temp_reset); - - comp_ctx->curr_func.allow_regexp_in_adv = 1; - duk__advance_expect(comp_ctx, DUK_TOK_RPAREN); /* Allow RegExp as part of next stmt. */ - - duk__parse_stmt(comp_ctx, res, 0 /*allow_source_elem*/); - - /* The 'else' ambiguity is resolved by 'else' binding to the innermost - * construct, so greedy matching is correct here. - */ - - if (comp_ctx->curr_token.t == DUK_TOK_ELSE) { - duk_int_t pc_jump_end; - - DUK_DDD(DUK_DDDPRINT("if has else part")); - - duk__advance(comp_ctx); - - pc_jump_end = duk__emit_jump_empty(comp_ctx); /* jump from true part to end */ - duk__patch_jump_here(comp_ctx, pc_jump_false); - - duk__parse_stmt(comp_ctx, res, 0 /*allow_source_elem*/); - - duk__patch_jump_here(comp_ctx, pc_jump_end); - } else { - DUK_DDD(DUK_DDDPRINT("if does not have else part")); - - duk__patch_jump_here(comp_ctx, pc_jump_false); - } - - DUK_DDD(DUK_DDDPRINT("end parsing if statement")); -} - -DUK_LOCAL void duk__parse_do_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site) { - duk_regconst_t rc_cond; - duk_int_t pc_start; - - DUK_DDD(DUK_DDDPRINT("begin parsing do statement")); - - duk__advance(comp_ctx); /* Eat 'do'; allow RegExp as part of next stmt. */ - - pc_start = duk__get_current_pc(comp_ctx); - duk__parse_stmt(comp_ctx, res, 0 /*allow_source_elem*/); - duk__patch_jump_here(comp_ctx, pc_label_site + 2); /* continue jump */ - - duk__advance_expect(comp_ctx, DUK_TOK_WHILE); - duk__advance_expect(comp_ctx, DUK_TOK_LPAREN); - - rc_cond = duk__exprtop_toregconst(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/); - duk__emit_if_false_skip(comp_ctx, rc_cond); - duk__emit_jump(comp_ctx, pc_start); - /* no need to reset temps, as we're finished emitting code */ - - comp_ctx->curr_func.allow_regexp_in_adv = 1; /* Allow RegExp as part of next stmt. */ - duk__advance_expect(comp_ctx, DUK_TOK_RPAREN); - - duk__patch_jump_here(comp_ctx, pc_label_site + 1); /* break jump */ - - DUK_DDD(DUK_DDDPRINT("end parsing do statement")); -} - -DUK_LOCAL void duk__parse_while_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site) { - duk_regconst_t temp_reset; - duk_regconst_t rc_cond; - duk_int_t pc_start; - duk_int_t pc_jump_false; - - DUK_DDD(DUK_DDDPRINT("begin parsing while statement")); - - temp_reset = DUK__GETTEMP(comp_ctx); - - duk__advance(comp_ctx); /* eat 'while' */ - - duk__advance_expect(comp_ctx, DUK_TOK_LPAREN); - - pc_start = duk__get_current_pc(comp_ctx); - duk__patch_jump_here(comp_ctx, pc_label_site + 2); /* continue jump */ - - rc_cond = duk__exprtop_toregconst(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/); - duk__emit_if_true_skip(comp_ctx, rc_cond); - pc_jump_false = duk__emit_jump_empty(comp_ctx); - DUK__SETTEMP(comp_ctx, temp_reset); - - comp_ctx->curr_func.allow_regexp_in_adv = 1; - duk__advance_expect(comp_ctx, DUK_TOK_RPAREN); /* Allow RegExp as part of next stmt. */ - - duk__parse_stmt(comp_ctx, res, 0 /*allow_source_elem*/); - duk__emit_jump(comp_ctx, pc_start); - - duk__patch_jump_here(comp_ctx, pc_jump_false); - duk__patch_jump_here(comp_ctx, pc_label_site + 1); /* break jump */ - - DUK_DDD(DUK_DDDPRINT("end parsing while statement")); -} - -DUK_LOCAL void duk__parse_break_or_continue_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { - duk_hthread *thr = comp_ctx->thr; - duk_bool_t is_break = (comp_ctx->curr_token.t == DUK_TOK_BREAK); - duk_int_t label_id; - duk_int_t label_catch_depth; - duk_int_t label_pc; /* points to LABEL; pc+1 = jump site for break; pc+2 = jump site for continue */ - duk_bool_t label_is_closest; - - DUK_UNREF(res); - - duk__advance(comp_ctx); /* eat 'break' or 'continue' */ - - if (comp_ctx->curr_token.t == DUK_TOK_SEMICOLON || /* explicit semi follows */ - comp_ctx->curr_token.lineterm || /* automatic semi will be inserted */ - comp_ctx->curr_token.allow_auto_semi) { /* automatic semi will be inserted */ - /* break/continue without label */ - - duk__lookup_active_label(comp_ctx, - DUK_HTHREAD_STRING_EMPTY_STRING(thr), - is_break, - &label_id, - &label_catch_depth, - &label_pc, - &label_is_closest); - } else if (comp_ctx->curr_token.t == DUK_TOK_IDENTIFIER) { - /* break/continue with label (label cannot be a reserved word, production is 'Identifier' */ - DUK_ASSERT(comp_ctx->curr_token.str1 != NULL); - duk__lookup_active_label(comp_ctx, - comp_ctx->curr_token.str1, - is_break, - &label_id, - &label_catch_depth, - &label_pc, - &label_is_closest); - duk__advance(comp_ctx); - } else { - DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_BREAK_CONT_LABEL); - DUK_WO_NORETURN(return;); - } - - /* Use a fast break/continue when possible. A fast break/continue is - * just a jump to the LABEL break/continue jump slot, which then jumps - * to an appropriate place (for break, going through ENDLABEL correctly). - * The peephole optimizer will optimize the jump to a direct one. - */ - - if (label_catch_depth == comp_ctx->curr_func.catch_depth && label_is_closest) { - DUK_DDD(DUK_DDDPRINT("break/continue: is_break=%ld, label_id=%ld, label_is_closest=%ld, " - "label_catch_depth=%ld, catch_depth=%ld " - "-> use fast variant (direct jump)", - (long) is_break, - (long) label_id, - (long) label_is_closest, - (long) label_catch_depth, - (long) comp_ctx->curr_func.catch_depth)); - - duk__emit_jump(comp_ctx, label_pc + (is_break ? 1 : 2)); - } else { - DUK_DDD(DUK_DDDPRINT("break/continue: is_break=%ld, label_id=%ld, label_is_closest=%ld, " - "label_catch_depth=%ld, catch_depth=%ld " - "-> use slow variant (longjmp)", - (long) is_break, - (long) label_id, - (long) label_is_closest, - (long) label_catch_depth, - (long) comp_ctx->curr_func.catch_depth)); - - duk__emit_bc(comp_ctx, is_break ? DUK_OP_BREAK : DUK_OP_CONTINUE, (duk_regconst_t) label_id); - } -} - -DUK_LOCAL void duk__parse_return_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { - duk_hthread *thr = comp_ctx->thr; - duk_regconst_t rc_val; - - duk__advance(comp_ctx); /* eat 'return' */ - - /* A 'return' statement is only allowed inside an actual function body, - * not as part of eval or global code. - */ - if (!comp_ctx->curr_func.is_function) { - DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_RETURN); - DUK_WO_NORETURN(return;); - } - - if (comp_ctx->curr_token.t == DUK_TOK_SEMICOLON || /* explicit semi follows */ - comp_ctx->curr_token.lineterm || /* automatic semi will be inserted */ - comp_ctx->curr_token.allow_auto_semi) { /* automatic semi will be inserted */ - DUK_DDD(DUK_DDDPRINT("empty return value -> undefined")); - duk__emit_op_only(comp_ctx, DUK_OP_RETUNDEF); - } else { - duk_int_t pc_before_expr; - duk_int_t pc_after_expr; - - DUK_DDD(DUK_DDDPRINT("return with a value")); - - DUK_UNREF(pc_before_expr); - DUK_UNREF(pc_after_expr); - - pc_before_expr = duk__get_current_pc(comp_ctx); - rc_val = duk__exprtop_toregconst(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/); - pc_after_expr = duk__get_current_pc(comp_ctx); - - /* Tail call check: if last opcode emitted was CALL, and - * the context allows it, add a tailcall flag to the CALL. - * This doesn't guarantee that a tail call will be allowed at - * runtime, so the RETURN must still be emitted. (Duktape - * 0.10.0 avoided this and simulated a RETURN if a tail call - * couldn't be used at runtime; but this didn't work - * correctly with a thread yield/resume, see - * test-bug-tailcall-thread-yield-resume.js for discussion.) - * - * In addition to the last opcode being CALL, we also need to - * be sure that 'rc_val' is the result register of the CALL. - * For instance, for the expression 'return 0, (function () - * { return 1; }), 2' the last opcode emitted is CALL (no - * bytecode is emitted for '2') but 'rc_val' indicates - * constant '2'. Similarly if '2' is replaced by a register - * bound variable, no opcodes are emitted but tail call would - * be incorrect. - * - * This is tricky and easy to get wrong. It would be best to - * track enough expression metadata to check that 'rc_val' came - * from that last CALL instruction. We don't have that metadata - * now, so we check that 'rc_val' is a temporary register result - * (not a constant or a register bound variable). There should - * be no way currently for 'rc_val' to be a temporary for an - * expression following the CALL instruction without emitting - * some opcodes following the CALL. This proxy check is used - * below. - * - * See: test-bug-comma-expr-gh131.js. - * - * The non-standard 'caller' property disables tail calls - * because they pose some special cases which haven't been - * fixed yet. - */ - -#if defined(DUK_USE_TAILCALL) - if (comp_ctx->curr_func.catch_depth == 0 && /* no catchers */ - pc_after_expr > pc_before_expr) { /* at least one opcode emitted */ - duk_compiler_instr *instr; - duk_instr_t ins; - duk_small_uint_t op; - - instr = duk__get_instr_ptr(comp_ctx, pc_after_expr - 1); - DUK_ASSERT(instr != NULL); - - ins = instr->ins; - op = (duk_small_uint_t) DUK_DEC_OP(ins); - if ((op & ~0x0fU) == DUK_OP_CALL0 && DUK__ISREG_TEMP(comp_ctx, rc_val) /* see above */) { - DUK_DDD(DUK_DDDPRINT("return statement detected a tail call opportunity: " - "catch depth is 0, duk__exprtop() emitted >= 1 instructions, " - "and last instruction is a CALL " - "-> change to TAILCALL")); - ins |= DUK_ENC_OP(DUK_BC_CALL_FLAG_TAILCALL); - instr->ins = ins; - } - } -#endif /* DUK_USE_TAILCALL */ - - if (DUK__ISREG(rc_val)) { - duk__emit_bc(comp_ctx, DUK_OP_RETREG, rc_val); - } else { - rc_val = DUK__REMOVECONST(rc_val); - if (duk__const_needs_refcount(comp_ctx, rc_val)) { - duk__emit_bc(comp_ctx, DUK_OP_RETCONST, rc_val); - } else { - duk__emit_bc(comp_ctx, DUK_OP_RETCONSTN, rc_val); - } - } - } -} - -DUK_LOCAL void duk__parse_throw_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { - duk_regconst_t reg_val; - - duk__advance(comp_ctx); /* eat 'throw' */ - - /* Unlike break/continue, throw statement does not allow an empty value. */ - - if (comp_ctx->curr_token.lineterm) { - DUK_ERROR_SYNTAX(comp_ctx->thr, DUK_STR_INVALID_THROW); - DUK_WO_NORETURN(return;); - } - - reg_val = duk__exprtop_toreg(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/); - duk__emit_bc(comp_ctx, DUK_OP_THROW, reg_val); -} - -DUK_LOCAL void duk__parse_try_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { - duk_hthread *thr = comp_ctx->thr; - duk_regconst_t reg_catch; /* reg_catch+0 and reg_catch+1 are reserved for TRYCATCH */ - duk_regconst_t rc_varname = 0; - duk_small_uint_t trycatch_flags = 0; - duk_int_t pc_ldconst = -1; - duk_int_t pc_trycatch = -1; - duk_int_t pc_catch = -1; - duk_int_t pc_finally = -1; - - DUK_UNREF(res); - - /* - * See the following documentation for discussion: - * - * doc/execution.rst: control flow details - * - * Try, catch, and finally "parts" are Blocks, not Statements, so - * they must always be delimited by curly braces. This is unlike e.g. - * the if statement, which accepts any Statement. This eliminates any - * questions of matching parts of nested try statements. The Block - * parsing is implemented inline here (instead of calling out). - * - * Finally part has a 'let scoped' variable, which requires a few kinks - * here. - */ - - comp_ctx->curr_func.catch_depth++; - - duk__advance(comp_ctx); /* eat 'try' */ - - reg_catch = DUK__ALLOCTEMPS(comp_ctx, 2); - - /* The target for this LDCONST may need output shuffling, but we assume - * that 'pc_ldconst' will be the LDCONST that we can patch later. This - * should be the case because there's no input shuffling. (If there's - * no catch clause, this LDCONST will be replaced with a NOP.) - */ - pc_ldconst = duk__get_current_pc(comp_ctx); - duk__emit_a_bc(comp_ctx, DUK_OP_LDCONST, reg_catch, 0 /*patched later*/); - - pc_trycatch = duk__get_current_pc(comp_ctx); - duk__emit_invalid(comp_ctx); /* TRYCATCH, cannot emit now (not enough info) */ - duk__emit_invalid(comp_ctx); /* jump for 'catch' case */ - duk__emit_invalid(comp_ctx); /* jump for 'finally' case or end (if no finally) */ - - /* try part */ - duk__advance_expect(comp_ctx, DUK_TOK_LCURLY); - duk__parse_stmts(comp_ctx, 0 /*allow_source_elem*/, 0 /*expect_eof*/, 1 /*regexp_after*/); - /* the DUK_TOK_RCURLY is eaten by duk__parse_stmts() */ - duk__emit_op_only(comp_ctx, DUK_OP_ENDTRY); - - if (comp_ctx->curr_token.t == DUK_TOK_CATCH) { - /* - * The catch variable must be updated to reflect the new allocated - * register for the duration of the catch clause. We need to store - * and restore the original value for the varmap entry (if any). - */ - - /* - * Note: currently register bindings must be fixed for the entire - * function. So, even though the catch variable is in a register - * we know, we must use an explicit environment record and slow path - * accesses to read/write the catch binding to make closures created - * within the catch clause work correctly. This restriction should - * be fixable (at least in common cases) later. - * - * See: test-bug-catch-binding-2.js. - * - * XXX: improve to get fast path access to most catch clauses. - */ - - duk_hstring *h_var; - duk_int_t varmap_value; /* for storing/restoring the varmap binding for catch variable */ - - DUK_DDD(DUK_DDDPRINT("stack top at start of catch clause: %ld", (long) duk_get_top(thr))); - - trycatch_flags |= DUK_BC_TRYCATCH_FLAG_HAVE_CATCH; - - pc_catch = duk__get_current_pc(comp_ctx); - - duk__advance(comp_ctx); - duk__advance_expect(comp_ctx, DUK_TOK_LPAREN); - - if (comp_ctx->curr_token.t != DUK_TOK_IDENTIFIER) { - /* Identifier, i.e. don't allow reserved words */ - goto syntax_error; - } - h_var = comp_ctx->curr_token.str1; - DUK_ASSERT(h_var != NULL); - - duk_push_hstring(thr, h_var); /* keep in on valstack, use borrowed ref below */ - - if (comp_ctx->curr_func.is_strict && - ((h_var == DUK_HTHREAD_STRING_EVAL(thr)) || (h_var == DUK_HTHREAD_STRING_LC_ARGUMENTS(thr)))) { - DUK_DDD(DUK_DDDPRINT("catch identifier 'eval' or 'arguments' in strict mode -> SyntaxError")); - goto syntax_error; - } - - duk_dup_top(thr); - rc_varname = duk__getconst(comp_ctx); - DUK_DDD(DUK_DDDPRINT("catch clause, rc_varname=0x%08lx (%ld)", (unsigned long) rc_varname, (long) rc_varname)); - - duk__advance(comp_ctx); - duk__advance_expect(comp_ctx, DUK_TOK_RPAREN); - - duk__advance_expect(comp_ctx, DUK_TOK_LCURLY); - - DUK_DDD(DUK_DDDPRINT("varmap before modifying for catch clause: %!iT", - (duk_tval *) duk_get_tval(thr, comp_ctx->curr_func.varmap_idx))); - - duk_dup_top(thr); - duk_get_prop(thr, comp_ctx->curr_func.varmap_idx); - if (duk_is_undefined(thr, -1)) { - varmap_value = -2; - } else if (duk_is_null(thr, -1)) { - varmap_value = -1; - } else { - DUK_ASSERT(duk_is_number(thr, -1)); - varmap_value = duk_get_int(thr, -1); - DUK_ASSERT(varmap_value >= 0); - } - duk_pop(thr); - -#if 0 - /* It'd be nice to do something like this - but it doesn't - * work for closures created inside the catch clause. - */ - duk_dup_top(thr); - duk_push_int(thr, (duk_int_t) (reg_catch + 0)); - duk_put_prop(thr, comp_ctx->curr_func.varmap_idx); -#endif - duk_dup_top(thr); - duk_push_null(thr); - duk_put_prop(thr, comp_ctx->curr_func.varmap_idx); - - duk__emit_a_bc(comp_ctx, - DUK_OP_PUTVAR | DUK__EMIT_FLAG_A_IS_SOURCE, - reg_catch + 0 /*value*/, - rc_varname /*varname*/); - - DUK_DDD(DUK_DDDPRINT("varmap before parsing catch clause: %!iT", - (duk_tval *) duk_get_tval(thr, comp_ctx->curr_func.varmap_idx))); - - duk__parse_stmts(comp_ctx, 0 /*allow_source_elem*/, 0 /*expect_eof*/, 1 /*regexp_after*/); - /* the DUK_TOK_RCURLY is eaten by duk__parse_stmts() */ - - if (varmap_value == -2) { - /* not present */ - duk_del_prop(thr, comp_ctx->curr_func.varmap_idx); - } else { - if (varmap_value == -1) { - duk_push_null(thr); - } else { - DUK_ASSERT(varmap_value >= 0); - duk_push_int(thr, varmap_value); - } - duk_put_prop(thr, comp_ctx->curr_func.varmap_idx); - } - /* varname is popped by above code */ - - DUK_DDD(DUK_DDDPRINT("varmap after restore catch clause: %!iT", - (duk_tval *) duk_get_tval(thr, comp_ctx->curr_func.varmap_idx))); - - duk__emit_op_only(comp_ctx, DUK_OP_ENDCATCH); - - /* - * XXX: for now, indicate that an expensive catch binding - * declarative environment is always needed. If we don't - * need it, we don't need the const_varname either. - */ - - trycatch_flags |= DUK_BC_TRYCATCH_FLAG_CATCH_BINDING; - - DUK_DDD(DUK_DDDPRINT("stack top at end of catch clause: %ld", (long) duk_get_top(thr))); - } - - if (comp_ctx->curr_token.t == DUK_TOK_FINALLY) { - trycatch_flags |= DUK_BC_TRYCATCH_FLAG_HAVE_FINALLY; - - pc_finally = duk__get_current_pc(comp_ctx); - - duk__advance(comp_ctx); - - duk__advance_expect(comp_ctx, DUK_TOK_LCURLY); - duk__parse_stmts(comp_ctx, 0 /*allow_source_elem*/, 0 /*expect_eof*/, 1 /*regexp_after*/); - /* the DUK_TOK_RCURLY is eaten by duk__parse_stmts() */ - duk__emit_abc(comp_ctx, DUK_OP_ENDFIN, reg_catch); /* rethrow */ - } - - if (!(trycatch_flags & DUK_BC_TRYCATCH_FLAG_HAVE_CATCH) && !(trycatch_flags & DUK_BC_TRYCATCH_FLAG_HAVE_FINALLY)) { - /* must have catch and/or finally */ - goto syntax_error; - } - - /* If there's no catch block, rc_varname will be 0 and duk__patch_trycatch() - * will replace the LDCONST with a NOP. For any actual constant (including - * constant 0) the DUK__CONST_MARKER flag will be set in rc_varname. - */ - - duk__patch_trycatch(comp_ctx, pc_ldconst, pc_trycatch, reg_catch, rc_varname, trycatch_flags); - - if (trycatch_flags & DUK_BC_TRYCATCH_FLAG_HAVE_CATCH) { - DUK_ASSERT(pc_catch >= 0); - duk__patch_jump(comp_ctx, pc_trycatch + 1, pc_catch); - } - - if (trycatch_flags & DUK_BC_TRYCATCH_FLAG_HAVE_FINALLY) { - DUK_ASSERT(pc_finally >= 0); - duk__patch_jump(comp_ctx, pc_trycatch + 2, pc_finally); - } else { - /* without finally, the second jump slot is used to jump to end of stmt */ - duk__patch_jump_here(comp_ctx, pc_trycatch + 2); - } - - comp_ctx->curr_func.catch_depth--; - return; - -syntax_error: - DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_TRY); - DUK_WO_NORETURN(return;); -} - -DUK_LOCAL void duk__parse_with_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { - duk_int_t pc_trycatch; - duk_int_t pc_finished; - duk_regconst_t reg_catch; - duk_small_uint_t trycatch_flags; - - if (comp_ctx->curr_func.is_strict) { - DUK_ERROR_SYNTAX(comp_ctx->thr, DUK_STR_WITH_IN_STRICT_MODE); - DUK_WO_NORETURN(return;); - } - - comp_ctx->curr_func.catch_depth++; - - duk__advance(comp_ctx); /* eat 'with' */ - - reg_catch = DUK__ALLOCTEMPS(comp_ctx, 2); - - duk__advance_expect(comp_ctx, DUK_TOK_LPAREN); - duk__exprtop_toforcedreg(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/, reg_catch); - comp_ctx->curr_func.allow_regexp_in_adv = 1; - duk__advance_expect(comp_ctx, DUK_TOK_RPAREN); /* Allow RegExp as part of next stmt. */ - - pc_trycatch = duk__get_current_pc(comp_ctx); - trycatch_flags = DUK_BC_TRYCATCH_FLAG_WITH_BINDING; - duk__emit_a_bc(comp_ctx, - DUK_OP_TRYCATCH | DUK__EMIT_FLAG_NO_SHUFFLE_A, - (duk_regconst_t) trycatch_flags /*a*/, - reg_catch /*bc*/); - duk__emit_invalid(comp_ctx); /* catch jump */ - duk__emit_invalid(comp_ctx); /* finished jump */ - - duk__parse_stmt(comp_ctx, res, 0 /*allow_source_elem*/); - duk__emit_op_only(comp_ctx, DUK_OP_ENDTRY); - - pc_finished = duk__get_current_pc(comp_ctx); - - duk__patch_jump(comp_ctx, pc_trycatch + 2, pc_finished); - - comp_ctx->curr_func.catch_depth--; -} - -DUK_LOCAL duk_int_t duk__stmt_label_site(duk_compiler_ctx *comp_ctx, duk_int_t label_id) { - /* if a site already exists, nop: max one label site per statement */ - if (label_id >= 0) { - return label_id; - } - - label_id = comp_ctx->curr_func.label_next++; - DUK_DDD(DUK_DDDPRINT("allocated new label id for label site: %ld", (long) label_id)); - - duk__emit_bc(comp_ctx, DUK_OP_LABEL, (duk_regconst_t) label_id); - duk__emit_invalid(comp_ctx); - duk__emit_invalid(comp_ctx); - - return label_id; -} - -/* Parse a single statement. - * - * Creates a label site (with an empty label) automatically for iteration - * statements. Also "peels off" any label statements for explicit labels. - */ -DUK_LOCAL void duk__parse_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_bool_t allow_source_elem) { - duk_hthread *thr = comp_ctx->thr; - duk_bool_t dir_prol_at_entry; /* directive prologue status at entry */ - duk_regconst_t temp_at_entry; - duk_size_t labels_len_at_entry; - duk_int_t pc_at_entry; /* assumed to also be PC of "LABEL" */ - duk_int_t stmt_id; - duk_small_uint_t stmt_flags = 0; - duk_int_t label_id = -1; - duk_small_uint_t tok; - duk_bool_t test_func_decl; - - DUK__RECURSION_INCREASE(comp_ctx, thr); - - temp_at_entry = DUK__GETTEMP(comp_ctx); - pc_at_entry = duk__get_current_pc(comp_ctx); - labels_len_at_entry = duk_get_length(thr, comp_ctx->curr_func.labelnames_idx); - stmt_id = comp_ctx->curr_func.stmt_next++; - dir_prol_at_entry = comp_ctx->curr_func.in_directive_prologue; - - DUK_UNREF(stmt_id); - - DUK_DDD(DUK_DDDPRINT("parsing a statement, stmt_id=%ld, temp_at_entry=%ld, labels_len_at_entry=%ld, " - "is_strict=%ld, in_directive_prologue=%ld, catch_depth=%ld", - (long) stmt_id, - (long) temp_at_entry, - (long) labels_len_at_entry, - (long) comp_ctx->curr_func.is_strict, - (long) comp_ctx->curr_func.in_directive_prologue, - (long) comp_ctx->curr_func.catch_depth)); - - /* The directive prologue flag is cleared by default so that it is - * unset for any recursive statement parsing. It is only "revived" - * if a directive is detected. (We could also make directives only - * allowed if 'allow_source_elem' was true.) - */ - comp_ctx->curr_func.in_directive_prologue = 0; - -retry_parse: - - DUK_DDD(DUK_DDDPRINT("try stmt parse, stmt_id=%ld, label_id=%ld, allow_source_elem=%ld, catch_depth=%ld", - (long) stmt_id, - (long) label_id, - (long) allow_source_elem, - (long) comp_ctx->curr_func.catch_depth)); - - /* - * Detect iteration statements; if encountered, establish an - * empty label. - */ - - tok = comp_ctx->curr_token.t; - if (tok == DUK_TOK_FOR || tok == DUK_TOK_DO || tok == DUK_TOK_WHILE || tok == DUK_TOK_SWITCH) { - DUK_DDD(DUK_DDDPRINT("iteration/switch statement -> add empty label")); - - label_id = duk__stmt_label_site(comp_ctx, label_id); - duk__add_label(comp_ctx, DUK_HTHREAD_STRING_EMPTY_STRING(thr), pc_at_entry /*pc_label*/, label_id); - } - - /* - * Main switch for statement / source element type. - */ - - switch (comp_ctx->curr_token.t) { - case DUK_TOK_FUNCTION: { - /* - * Function declaration, function expression, or (non-standard) - * function statement. - * - * The E5 specification only allows function declarations at - * the top level (in "source elements"). An ExpressionStatement - * is explicitly not allowed to begin with a "function" keyword - * (E5 Section 12.4). Hence any non-error semantics for such - * non-top-level statements are non-standard. Duktape semantics - * for function statements are modelled after V8, see - * test-dev-func-decl-outside-top.js. - */ - test_func_decl = allow_source_elem; -#if defined(DUK_USE_NONSTD_FUNC_STMT) - /* Lenient: allow function declarations outside top level in both - * strict and non-strict modes. However, don't allow labelled - * function declarations in strict mode. - */ - test_func_decl = test_func_decl || !comp_ctx->curr_func.is_strict || label_id < 0; -#endif /* DUK_USE_NONSTD_FUNC_STMT */ - /* Strict: never allow function declarations outside top level. */ - if (test_func_decl) { - /* FunctionDeclaration: not strictly a statement but handled as such. - * - * O(depth^2) parse count for inner functions is handled by recording a - * lexer offset on the first compilation pass, so that the function can - * be efficiently skipped on the second pass. This is encapsulated into - * duk__parse_func_like_fnum(). - */ - - duk_int_t fnum; -#if defined(DUK_USE_ASSERTIONS) - duk_idx_t top_before; -#endif - - DUK_DDD(DUK_DDDPRINT("function declaration statement")); - -#if defined(DUK_USE_ASSERTIONS) - top_before = duk_get_top(thr); -#endif - - duk__advance(comp_ctx); /* eat 'function' */ - fnum = duk__parse_func_like_fnum(comp_ctx, DUK__FUNC_FLAG_DECL | DUK__FUNC_FLAG_PUSHNAME_PASS1); - - /* The value stack convention here is a bit odd: the function - * name is only pushed on pass 1 (in_scanning), and is needed - * to process function declarations. - */ - if (comp_ctx->curr_func.in_scanning) { - duk_uarridx_t n; - -#if defined(DUK_USE_ASSERTIONS) - DUK_ASSERT(duk_get_top(thr) == top_before + 1); -#endif - DUK_DDD(DUK_DDDPRINT("register function declaration %!T in pass 1, fnum %ld", - duk_get_tval(thr, -1), - (long) fnum)); - n = (duk_uarridx_t) duk_get_length(thr, comp_ctx->curr_func.decls_idx); - /* funcname is at index -1 */ - duk_put_prop_index(thr, comp_ctx->curr_func.decls_idx, n); - duk_push_int(thr, (duk_int_t) (DUK_DECL_TYPE_FUNC + (fnum << 8))); - duk_put_prop_index(thr, comp_ctx->curr_func.decls_idx, n + 1); - } else { -#if defined(DUK_USE_ASSERTIONS) - DUK_ASSERT(duk_get_top(thr) == top_before); -#endif - } - - /* no statement value (unlike function expression) */ - stmt_flags = 0; - break; - } else { - DUK_ERROR_SYNTAX(thr, DUK_STR_FUNC_STMT_NOT_ALLOWED); - DUK_WO_NORETURN(return;); - } - break; - } - case DUK_TOK_LCURLY: { - DUK_DDD(DUK_DDDPRINT("block statement")); - duk__advance(comp_ctx); - duk__parse_stmts(comp_ctx, 0 /*allow_source_elem*/, 0 /*expect_eof*/, 1 /*regexp_after*/); - /* the DUK_TOK_RCURLY is eaten by duk__parse_stmts() */ - if (label_id >= 0) { - duk__patch_jump_here(comp_ctx, pc_at_entry + 1); /* break jump */ - } - stmt_flags = 0; - break; - } - case DUK_TOK_CONST: { - DUK_DDD(DUK_DDDPRINT("constant declaration statement")); - duk__parse_var_stmt(comp_ctx, res, DUK__EXPR_FLAG_REQUIRE_INIT /*expr_flags*/); - stmt_flags = DUK__HAS_TERM; - break; - } - case DUK_TOK_VAR: { - DUK_DDD(DUK_DDDPRINT("variable declaration statement")); - duk__parse_var_stmt(comp_ctx, res, 0 /*expr_flags*/); - stmt_flags = DUK__HAS_TERM; - break; - } - case DUK_TOK_SEMICOLON: { - /* empty statement with an explicit semicolon */ - DUK_DDD(DUK_DDDPRINT("empty statement")); - stmt_flags = DUK__HAS_TERM; - break; - } - case DUK_TOK_IF: { - DUK_DDD(DUK_DDDPRINT("if statement")); - duk__parse_if_stmt(comp_ctx, res); - if (label_id >= 0) { - duk__patch_jump_here(comp_ctx, pc_at_entry + 1); /* break jump */ - } - stmt_flags = 0; - break; - } - case DUK_TOK_DO: { - /* - * Do-while statement is mostly trivial, but there is special - * handling for automatic semicolon handling (triggered by the - * DUK__ALLOW_AUTO_SEMI_ALWAYS) flag related to a bug filed at: - * - * https://bugs.ecmascript.org/show_bug.cgi?id=8 - * - * See doc/compiler.rst for details. - */ - DUK_DDD(DUK_DDDPRINT("do statement")); - DUK_ASSERT(label_id >= 0); - duk__update_label_flags(comp_ctx, label_id, DUK_LABEL_FLAG_ALLOW_BREAK | DUK_LABEL_FLAG_ALLOW_CONTINUE); - duk__parse_do_stmt(comp_ctx, res, pc_at_entry); - stmt_flags = DUK__HAS_TERM | DUK__ALLOW_AUTO_SEMI_ALWAYS; /* DUK__ALLOW_AUTO_SEMI_ALWAYS workaround */ - break; - } - case DUK_TOK_WHILE: { - DUK_DDD(DUK_DDDPRINT("while statement")); - DUK_ASSERT(label_id >= 0); - duk__update_label_flags(comp_ctx, label_id, DUK_LABEL_FLAG_ALLOW_BREAK | DUK_LABEL_FLAG_ALLOW_CONTINUE); - duk__parse_while_stmt(comp_ctx, res, pc_at_entry); - stmt_flags = 0; - break; - } - case DUK_TOK_FOR: { - /* - * For/for-in statement is complicated to parse because - * determining the statement type (three-part for vs. a - * for-in) requires potential backtracking. - * - * See the helper for the messy stuff. - */ - DUK_DDD(DUK_DDDPRINT("for/for-in statement")); - DUK_ASSERT(label_id >= 0); - duk__update_label_flags(comp_ctx, label_id, DUK_LABEL_FLAG_ALLOW_BREAK | DUK_LABEL_FLAG_ALLOW_CONTINUE); - duk__parse_for_stmt(comp_ctx, res, pc_at_entry); - stmt_flags = 0; - break; - } - case DUK_TOK_CONTINUE: - case DUK_TOK_BREAK: { - DUK_DDD(DUK_DDDPRINT("break/continue statement")); - duk__parse_break_or_continue_stmt(comp_ctx, res); - stmt_flags = DUK__HAS_TERM | DUK__IS_TERMINAL; - break; - } - case DUK_TOK_RETURN: { - DUK_DDD(DUK_DDDPRINT("return statement")); - duk__parse_return_stmt(comp_ctx, res); - stmt_flags = DUK__HAS_TERM | DUK__IS_TERMINAL; - break; - } - case DUK_TOK_WITH: { - DUK_DDD(DUK_DDDPRINT("with statement")); - comp_ctx->curr_func.with_depth++; - duk__parse_with_stmt(comp_ctx, res); - if (label_id >= 0) { - duk__patch_jump_here(comp_ctx, pc_at_entry + 1); /* break jump */ - } - comp_ctx->curr_func.with_depth--; - stmt_flags = 0; - break; - } - case DUK_TOK_SWITCH: { - /* - * The switch statement is pretty messy to compile. - * See the helper for details. - */ - DUK_DDD(DUK_DDDPRINT("switch statement")); - DUK_ASSERT(label_id >= 0); - duk__update_label_flags(comp_ctx, label_id, DUK_LABEL_FLAG_ALLOW_BREAK); /* don't allow continue */ - duk__parse_switch_stmt(comp_ctx, res, pc_at_entry); - stmt_flags = 0; - break; - } - case DUK_TOK_THROW: { - DUK_DDD(DUK_DDDPRINT("throw statement")); - duk__parse_throw_stmt(comp_ctx, res); - stmt_flags = DUK__HAS_TERM | DUK__IS_TERMINAL; - break; - } - case DUK_TOK_TRY: { - DUK_DDD(DUK_DDDPRINT("try statement")); - duk__parse_try_stmt(comp_ctx, res); - stmt_flags = 0; - break; - } - case DUK_TOK_DEBUGGER: { - duk__advance(comp_ctx); -#if defined(DUK_USE_DEBUGGER_SUPPORT) - DUK_DDD(DUK_DDDPRINT("debugger statement: debugging enabled, emit debugger opcode")); - duk__emit_op_only(comp_ctx, DUK_OP_DEBUGGER); -#else - DUK_DDD(DUK_DDDPRINT("debugger statement: ignored")); -#endif - stmt_flags = DUK__HAS_TERM; - break; - } - default: { - /* - * Else, must be one of: - * - ExpressionStatement, possibly a directive (String) - * - LabelledStatement (Identifier followed by ':') - * - * Expressions beginning with 'function' keyword are covered by a case - * above (such expressions are not allowed in standard E5 anyway). - * Also expressions starting with '{' are interpreted as block - * statements. See E5 Section 12.4. - * - * Directive detection is tricky; see E5 Section 14.1 on directive - * prologue. A directive is an expression statement with a single - * string literal and an explicit or automatic semicolon. Escape - * characters are significant and no parens etc are allowed: - * - * 'use strict'; // valid 'use strict' directive - * 'use\u0020strict'; // valid directive, not a 'use strict' directive - * ('use strict'); // not a valid directive - * - * The expression is determined to consist of a single string literal - * based on duk__expr_nud() and duk__expr_led() call counts. The string literal - * of a 'use strict' directive is determined to lack any escapes based - * num_escapes count from the lexer. Note that other directives may be - * allowed to contain escapes, so a directive with escapes does not - * terminate a directive prologue. - * - * We rely on the fact that the expression parser will not emit any - * code for a single token expression. However, it will generate an - * intermediate value which we will then successfully ignore. - * - * A similar approach is used for labels. - */ - - duk_bool_t single_token; - - DUK_DDD(DUK_DDDPRINT("expression statement")); - duk__exprtop(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/); - - single_token = (comp_ctx->curr_func.nud_count == 1 && /* one token */ - comp_ctx->curr_func.led_count == 0); /* no operators */ - - if (single_token && comp_ctx->prev_token.t == DUK_TOK_IDENTIFIER && comp_ctx->curr_token.t == DUK_TOK_COLON) { - /* - * Detected label - */ - - duk_hstring *h_lab; - - /* expected ival */ - DUK_ASSERT(res->t == DUK_IVAL_VAR); - DUK_ASSERT(res->x1.t == DUK_ISPEC_VALUE); - DUK_ASSERT(DUK_TVAL_IS_STRING(duk_get_tval(thr, res->x1.valstack_idx))); - h_lab = comp_ctx->prev_token.str1; - DUK_ASSERT(h_lab != NULL); - - DUK_DDD(DUK_DDDPRINT("explicit label site for label '%!O'", (duk_heaphdr *) h_lab)); - - duk__advance(comp_ctx); /* eat colon */ - - label_id = duk__stmt_label_site(comp_ctx, label_id); - - duk__add_label(comp_ctx, h_lab, pc_at_entry /*pc_label*/, label_id); - - /* a statement following a label cannot be a source element - * (a function declaration). - */ - allow_source_elem = 0; - - DUK_DDD(DUK_DDDPRINT("label handled, retry statement parsing")); - goto retry_parse; - } - - stmt_flags = 0; - - if (dir_prol_at_entry && /* still in prologue */ - single_token && /* single string token */ - comp_ctx->prev_token.t == DUK_TOK_STRING) { - /* - * Detected a directive - */ - duk_hstring *h_dir; - - /* expected ival */ - DUK_ASSERT(res->t == DUK_IVAL_PLAIN); - DUK_ASSERT(res->x1.t == DUK_ISPEC_VALUE); - DUK_ASSERT(DUK_TVAL_IS_STRING(duk_get_tval(thr, res->x1.valstack_idx))); - h_dir = comp_ctx->prev_token.str1; - DUK_ASSERT(h_dir != NULL); - - DUK_DDD(DUK_DDDPRINT("potential directive: %!O", h_dir)); - - stmt_flags |= DUK__STILL_PROLOGUE; - - /* Note: escaped characters differentiate directives */ - - if (comp_ctx->prev_token.num_escapes > 0) { - DUK_DDD(DUK_DDDPRINT("directive contains escapes: valid directive " - "but we ignore such directives")); - } else { - /* - * The length comparisons are present to handle - * strings like "use strict\u0000foo" as required. - */ - - if (DUK_HSTRING_GET_BYTELEN(h_dir) == 10 && - DUK_STRCMP((const char *) DUK_HSTRING_GET_DATA(h_dir), "use strict") == 0) { -#if defined(DUK_USE_STRICT_DECL) - DUK_DDD(DUK_DDDPRINT("use strict directive detected: strict flag %ld -> %ld", - (long) comp_ctx->curr_func.is_strict, - (long) 1)); - comp_ctx->curr_func.is_strict = 1; -#else - DUK_DDD(DUK_DDDPRINT("use strict detected but strict declarations disabled, ignoring")); -#endif - } else if (DUK_HSTRING_GET_BYTELEN(h_dir) == 14 && - DUK_STRCMP((const char *) DUK_HSTRING_GET_DATA(h_dir), "use duk notail") == 0) { - DUK_DDD(DUK_DDDPRINT("use duk notail directive detected: notail flag %ld -> %ld", - (long) comp_ctx->curr_func.is_notail, - (long) 1)); - comp_ctx->curr_func.is_notail = 1; - } else { - DUK_DD(DUK_DDPRINT("unknown directive: '%!O', ignoring but not terminating " - "directive prologue", - (duk_hobject *) h_dir)); - } - } - } else { - DUK_DDD(DUK_DDDPRINT("non-directive expression statement or no longer in prologue; " - "prologue terminated if still active")); - } - - stmt_flags |= DUK__HAS_VAL | DUK__HAS_TERM; - } - } /* end switch (tok) */ - - /* - * Statement value handling. - * - * Global code and eval code has an implicit return value - * which comes from the last statement with a value - * (technically a non-"empty" continuation, which is - * different from an empty statement). - * - * Since we don't know whether a later statement will - * override the value of the current statement, we need - * to coerce the statement value to a register allocated - * for implicit return values. In other cases we need - * to coerce the statement value to a plain value to get - * any side effects out (consider e.g. "foo.bar;"). - */ - - /* XXX: what about statements which leave a half-cooked value in 'res' - * but have no stmt value? Any such statements? - */ - - if (stmt_flags & DUK__HAS_VAL) { - duk_regconst_t reg_stmt_value = comp_ctx->curr_func.reg_stmt_value; - if (reg_stmt_value >= 0) { - duk__ivalue_toforcedreg(comp_ctx, res, reg_stmt_value); - } else { - duk__ivalue_toplain_ignore(comp_ctx, res); - } - } else { - ; - } - - /* - * Statement terminator check, including automatic semicolon - * handling. After this step, 'curr_tok' should be the first - * token after a possible statement terminator. - */ - - if (stmt_flags & DUK__HAS_TERM) { - if (comp_ctx->curr_token.t == DUK_TOK_SEMICOLON) { - DUK_DDD(DUK_DDDPRINT("explicit semicolon terminates statement")); - duk__advance(comp_ctx); - } else { - if (comp_ctx->curr_token.allow_auto_semi) { - DUK_DDD(DUK_DDDPRINT("automatic semicolon terminates statement")); - } else if (stmt_flags & DUK__ALLOW_AUTO_SEMI_ALWAYS) { - /* XXX: make this lenience dependent on flags or strictness? */ - DUK_DDD(DUK_DDDPRINT("automatic semicolon terminates statement (allowed for compatibility " - "even though no lineterm present before next token)")); - } else { - DUK_ERROR_SYNTAX(thr, DUK_STR_UNTERMINATED_STMT); - DUK_WO_NORETURN(return;); - } - } - } else { - DUK_DDD(DUK_DDDPRINT("statement has no terminator")); - } - - /* - * Directive prologue tracking. - */ - - if (stmt_flags & DUK__STILL_PROLOGUE) { - DUK_DDD(DUK_DDDPRINT("setting in_directive_prologue")); - comp_ctx->curr_func.in_directive_prologue = 1; - } - - /* - * Cleanups (all statement parsing flows through here). - * - * Pop label site and reset labels. Reset 'next temp' to value at - * entry to reuse temps. - */ - - if (label_id >= 0) { - duk__emit_bc(comp_ctx, DUK_OP_ENDLABEL, (duk_regconst_t) label_id); - } - - DUK__SETTEMP(comp_ctx, temp_at_entry); - - duk__reset_labels_to_length(comp_ctx, labels_len_at_entry); - - /* XXX: return indication of "terminalness" (e.g. a 'throw' is terminal) */ - - DUK__RECURSION_DECREASE(comp_ctx, thr); -} - -/* - * Parse a statement list. - * - * Handles automatic semicolon insertion and implicit return value. - * - * Upon entry, 'curr_tok' should contain the first token of the first - * statement (parsed in the "allow regexp literal" mode). Upon exit, - * 'curr_tok' contains the token following the statement list terminator - * (EOF or closing brace). - */ - -DUK_LOCAL void duk__parse_stmts(duk_compiler_ctx *comp_ctx, - duk_bool_t allow_source_elem, - duk_bool_t expect_eof, - duk_bool_t regexp_after) { - duk_hthread *thr = comp_ctx->thr; - duk_ivalue res_alloc; - duk_ivalue *res = &res_alloc; - - /* Setup state. Initial ivalue is 'undefined'. */ - - duk_require_stack(thr, DUK__PARSE_STATEMENTS_SLOTS); - - /* XXX: 'res' setup can be moved to function body level; in fact, two 'res' - * intermediate values suffice for parsing of each function. Nesting is needed - * for nested functions (which may occur inside expressions). - */ - - duk_memzero(&res_alloc, sizeof(res_alloc)); - res->t = DUK_IVAL_PLAIN; - res->x1.t = DUK_ISPEC_VALUE; - res->x1.valstack_idx = duk_get_top(thr); - res->x2.valstack_idx = res->x1.valstack_idx + 1; - duk_push_undefined(thr); - duk_push_undefined(thr); - - /* Parse statements until a closing token (EOF or '}') is found. */ - - for (;;) { - /* Check whether statement list ends. */ - - if (expect_eof) { - if (comp_ctx->curr_token.t == DUK_TOK_EOF) { - break; - } - } else { - if (comp_ctx->curr_token.t == DUK_TOK_RCURLY) { - break; - } - } - - /* Check statement type based on the first token type. - * - * Note: expression parsing helpers expect 'curr_tok' to - * contain the first token of the expression upon entry. - */ - - DUK_DDD(DUK_DDDPRINT("TOKEN %ld (non-whitespace, non-comment)", (long) comp_ctx->curr_token.t)); - - duk__parse_stmt(comp_ctx, res, allow_source_elem); - } - - /* RegExp is allowed / not allowed depending on context. For function - * declarations RegExp is allowed because it follows a function - * declaration statement and may appear as part of the next statement. - * For function expressions RegExp is not allowed, and it's possible - * to do something like '(function () {} / 123)'. - */ - if (regexp_after) { - comp_ctx->curr_func.allow_regexp_in_adv = 1; - } - duk__advance(comp_ctx); - - /* Tear down state. */ - - duk_pop_2(thr); -} - -/* - * Declaration binding instantiation conceptually happens when calling a - * function; for us it essentially means that function prologue. The - * conceptual process is described in E5 Section 10.5. - * - * We need to keep track of all encountered identifiers to (1) create an - * identifier-to-register map ("varmap"); and (2) detect duplicate - * declarations. Identifiers which are not bound to registers still need - * to be tracked for detecting duplicates. Currently such identifiers - * are put into the varmap with a 'null' value, which is later cleaned up. - * - * To support functions with a large number of variable and function - * declarations, registers are not allocated beyond a certain limit; - * after that limit, variables and functions need slow path access. - * Arguments are currently always register bound, which imposes a hard - * (and relatively small) argument count limit. - * - * Some bindings in E5 are not configurable (= deletable) and almost all - * are mutable (writable). Exceptions are: - * - * - The 'arguments' binding, established only if no shadowing argument - * or function declaration exists. We handle 'arguments' creation - * and binding through an explicit slow path environment record. - * - * - The "name" binding for a named function expression. This is also - * handled through an explicit slow path environment record. - */ - -/* XXX: add support for variables to not be register bound always, to - * handle cases with a very large number of variables? - */ - -DUK_LOCAL void duk__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ctx, duk_regconst_t *out_stmt_value_reg) { - duk_hthread *thr = comp_ctx->thr; - duk_hstring *h_name; - duk_bool_t configurable_bindings; - duk_uarridx_t num_args; - duk_uarridx_t num_decls; - duk_regconst_t rc_name; - duk_small_uint_t declvar_flags; - duk_uarridx_t i; -#if defined(DUK_USE_ASSERTIONS) - duk_idx_t entry_top; -#endif - -#if defined(DUK_USE_ASSERTIONS) - entry_top = duk_get_top(thr); -#endif - - /* - * Preliminaries - */ - - configurable_bindings = comp_ctx->curr_func.is_eval; - DUK_DDD(DUK_DDDPRINT("configurable_bindings=%ld", (long) configurable_bindings)); - - /* varmap is already in comp_ctx->curr_func.varmap_idx */ - - /* - * Function formal arguments, always bound to registers - * (there's no support for shuffling them now). - */ - - num_args = (duk_uarridx_t) duk_get_length(thr, comp_ctx->curr_func.argnames_idx); - DUK_DDD(DUK_DDDPRINT("num_args=%ld", (long) num_args)); - /* XXX: check num_args */ - - for (i = 0; i < num_args; i++) { - duk_get_prop_index(thr, comp_ctx->curr_func.argnames_idx, i); - h_name = duk_known_hstring(thr, -1); - - if (comp_ctx->curr_func.is_strict) { - if (duk__hstring_is_eval_or_arguments(comp_ctx, h_name)) { - DUK_DDD(DUK_DDDPRINT("arg named 'eval' or 'arguments' in strict mode -> SyntaxError")); - goto error_argname; - } - duk_dup_top(thr); - if (duk_has_prop(thr, comp_ctx->curr_func.varmap_idx)) { - DUK_DDD(DUK_DDDPRINT("duplicate arg name in strict mode -> SyntaxError")); - goto error_argname; - } - - /* Ensure argument name is not a reserved word in current - * (final) strictness. Formal argument parsing may not - * catch reserved names if strictness changes during - * parsing. - * - * We only need to do this in strict mode because non-strict - * keyword are always detected in formal argument parsing. - */ - - if (DUK_HSTRING_HAS_STRICT_RESERVED_WORD(h_name)) { - goto error_argname; - } - } - - /* overwrite any previous binding of the same name; the effect is - * that last argument of a certain name wins. - */ - - /* only functions can have arguments */ - DUK_ASSERT(comp_ctx->curr_func.is_function); - duk_push_uarridx(thr, i); /* -> [ ... name index ] */ - duk_put_prop(thr, comp_ctx->curr_func.varmap_idx); /* -> [ ... ] */ - - /* no code needs to be emitted, the regs already have values */ - } - - /* use temp_next for tracking register allocations */ - DUK__SETTEMP_CHECKMAX(comp_ctx, (duk_regconst_t) num_args); - - /* - * After arguments, allocate special registers (like shuffling temps) - */ - - if (out_stmt_value_reg) { - *out_stmt_value_reg = DUK__ALLOCTEMP(comp_ctx); - } - if (comp_ctx->curr_func.needs_shuffle) { - duk_regconst_t shuffle_base = DUK__ALLOCTEMPS(comp_ctx, 3); - comp_ctx->curr_func.shuffle1 = shuffle_base; - comp_ctx->curr_func.shuffle2 = shuffle_base + 1; - comp_ctx->curr_func.shuffle3 = shuffle_base + 2; - DUK_D(DUK_DPRINT("shuffle registers needed by function, allocated: %ld %ld %ld", - (long) comp_ctx->curr_func.shuffle1, - (long) comp_ctx->curr_func.shuffle2, - (long) comp_ctx->curr_func.shuffle3)); - } - if (comp_ctx->curr_func.temp_next > 0x100) { - DUK_D(DUK_DPRINT("not enough 8-bit regs: temp_next=%ld", (long) comp_ctx->curr_func.temp_next)); - goto error_outofregs; - } - - /* - * Function declarations - */ - - num_decls = (duk_uarridx_t) duk_get_length(thr, comp_ctx->curr_func.decls_idx); - DUK_DDD( - DUK_DDDPRINT("num_decls=%ld -> %!T", (long) num_decls, (duk_tval *) duk_get_tval(thr, comp_ctx->curr_func.decls_idx))); - for (i = 0; i < num_decls; i += 2) { - duk_int_t decl_type; - duk_int_t fnum; - - duk_get_prop_index(thr, comp_ctx->curr_func.decls_idx, i + 1); /* decl type */ - decl_type = duk_to_int(thr, -1); - fnum = decl_type >> 8; /* XXX: macros */ - decl_type = decl_type & 0xff; - duk_pop(thr); - - if (decl_type != DUK_DECL_TYPE_FUNC) { - continue; - } - - duk_get_prop_index(thr, comp_ctx->curr_func.decls_idx, i); /* decl name */ - - /* XXX: spilling */ - if (comp_ctx->curr_func.is_function) { - duk_regconst_t reg_bind; - duk_dup_top(thr); - if (duk_has_prop(thr, comp_ctx->curr_func.varmap_idx)) { - /* shadowed; update value */ - duk_dup_top(thr); - duk_get_prop(thr, comp_ctx->curr_func.varmap_idx); - reg_bind = duk_to_int(thr, -1); /* [ ... name reg_bind ] */ - duk__emit_a_bc(comp_ctx, DUK_OP_CLOSURE, reg_bind, (duk_regconst_t) fnum); - } else { - /* function: always register bound */ - reg_bind = DUK__ALLOCTEMP(comp_ctx); - duk__emit_a_bc(comp_ctx, DUK_OP_CLOSURE, reg_bind, (duk_regconst_t) fnum); - duk_push_int(thr, (duk_int_t) reg_bind); - } - } else { - /* Function declaration for global/eval code is emitted even - * for duplicates, because of E5 Section 10.5, step 5.e of - * E5.1 (special behavior for variable bound to global object). - * - * DECLVAR will not re-declare a variable as such, but will - * update the binding value. - */ - - duk_regconst_t reg_temp = DUK__ALLOCTEMP(comp_ctx); - duk_dup_top(thr); - rc_name = duk__getconst(comp_ctx); - duk_push_null(thr); - - duk__emit_a_bc(comp_ctx, DUK_OP_CLOSURE, reg_temp, (duk_regconst_t) fnum); - - declvar_flags = DUK_PROPDESC_FLAG_WRITABLE | DUK_PROPDESC_FLAG_ENUMERABLE | DUK_BC_DECLVAR_FLAG_FUNC_DECL; - - if (configurable_bindings) { - declvar_flags |= DUK_PROPDESC_FLAG_CONFIGURABLE; - } - - duk__emit_a_b_c(comp_ctx, - DUK_OP_DECLVAR | DUK__EMIT_FLAG_NO_SHUFFLE_A | DUK__EMIT_FLAG_BC_REGCONST, - (duk_regconst_t) declvar_flags /*flags*/, - rc_name /*name*/, - reg_temp /*value*/); - - DUK__SETTEMP(comp_ctx, reg_temp); /* forget temp */ - } - - DUK_DDD(DUK_DDDPRINT("function declaration to varmap: %!T -> %!T", - (duk_tval *) duk_get_tval(thr, -2), - (duk_tval *) duk_get_tval(thr, -1))); - -#if defined(DUK_USE_FASTINT) - DUK_ASSERT(DUK_TVAL_IS_NULL(duk_get_tval(thr, -1)) || DUK_TVAL_IS_FASTINT(duk_get_tval(thr, -1))); -#endif - duk_put_prop(thr, comp_ctx->curr_func.varmap_idx); /* [ ... name reg/null ] -> [ ... ] */ - } - - /* - * 'arguments' binding is special; if a shadowing argument or - * function declaration exists, an arguments object will - * definitely not be needed, regardless of whether the identifier - * 'arguments' is referenced inside the function body. - */ - - if (duk_has_prop_stridx(thr, comp_ctx->curr_func.varmap_idx, DUK_STRIDX_LC_ARGUMENTS)) { - DUK_DDD(DUK_DDDPRINT("'arguments' is shadowed by argument or function declaration " - "-> arguments object creation can be skipped")); - comp_ctx->curr_func.is_arguments_shadowed = 1; - } - - /* - * Variable declarations. - * - * Unlike function declarations, variable declaration values don't get - * assigned on entry. If a binding of the same name already exists, just - * ignore it silently. - */ - - for (i = 0; i < num_decls; i += 2) { - duk_int_t decl_type; - - duk_get_prop_index(thr, comp_ctx->curr_func.decls_idx, i + 1); /* decl type */ - decl_type = duk_to_int(thr, -1); - decl_type = decl_type & 0xff; - duk_pop(thr); - - if (decl_type != DUK_DECL_TYPE_VAR) { - continue; - } - - duk_get_prop_index(thr, comp_ctx->curr_func.decls_idx, i); /* decl name */ - - if (duk_has_prop(thr, comp_ctx->curr_func.varmap_idx)) { - /* shadowed, ignore */ - } else { - duk_get_prop_index(thr, comp_ctx->curr_func.decls_idx, i); /* decl name */ - h_name = duk_known_hstring(thr, -1); - - if (h_name == DUK_HTHREAD_STRING_LC_ARGUMENTS(thr) && !comp_ctx->curr_func.is_arguments_shadowed) { - /* E5 Section steps 7-8 */ - DUK_DDD(DUK_DDDPRINT("'arguments' not shadowed by a function declaration, " - "but appears as a variable declaration -> treat as " - "a no-op for variable declaration purposes")); - duk_pop(thr); - continue; - } - - /* XXX: spilling */ - if (comp_ctx->curr_func.is_function) { - duk_regconst_t reg_bind = DUK__ALLOCTEMP(comp_ctx); - /* no need to init reg, it will be undefined on entry */ - duk_push_int(thr, (duk_int_t) reg_bind); - } else { - duk_dup_top(thr); - rc_name = duk__getconst(comp_ctx); - duk_push_null(thr); - - declvar_flags = DUK_PROPDESC_FLAG_WRITABLE | DUK_PROPDESC_FLAG_ENUMERABLE; - if (configurable_bindings) { - declvar_flags |= DUK_PROPDESC_FLAG_CONFIGURABLE; - } - - duk__emit_a_b_c(comp_ctx, - DUK_OP_DECLVAR | DUK__EMIT_FLAG_NO_SHUFFLE_A | DUK__EMIT_FLAG_BC_REGCONST, - (duk_regconst_t) declvar_flags /*flags*/, - rc_name /*name*/, - 0 /*value*/); - } - - duk_put_prop(thr, comp_ctx->curr_func.varmap_idx); /* [ ... name reg/null ] -> [ ... ] */ - } - } - - /* - * Wrap up - */ - - DUK_DDD(DUK_DDDPRINT("varmap: %!T, is_arguments_shadowed=%ld", - (duk_tval *) duk_get_tval(thr, comp_ctx->curr_func.varmap_idx), - (long) comp_ctx->curr_func.is_arguments_shadowed)); - - DUK_ASSERT_TOP(thr, entry_top); - return; - -error_outofregs: - DUK_ERROR_RANGE(thr, DUK_STR_REG_LIMIT); - DUK_WO_NORETURN(return;); - -error_argname: - DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_ARG_NAME); - DUK_WO_NORETURN(return;); -} - -/* - * Parse a function-body-like expression (FunctionBody or Program - * in E5 grammar) using a two-pass parse. The productions appear - * in the following contexts: - * - * - function expression - * - function statement - * - function declaration - * - getter in object literal - * - setter in object literal - * - global code - * - eval code - * - Function constructor body - * - * This function only parses the statement list of the body; the argument - * list and possible function name must be initialized by the caller. - * For instance, for Function constructor, the argument names are originally - * on the value stack. The parsing of statements ends either at an EOF or - * a closing brace; this is controlled by an input flag. - * - * Note that there are many differences affecting parsing and even code - * generation: - * - * - Global and eval code have an implicit return value generated - * by the last statement; function code does not - * - * - Global code, eval code, and Function constructor body end in - * an EOF, other bodies in a closing brace ('}') - * - * Upon entry, 'curr_tok' is ignored and the function will pull in the - * first token on its own. Upon exit, 'curr_tok' is the terminating - * token (EOF or closing brace). - */ - -DUK_LOCAL void duk__parse_func_body(duk_compiler_ctx *comp_ctx, - duk_bool_t expect_eof, - duk_bool_t implicit_return_value, - duk_bool_t regexp_after, - duk_small_int_t expect_token) { - duk_compiler_func *func; - duk_hthread *thr; - duk_regconst_t reg_stmt_value = -1; - duk_lexer_point lex_pt; - duk_regconst_t temp_first; - duk_small_int_t compile_round = 1; - - DUK_ASSERT(comp_ctx != NULL); - - thr = comp_ctx->thr; - DUK_ASSERT(thr != NULL); - - func = &comp_ctx->curr_func; - DUK_ASSERT(func != NULL); - - DUK__RECURSION_INCREASE(comp_ctx, thr); - - duk_require_stack(thr, DUK__FUNCTION_BODY_REQUIRE_SLOTS); - - /* - * Store lexer position for a later rewind - */ - - DUK_LEXER_GETPOINT(&comp_ctx->lex, &lex_pt); - - /* - * Program code (global and eval code) has an implicit return value - * from the last statement value (e.g. eval("1; 2+3;") returns 3). - * This is not the case with functions. If implicit statement return - * value is requested, all statements are coerced to a register - * allocated here, and used in the implicit return statement below. - */ - - /* XXX: this is pointless here because pass 1 is throw-away */ - if (implicit_return_value) { - reg_stmt_value = DUK__ALLOCTEMP(comp_ctx); - - /* If an implicit return value is needed by caller, it must be - * initialized to 'undefined' because we don't know whether any - * non-empty (where "empty" is a continuation type, and different - * from an empty statement) statements will be executed. - * - * However, since 1st pass is a throwaway one, no need to emit - * it here. - */ -#if 0 - duk__emit_bc(comp_ctx, - DUK_OP_LDUNDEF, - 0); -#endif - } - - /* - * First pass. - * - * Gather variable/function declarations needed for second pass. - * Code generated is dummy and discarded. - */ - - func->in_directive_prologue = 1; - func->in_scanning = 1; - func->may_direct_eval = 0; - func->id_access_arguments = 0; - func->id_access_slow = 0; - func->id_access_slow_own = 0; - func->reg_stmt_value = reg_stmt_value; -#if defined(DUK_USE_DEBUGGER_SUPPORT) - func->min_line = DUK_INT_MAX; - func->max_line = 0; -#endif - - /* duk__parse_stmts() expects curr_tok to be set; parse in "allow - * regexp literal" mode with current strictness. - */ - if (expect_token >= 0) { - /* Eating a left curly; regexp mode is allowed by left curly - * based on duk__token_lbp[] automatically. - */ - DUK_ASSERT(expect_token == DUK_TOK_LCURLY); - duk__update_lineinfo_currtoken(comp_ctx); - duk__advance_expect(comp_ctx, expect_token); - } else { - /* Need to set curr_token.t because lexing regexp mode depends on current - * token type. Zero value causes "allow regexp" mode. - */ - comp_ctx->curr_token.t = 0; - duk__advance(comp_ctx); - } - - DUK_DDD(DUK_DDDPRINT("begin 1st pass")); - duk__parse_stmts(comp_ctx, - 1, /* allow source elements */ - expect_eof, /* expect EOF instead of } */ - regexp_after); /* regexp after */ - DUK_DDD(DUK_DDDPRINT("end 1st pass")); - - /* - * Second (and possibly third) pass. - * - * Generate actual code. In most cases the need for shuffle - * registers is detected during pass 1, but in some corner cases - * we'll only detect it during pass 2 and a third pass is then - * needed (see GH-115). - */ - - for (;;) { - duk_bool_t needs_shuffle_before = comp_ctx->curr_func.needs_shuffle; - compile_round++; - - /* - * Rewind lexer. - * - * duk__parse_stmts() expects curr_tok to be set; parse in "allow regexp - * literal" mode with current strictness. - * - * curr_token line number info should be initialized for pass 2 before - * generating prologue, to ensure prologue bytecode gets nice line numbers. - */ - - DUK_DDD(DUK_DDDPRINT("rewind lexer")); - DUK_LEXER_SETPOINT(&comp_ctx->lex, &lex_pt); - comp_ctx->curr_token.t = 0; /* this is needed for regexp mode */ - comp_ctx->curr_token.start_line = 0; /* needed for line number tracking (becomes prev_token.start_line) */ - duk__advance(comp_ctx); - - /* - * Reset function state and perform register allocation, which creates - * 'varmap' for second pass. Function prologue for variable declarations, - * binding value initializations etc is emitted as a by-product. - * - * Strict mode restrictions for duplicate and invalid argument - * names are checked here now that we know whether the function - * is actually strict. See: test-dev-strict-mode-boundary.js. - * - * Inner functions are compiled during pass 1 and are not reset. - */ - - duk__reset_func_for_pass2(comp_ctx); - func->in_directive_prologue = 1; - func->in_scanning = 0; - - /* must be able to emit code, alloc consts, etc. */ - - duk__init_varmap_and_prologue_for_pass2(comp_ctx, (implicit_return_value ? ®_stmt_value : NULL)); - func->reg_stmt_value = reg_stmt_value; - - temp_first = DUK__GETTEMP(comp_ctx); - - func->temp_first = temp_first; - func->temp_next = temp_first; - func->stmt_next = 0; - func->label_next = 0; - - /* XXX: init or assert catch depth etc -- all values */ - func->id_access_arguments = 0; - func->id_access_slow = 0; - func->id_access_slow_own = 0; - - /* - * Check function name validity now that we know strictness. - * This only applies to function declarations and expressions, - * not setter/getter name. - * - * See: test-dev-strict-mode-boundary.js - */ - - if (func->is_function && !func->is_setget && func->h_name != NULL) { - if (func->is_strict) { - if (duk__hstring_is_eval_or_arguments(comp_ctx, func->h_name)) { - DUK_DDD(DUK_DDDPRINT("func name is 'eval' or 'arguments' in strict mode")); - goto error_funcname; - } - if (DUK_HSTRING_HAS_STRICT_RESERVED_WORD(func->h_name)) { - DUK_DDD(DUK_DDDPRINT("func name is a reserved word in strict mode")); - goto error_funcname; - } - } else { - if (DUK_HSTRING_HAS_RESERVED_WORD(func->h_name) && - !DUK_HSTRING_HAS_STRICT_RESERVED_WORD(func->h_name)) { - DUK_DDD(DUK_DDDPRINT("func name is a reserved word in non-strict mode")); - goto error_funcname; - } - } - } - - /* - * Second pass parsing. - */ - - if (implicit_return_value) { - /* Default implicit return value. */ - duk__emit_bc(comp_ctx, DUK_OP_LDUNDEF, 0); - } - - DUK_DDD(DUK_DDDPRINT("begin 2nd pass")); - duk__parse_stmts(comp_ctx, - 1, /* allow source elements */ - expect_eof, /* expect EOF instead of } */ - regexp_after); /* regexp after */ - DUK_DDD(DUK_DDDPRINT("end 2nd pass")); - - duk__update_lineinfo_currtoken(comp_ctx); - - if (needs_shuffle_before == comp_ctx->curr_func.needs_shuffle) { - /* Shuffle decision not changed. */ - break; - } - if (compile_round >= 3) { - /* Should never happen but avoid infinite loop just in case. */ - DUK_D(DUK_DPRINT("more than 3 compile passes needed, should never happen")); - DUK_ERROR_INTERNAL(thr); - DUK_WO_NORETURN(return;); - } - DUK_D(DUK_DPRINT("need additional round to compile function, round now %d", (int) compile_round)); - } - - /* - * Emit a final RETURN. - * - * It would be nice to avoid emitting an unnecessary "return" opcode - * if the current PC is not reachable. However, this cannot be reliably - * detected; even if the previous instruction is an unconditional jump, - * there may be a previous jump which jumps to current PC (which is the - * case for iteration and conditional statements, for instance). - */ - - /* XXX: request a "last statement is terminal" from duk__parse_stmt() and duk__parse_stmts(); - * we could avoid the last RETURN if we could ensure there is no way to get here - * (directly or via a jump) - */ - - DUK_ASSERT(comp_ctx->curr_func.catch_depth == 0); - if (reg_stmt_value >= 0) { - DUK_ASSERT(DUK__ISREG(reg_stmt_value)); - duk__emit_bc(comp_ctx, DUK_OP_RETREG, reg_stmt_value /*reg*/); - } else { - duk__emit_op_only(comp_ctx, DUK_OP_RETUNDEF); - } - - /* - * Peephole optimize JUMP chains. - */ - - duk__peephole_optimize_bytecode(comp_ctx); - - /* - * comp_ctx->curr_func is now ready to be converted into an actual - * function template. - */ - - DUK__RECURSION_DECREASE(comp_ctx, thr); - return; - -error_funcname: - DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_FUNC_NAME); - DUK_WO_NORETURN(return;); -} - -/* - * Parse a function-like expression: - * - * - function expression - * - function declaration - * - function statement (non-standard) - * - setter/getter - * - * Adds the function to comp_ctx->curr_func function table and returns the - * function number. - * - * On entry, curr_token points to: - * - * - the token after 'function' for function expression/declaration/statement - * - the token after 'set' or 'get' for setter/getter - */ - -/* Parse formals. */ -DUK_LOCAL void duk__parse_func_formals(duk_compiler_ctx *comp_ctx) { - duk_hthread *thr = comp_ctx->thr; - duk_bool_t first = 1; - duk_uarridx_t n; - - for (;;) { - if (comp_ctx->curr_token.t == DUK_TOK_RPAREN) { - break; - } - - if (first) { - /* no comma */ - first = 0; - } else { - duk__advance_expect(comp_ctx, DUK_TOK_COMMA); - } - - /* Note: when parsing a formal list in non-strict context, e.g. - * "implements" is parsed as an identifier. When the function is - * later detected to be strict, the argument list must be rechecked - * against a larger set of reserved words (that of strict mode). - * This is handled by duk__parse_func_body(). Here we recognize - * whatever tokens are considered reserved in current strictness - * (which is not always enough). - */ - - if (comp_ctx->curr_token.t != DUK_TOK_IDENTIFIER) { - DUK_ERROR_SYNTAX(thr, DUK_STR_EXPECTED_IDENTIFIER); - DUK_WO_NORETURN(return;); - } - DUK_ASSERT(comp_ctx->curr_token.t == DUK_TOK_IDENTIFIER); - DUK_ASSERT(comp_ctx->curr_token.str1 != NULL); - DUK_DDD(DUK_DDDPRINT("formal argument: %!O", (duk_heaphdr *) comp_ctx->curr_token.str1)); - - /* XXX: append primitive */ - duk_push_hstring(thr, comp_ctx->curr_token.str1); - n = (duk_uarridx_t) duk_get_length(thr, comp_ctx->curr_func.argnames_idx); - duk_put_prop_index(thr, comp_ctx->curr_func.argnames_idx, n); - - duk__advance(comp_ctx); /* eat identifier */ - } -} - -/* Parse a function-like expression, assuming that 'comp_ctx->curr_func' is - * correctly set up. Assumes that curr_token is just after 'function' (or - * 'set'/'get' etc). - */ -DUK_LOCAL void duk__parse_func_like_raw(duk_compiler_ctx *comp_ctx, duk_small_uint_t flags) { - duk_hthread *thr = comp_ctx->thr; - duk_token *tok; - duk_bool_t no_advance; - - DUK_ASSERT(comp_ctx->curr_func.num_formals == 0); - DUK_ASSERT(comp_ctx->curr_func.is_function == 1); - DUK_ASSERT(comp_ctx->curr_func.is_eval == 0); - DUK_ASSERT(comp_ctx->curr_func.is_global == 0); - DUK_ASSERT(comp_ctx->curr_func.is_setget == ((flags & DUK__FUNC_FLAG_GETSET) != 0)); - - duk__update_lineinfo_currtoken(comp_ctx); - - /* - * Function name (if any) - * - * We don't check for prohibited names here, because we don't - * yet know whether the function will be strict. Function body - * parsing handles this retroactively. - * - * For function expressions and declarations function name must - * be an Identifer (excludes reserved words). For setter/getter - * it is a PropertyName which allows reserved words and also - * strings and numbers (e.g. "{ get 1() { ... } }"). - * - * Function parsing may start either from prev_token or curr_token - * (object literal method definition uses prev_token for example). - * This is dealt with for the initial token. - */ - - no_advance = (flags & DUK__FUNC_FLAG_USE_PREVTOKEN); - if (no_advance) { - tok = &comp_ctx->prev_token; - } else { - tok = &comp_ctx->curr_token; - } - - if (flags & DUK__FUNC_FLAG_GETSET) { - /* PropertyName -> IdentifierName | StringLiteral | NumericLiteral */ - if (tok->t_nores == DUK_TOK_IDENTIFIER || tok->t == DUK_TOK_STRING) { - duk_push_hstring(thr, tok->str1); /* keep in valstack */ - } else if (tok->t == DUK_TOK_NUMBER) { - duk_push_number(thr, tok->num); - duk_to_string(thr, -1); - } else { - DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_GETSET_NAME); - DUK_WO_NORETURN(return;); - } - comp_ctx->curr_func.h_name = duk_known_hstring(thr, -1); /* borrowed reference */ - } else { - /* Function name is an Identifier (not IdentifierName), but we get - * the raw name (not recognizing keywords) here and perform the name - * checks only after pass 1. - */ - if (tok->t_nores == DUK_TOK_IDENTIFIER) { - duk_push_hstring(thr, tok->str1); /* keep in valstack */ - comp_ctx->curr_func.h_name = duk_known_hstring(thr, -1); /* borrowed reference */ - } else { - /* valstack will be unbalanced, which is OK */ - DUK_ASSERT((flags & DUK__FUNC_FLAG_GETSET) == 0); - DUK_ASSERT(comp_ctx->curr_func.h_name == NULL); - no_advance = 1; - if (flags & DUK__FUNC_FLAG_DECL) { - DUK_ERROR_SYNTAX(thr, DUK_STR_FUNC_NAME_REQUIRED); - DUK_WO_NORETURN(return;); - } - } - } - - DUK_DD(DUK_DDPRINT("function name: %!O", (duk_heaphdr *) comp_ctx->curr_func.h_name)); - - if (!no_advance) { - duk__advance(comp_ctx); - } - - /* - * Formal argument list - * - * We don't check for prohibited names or for duplicate argument - * names here, becase we don't yet know whether the function will - * be strict. Function body parsing handles this retroactively. - */ - - duk__advance_expect(comp_ctx, DUK_TOK_LPAREN); - - duk__parse_func_formals(comp_ctx); - - DUK_ASSERT(comp_ctx->curr_token.t == DUK_TOK_RPAREN); - duk__advance(comp_ctx); - - /* - * Parse function body - */ - - duk__parse_func_body(comp_ctx, - 0, /* expect_eof */ - 0, /* implicit_return_value */ - flags & DUK__FUNC_FLAG_DECL, /* regexp_after */ - DUK_TOK_LCURLY); /* expect_token */ - - /* - * Convert duk_compiler_func to a function template and add it - * to the parent function table. - */ - - duk__convert_to_func_template(comp_ctx); /* -> [ ... func ] */ -} - -/* Parse an inner function, adding the function template to the current function's - * function table. Return a function number to be used by the outer function. - * - * Avoiding O(depth^2) inner function parsing is handled here. On the first pass, - * compile and register the function normally into the 'funcs' array, also recording - * a lexer point (offset/line) to the closing brace of the function. On the second - * pass, skip the function and return the same 'fnum' as on the first pass by using - * a running counter. - * - * An unfortunate side effect of this is that when parsing the inner function, almost - * nothing is known of the outer function, i.e. the inner function's scope. We don't - * need that information at the moment, but it would allow some optimizations if it - * were used. - */ -DUK_LOCAL duk_int_t duk__parse_func_like_fnum(duk_compiler_ctx *comp_ctx, duk_small_uint_t flags) { - duk_hthread *thr = comp_ctx->thr; - duk_compiler_func old_func; - duk_idx_t entry_top; - duk_int_t fnum; - - /* - * On second pass, skip the function. - */ - - if (!comp_ctx->curr_func.in_scanning) { - duk_lexer_point lex_pt; - - fnum = comp_ctx->curr_func.fnum_next++; - duk_get_prop_index(thr, comp_ctx->curr_func.funcs_idx, (duk_uarridx_t) (fnum * 3 + 1)); - lex_pt.offset = (duk_size_t) duk_to_uint(thr, -1); - duk_pop(thr); - duk_get_prop_index(thr, comp_ctx->curr_func.funcs_idx, (duk_uarridx_t) (fnum * 3 + 2)); - lex_pt.line = duk_to_int(thr, -1); - duk_pop(thr); - - DUK_DDD( - DUK_DDDPRINT("second pass of an inner func, skip the function, reparse closing brace; lex offset=%ld, line=%ld", - (long) lex_pt.offset, - (long) lex_pt.line)); - - DUK_LEXER_SETPOINT(&comp_ctx->lex, &lex_pt); - comp_ctx->curr_token.t = 0; /* this is needed for regexp mode */ - comp_ctx->curr_token.start_line = 0; /* needed for line number tracking (becomes prev_token.start_line) */ - duk__advance(comp_ctx); - - /* RegExp is not allowed after a function expression, e.g. in - * (function () {} / 123). A RegExp *is* allowed after a - * function declaration! - */ - if (flags & DUK__FUNC_FLAG_DECL) { - comp_ctx->curr_func.allow_regexp_in_adv = 1; - } - duk__advance_expect(comp_ctx, DUK_TOK_RCURLY); - - return fnum; - } - - /* - * On first pass, perform actual parsing. Remember valstack top on entry - * to restore it later, and switch to using a new function in comp_ctx. - */ - - entry_top = duk_get_top(thr); - DUK_DDD(DUK_DDDPRINT("before func: entry_top=%ld, curr_tok.start_offset=%ld", - (long) entry_top, - (long) comp_ctx->curr_token.start_offset)); - - duk_memcpy(&old_func, &comp_ctx->curr_func, sizeof(duk_compiler_func)); - - duk_memzero(&comp_ctx->curr_func, sizeof(duk_compiler_func)); - duk__init_func_valstack_slots(comp_ctx); - DUK_ASSERT(comp_ctx->curr_func.num_formals == 0); - - /* inherit initial strictness from parent */ - comp_ctx->curr_func.is_strict = old_func.is_strict; - - /* XXX: It might be better to just store the flags into the curr_func - * struct and use them as is without this flag interpretation step - * here. - */ - DUK_ASSERT(comp_ctx->curr_func.is_notail == 0); - comp_ctx->curr_func.is_function = 1; - DUK_ASSERT(comp_ctx->curr_func.is_eval == 0); - DUK_ASSERT(comp_ctx->curr_func.is_global == 0); - comp_ctx->curr_func.is_setget = ((flags & DUK__FUNC_FLAG_GETSET) != 0); - comp_ctx->curr_func.is_namebinding = - !(flags & (DUK__FUNC_FLAG_GETSET | DUK__FUNC_FLAG_METDEF | - DUK__FUNC_FLAG_DECL)); /* no name binding for: declarations, objlit getset, objlit method def */ - comp_ctx->curr_func.is_constructable = - !(flags & (DUK__FUNC_FLAG_GETSET | DUK__FUNC_FLAG_METDEF)); /* not constructable: objlit getset, objlit method def */ - - /* - * Parse inner function - */ - - duk__parse_func_like_raw(comp_ctx, flags); /* pushes function template */ - - /* prev_token.start_offset points to the closing brace here; when skipping - * we're going to reparse the closing brace to ensure semicolon insertion - * etc work as expected. - */ - DUK_DDD(DUK_DDDPRINT("after func: prev_tok.start_offset=%ld, curr_tok.start_offset=%ld", - (long) comp_ctx->prev_token.start_offset, - (long) comp_ctx->curr_token.start_offset)); - DUK_ASSERT(comp_ctx->lex.input[comp_ctx->prev_token.start_offset] == (duk_uint8_t) DUK_ASC_RCURLY); - - /* XXX: append primitive */ - DUK_ASSERT(duk_get_length(thr, old_func.funcs_idx) == (duk_size_t) (old_func.fnum_next * 3)); - fnum = old_func.fnum_next++; - - if (fnum > DUK__MAX_FUNCS) { - DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_FUNC_LIMIT); - DUK_WO_NORETURN(return 0;); - } - - /* array writes autoincrement length */ - (void) duk_put_prop_index(thr, old_func.funcs_idx, (duk_uarridx_t) (fnum * 3)); - duk_push_size_t(thr, comp_ctx->prev_token.start_offset); - (void) duk_put_prop_index(thr, old_func.funcs_idx, (duk_uarridx_t) (fnum * 3 + 1)); - duk_push_int(thr, comp_ctx->prev_token.start_line); - (void) duk_put_prop_index(thr, old_func.funcs_idx, (duk_uarridx_t) (fnum * 3 + 2)); - - /* - * Cleanup: restore original function, restore valstack state. - * - * Function declaration handling needs the function name to be pushed - * on the value stack. - */ - - if (flags & DUK__FUNC_FLAG_PUSHNAME_PASS1) { - DUK_ASSERT(comp_ctx->curr_func.h_name != NULL); - duk_push_hstring(thr, comp_ctx->curr_func.h_name); - duk_replace(thr, entry_top); - duk_set_top(thr, entry_top + 1); - } else { - duk_set_top(thr, entry_top); - } - duk_memcpy((void *) &comp_ctx->curr_func, (void *) &old_func, sizeof(duk_compiler_func)); - - return fnum; -} - -/* - * Compile input string into an executable function template without - * arguments. - * - * The string is parsed as the "Program" production of ECMAScript E5. - * Compilation context can be either global code or eval code (see E5 - * Sections 14 and 15.1.2.1). - * - * Input stack: [ ... filename ] - * Output stack: [ ... func_template ] - */ - -/* XXX: source code property */ - -DUK_LOCAL duk_ret_t duk__js_compile_raw(duk_hthread *thr, void *udata) { - duk_hstring *h_filename; - duk__compiler_stkstate *comp_stk; - duk_compiler_ctx *comp_ctx; - duk_lexer_point *lex_pt; - duk_compiler_func *func; - duk_idx_t entry_top; - duk_bool_t is_strict; - duk_bool_t is_eval; - duk_bool_t is_funcexpr; - duk_small_uint_t flags; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(udata != NULL); - - /* - * Arguments check - */ - - entry_top = duk_get_top(thr); - DUK_ASSERT(entry_top >= 1); - - comp_stk = (duk__compiler_stkstate *) udata; - comp_ctx = &comp_stk->comp_ctx_alloc; - lex_pt = &comp_stk->lex_pt_alloc; - DUK_ASSERT(comp_ctx != NULL); - DUK_ASSERT(lex_pt != NULL); - - flags = comp_stk->flags; - is_eval = (flags & DUK_COMPILE_EVAL ? 1 : 0); - is_strict = (flags & DUK_COMPILE_STRICT ? 1 : 0); - is_funcexpr = (flags & DUK_COMPILE_FUNCEXPR ? 1 : 0); - - h_filename = duk_get_hstring(thr, -1); /* may be undefined */ - - /* - * Init compiler and lexer contexts - */ - - func = &comp_ctx->curr_func; -#if defined(DUK_USE_EXPLICIT_NULL_INIT) - comp_ctx->thr = NULL; - comp_ctx->h_filename = NULL; - comp_ctx->prev_token.str1 = NULL; - comp_ctx->prev_token.str2 = NULL; - comp_ctx->curr_token.str1 = NULL; - comp_ctx->curr_token.str2 = NULL; -#endif - - duk_require_stack(thr, DUK__COMPILE_ENTRY_SLOTS); - - duk_push_dynamic_buffer(thr, 0); /* entry_top + 0 */ - duk_push_undefined(thr); /* entry_top + 1 */ - duk_push_undefined(thr); /* entry_top + 2 */ - duk_push_undefined(thr); /* entry_top + 3 */ - duk_push_undefined(thr); /* entry_top + 4 */ - - comp_ctx->thr = thr; - comp_ctx->h_filename = h_filename; - comp_ctx->tok11_idx = entry_top + 1; - comp_ctx->tok12_idx = entry_top + 2; - comp_ctx->tok21_idx = entry_top + 3; - comp_ctx->tok22_idx = entry_top + 4; - comp_ctx->recursion_limit = DUK_USE_COMPILER_RECLIMIT; - - /* comp_ctx->lex has been pre-initialized by caller: it has been - * zeroed and input/input_length has been set. - */ - comp_ctx->lex.thr = thr; - /* comp_ctx->lex.input and comp_ctx->lex.input_length filled by caller */ - comp_ctx->lex.slot1_idx = comp_ctx->tok11_idx; - comp_ctx->lex.slot2_idx = comp_ctx->tok12_idx; - comp_ctx->lex.buf_idx = entry_top + 0; - comp_ctx->lex.buf = (duk_hbuffer_dynamic *) duk_known_hbuffer(thr, entry_top + 0); - DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(comp_ctx->lex.buf) && !DUK_HBUFFER_HAS_EXTERNAL(comp_ctx->lex.buf)); - comp_ctx->lex.token_limit = DUK_COMPILER_TOKEN_LIMIT; - - lex_pt->offset = 0; - lex_pt->line = 1; - DUK_LEXER_SETPOINT(&comp_ctx->lex, lex_pt); /* fills window */ - comp_ctx->curr_token.start_line = 0; /* needed for line number tracking (becomes prev_token.start_line) */ - - /* - * Initialize function state for a zero-argument function - */ - - duk__init_func_valstack_slots(comp_ctx); - DUK_ASSERT(func->num_formals == 0); - - if (is_funcexpr) { - /* Name will be filled from function expression, not by caller. - * This case is used by Function constructor and duk_compile() - * API with the DUK_COMPILE_FUNCTION option. - */ - DUK_ASSERT(func->h_name == NULL); - } else { - duk_push_hstring_stridx(thr, (is_eval ? DUK_STRIDX_EVAL : DUK_STRIDX_GLOBAL)); - func->h_name = duk_get_hstring(thr, -1); - } - - /* - * Parse a function body or a function-like expression, depending - * on flags. - */ - - DUK_ASSERT(func->is_setget == 0); - func->is_strict = (duk_uint8_t) is_strict; - DUK_ASSERT(func->is_notail == 0); - - if (is_funcexpr) { - func->is_function = 1; - DUK_ASSERT(func->is_eval == 0); - DUK_ASSERT(func->is_global == 0); - func->is_namebinding = 1; - func->is_constructable = 1; - - duk__advance(comp_ctx); /* init 'curr_token' */ - duk__advance_expect(comp_ctx, DUK_TOK_FUNCTION); - (void) duk__parse_func_like_raw(comp_ctx, 0 /*flags*/); - } else { - DUK_ASSERT(func->is_function == 0); - DUK_ASSERT(is_eval == 0 || is_eval == 1); - func->is_eval = (duk_uint8_t) is_eval; - func->is_global = (duk_uint8_t) !is_eval; - DUK_ASSERT(func->is_namebinding == 0); - DUK_ASSERT(func->is_constructable == 0); - - duk__parse_func_body(comp_ctx, - 1, /* expect_eof */ - 1, /* implicit_return_value */ - 1, /* regexp_after (does not matter) */ - -1); /* expect_token */ - } - - /* - * Convert duk_compiler_func to a function template - */ - - duk__convert_to_func_template(comp_ctx); - - /* - * Wrapping duk_safe_call() will mangle the stack, just return stack top - */ - - /* [ ... filename (temps) func ] */ - - return 1; -} - -DUK_INTERNAL void duk_js_compile(duk_hthread *thr, const duk_uint8_t *src_buffer, duk_size_t src_length, duk_small_uint_t flags) { - duk__compiler_stkstate comp_stk; - duk_compiler_ctx *prev_ctx; - duk_ret_t safe_rc; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(src_buffer != NULL); - - /* preinitialize lexer state partially */ - duk_memzero(&comp_stk, sizeof(comp_stk)); - comp_stk.flags = flags; - DUK_LEXER_INITCTX(&comp_stk.comp_ctx_alloc.lex); - comp_stk.comp_ctx_alloc.lex.input = src_buffer; - comp_stk.comp_ctx_alloc.lex.input_length = src_length; - comp_stk.comp_ctx_alloc.lex.flags = flags; /* Forward flags directly for now. */ - - /* [ ... filename ] */ - - prev_ctx = thr->compile_ctx; - thr->compile_ctx = &comp_stk.comp_ctx_alloc; /* for duk_error_augment.c */ - safe_rc = duk_safe_call(thr, duk__js_compile_raw, (void *) &comp_stk /*udata*/, 1 /*nargs*/, 1 /*nrets*/); - thr->compile_ctx = prev_ctx; /* must restore reliably before returning */ - - if (safe_rc != DUK_EXEC_SUCCESS) { - DUK_D(DUK_DPRINT("compilation failed: %!T", duk_get_tval(thr, -1))); - (void) duk_throw(thr); - DUK_WO_NORETURN(return;); - } - - /* [ ... template ] */ -} - -/* automatic undefs */ -#undef DUK__ALLOCTEMP -#undef DUK__ALLOCTEMPS -#undef DUK__ALLOW_AUTO_SEMI_ALWAYS -#undef DUK__BC_INITIAL_INSTS -#undef DUK__BP_ADDITIVE -#undef DUK__BP_ASSIGNMENT -#undef DUK__BP_BAND -#undef DUK__BP_BOR -#undef DUK__BP_BXOR -#undef DUK__BP_CALL -#undef DUK__BP_CLOSING -#undef DUK__BP_COMMA -#undef DUK__BP_CONDITIONAL -#undef DUK__BP_EOF -#undef DUK__BP_EQUALITY -#undef DUK__BP_EXPONENTIATION -#undef DUK__BP_FOR_EXPR -#undef DUK__BP_INVALID -#undef DUK__BP_LAND -#undef DUK__BP_LOR -#undef DUK__BP_MEMBER -#undef DUK__BP_MULTIPLICATIVE -#undef DUK__BP_POSTFIX -#undef DUK__BP_RELATIONAL -#undef DUK__BP_SHIFT -#undef DUK__COMPILE_ENTRY_SLOTS -#undef DUK__CONST_MARKER -#undef DUK__DUMP_ISPEC -#undef DUK__DUMP_IVALUE -#undef DUK__EMIT_FLAG_A_IS_SOURCE -#undef DUK__EMIT_FLAG_BC_REGCONST -#undef DUK__EMIT_FLAG_B_IS_TARGET -#undef DUK__EMIT_FLAG_C_IS_TARGET -#undef DUK__EMIT_FLAG_NO_SHUFFLE_A -#undef DUK__EMIT_FLAG_NO_SHUFFLE_B -#undef DUK__EMIT_FLAG_NO_SHUFFLE_C -#undef DUK__EMIT_FLAG_RESERVE_JUMPSLOT -#undef DUK__EXPR_FLAG_ALLOW_EMPTY -#undef DUK__EXPR_FLAG_REJECT_IN -#undef DUK__EXPR_FLAG_REQUIRE_INIT -#undef DUK__EXPR_RBP_MASK -#undef DUK__FUNCTION_BODY_REQUIRE_SLOTS -#undef DUK__FUNCTION_INIT_REQUIRE_SLOTS -#undef DUK__FUNC_FLAG_DECL -#undef DUK__FUNC_FLAG_GETSET -#undef DUK__FUNC_FLAG_METDEF -#undef DUK__FUNC_FLAG_PUSHNAME_PASS1 -#undef DUK__FUNC_FLAG_USE_PREVTOKEN -#undef DUK__GETCONST_MAX_CONSTS_CHECK -#undef DUK__GETTEMP -#undef DUK__HAS_TERM -#undef DUK__HAS_VAL -#undef DUK__ISCONST -#undef DUK__ISREG -#undef DUK__ISREG_NOTTEMP -#undef DUK__ISREG_TEMP -#undef DUK__IS_TERMINAL -#undef DUK__IVAL_FLAG_ALLOW_CONST -#undef DUK__IVAL_FLAG_REQUIRE_SHORT -#undef DUK__IVAL_FLAG_REQUIRE_TEMP -#undef DUK__MAX_ARRAY_INIT_VALUES -#undef DUK__MAX_CONSTS -#undef DUK__MAX_FUNCS -#undef DUK__MAX_OBJECT_INIT_PAIRS -#undef DUK__MAX_TEMPS -#undef DUK__MK_LBP -#undef DUK__MK_LBP_FLAGS -#undef DUK__OBJ_LIT_KEY_GET -#undef DUK__OBJ_LIT_KEY_PLAIN -#undef DUK__OBJ_LIT_KEY_SET -#undef DUK__PARSE_EXPR_SLOTS -#undef DUK__PARSE_STATEMENTS_SLOTS -#undef DUK__RECURSION_DECREASE -#undef DUK__RECURSION_INCREASE -#undef DUK__REMOVECONST -#undef DUK__SETTEMP -#undef DUK__SETTEMP_CHECKMAX -#undef DUK__STILL_PROLOGUE -#undef DUK__TOKEN_LBP_BP_MASK -#undef DUK__TOKEN_LBP_FLAG_NO_REGEXP -#undef DUK__TOKEN_LBP_FLAG_TERMINATES -#undef DUK__TOKEN_LBP_FLAG_UNUSED -#undef DUK__TOKEN_LBP_GET_BP -/* - * ECMAScript bytecode executor. - */ - -/* #include duk_internal.h -> already included */ - -/* - * Local declarations. - */ - -DUK_LOCAL_DECL void duk__js_execute_bytecode_inner(duk_hthread *entry_thread, duk_activation *entry_act); - -/* - * Misc helpers. - */ - -/* Replace value stack top to value at 'tv_ptr'. Optimize for - * performance by only applying the net refcount change. - */ -#define DUK__REPLACE_TO_TVPTR(thr, tv_ptr) \ - do { \ - duk_hthread *duk__thr; \ - duk_tval *duk__tvsrc; \ - duk_tval *duk__tvdst; \ - duk_tval duk__tvtmp; \ - duk__thr = (thr); \ - duk__tvsrc = DUK_GET_TVAL_NEGIDX(duk__thr, -1); \ - duk__tvdst = (tv_ptr); \ - DUK_TVAL_SET_TVAL(&duk__tvtmp, duk__tvdst); \ - DUK_TVAL_SET_TVAL(duk__tvdst, duk__tvsrc); \ - DUK_TVAL_SET_UNDEFINED(duk__tvsrc); /* value stack init policy */ \ - duk__thr->valstack_top = duk__tvsrc; \ - DUK_TVAL_DECREF(duk__thr, &duk__tvtmp); \ - } while (0) - -/* XXX: candidate of being an internal shared API call */ -#if 0 /* unused */ -DUK_LOCAL void duk__push_tvals_incref_only(duk_hthread *thr, duk_tval *tv_src, duk_small_uint_fast_t count) { - duk_tval *tv_dst; - duk_size_t copy_size; - duk_size_t i; - - tv_dst = thr->valstack_top; - copy_size = sizeof(duk_tval) * count; - duk_memcpy((void *) tv_dst, (const void *) tv_src, copy_size); - for (i = 0; i < count; i++) { - DUK_TVAL_INCREF(thr, tv_dst); - tv_dst++; - } - thr->valstack_top = tv_dst; -} -#endif - -/* - * Arithmetic, binary, and logical helpers. - * - * Note: there is no opcode for logical AND or logical OR; this is on - * purpose, because the evalution order semantics for them make such - * opcodes pretty pointless: short circuiting means they are most - * comfortably implemented as jumps. However, a logical NOT opcode - * is useful. - * - * Note: careful with duk_tval pointers here: they are potentially - * invalidated by any DECREF and almost any API call. It's still - * preferable to work without making a copy but that's not always - * possible. - */ - -DUK_LOCAL DUK_EXEC_ALWAYS_INLINE_PERF duk_double_t duk__compute_mod(duk_double_t d1, duk_double_t d2) { - return (duk_double_t) duk_js_arith_mod((double) d1, (double) d2); -} - -#if defined(DUK_USE_ES7_EXP_OPERATOR) -DUK_LOCAL DUK_EXEC_ALWAYS_INLINE_PERF duk_double_t duk__compute_exp(duk_double_t d1, duk_double_t d2) { - return (duk_double_t) duk_js_arith_pow((double) d1, (double) d2); -} -#endif - -DUK_LOCAL DUK_EXEC_ALWAYS_INLINE_PERF void duk__vm_arith_add(duk_hthread *thr, - duk_tval *tv_x, - duk_tval *tv_y, - duk_small_uint_fast_t idx_z) { - /* - * Addition operator is different from other arithmetic - * operations in that it also provides string concatenation. - * Hence it is implemented separately. - * - * There is a fast path for number addition. Other cases go - * through potentially multiple coercions as described in the - * E5 specification. It may be possible to reduce the number - * of coercions, but this must be done carefully to preserve - * the exact semantics. - * - * E5 Section 11.6.1. - * - * Custom types also have special behavior implemented here. - */ - - duk_double_union du; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(tv_x != NULL); /* may be reg or const */ - DUK_ASSERT(tv_y != NULL); /* may be reg or const */ - DUK_ASSERT_DISABLE(idx_z >= 0); /* unsigned */ - DUK_ASSERT((duk_uint_t) idx_z < (duk_uint_t) duk_get_top(thr)); - - /* - * Fast paths - */ - -#if defined(DUK_USE_FASTINT) - if (DUK_TVAL_IS_FASTINT(tv_x) && DUK_TVAL_IS_FASTINT(tv_y)) { - duk_int64_t v1, v2, v3; - duk_int32_t v3_hi; - duk_tval *tv_z; - - /* Input values are signed 48-bit so we can detect overflow - * reliably from high bits or just a comparison. - */ - - v1 = DUK_TVAL_GET_FASTINT(tv_x); - v2 = DUK_TVAL_GET_FASTINT(tv_y); - v3 = v1 + v2; - v3_hi = (duk_int32_t) (v3 >> 32); - if (DUK_LIKELY(v3_hi >= DUK_I64_CONSTANT(-0x8000) && v3_hi <= DUK_I64_CONSTANT(0x7fff))) { - tv_z = thr->valstack_bottom + idx_z; - DUK_TVAL_SET_FASTINT_UPDREF(thr, tv_z, v3); /* side effects */ - return; - } else { - /* overflow, fall through */ - ; - } - } -#endif /* DUK_USE_FASTINT */ - - if (DUK_TVAL_IS_NUMBER(tv_x) && DUK_TVAL_IS_NUMBER(tv_y)) { -#if !defined(DUK_USE_EXEC_PREFER_SIZE) - duk_tval *tv_z; -#endif - - du.d = DUK_TVAL_GET_NUMBER(tv_x) + DUK_TVAL_GET_NUMBER(tv_y); -#if defined(DUK_USE_EXEC_PREFER_SIZE) - duk_push_number(thr, du.d); /* will NaN normalize result */ - duk_replace(thr, (duk_idx_t) idx_z); -#else /* DUK_USE_EXEC_PREFER_SIZE */ - DUK_DBLUNION_NORMALIZE_NAN_CHECK(&du); - DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du)); - tv_z = thr->valstack_bottom + idx_z; - DUK_TVAL_SET_NUMBER_UPDREF(thr, tv_z, du.d); /* side effects */ -#endif /* DUK_USE_EXEC_PREFER_SIZE */ - return; - } - - /* - * Slow path: potentially requires function calls for coercion - */ - - duk_push_tval(thr, tv_x); - duk_push_tval(thr, tv_y); - duk_to_primitive(thr, -2, DUK_HINT_NONE); /* side effects -> don't use tv_x, tv_y after */ - duk_to_primitive(thr, -1, DUK_HINT_NONE); - - /* Since Duktape 2.x plain buffers are treated like ArrayBuffer. */ - if (duk_is_string(thr, -2) || duk_is_string(thr, -1)) { - /* Symbols shouldn't technically be handled here, but should - * go into the default ToNumber() coercion path instead and - * fail there with a TypeError. However, there's a ToString() - * in duk_concat_2() which also fails with TypeError so no - * explicit check is needed. - */ - duk_concat_2(thr); /* [... s1 s2] -> [... s1+s2] */ - } else { - duk_double_t d1, d2; - - d1 = duk_to_number_m2(thr); - d2 = duk_to_number_m1(thr); - DUK_ASSERT(duk_is_number(thr, -2)); - DUK_ASSERT(duk_is_number(thr, -1)); - DUK_ASSERT_DOUBLE_IS_NORMALIZED(d1); - DUK_ASSERT_DOUBLE_IS_NORMALIZED(d2); - - du.d = d1 + d2; - duk_pop_2_unsafe(thr); - duk_push_number(thr, du.d); /* will NaN normalize result */ - } - duk_replace(thr, (duk_idx_t) idx_z); /* side effects */ -} - -DUK_LOCAL DUK_EXEC_ALWAYS_INLINE_PERF void duk__vm_arith_binary_op(duk_hthread *thr, - duk_tval *tv_x, - duk_tval *tv_y, - duk_uint_fast_t idx_z, - duk_small_uint_fast_t opcode) { - /* - * Arithmetic operations other than '+' have number-only semantics - * and are implemented here. The separate switch-case here means a - * "double dispatch" of the arithmetic opcode, but saves code space. - * - * E5 Sections 11.5, 11.5.1, 11.5.2, 11.5.3, 11.6, 11.6.1, 11.6.2, 11.6.3. - */ - - duk_double_t d1, d2; - duk_double_union du; - duk_small_uint_fast_t opcode_shifted; -#if defined(DUK_USE_FASTINT) || !defined(DUK_USE_EXEC_PREFER_SIZE) - duk_tval *tv_z; -#endif - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(tv_x != NULL); /* may be reg or const */ - DUK_ASSERT(tv_y != NULL); /* may be reg or const */ - DUK_ASSERT_DISABLE(idx_z >= 0); /* unsigned */ - DUK_ASSERT((duk_uint_t) idx_z < (duk_uint_t) duk_get_top(thr)); - - opcode_shifted = opcode >> 2; /* Get base opcode without reg/const modifiers. */ - -#if defined(DUK_USE_FASTINT) - if (DUK_TVAL_IS_FASTINT(tv_x) && DUK_TVAL_IS_FASTINT(tv_y)) { - duk_int64_t v1, v2, v3; - duk_int32_t v3_hi; - - v1 = DUK_TVAL_GET_FASTINT(tv_x); - v2 = DUK_TVAL_GET_FASTINT(tv_y); - - switch (opcode_shifted) { - case DUK_OP_SUB >> 2: { - v3 = v1 - v2; - break; - } - case DUK_OP_MUL >> 2: { - /* Must ensure result is 64-bit (no overflow); a - * simple and sufficient fast path is to allow only - * 32-bit inputs. Avoid zero inputs to avoid - * negative zero issues (-1 * 0 = -0, for instance). - */ - if (v1 >= DUK_I64_CONSTANT(-0x80000000) && v1 <= DUK_I64_CONSTANT(0x7fffffff) && v1 != 0 && - v2 >= DUK_I64_CONSTANT(-0x80000000) && v2 <= DUK_I64_CONSTANT(0x7fffffff) && v2 != 0) { - v3 = v1 * v2; - } else { - goto skip_fastint; - } - break; - } - case DUK_OP_DIV >> 2: { - /* Don't allow a zero divisor. Fast path check by - * "verifying" with multiplication. Also avoid zero - * dividend to avoid negative zero issues (0 / -1 = -0 - * for instance). - */ - if (v1 == 0 || v2 == 0) { - goto skip_fastint; - } - v3 = v1 / v2; - if (v3 * v2 != v1) { - goto skip_fastint; - } - break; - } - case DUK_OP_MOD >> 2: { - /* Don't allow a zero divisor. Restrict both v1 and - * v2 to positive values to avoid compiler specific - * behavior. - */ - if (v1 < 1 || v2 < 1) { - goto skip_fastint; - } - v3 = v1 % v2; - DUK_ASSERT(v3 >= 0); - DUK_ASSERT(v3 < v2); - DUK_ASSERT(v1 - (v1 / v2) * v2 == v3); - break; - } - default: { - /* Possible with DUK_OP_EXP. */ - goto skip_fastint; - } - } - - v3_hi = (duk_int32_t) (v3 >> 32); - if (DUK_LIKELY(v3_hi >= DUK_I64_CONSTANT(-0x8000) && v3_hi <= DUK_I64_CONSTANT(0x7fff))) { - tv_z = thr->valstack_bottom + idx_z; - DUK_TVAL_SET_FASTINT_UPDREF(thr, tv_z, v3); /* side effects */ - return; - } - /* fall through if overflow etc */ - } -skip_fastint: -#endif /* DUK_USE_FASTINT */ - - if (DUK_TVAL_IS_NUMBER(tv_x) && DUK_TVAL_IS_NUMBER(tv_y)) { - /* fast path */ - d1 = DUK_TVAL_GET_NUMBER(tv_x); - d2 = DUK_TVAL_GET_NUMBER(tv_y); - } else { - duk_push_tval(thr, tv_x); - duk_push_tval(thr, tv_y); - d1 = duk_to_number_m2(thr); /* side effects */ - d2 = duk_to_number_m1(thr); - DUK_ASSERT(duk_is_number(thr, -2)); - DUK_ASSERT(duk_is_number(thr, -1)); - DUK_ASSERT_DOUBLE_IS_NORMALIZED(d1); - DUK_ASSERT_DOUBLE_IS_NORMALIZED(d2); - duk_pop_2_unsafe(thr); - } - - switch (opcode_shifted) { - case DUK_OP_SUB >> 2: { - du.d = d1 - d2; - break; - } - case DUK_OP_MUL >> 2: { - du.d = d1 * d2; - break; - } - case DUK_OP_DIV >> 2: { - /* Division-by-zero is undefined behavior, so - * rely on a helper. - */ - du.d = duk_double_div(d1, d2); - break; - } - case DUK_OP_MOD >> 2: { - du.d = duk__compute_mod(d1, d2); - break; - } -#if defined(DUK_USE_ES7_EXP_OPERATOR) - case DUK_OP_EXP >> 2: { - du.d = duk__compute_exp(d1, d2); - break; - } -#endif - default: { - DUK_UNREACHABLE(); - du.d = DUK_DOUBLE_NAN; /* should not happen */ - break; - } - } - -#if defined(DUK_USE_EXEC_PREFER_SIZE) - duk_push_number(thr, du.d); /* will NaN normalize result */ - duk_replace(thr, (duk_idx_t) idx_z); -#else /* DUK_USE_EXEC_PREFER_SIZE */ - /* important to use normalized NaN with 8-byte tagged types */ - DUK_DBLUNION_NORMALIZE_NAN_CHECK(&du); - DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du)); - tv_z = thr->valstack_bottom + idx_z; - DUK_TVAL_SET_NUMBER_UPDREF(thr, tv_z, du.d); /* side effects */ -#endif /* DUK_USE_EXEC_PREFER_SIZE */ -} - -DUK_LOCAL DUK_EXEC_ALWAYS_INLINE_PERF void duk__vm_bitwise_binary_op(duk_hthread *thr, - duk_tval *tv_x, - duk_tval *tv_y, - duk_small_uint_fast_t idx_z, - duk_small_uint_fast_t opcode) { - /* - * Binary bitwise operations use different coercions (ToInt32, ToUint32) - * depending on the operation. We coerce the arguments first using - * ToInt32(), and then cast to an 32-bit value if necessary. Note that - * such casts must be correct even if there is no native 32-bit type - * (e.g., duk_int32_t and duk_uint32_t are 64-bit). - * - * E5 Sections 11.10, 11.7.1, 11.7.2, 11.7.3 - */ - - duk_int32_t i1, i2, i3; - duk_uint32_t u1, u2, u3; -#if defined(DUK_USE_FASTINT) - duk_int64_t fi3; -#else - duk_double_t d3; -#endif - duk_small_uint_fast_t opcode_shifted; -#if defined(DUK_USE_FASTINT) || !defined(DUK_USE_EXEC_PREFER_SIZE) - duk_tval *tv_z; -#endif - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(tv_x != NULL); /* may be reg or const */ - DUK_ASSERT(tv_y != NULL); /* may be reg or const */ - DUK_ASSERT_DISABLE(idx_z >= 0); /* unsigned */ - DUK_ASSERT((duk_uint_t) idx_z < (duk_uint_t) duk_get_top(thr)); - - opcode_shifted = opcode >> 2; /* Get base opcode without reg/const modifiers. */ - -#if defined(DUK_USE_FASTINT) - if (DUK_TVAL_IS_FASTINT(tv_x) && DUK_TVAL_IS_FASTINT(tv_y)) { - i1 = (duk_int32_t) DUK_TVAL_GET_FASTINT_I32(tv_x); - i2 = (duk_int32_t) DUK_TVAL_GET_FASTINT_I32(tv_y); - } else -#endif /* DUK_USE_FASTINT */ - { - duk_push_tval(thr, tv_x); - duk_push_tval(thr, tv_y); - i1 = duk_to_int32(thr, -2); - i2 = duk_to_int32(thr, -1); - duk_pop_2_unsafe(thr); - } - - switch (opcode_shifted) { - case DUK_OP_BAND >> 2: { - i3 = i1 & i2; - break; - } - case DUK_OP_BOR >> 2: { - i3 = i1 | i2; - break; - } - case DUK_OP_BXOR >> 2: { - i3 = i1 ^ i2; - break; - } - case DUK_OP_BASL >> 2: { - /* Signed shift, named "arithmetic" (asl) because the result - * is signed, e.g. 4294967295 << 1 -> -2. Note that result - * must be masked. - */ - - u2 = ((duk_uint32_t) i2) & 0xffffffffUL; - i3 = (duk_int32_t) (((duk_uint32_t) i1) << (u2 & 0x1fUL)); /* E5 Section 11.7.1, steps 7 and 8 */ - i3 = i3 & ((duk_int32_t) 0xffffffffUL); /* Note: left shift, should mask */ - break; - } - case DUK_OP_BASR >> 2: { - /* signed shift */ - - u2 = ((duk_uint32_t) i2) & 0xffffffffUL; - i3 = i1 >> (u2 & 0x1fUL); /* E5 Section 11.7.2, steps 7 and 8 */ - break; - } - case DUK_OP_BLSR >> 2: { - /* unsigned shift */ - - u1 = ((duk_uint32_t) i1) & 0xffffffffUL; - u2 = ((duk_uint32_t) i2) & 0xffffffffUL; - - /* special result value handling */ - u3 = u1 >> (u2 & 0x1fUL); /* E5 Section 11.7.2, steps 7 and 8 */ -#if defined(DUK_USE_FASTINT) - fi3 = (duk_int64_t) u3; - goto fastint_result_set; -#else - d3 = (duk_double_t) u3; - goto result_set; -#endif - } - default: { - DUK_UNREACHABLE(); - i3 = 0; /* should not happen */ - break; - } - } - -#if defined(DUK_USE_FASTINT) - /* Result is always fastint compatible. */ - /* XXX: Set 32-bit result (but must then handle signed and - * unsigned results separately). - */ - fi3 = (duk_int64_t) i3; - -fastint_result_set: - tv_z = thr->valstack_bottom + idx_z; - DUK_TVAL_SET_FASTINT_UPDREF(thr, tv_z, fi3); /* side effects */ -#else /* DUK_USE_FASTINT */ - d3 = (duk_double_t) i3; - -result_set: - DUK_ASSERT(!DUK_ISNAN(d3)); /* 'd3' is never NaN, so no need to normalize */ - DUK_ASSERT_DOUBLE_IS_NORMALIZED(d3); /* always normalized */ - -#if defined(DUK_USE_EXEC_PREFER_SIZE) - duk_push_number(thr, d3); /* would NaN normalize result, but unnecessary */ - duk_replace(thr, (duk_idx_t) idx_z); -#else /* DUK_USE_EXEC_PREFER_SIZE */ - tv_z = thr->valstack_bottom + idx_z; - DUK_TVAL_SET_NUMBER_UPDREF(thr, tv_z, d3); /* side effects */ -#endif /* DUK_USE_EXEC_PREFER_SIZE */ -#endif /* DUK_USE_FASTINT */ -} - -/* In-place unary operation. */ -DUK_LOCAL DUK_EXEC_ALWAYS_INLINE_PERF void duk__vm_arith_unary_op(duk_hthread *thr, - duk_uint_fast_t idx_src, - duk_uint_fast_t idx_dst, - duk_small_uint_fast_t opcode) { - /* - * Arithmetic operations other than '+' have number-only semantics - * and are implemented here. The separate switch-case here means a - * "double dispatch" of the arithmetic opcode, but saves code space. - * - * E5 Sections 11.5, 11.5.1, 11.5.2, 11.5.3, 11.6, 11.6.1, 11.6.2, 11.6.3. - */ - - duk_tval *tv; - duk_double_t d1; - duk_double_union du; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(opcode == DUK_OP_UNM || opcode == DUK_OP_UNP); - DUK_ASSERT_DISABLE(idx_src >= 0); - DUK_ASSERT_DISABLE(idx_dst >= 0); - - tv = DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_src); - -#if defined(DUK_USE_FASTINT) - if (DUK_TVAL_IS_FASTINT(tv)) { - duk_int64_t v1, v2; - - v1 = DUK_TVAL_GET_FASTINT(tv); - if (opcode == DUK_OP_UNM) { - /* The smallest fastint is no longer 48-bit when - * negated. Positive zero becames negative zero - * (cannot be represented) when negated. - */ - if (DUK_LIKELY(v1 != DUK_FASTINT_MIN && v1 != 0)) { - v2 = -v1; - tv = DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_dst); - DUK_TVAL_SET_FASTINT_UPDREF(thr, tv, v2); - return; - } - } else { - /* ToNumber() for a fastint is a no-op. */ - DUK_ASSERT(opcode == DUK_OP_UNP); - v2 = v1; - tv = DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_dst); - DUK_TVAL_SET_FASTINT_UPDREF(thr, tv, v2); - return; - } - /* fall through if overflow etc */ - } -#endif /* DUK_USE_FASTINT */ - - if (DUK_TVAL_IS_NUMBER(tv)) { - d1 = DUK_TVAL_GET_NUMBER(tv); - } else { - d1 = duk_to_number_tval(thr, tv); /* side effects */ - } - - if (opcode == DUK_OP_UNP) { - /* ToNumber() for a double is a no-op, but unary plus is - * used to force a fastint check so do that here. - */ - du.d = d1; - DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du)); -#if defined(DUK_USE_FASTINT) - tv = DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_dst); - DUK_TVAL_SET_NUMBER_CHKFAST_UPDREF(thr, tv, du.d); /* always 'fast', i.e. inlined */ - return; -#endif - } else { - DUK_ASSERT(opcode == DUK_OP_UNM); - du.d = -d1; - DUK_DBLUNION_NORMALIZE_NAN_CHECK(&du); /* mandatory if du.d is a NaN */ - DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du)); - } - - /* XXX: size optimize: push+replace? */ - tv = DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_dst); - DUK_TVAL_SET_NUMBER_UPDREF(thr, tv, du.d); -} - -DUK_LOCAL DUK_EXEC_ALWAYS_INLINE_PERF void duk__vm_bitwise_not(duk_hthread *thr, duk_uint_fast_t idx_src, duk_uint_fast_t idx_dst) { - /* - * E5 Section 11.4.8 - */ - - duk_tval *tv; - duk_int32_t i1, i2; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT_DISABLE(idx_src >= 0); - DUK_ASSERT_DISABLE(idx_dst >= 0); - DUK_ASSERT((duk_uint_t) idx_src < (duk_uint_t) duk_get_top(thr)); - DUK_ASSERT((duk_uint_t) idx_dst < (duk_uint_t) duk_get_top(thr)); - - tv = DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_src); - -#if defined(DUK_USE_FASTINT) - if (DUK_TVAL_IS_FASTINT(tv)) { - i1 = (duk_int32_t) DUK_TVAL_GET_FASTINT_I32(tv); - } else -#endif /* DUK_USE_FASTINT */ - { - duk_push_tval(thr, tv); - i1 = duk_to_int32(thr, -1); /* side effects */ - duk_pop_unsafe(thr); - } - - /* Result is always fastint compatible. */ - i2 = ~i1; - tv = DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_dst); - DUK_TVAL_SET_I32_UPDREF(thr, tv, i2); /* side effects */ -} - -DUK_LOCAL DUK_EXEC_ALWAYS_INLINE_PERF void duk__vm_logical_not(duk_hthread *thr, duk_uint_fast_t idx_src, duk_uint_fast_t idx_dst) { - /* - * E5 Section 11.4.9 - */ - - duk_tval *tv; - duk_bool_t res; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT_DISABLE(idx_src >= 0); - DUK_ASSERT_DISABLE(idx_dst >= 0); - DUK_ASSERT((duk_uint_t) idx_src < (duk_uint_t) duk_get_top(thr)); - DUK_ASSERT((duk_uint_t) idx_dst < (duk_uint_t) duk_get_top(thr)); - - /* ToBoolean() does not require any operations with side effects so - * we can do it efficiently. For footprint it would be better to use - * duk_js_toboolean() and then push+replace to the result slot. - */ - tv = DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_src); - res = duk_js_toboolean(tv); /* does not modify 'tv' */ - DUK_ASSERT(res == 0 || res == 1); - res ^= 1; - tv = DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_dst); - /* XXX: size optimize: push+replace? */ - DUK_TVAL_SET_BOOLEAN_UPDREF(thr, tv, res); /* side effects */ -} - -/* XXX: size optimized variant */ -DUK_LOCAL DUK_EXEC_ALWAYS_INLINE_PERF void duk__prepost_incdec_reg_helper(duk_hthread *thr, - duk_tval *tv_dst, - duk_tval *tv_src, - duk_small_uint_t op) { - duk_double_t x, y, z; - - /* Two lowest bits of opcode are used to distinguish - * variants. Bit 0 = inc(0)/dec(1), bit 1 = pre(0)/post(1). - */ - DUK_ASSERT((DUK_OP_PREINCR & 0x03) == 0x00); - DUK_ASSERT((DUK_OP_PREDECR & 0x03) == 0x01); - DUK_ASSERT((DUK_OP_POSTINCR & 0x03) == 0x02); - DUK_ASSERT((DUK_OP_POSTDECR & 0x03) == 0x03); - -#if defined(DUK_USE_FASTINT) - if (DUK_TVAL_IS_FASTINT(tv_src)) { - duk_int64_t x_fi, y_fi, z_fi; - x_fi = DUK_TVAL_GET_FASTINT(tv_src); - if (op & 0x01) { - if (DUK_UNLIKELY(x_fi == DUK_FASTINT_MIN)) { - goto skip_fastint; - } - y_fi = x_fi - 1; - } else { - if (DUK_UNLIKELY(x_fi == DUK_FASTINT_MAX)) { - goto skip_fastint; - } - y_fi = x_fi + 1; - } - - DUK_TVAL_SET_FASTINT(tv_src, y_fi); /* no need for refcount update */ - - z_fi = (op & 0x02) ? x_fi : y_fi; - DUK_TVAL_SET_FASTINT_UPDREF(thr, tv_dst, z_fi); /* side effects */ - return; - } -skip_fastint: -#endif - if (DUK_TVAL_IS_NUMBER(tv_src)) { - /* Fast path for the case where the register - * is a number (e.g. loop counter). - */ - - x = DUK_TVAL_GET_NUMBER(tv_src); - if (op & 0x01) { - y = x - 1.0; - } else { - y = x + 1.0; - } - - DUK_TVAL_SET_NUMBER(tv_src, y); /* no need for refcount update */ - } else { - /* Preserve duk_tval pointer(s) across a potential valstack - * resize by converting them into offsets temporarily. - */ - duk_idx_t bc; - duk_size_t off_dst; - - off_dst = (duk_size_t) ((duk_uint8_t *) tv_dst - (duk_uint8_t *) thr->valstack_bottom); - bc = (duk_idx_t) (tv_src - thr->valstack_bottom); /* XXX: pass index explicitly? */ - tv_src = NULL; /* no longer referenced */ - - x = duk_to_number(thr, bc); - if (op & 0x01) { - y = x - 1.0; - } else { - y = x + 1.0; - } - - duk_push_number(thr, y); - duk_replace(thr, bc); - - tv_dst = (duk_tval *) (void *) (((duk_uint8_t *) thr->valstack_bottom) + off_dst); - } - - z = (op & 0x02) ? x : y; - DUK_TVAL_SET_NUMBER_UPDREF(thr, tv_dst, z); /* side effects */ -} - -DUK_LOCAL DUK_EXEC_ALWAYS_INLINE_PERF void duk__prepost_incdec_var_helper(duk_hthread *thr, - duk_small_uint_t idx_dst, - duk_tval *tv_id, - duk_small_uint_t op, - duk_small_uint_t is_strict) { - duk_activation *act; - duk_double_t x, y; - duk_hstring *name; - - /* XXX: The pre/post inc/dec for an identifier lookup is - * missing the important fast path where the identifier - * has a storage location e.g. in a scope object so that - * it can be updated in-place. In particular, the case - * where the identifier has a storage location AND the - * previous value is a number should be optimized because - * it's side effect free. - */ - - /* Two lowest bits of opcode are used to distinguish - * variants. Bit 0 = inc(0)/dec(1), bit 1 = pre(0)/post(1). - */ - DUK_ASSERT((DUK_OP_PREINCV & 0x03) == 0x00); - DUK_ASSERT((DUK_OP_PREDECV & 0x03) == 0x01); - DUK_ASSERT((DUK_OP_POSTINCV & 0x03) == 0x02); - DUK_ASSERT((DUK_OP_POSTDECV & 0x03) == 0x03); - - DUK_ASSERT(DUK_TVAL_IS_STRING(tv_id)); - name = DUK_TVAL_GET_STRING(tv_id); - DUK_ASSERT(name != NULL); - act = thr->callstack_curr; - (void) duk_js_getvar_activation(thr, act, name, 1 /*throw*/); /* -> [ ... val this ] */ - - /* XXX: Fastint fast path would be useful here. Also fastints - * now lose their fastint status in current handling which is - * not intuitive. - */ - - x = duk_to_number_m2(thr); - if (op & 0x01) { - y = x - 1.0; - } else { - y = x + 1.0; - } - - /* [... x this] */ - - if (op & 0x02) { - duk_push_number(thr, y); /* -> [ ... x this y ] */ - DUK_ASSERT(act == thr->callstack_curr); - duk_js_putvar_activation(thr, act, name, DUK_GET_TVAL_NEGIDX(thr, -1), is_strict); - duk_pop_2_unsafe(thr); /* -> [ ... x ] */ - } else { - duk_pop_2_unsafe(thr); /* -> [ ... ] */ - duk_push_number(thr, y); /* -> [ ... y ] */ - DUK_ASSERT(act == thr->callstack_curr); - duk_js_putvar_activation(thr, act, name, DUK_GET_TVAL_NEGIDX(thr, -1), is_strict); - } - -#if defined(DUK_USE_EXEC_PREFER_SIZE) - duk_replace(thr, (duk_idx_t) idx_dst); -#else /* DUK_USE_EXEC_PREFER_SIZE */ - DUK__REPLACE_TO_TVPTR(thr, DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_dst)); -#endif /* DUK_USE_EXEC_PREFER_SIZE */ -} - -/* - * Longjmp and other control flow transfer for the bytecode executor. - * - * The longjmp handler can handle all longjmp types: error, yield, and - * resume (pseudotypes are never actually thrown). - * - * Error policy for longjmp: should not ordinarily throw errors; if errors - * occur (e.g. due to out-of-memory) they bubble outwards rather than being - * handled recursively. - */ - -#define DUK__LONGJMP_RESTART 0 /* state updated, restart bytecode execution */ -#define DUK__LONGJMP_RETHROW 1 /* exit bytecode executor by rethrowing an error to caller */ - -#define DUK__RETHAND_RESTART 0 /* state updated, restart bytecode execution */ -#define DUK__RETHAND_FINISHED 1 /* exit bytecode execution with return value */ - -/* XXX: optimize reconfig valstack operations so that resize, clamp, and setting - * top are combined into one pass. - */ - -/* Reconfigure value stack for return to an ECMAScript function at - * callstack top (caller unwinds). - */ -DUK_LOCAL void duk__reconfig_valstack_ecma_return(duk_hthread *thr) { - duk_activation *act; - duk_hcompfunc *h_func; - duk_idx_t clamp_top; - - DUK_ASSERT(thr != NULL); - act = thr->callstack_curr; - DUK_ASSERT(act != NULL); - DUK_ASSERT(DUK_ACT_GET_FUNC(act) != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(act))); - - /* Clamp so that values at 'clamp_top' and above are wiped and won't - * retain reachable garbage. Then extend to 'nregs' because we're - * returning to an ECMAScript function. - */ - - h_func = (duk_hcompfunc *) DUK_ACT_GET_FUNC(act); - - thr->valstack_bottom = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + act->bottom_byteoff); - DUK_ASSERT(act->retval_byteoff >= act->bottom_byteoff); - clamp_top = - (duk_idx_t) ((act->retval_byteoff - act->bottom_byteoff + sizeof(duk_tval)) / sizeof(duk_tval)); /* +1 = one retval */ - duk_set_top_and_wipe(thr, h_func->nregs, clamp_top); - - DUK_ASSERT((duk_uint8_t *) thr->valstack_end >= (duk_uint8_t *) thr->valstack + act->reserve_byteoff); - thr->valstack_end = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + act->reserve_byteoff); - - /* XXX: a best effort shrink check would be OK here */ -} - -/* Reconfigure value stack for an ECMAScript catcher. Use topmost catcher - * in 'act'. - */ -DUK_LOCAL void duk__reconfig_valstack_ecma_catcher(duk_hthread *thr, duk_activation *act) { - duk_catcher *cat; - duk_hcompfunc *h_func; - duk_size_t idx_bottom; - duk_idx_t clamp_top; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(act != NULL); - DUK_ASSERT(DUK_ACT_GET_FUNC(act) != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(act))); - cat = act->cat; - DUK_ASSERT(cat != NULL); - - h_func = (duk_hcompfunc *) DUK_ACT_GET_FUNC(act); - - thr->valstack_bottom = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + act->bottom_byteoff); - idx_bottom = (duk_size_t) (thr->valstack_bottom - thr->valstack); - DUK_ASSERT(cat->idx_base >= idx_bottom); - clamp_top = (duk_idx_t) (cat->idx_base - idx_bottom + 2); /* +2 = catcher value, catcher lj_type */ - duk_set_top_and_wipe(thr, h_func->nregs, clamp_top); - - DUK_ASSERT((duk_uint8_t *) thr->valstack_end >= (duk_uint8_t *) thr->valstack + act->reserve_byteoff); - thr->valstack_end = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + act->reserve_byteoff); - - /* XXX: a best effort shrink check would be OK here */ -} - -/* Set catcher regs: idx_base+0 = value, idx_base+1 = lj_type. - * No side effects. - */ -DUK_LOCAL void duk__set_catcher_regs_norz(duk_hthread *thr, duk_catcher *cat, duk_tval *tv_val_unstable, duk_small_uint_t lj_type) { - duk_tval *tv1; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(tv_val_unstable != NULL); - - tv1 = thr->valstack + cat->idx_base; - DUK_ASSERT(tv1 < thr->valstack_top); - DUK_TVAL_SET_TVAL_UPDREF_NORZ(thr, tv1, tv_val_unstable); - - tv1++; - DUK_ASSERT(tv1 == thr->valstack + cat->idx_base + 1); - DUK_ASSERT(tv1 < thr->valstack_top); - DUK_TVAL_SET_U32_UPDREF_NORZ(thr, tv1, (duk_uint32_t) lj_type); -} - -DUK_LOCAL void duk__handle_catch_part1(duk_hthread *thr, - duk_tval *tv_val_unstable, - duk_small_uint_t lj_type, - volatile duk_bool_t *out_delayed_catch_setup) { - duk_activation *act; - duk_catcher *cat; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(tv_val_unstable != NULL); - - act = thr->callstack_curr; - DUK_ASSERT(act != NULL); - DUK_DD(DUK_DDPRINT("handle catch, part 1; act=%!A, cat=%!C", act, act->cat)); - - DUK_ASSERT(act->cat != NULL); - DUK_ASSERT(DUK_CAT_GET_TYPE(act->cat) == DUK_CAT_TYPE_TCF); - - /* The part1/part2 split could also be made here at the very top - * of catch handling. Value stack would be reconfigured inside - * part2's protection. Value stack reconfiguration should be free - * of allocs, however. - */ - - duk__set_catcher_regs_norz(thr, act->cat, tv_val_unstable, lj_type); - - DUK_ASSERT(thr->callstack_top >= 1); - DUK_ASSERT(thr->callstack_curr != NULL); - DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr))); - - DUK_ASSERT(thr->callstack_top >= 1); - DUK_ASSERT(act == thr->callstack_curr); - DUK_ASSERT(act != NULL); - duk__reconfig_valstack_ecma_catcher(thr, act); - - DUK_ASSERT(thr->callstack_top >= 1); - DUK_ASSERT(act == thr->callstack_curr); - DUK_ASSERT(act != NULL); - cat = act->cat; - DUK_ASSERT(cat != NULL); - - act->curr_pc = cat->pc_base + 0; /* +0 = catch */ - - /* - * If the catch block has an automatic catch variable binding, - * we need to create a lexical environment for it which requires - * allocations. Move out of "error handling state" before the - * allocations to avoid e.g. out-of-memory errors (leading to - * GH-2022 or similar). - */ - - if (DUK_CAT_HAS_CATCH_BINDING_ENABLED(cat)) { - DUK_DDD(DUK_DDDPRINT("catcher has an automatic catch binding, handle in part 2")); - *out_delayed_catch_setup = 1; - } else { - DUK_DDD(DUK_DDDPRINT("catcher has no catch binding")); - } - - DUK_CAT_CLEAR_CATCH_ENABLED(cat); -} - -DUK_LOCAL void duk__handle_catch_part2(duk_hthread *thr) { - duk_activation *act; - duk_catcher *cat; - duk_hdecenv *new_env; - - DUK_ASSERT(thr != NULL); - - act = thr->callstack_curr; - DUK_ASSERT(act != NULL); - DUK_DD(DUK_DDPRINT("handle catch, part 2; act=%!A, cat=%!C", act, act->cat)); - - DUK_ASSERT(act->cat != NULL); - cat = act->cat; - DUK_ASSERT(cat != NULL); - DUK_ASSERT(DUK_CAT_GET_TYPE(cat) == DUK_CAT_TYPE_TCF); - DUK_ASSERT(DUK_CAT_HAS_CATCH_BINDING_ENABLED(cat)); - DUK_ASSERT(thr->valstack + cat->idx_base < thr->valstack_top); - - /* - * Create lexical environment for the catch clause, containing - * a binding for the caught value. - * - * The binding is mutable (= writable) but not deletable. - * Step 4 for the catch production in E5 Section 12.14; - * no value is given for CreateMutableBinding 'D' argument, - * which implies the binding is not deletable. - */ - - if (act->lex_env == NULL) { - DUK_ASSERT(act->var_env == NULL); - DUK_DDD(DUK_DDDPRINT("delayed environment initialization")); - - duk_js_init_activation_environment_records_delayed(thr, act); - DUK_ASSERT(act == thr->callstack_curr); - DUK_ASSERT(act != NULL); - } - DUK_ASSERT(act->lex_env != NULL); - DUK_ASSERT(act->var_env != NULL); - DUK_ASSERT(DUK_ACT_GET_FUNC(act) != NULL); - - new_env = duk_hdecenv_alloc(thr, DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV)); - DUK_ASSERT(new_env != NULL); - duk_push_hobject(thr, (duk_hobject *) new_env); - DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) new_env) == NULL); - DUK_DDD(DUK_DDDPRINT("new_env allocated: %!iO", (duk_heaphdr *) new_env)); - - /* Note: currently the catch binding is handled without a register - * binding because we don't support dynamic register bindings (they - * must be fixed for an entire function). So, there is no need to - * record regbases etc. - */ - - /* [ ...env ] */ - - DUK_ASSERT(cat->h_varname != NULL); - duk_push_hstring(thr, cat->h_varname); - DUK_ASSERT(thr->valstack + cat->idx_base < thr->valstack_top); - duk_push_tval(thr, thr->valstack + cat->idx_base); - duk_xdef_prop(thr, -3, DUK_PROPDESC_FLAGS_W); /* writable, not configurable */ - - /* [ ... env ] */ - - DUK_ASSERT(act == thr->callstack_curr); - DUK_ASSERT(act != NULL); - DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) new_env, act->lex_env); - act->lex_env = (duk_hobject *) new_env; - DUK_HOBJECT_INCREF(thr, (duk_hobject *) new_env); /* reachable through activation */ - /* Net refcount change to act->lex_env is 0: incref for new_env's - * prototype, decref for act->lex_env overwrite. - */ - - DUK_CAT_SET_LEXENV_ACTIVE(cat); - - duk_pop_unsafe(thr); - - DUK_DDD(DUK_DDDPRINT("new_env finished: %!iO", (duk_heaphdr *) new_env)); -} - -DUK_LOCAL void duk__handle_finally(duk_hthread *thr, duk_tval *tv_val_unstable, duk_small_uint_t lj_type) { - duk_activation *act; - duk_catcher *cat; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(tv_val_unstable != NULL); - - act = thr->callstack_curr; - DUK_ASSERT(act != NULL); - DUK_ASSERT(act->cat != NULL); - DUK_ASSERT(DUK_CAT_GET_TYPE(act->cat) == DUK_CAT_TYPE_TCF); - - duk__set_catcher_regs_norz(thr, act->cat, tv_val_unstable, lj_type); - - DUK_ASSERT(thr->callstack_top >= 1); - DUK_ASSERT(thr->callstack_curr != NULL); - DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr))); - - DUK_ASSERT(thr->callstack_top >= 1); - DUK_ASSERT(act == thr->callstack_curr); - DUK_ASSERT(act != NULL); - duk__reconfig_valstack_ecma_catcher(thr, act); - - DUK_ASSERT(thr->callstack_top >= 1); - DUK_ASSERT(act == thr->callstack_curr); - DUK_ASSERT(act != NULL); - cat = act->cat; - DUK_ASSERT(cat != NULL); - - act->curr_pc = cat->pc_base + 1; /* +1 = finally */ - - DUK_CAT_CLEAR_FINALLY_ENABLED(cat); -} - -DUK_LOCAL void duk__handle_label(duk_hthread *thr, duk_small_uint_t lj_type) { - duk_activation *act; - duk_catcher *cat; - - DUK_ASSERT(thr != NULL); - - DUK_ASSERT(thr->callstack_top >= 1); - act = thr->callstack_curr; - DUK_ASSERT(act != NULL); - DUK_ASSERT(DUK_ACT_GET_FUNC(act) != NULL); - DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(DUK_ACT_GET_FUNC(act))); - - /* +0 = break, +1 = continue */ - cat = act->cat; - DUK_ASSERT(cat != NULL); - DUK_ASSERT(DUK_CAT_GET_TYPE(cat) == DUK_CAT_TYPE_LABEL); - - act->curr_pc = cat->pc_base + (lj_type == DUK_LJ_TYPE_CONTINUE ? 1 : 0); - - /* valstack should not need changes */ -#if defined(DUK_USE_ASSERTIONS) - DUK_ASSERT(thr->callstack_top >= 1); - DUK_ASSERT(act == thr->callstack_curr); - DUK_ASSERT(act != NULL); - DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) == - (duk_size_t) ((duk_hcompfunc *) DUK_ACT_GET_FUNC(act))->nregs); -#endif -} - -/* Called for handling both a longjmp() with type DUK_LJ_TYPE_YIELD and - * when a RETURN opcode terminates a thread and yields to the resumer. - * Caller unwinds so that top of callstack is the activation we return to. - */ -#if defined(DUK_USE_COROUTINE_SUPPORT) -DUK_LOCAL void duk__handle_yield(duk_hthread *thr, duk_hthread *resumer, duk_tval *tv_val_unstable) { - duk_activation *act_resumer; - duk_tval *tv1; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(resumer != NULL); - DUK_ASSERT(tv_val_unstable != NULL); - act_resumer = resumer->callstack_curr; - DUK_ASSERT(act_resumer != NULL); - DUK_ASSERT(DUK_ACT_GET_FUNC(act_resumer) != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(act_resumer))); /* resume caller must be an ECMAScript func */ - - tv1 = (duk_tval *) (void *) ((duk_uint8_t *) resumer->valstack + - act_resumer->retval_byteoff); /* return value from Duktape.Thread.resume() */ - DUK_TVAL_SET_TVAL_UPDREF(thr, tv1, tv_val_unstable); /* side effects */ /* XXX: avoid side effects */ - - duk__reconfig_valstack_ecma_return(resumer); - - /* caller must change active thread, and set thr->resumer to NULL */ -} -#endif /* DUK_USE_COROUTINE_SUPPORT */ - -DUK_LOCAL duk_small_uint_t duk__handle_longjmp(duk_hthread *thr, - duk_activation *entry_act, - volatile duk_bool_t *out_delayed_catch_setup) { - duk_small_uint_t retval = DUK__LONGJMP_RESTART; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(entry_act != NULL); - - /* 'thr' is the current thread, as no-one resumes except us and we - * switch 'thr' in that case. - */ - DUK_ASSERT(thr == thr->heap->curr_thread); - - /* - * (Re)try handling the longjmp. - * - * A longjmp handler may convert the longjmp to a different type and - * "virtually" rethrow by goto'ing to 'check_longjmp'. Before the goto, - * the following must be updated: - * - the heap 'lj' state - * - 'thr' must reflect the "throwing" thread - */ - -check_longjmp: - - DUK_DD(DUK_DDPRINT("handling longjmp: type=%ld, value1=%!T, value2=%!T, iserror=%ld, top=%ld", - (long) thr->heap->lj.type, - (duk_tval *) &thr->heap->lj.value1, - (duk_tval *) &thr->heap->lj.value2, - (long) thr->heap->lj.iserror, - (long) duk_get_top(thr))); - - switch (thr->heap->lj.type) { -#if defined(DUK_USE_COROUTINE_SUPPORT) - case DUK_LJ_TYPE_RESUME: { - /* - * Note: lj.value1 is 'value', lj.value2 is 'resumee'. - * This differs from YIELD. - */ - - duk_tval *tv; - duk_tval *tv2; - duk_hthread *resumee; - - /* duk_bi_duk_object_yield() and duk_bi_duk_object_resume() ensure all of these are met */ - - DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING); /* unchanged by Duktape.Thread.resume() */ - DUK_ASSERT(thr->callstack_top >= 2); /* ECMAScript activation + Duktape.Thread.resume() activation */ - DUK_ASSERT(thr->callstack_curr != NULL); - DUK_ASSERT(thr->callstack_curr->parent != NULL); - DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL && - DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr)) && - ((duk_hnatfunc *) DUK_ACT_GET_FUNC(thr->callstack_curr))->func == duk_bi_thread_resume); - - tv = &thr->heap->lj.value2; /* resumee */ - DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv)); - DUK_ASSERT(DUK_TVAL_GET_OBJECT(tv) != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_THREAD(DUK_TVAL_GET_OBJECT(tv))); - resumee = (duk_hthread *) DUK_TVAL_GET_OBJECT(tv); - - DUK_ASSERT(resumee != NULL); - DUK_ASSERT(resumee->resumer == NULL); - DUK_ASSERT(resumee->state == DUK_HTHREAD_STATE_INACTIVE || - resumee->state == DUK_HTHREAD_STATE_YIELDED); /* checked by Duktape.Thread.resume() */ - DUK_ASSERT(resumee->state != DUK_HTHREAD_STATE_YIELDED || - resumee->callstack_top >= 2); /* YIELDED: ECMAScript activation + Duktape.Thread.yield() activation */ - DUK_ASSERT(resumee->state != DUK_HTHREAD_STATE_YIELDED || - (DUK_ACT_GET_FUNC(resumee->callstack_curr) != NULL && - DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(resumee->callstack_curr)) && - ((duk_hnatfunc *) DUK_ACT_GET_FUNC(resumee->callstack_curr))->func == duk_bi_thread_yield)); - DUK_ASSERT(resumee->state != DUK_HTHREAD_STATE_INACTIVE || - resumee->callstack_top == 0); /* INACTIVE: no activation, single function value on valstack */ - - if (thr->heap->lj.iserror) { - /* - * Throw the error in the resumed thread's context; the - * error value is pushed onto the resumee valstack. - * - * Note: the callstack of the target may empty in this case - * too (i.e. the target thread has never been resumed). The - * value stack will contain the initial function in that case, - * which we simply ignore. - */ - - DUK_ASSERT(resumee->resumer == NULL); - resumee->resumer = thr; - DUK_HTHREAD_INCREF(thr, thr); - resumee->state = DUK_HTHREAD_STATE_RUNNING; - thr->state = DUK_HTHREAD_STATE_RESUMED; - DUK_HEAP_SWITCH_THREAD(thr->heap, resumee); - thr = resumee; - - thr->heap->lj.type = DUK_LJ_TYPE_THROW; - - /* thr->heap->lj.value1 is already the value to throw */ - /* thr->heap->lj.value2 is 'thread', will be wiped out at the end */ - - DUK_ASSERT(thr->heap->lj.iserror); /* already set */ - - DUK_DD(DUK_DDPRINT("-> resume with an error, converted to a throw in the resumee, propagate")); - goto check_longjmp; - } else if (resumee->state == DUK_HTHREAD_STATE_YIELDED) { - /* Unwind previous Duktape.Thread.yield() call. The - * activation remaining must always be an ECMAScript - * call now (yield() accepts calls from ECMAScript - * only). - */ - duk_activation *act_resumee; - - DUK_ASSERT(resumee->callstack_top >= 2); - act_resumee = resumee->callstack_curr; /* Duktape.Thread.yield() */ - DUK_ASSERT(act_resumee != NULL); - act_resumee = act_resumee->parent; /* ECMAScript call site for yield() */ - DUK_ASSERT(act_resumee != NULL); - - tv = (duk_tval *) (void *) ((duk_uint8_t *) resumee->valstack + - act_resumee->retval_byteoff); /* return value from Duktape.Thread.yield() */ - DUK_ASSERT(tv >= resumee->valstack && tv < resumee->valstack_top); - tv2 = &thr->heap->lj.value1; - DUK_TVAL_SET_TVAL_UPDREF(thr, tv, tv2); /* side effects */ /* XXX: avoid side effects */ - - duk_hthread_activation_unwind_norz(resumee); /* unwind to 'yield' caller */ - /* no need to unwind catch stack */ - - duk__reconfig_valstack_ecma_return(resumee); - - DUK_ASSERT(resumee->resumer == NULL); - resumee->resumer = thr; - DUK_HTHREAD_INCREF(thr, thr); - resumee->state = DUK_HTHREAD_STATE_RUNNING; - thr->state = DUK_HTHREAD_STATE_RESUMED; - DUK_HEAP_SWITCH_THREAD(thr->heap, resumee); -#if 0 - thr = resumee; /* not needed, as we exit right away */ -#endif - DUK_DD(DUK_DDPRINT("-> resume with a value, restart execution in resumee")); - retval = DUK__LONGJMP_RESTART; - goto wipe_and_return; - } else { - /* Initial resume call. */ - duk_small_uint_t call_flags; - duk_int_t setup_rc; - - /* resumee: [... initial_func] (currently actually: [initial_func]) */ - - duk_push_undefined(resumee); - tv = &thr->heap->lj.value1; - duk_push_tval(resumee, tv); - - /* resumee: [... initial_func undefined(= this) resume_value ] */ - - call_flags = DUK_CALL_FLAG_ALLOW_ECMATOECMA; /* not tailcall, ecma-to-ecma (assumed to succeed) */ - - setup_rc = duk_handle_call_unprotected_nargs(resumee, 1 /*nargs*/, call_flags); - if (setup_rc == 0) { - /* This shouldn't happen; Duktape.Thread.resume() - * should make sure of that. If it does happen - * this internal error will propagate out of the - * executor which can be quite misleading. - */ - DUK_ERROR_INTERNAL(thr); - DUK_WO_NORETURN(return 0;); - } - - DUK_ASSERT(resumee->resumer == NULL); - resumee->resumer = thr; - DUK_HTHREAD_INCREF(thr, thr); - resumee->state = DUK_HTHREAD_STATE_RUNNING; - thr->state = DUK_HTHREAD_STATE_RESUMED; - DUK_HEAP_SWITCH_THREAD(thr->heap, resumee); -#if 0 - thr = resumee; /* not needed, as we exit right away */ -#endif - DUK_DD(DUK_DDPRINT("-> resume with a value, restart execution in resumee")); - retval = DUK__LONGJMP_RESTART; - goto wipe_and_return; - } - DUK_UNREACHABLE(); - break; /* never here */ - } - - case DUK_LJ_TYPE_YIELD: { - /* - * Currently only allowed only if yielding thread has only - * ECMAScript activations (except for the Duktape.Thread.yield() - * call at the callstack top) and none of them constructor - * calls. - * - * This excludes the 'entry' thread which will always have - * a preventcount > 0. - */ - - duk_hthread *resumer; - - /* duk_bi_duk_object_yield() and duk_bi_duk_object_resume() ensure all of these are met */ - -#if 0 /* entry_thread not available for assert */ - DUK_ASSERT(thr != entry_thread); /* Duktape.Thread.yield() should prevent */ -#endif - DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING); /* unchanged from Duktape.Thread.yield() */ - DUK_ASSERT(thr->callstack_top >= 2); /* ECMAScript activation + Duktape.Thread.yield() activation */ - DUK_ASSERT(thr->callstack_curr != NULL); - DUK_ASSERT(thr->callstack_curr->parent != NULL); - DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL && - DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr)) && - ((duk_hnatfunc *) DUK_ACT_GET_FUNC(thr->callstack_curr))->func == duk_bi_thread_yield); - DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr->parent) != NULL && - DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr->parent))); /* an ECMAScript function */ - - resumer = thr->resumer; - - DUK_ASSERT(resumer != NULL); - DUK_ASSERT(resumer->state == DUK_HTHREAD_STATE_RESUMED); /* written by a previous RESUME handling */ - DUK_ASSERT(resumer->callstack_top >= 2); /* ECMAScript activation + Duktape.Thread.resume() activation */ - DUK_ASSERT(resumer->callstack_curr != NULL); - DUK_ASSERT(resumer->callstack_curr->parent != NULL); - DUK_ASSERT(DUK_ACT_GET_FUNC(resumer->callstack_curr) != NULL && - DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(resumer->callstack_curr)) && - ((duk_hnatfunc *) DUK_ACT_GET_FUNC(resumer->callstack_curr))->func == duk_bi_thread_resume); - DUK_ASSERT(DUK_ACT_GET_FUNC(resumer->callstack_curr->parent) != NULL && - DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(resumer->callstack_curr->parent))); /* an ECMAScript function */ - - if (thr->heap->lj.iserror) { - thr->state = DUK_HTHREAD_STATE_YIELDED; - thr->resumer = NULL; - DUK_HTHREAD_DECREF_NORZ(thr, resumer); - resumer->state = DUK_HTHREAD_STATE_RUNNING; - DUK_HEAP_SWITCH_THREAD(thr->heap, resumer); - thr = resumer; - - thr->heap->lj.type = DUK_LJ_TYPE_THROW; - /* lj.value1 is already set */ - DUK_ASSERT(thr->heap->lj.iserror); /* already set */ - - DUK_DD(DUK_DDPRINT("-> yield an error, converted to a throw in the resumer, propagate")); - goto check_longjmp; - } else { - /* When handling the yield, the last reference to - * 'thr' may disappear. - */ - - DUK_GC_TORTURE(resumer->heap); - duk_hthread_activation_unwind_norz(resumer); - DUK_GC_TORTURE(resumer->heap); - thr->state = DUK_HTHREAD_STATE_YIELDED; - thr->resumer = NULL; - DUK_HTHREAD_DECREF_NORZ(thr, resumer); - resumer->state = DUK_HTHREAD_STATE_RUNNING; - DUK_HEAP_SWITCH_THREAD(thr->heap, resumer); - duk__handle_yield(thr, resumer, &thr->heap->lj.value1); - thr = resumer; - DUK_GC_TORTURE(resumer->heap); - - DUK_DD(DUK_DDPRINT("-> yield a value, restart execution in resumer")); - retval = DUK__LONGJMP_RESTART; - goto wipe_and_return; - } - DUK_UNREACHABLE(); - break; /* never here */ - } -#endif /* DUK_USE_COROUTINE_SUPPORT */ - - case DUK_LJ_TYPE_THROW: { - /* - * Three possible outcomes: - * * A try or finally catcher is found => resume there. - * (or) - * * The error propagates to the bytecode executor entry - * level (and we're in the entry thread) => rethrow - * with a new longjmp(), after restoring the previous - * catchpoint. - * * The error is not caught in the current thread, so - * the thread finishes with an error. This works like - * a yielded error, except that the thread is finished - * and can no longer be resumed. (There is always a - * resumer in this case.) - * - * Note: until we hit the entry level, there can only be - * ECMAScript activations. - */ - - duk_activation *act; - duk_catcher *cat; - duk_hthread *resumer; - - for (;;) { - act = thr->callstack_curr; - if (act == NULL) { - break; - } - - for (;;) { - cat = act->cat; - if (cat == NULL) { - break; - } - - if (DUK_CAT_HAS_CATCH_ENABLED(cat)) { - DUK_ASSERT(DUK_CAT_GET_TYPE(cat) == DUK_CAT_TYPE_TCF); - - DUK_DDD(DUK_DDDPRINT("before catch part 1: thr=%p, act=%p, cat=%p", - (void *) thr, - (void *) act, - (void *) act->cat)); - duk__handle_catch_part1(thr, - &thr->heap->lj.value1, - DUK_LJ_TYPE_THROW, - out_delayed_catch_setup); - - DUK_DD(DUK_DDPRINT("-> throw caught by a 'catch' clause, restart execution")); - retval = DUK__LONGJMP_RESTART; - goto wipe_and_return; - } - - if (DUK_CAT_HAS_FINALLY_ENABLED(cat)) { - DUK_ASSERT(DUK_CAT_GET_TYPE(cat) == DUK_CAT_TYPE_TCF); - DUK_ASSERT(!DUK_CAT_HAS_CATCH_ENABLED(cat)); - - duk__handle_finally(thr, &thr->heap->lj.value1, DUK_LJ_TYPE_THROW); - - DUK_DD(DUK_DDPRINT("-> throw caught by a 'finally' clause, restart execution")); - retval = DUK__LONGJMP_RESTART; - goto wipe_and_return; - } - - duk_hthread_catcher_unwind_norz(thr, act); - } - - if (act == entry_act) { - /* Not caught by anything before entry level; rethrow and let the - * final catcher finish unwinding (esp. value stack). - */ - DUK_D(DUK_DPRINT("-> throw propagated up to entry level, rethrow and exit bytecode executor")); - retval = DUK__LONGJMP_RETHROW; - goto just_return; - } - - duk_hthread_activation_unwind_norz(thr); - } - - DUK_DD(DUK_DDPRINT("-> throw not caught by current thread, yield error to resumer and recheck longjmp")); - - /* Not caught by current thread, thread terminates (yield error to resumer); - * note that this may cause a cascade if the resumer terminates with an uncaught - * exception etc (this is OK, but needs careful testing). - */ - - DUK_ASSERT(thr->resumer != NULL); - DUK_ASSERT(thr->resumer->callstack_top >= 2); /* ECMAScript activation + Duktape.Thread.resume() activation */ - DUK_ASSERT(thr->resumer->callstack_curr != NULL); - DUK_ASSERT(thr->resumer->callstack_curr->parent != NULL); - DUK_ASSERT( - DUK_ACT_GET_FUNC(thr->resumer->callstack_curr->parent) != NULL && - DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr->parent))); /* an ECMAScript function */ - - resumer = thr->resumer; - - /* reset longjmp */ - - DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_THROW); /* already set */ - /* lj.value1 already set */ - - duk_hthread_terminate(thr); /* updates thread state, minimizes its allocations */ - DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_TERMINATED); - - thr->resumer = NULL; - DUK_HTHREAD_DECREF_NORZ(thr, resumer); - resumer->state = DUK_HTHREAD_STATE_RUNNING; - DUK_HEAP_SWITCH_THREAD(thr->heap, resumer); - thr = resumer; - goto check_longjmp; - } - - case DUK_LJ_TYPE_BREAK: /* pseudotypes, not used in actual longjmps */ - case DUK_LJ_TYPE_CONTINUE: - case DUK_LJ_TYPE_RETURN: - case DUK_LJ_TYPE_NORMAL: - default: { - /* should never happen, but be robust */ - DUK_D(DUK_DPRINT("caught unknown longjmp type %ld, treat as internal error", (long) thr->heap->lj.type)); - goto convert_to_internal_error; - } - - } /* end switch */ - - DUK_UNREACHABLE(); - -wipe_and_return: - DUK_DD(DUK_DDPRINT("handling longjmp done, wipe-and-return, top=%ld", (long) duk_get_top(thr))); - thr->heap->lj.type = DUK_LJ_TYPE_UNKNOWN; - thr->heap->lj.iserror = 0; - - DUK_TVAL_SET_UNDEFINED_UPDREF(thr, &thr->heap->lj.value1); /* side effects */ - DUK_TVAL_SET_UNDEFINED_UPDREF(thr, &thr->heap->lj.value2); /* side effects */ - - DUK_GC_TORTURE(thr->heap); - -just_return: - return retval; - -convert_to_internal_error: - /* This could also be thrown internally (set the error, goto check_longjmp), - * but it's better for internal errors to bubble outwards so that we won't - * infinite loop in this catchpoint. - */ - DUK_ERROR_INTERNAL(thr); - DUK_WO_NORETURN(return 0;); -} - -/* Handle a BREAK/CONTINUE opcode. Avoid using longjmp() for BREAK/CONTINUE - * handling because it has a measurable performance impact in ordinary - * environments and an extreme impact in Emscripten (GH-342). - */ -DUK_LOCAL DUK_EXEC_NOINLINE_PERF void duk__handle_break_or_continue(duk_hthread *thr, - duk_uint_t label_id, - duk_small_uint_t lj_type) { - duk_activation *act; - duk_catcher *cat; - - DUK_ASSERT(thr != NULL); - - /* Find a matching label catcher or 'finally' catcher in - * the same function, unwinding catchers as we go. - * - * A label catcher must always exist and will match unless - * a 'finally' captures the break/continue first. It is the - * compiler's responsibility to ensure that labels are used - * correctly. - */ - - act = thr->callstack_curr; - DUK_ASSERT(act != NULL); - - for (;;) { - cat = act->cat; - if (cat == NULL) { - break; - } - - DUK_DDD(DUK_DDDPRINT("considering catcher %p: type=%ld label=%ld", - (void *) cat, - (long) DUK_CAT_GET_TYPE(cat), - (long) DUK_CAT_GET_LABEL(cat))); - - /* XXX: bit mask test; FINALLY <-> TCF, single bit mask would suffice? */ - - if (DUK_CAT_GET_TYPE(cat) == DUK_CAT_TYPE_TCF && DUK_CAT_HAS_FINALLY_ENABLED(cat)) { - duk_tval tv_tmp; - - DUK_TVAL_SET_U32(&tv_tmp, (duk_uint32_t) label_id); - duk__handle_finally(thr, &tv_tmp, lj_type); - - DUK_DD(DUK_DDPRINT("-> break/continue caught by 'finally', restart execution")); - return; - } - if (DUK_CAT_GET_TYPE(cat) == DUK_CAT_TYPE_LABEL && (duk_uint_t) DUK_CAT_GET_LABEL(cat) == label_id) { - duk__handle_label(thr, lj_type); - - DUK_DD( - DUK_DDPRINT("-> break/continue caught by a label catcher (in the same function), restart execution")); - return; - } - - duk_hthread_catcher_unwind_norz(thr, act); - } - - /* Should never happen, but be robust. */ - DUK_D(DUK_DPRINT( - "-> break/continue not caught by anything in the current function (should never happen), throw internal error")); - DUK_ERROR_INTERNAL(thr); - DUK_WO_NORETURN(return;); -} - -/* Handle a RETURN opcode. Avoid using longjmp() for return handling because - * it has a measurable performance impact in ordinary environments and an extreme - * impact in Emscripten (GH-342). Return value is on value stack top. - */ -DUK_LOCAL duk_small_uint_t duk__handle_return(duk_hthread *thr, duk_activation *entry_act) { - duk_tval *tv1; - duk_tval *tv2; -#if defined(DUK_USE_COROUTINE_SUPPORT) - duk_hthread *resumer; -#endif - duk_activation *act; - duk_catcher *cat; - - /* We can directly access value stack here. */ - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(entry_act != NULL); - DUK_ASSERT(thr->valstack_top - 1 >= thr->valstack_bottom); - tv1 = thr->valstack_top - 1; - DUK_TVAL_CHKFAST_INPLACE_FAST(tv1); /* fastint downgrade check for return values */ - - /* - * Four possible outcomes: - * - * 1. A 'finally' in the same function catches the 'return'. - * It may continue to propagate when 'finally' is finished, - * or it may be neutralized by 'finally' (both handled by - * ENDFIN). - * - * 2. The return happens at the entry level of the bytecode - * executor, so return from the executor (in C stack). - * - * 3. There is a calling (ECMAScript) activation in the call - * stack => return to it, in the same executor instance. - * - * 4. There is no calling activation, and the thread is - * terminated. There is always a resumer in this case, - * which gets the return value similarly to a 'yield' - * (except that the current thread can no longer be - * resumed). - */ - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->callstack_top >= 1); - - act = thr->callstack_curr; - DUK_ASSERT(act != NULL); - - for (;;) { - cat = act->cat; - if (cat == NULL) { - break; - } - - if (DUK_CAT_GET_TYPE(cat) == DUK_CAT_TYPE_TCF && DUK_CAT_HAS_FINALLY_ENABLED(cat)) { - DUK_ASSERT(thr->valstack_top - 1 >= thr->valstack_bottom); - duk__handle_finally(thr, thr->valstack_top - 1, DUK_LJ_TYPE_RETURN); - - DUK_DD(DUK_DDPRINT("-> return caught by 'finally', restart execution")); - return DUK__RETHAND_RESTART; - } - - duk_hthread_catcher_unwind_norz(thr, act); - } - - if (act == entry_act) { - /* Return to the bytecode executor caller who will unwind stacks - * and handle constructor post-processing. - * Return value is already on the stack top: [ ... retval ]. - */ - - DUK_DDD(DUK_DDDPRINT("-> return propagated up to entry level, exit bytecode executor")); - return DUK__RETHAND_FINISHED; - } - - if (thr->callstack_top >= 2) { - /* There is a caller; it MUST be an ECMAScript caller (otherwise it would - * match entry_act check). - */ - DUK_DDD(DUK_DDDPRINT("return to ECMAScript caller, retval_byteoff=%ld, lj_value1=%!T", - (long) (thr->callstack_curr->parent->retval_byteoff), - (duk_tval *) &thr->heap->lj.value1)); - - DUK_ASSERT(thr->callstack_curr != NULL); - DUK_ASSERT(thr->callstack_curr->parent != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr->parent))); /* must be ECMAScript */ - -#if defined(DUK_USE_ES6_PROXY) - if (thr->callstack_curr->flags & (DUK_ACT_FLAG_CONSTRUCT | DUK_ACT_FLAG_CONSTRUCT_PROXY)) { - duk_call_construct_postprocess(thr, - thr->callstack_curr->flags & - DUK_ACT_FLAG_CONSTRUCT_PROXY); /* side effects */ - } -#else - if (thr->callstack_curr->flags & DUK_ACT_FLAG_CONSTRUCT) { - duk_call_construct_postprocess(thr, 0); /* side effects */ - } -#endif - - tv1 = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + thr->callstack_curr->parent->retval_byteoff); - DUK_ASSERT(thr->valstack_top - 1 >= thr->valstack_bottom); - tv2 = thr->valstack_top - 1; - DUK_TVAL_SET_TVAL_UPDREF(thr, tv1, tv2); /* side effects */ - - /* Catch stack unwind happens inline in callstack unwind. */ - duk_hthread_activation_unwind_norz(thr); - - duk__reconfig_valstack_ecma_return(thr); - - DUK_DD(DUK_DDPRINT("-> return not intercepted, restart execution in caller")); - return DUK__RETHAND_RESTART; - } - -#if defined(DUK_USE_COROUTINE_SUPPORT) - DUK_DD(DUK_DDPRINT("no calling activation, thread finishes (similar to yield)")); - - DUK_ASSERT(thr->resumer != NULL); - DUK_ASSERT(thr->resumer->callstack_top >= 2); /* ECMAScript activation + Duktape.Thread.resume() activation */ - DUK_ASSERT(thr->resumer->callstack_curr != NULL); - DUK_ASSERT(thr->resumer->callstack_curr->parent != NULL); - DUK_ASSERT(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr) != NULL && - DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr)) && - ((duk_hnatfunc *) DUK_ACT_GET_FUNC(thr->resumer->callstack_curr))->func == - duk_bi_thread_resume); /* Duktape.Thread.resume() */ - DUK_ASSERT(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr->parent) != NULL && - DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr->parent))); /* an ECMAScript function */ - DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING); - DUK_ASSERT(thr->resumer->state == DUK_HTHREAD_STATE_RESUMED); - - resumer = thr->resumer; - - /* Share yield longjmp handler. - * - * This sequence of steps is a bit fragile (see GH-1845): - * - We need the return value from 'thr' (resumed thread) value stack. - * The termination unwinds its value stack, losing the value. - * - We need a refcounted reference for 'thr', which may only exist - * in the caller value stack. We can't unwind or reconfigure the - * caller's value stack without potentially freeing 'thr'. - * - * Current approach is to capture the 'thr' return value and store - * a reference to 'thr' in the caller value stack temporarily. This - * keeps 'thr' reachable until final yield/return handling which - * removes the references atomatically. - */ - - DUK_ASSERT(thr->valstack_top - 1 >= thr->valstack_bottom); - duk_hthread_activation_unwind_norz(resumer); /* May remove last reference to 'thr', but is NORZ. */ - duk_push_tval(resumer, thr->valstack_top - 1); /* Capture return value, side effect free. */ - duk_push_hthread(resumer, thr); /* Make 'thr' reachable again, before side effects. */ - - duk_hthread_terminate(thr); /* Updates thread state, minimizes its allocations. */ - thr->resumer = NULL; - DUK_HTHREAD_DECREF(thr, resumer); - DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_TERMINATED); - - resumer->state = DUK_HTHREAD_STATE_RUNNING; - DUK_HEAP_SWITCH_THREAD(thr->heap, resumer); - - DUK_ASSERT(resumer->valstack_top - 2 >= resumer->valstack_bottom); - duk__handle_yield(thr, resumer, resumer->valstack_top - 2); - thr = NULL; /* 'thr' invalidated by call */ - -#if 0 - thr = resumer; /* not needed */ -#endif - - DUK_DD(DUK_DDPRINT("-> return not caught, thread terminated; handle like yield, restart execution in resumer")); - return DUK__RETHAND_RESTART; -#else - /* Without coroutine support this case should never happen. */ - DUK_ERROR_INTERNAL(thr); - DUK_WO_NORETURN(return 0;); -#endif -} - -/* - * Executor interrupt handling - * - * The handler is called whenever the interrupt countdown reaches zero - * (or below). The handler must perform whatever checks are activated, - * e.g. check for cumulative step count to impose an execution step - * limit or check for breakpoints or other debugger interaction. - * - * When the actions are done, the handler must reinit the interrupt - * init and counter values. The 'init' value must indicate how many - * bytecode instructions are executed before the next interrupt. The - * counter must interface with the bytecode executor loop. Concretely, - * the new init value is normally one higher than the new counter value. - * For instance, to execute exactly one bytecode instruction the init - * value is set to 1 and the counter to 0. If an error is thrown by the - * interrupt handler, the counters are set to the same value (e.g. both - * to 0 to cause an interrupt when the next bytecode instruction is about - * to be executed after error handling). - * - * Maintaining the init/counter value properly is important for accurate - * behavior. For instance, executor step limit needs a cumulative step - * count which is simply computed as a sum of 'init' values. This must - * work accurately even when single stepping. - */ - -#if defined(DUK_USE_INTERRUPT_COUNTER) - -#define DUK__INT_NOACTION 0 /* no specific action, resume normal execution */ -#define DUK__INT_RESTART 1 /* must "goto restart_execution", e.g. breakpoints changed */ - -#if defined(DUK_USE_DEBUGGER_SUPPORT) -DUK_LOCAL void duk__interrupt_handle_debugger(duk_hthread *thr, duk_bool_t *out_immediate, duk_small_uint_t *out_interrupt_retval) { - duk_activation *act; - duk_breakpoint *bp; - duk_breakpoint **bp_active; - duk_uint_fast32_t line = 0; - duk_bool_t process_messages; - duk_bool_t processed_messages = 0; - - DUK_ASSERT(thr->heap->dbg_processing == 0); /* don't re-enter e.g. during Eval */ - - act = thr->callstack_curr; - DUK_ASSERT(act != NULL); - - /* It might seem that replacing 'thr->heap' with just 'heap' below - * might be a good idea, but it increases code size slightly - * (probably due to unnecessary spilling) at least on x64. - */ - - /* - * Single opcode step check - */ - - if (thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_ONE_OPCODE_ACTIVE) { - DUK_D(DUK_DPRINT("PAUSE TRIGGERED by one opcode step")); - duk_debug_set_paused(thr->heap); - } - - /* - * Breakpoint and step state checks - */ - - if (act->flags & DUK_ACT_FLAG_BREAKPOINT_ACTIVE || (thr->heap->dbg_pause_act == thr->callstack_curr)) { - line = duk_debug_curr_line(thr); - - if (act->prev_line != line) { - /* Stepped? Step out is handled by callstack unwind. */ - if ((thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_LINE_CHANGE) && - (thr->heap->dbg_pause_act == thr->callstack_curr) && (line != thr->heap->dbg_pause_startline)) { - DUK_D(DUK_DPRINT("PAUSE TRIGGERED by line change, at line %ld", (long) line)); - duk_debug_set_paused(thr->heap); - } - - /* Check for breakpoints only on line transition. - * Breakpoint is triggered when we enter the target - * line from a different line, and the previous line - * was within the same function. - * - * This condition is tricky: the condition used to be - * that transition to -or across- the breakpoint line - * triggered the breakpoint. This seems intuitively - * better because it handles breakpoints on lines with - * no emitted opcodes; but this leads to the issue - * described in: https://github.com/svaarala/duktape/issues/263. - */ - bp_active = thr->heap->dbg_breakpoints_active; - for (;;) { - bp = *bp_active++; - if (bp == NULL) { - break; - } - - DUK_ASSERT(bp->filename != NULL); - if (act->prev_line != bp->line && line == bp->line) { - DUK_D(DUK_DPRINT("PAUSE TRIGGERED by breakpoint at %!O:%ld", - (duk_heaphdr *) bp->filename, - (long) bp->line)); - duk_debug_set_paused(thr->heap); - } - } - } else { - ; - } - - act->prev_line = (duk_uint32_t) line; - } - - /* - * Rate limit check for sending status update or peeking into - * the debug transport. Both can be expensive operations that - * we don't want to do on every opcode. - * - * Making sure the interval remains reasonable on a wide variety - * of targets and bytecode is difficult without a timestamp, so - * we use a Date-provided timestamp for the rate limit check. - * But since it's also expensive to get a timestamp, a bytecode - * counter is used to rate limit getting timestamps. - */ - - process_messages = 0; - if (thr->heap->dbg_state_dirty || DUK_HEAP_HAS_DEBUGGER_PAUSED(thr->heap) || thr->heap->dbg_detaching) { - /* Enter message processing loop for sending Status notifys and - * to finish a pending detach. - */ - process_messages = 1; - } - - /* XXX: remove heap->dbg_exec_counter, use heap->inst_count_interrupt instead? */ - DUK_ASSERT(thr->interrupt_init >= 0); - thr->heap->dbg_exec_counter += (duk_uint_t) thr->interrupt_init; - if (thr->heap->dbg_exec_counter - thr->heap->dbg_last_counter >= DUK_HEAP_DBG_RATELIMIT_OPCODES) { - /* Overflow of the execution counter is fine and doesn't break - * anything here. - */ - - duk_double_t now, diff_last; - - thr->heap->dbg_last_counter = thr->heap->dbg_exec_counter; - now = duk_time_get_monotonic_time(thr); - - diff_last = now - thr->heap->dbg_last_time; - if (diff_last < 0.0 || diff_last >= (duk_double_t) DUK_HEAP_DBG_RATELIMIT_MILLISECS) { - /* Monotonic time should not experience time jumps, - * but the provider may be missing and we're actually - * using ECMAScript time. So, tolerate negative values - * so that a time jump works reasonably. - * - * Same interval is now used for status sending and - * peeking. - */ - - thr->heap->dbg_last_time = now; - thr->heap->dbg_state_dirty = 1; - process_messages = 1; - } - } - - /* - * Process messages and send status if necessary. - * - * If we're paused, we'll block for new messages. If we're not - * paused, we'll process anything we can peek but won't block - * for more. Detach (and re-attach) handling is all localized - * to duk_debug_process_messages() too. - * - * Debugger writes outside the message loop may cause debugger - * detach1 phase to run, after which dbg_read_cb == NULL and - * dbg_detaching != 0. The message loop will finish the detach - * by running detach2 phase, so enter the message loop also when - * detaching. - */ - - if (process_messages) { - DUK_ASSERT(thr->heap->dbg_processing == 0); - processed_messages = duk_debug_process_messages(thr, 0 /*no_block*/); - DUK_ASSERT(thr->heap->dbg_processing == 0); - } - - /* Continue checked execution if there are breakpoints or we're stepping. - * Also use checked execution if paused flag is active - it shouldn't be - * because the debug message loop shouldn't terminate if it was. Step out - * is handled by callstack unwind and doesn't need checked execution. - * Note that debugger may have detached due to error or explicit request - * above, so we must recheck attach status. - */ - - if (duk_debug_is_attached(thr->heap)) { - DUK_ASSERT(act == thr->callstack_curr); - DUK_ASSERT(act != NULL); - if (act->flags & DUK_ACT_FLAG_BREAKPOINT_ACTIVE || (thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_ONE_OPCODE) || - ((thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_LINE_CHANGE) && - thr->heap->dbg_pause_act == thr->callstack_curr) || - DUK_HEAP_HAS_DEBUGGER_PAUSED(thr->heap)) { - *out_immediate = 1; - } - - /* If we processed any debug messages breakpoints may have - * changed; restart execution to re-check active breakpoints. - */ - if (processed_messages) { - DUK_D(DUK_DPRINT("processed debug messages, restart execution to recheck possibly changed breakpoints")); - *out_interrupt_retval = DUK__INT_RESTART; - } else { - if (thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_ONE_OPCODE) { - /* Set 'pause after one opcode' active only when we're - * actually just about to execute code. - */ - thr->heap->dbg_pause_flags |= DUK_PAUSE_FLAG_ONE_OPCODE_ACTIVE; - } - } - } else { - DUK_D(DUK_DPRINT("debugger became detached, resume normal execution")); - } -} -#endif /* DUK_USE_DEBUGGER_SUPPORT */ - -DUK_LOCAL DUK_EXEC_NOINLINE_PERF DUK_COLD duk_small_uint_t duk__executor_interrupt(duk_hthread *thr) { - duk_int_t ctr; - duk_activation *act; - duk_hcompfunc *fun; - duk_bool_t immediate = 0; - duk_small_uint_t retval; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - DUK_ASSERT(thr->callstack_top > 0); - -#if defined(DUK_USE_DEBUG) - thr->heap->inst_count_interrupt += thr->interrupt_init; - DUK_DD(DUK_DDPRINT("execution interrupt, counter=%ld, init=%ld, " - "instruction counts: executor=%ld, interrupt=%ld", - (long) thr->interrupt_counter, - (long) thr->interrupt_init, - (long) thr->heap->inst_count_exec, - (long) thr->heap->inst_count_interrupt)); -#endif - - retval = DUK__INT_NOACTION; - ctr = DUK_HTHREAD_INTCTR_DEFAULT; - - /* - * Avoid nested calls. Concretely this happens during debugging, e.g. - * when we eval() an expression. - * - * Also don't interrupt if we're currently doing debug processing - * (which can be initiated outside the bytecode executor) as this - * may cause the debugger to be called recursively. Check required - * for correct operation of throw intercept and other "exotic" halting - * scenarios. - */ - -#if defined(DUK_USE_DEBUGGER_SUPPORT) - if (DUK_HEAP_HAS_INTERRUPT_RUNNING(thr->heap) || thr->heap->dbg_processing) { -#else - if (DUK_HEAP_HAS_INTERRUPT_RUNNING(thr->heap)) { -#endif - DUK_DD(DUK_DDPRINT("nested executor interrupt, ignoring")); - - /* Set a high interrupt counter; the original executor - * interrupt invocation will rewrite before exiting. - */ - thr->interrupt_init = ctr; - thr->interrupt_counter = ctr - 1; - return DUK__INT_NOACTION; - } - DUK_HEAP_SET_INTERRUPT_RUNNING(thr->heap); - - act = thr->callstack_curr; - DUK_ASSERT(act != NULL); - - fun = (duk_hcompfunc *) DUK_ACT_GET_FUNC(act); - DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC((duk_hobject *) fun)); - - DUK_UNREF(fun); - -#if defined(DUK_USE_EXEC_TIMEOUT_CHECK) - /* - * Execution timeout check - */ - - if (DUK_USE_EXEC_TIMEOUT_CHECK(thr->heap->heap_udata)) { - /* Keep throwing an error whenever we get here. The unusual values - * are set this way because no instruction is ever executed, we just - * throw an error until all try/catch/finally and other catchpoints - * have been exhausted. Duktape/C code gets control at each protected - * call but whenever it enters back into Duktape the RangeError gets - * raised. User exec timeout check must consistently indicate a timeout - * until we've fully bubbled out of Duktape. - */ - DUK_D(DUK_DPRINT("execution timeout, throwing a RangeError")); - thr->interrupt_init = 0; - thr->interrupt_counter = 0; - DUK_HEAP_CLEAR_INTERRUPT_RUNNING(thr->heap); - DUK_ERROR_RANGE(thr, "execution timeout"); - DUK_WO_NORETURN(return 0;); - } -#endif /* DUK_USE_EXEC_TIMEOUT_CHECK */ - -#if defined(DUK_USE_DEBUGGER_SUPPORT) - if (!thr->heap->dbg_processing && (thr->heap->dbg_read_cb != NULL || thr->heap->dbg_detaching)) { - /* Avoid recursive re-entry; enter when we're attached or - * detaching (to finish off the pending detach). - */ - duk__interrupt_handle_debugger(thr, &immediate, &retval); - DUK_ASSERT(act == thr->callstack_curr); - } -#endif /* DUK_USE_DEBUGGER_SUPPORT */ - - /* - * Update the interrupt counter - */ - - if (immediate) { - /* Cause an interrupt after executing one instruction. */ - ctr = 1; - } - - /* The counter value is one less than the init value: init value should - * indicate how many instructions are executed before interrupt. To - * execute 1 instruction (after interrupt handler return), counter must - * be 0. - */ - DUK_ASSERT(ctr >= 1); - thr->interrupt_init = ctr; - thr->interrupt_counter = ctr - 1; - DUK_HEAP_CLEAR_INTERRUPT_RUNNING(thr->heap); - - return retval; -} -#endif /* DUK_USE_INTERRUPT_COUNTER */ - -/* - * Debugger handling for executor restart - * - * Check for breakpoints, stepping, etc, and figure out if we should execute - * in checked or normal mode. Note that we can't do this when an activation - * is created, because breakpoint status (and stepping status) may change - * later, so we must recheck every time we're executing an activation. - * This primitive should be side effect free to avoid changes during check. - */ - -#if defined(DUK_USE_DEBUGGER_SUPPORT) -DUK_LOCAL void duk__executor_recheck_debugger(duk_hthread *thr, duk_activation *act, duk_hcompfunc *fun) { - duk_heap *heap; - duk_tval *tv_tmp; - duk_hstring *filename; - duk_small_uint_t bp_idx; - duk_breakpoint **bp_active; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(act != NULL); - DUK_ASSERT(fun != NULL); - - heap = thr->heap; - bp_active = heap->dbg_breakpoints_active; - act->flags &= ~DUK_ACT_FLAG_BREAKPOINT_ACTIVE; - - tv_tmp = duk_hobject_find_entry_tval_ptr_stridx(thr->heap, (duk_hobject *) fun, DUK_STRIDX_FILE_NAME); - if (tv_tmp && DUK_TVAL_IS_STRING(tv_tmp)) { - filename = DUK_TVAL_GET_STRING(tv_tmp); - - /* Figure out all active breakpoints. A breakpoint is - * considered active if the current function's fileName - * matches the breakpoint's fileName, AND there is no - * inner function that has matching line numbers - * (otherwise a breakpoint would be triggered both - * inside and outside of the inner function which would - * be confusing). Example: - * - * function foo() { - * print('foo'); - * function bar() { <-. breakpoints in these - * print('bar'); | lines should not affect - * } <-' foo() execution - * bar(); - * } - * - * We need a few things that are only available when - * debugger support is enabled: (1) a line range for - * each function, and (2) access to the function - * template to access the inner functions (and their - * line ranges). - * - * It's important to have a narrow match for active - * breakpoints so that we don't enter checked execution - * when that's not necessary. For instance, if we're - * running inside a certain function and there's - * breakpoint outside in (after the call site), we - * don't want to slow down execution of the function. - */ - - for (bp_idx = 0; bp_idx < heap->dbg_breakpoint_count; bp_idx++) { - duk_breakpoint *bp = heap->dbg_breakpoints + bp_idx; - duk_hobject **funcs, **funcs_end; - duk_hcompfunc *inner_fun; - duk_bool_t bp_match; - - if (bp->filename == filename && bp->line >= fun->start_line && bp->line <= fun->end_line) { - bp_match = 1; - DUK_DD(DUK_DDPRINT("breakpoint filename and line match: " - "%s:%ld vs. %s (line %ld vs. %ld-%ld)", - DUK_HSTRING_GET_DATA(bp->filename), - (long) bp->line, - DUK_HSTRING_GET_DATA(filename), - (long) bp->line, - (long) fun->start_line, - (long) fun->end_line)); - - funcs = DUK_HCOMPFUNC_GET_FUNCS_BASE(thr->heap, fun); - funcs_end = DUK_HCOMPFUNC_GET_FUNCS_END(thr->heap, fun); - while (funcs != funcs_end) { - inner_fun = (duk_hcompfunc *) *funcs; - DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC((duk_hobject *) inner_fun)); - if (bp->line >= inner_fun->start_line && bp->line <= inner_fun->end_line) { - DUK_DD(DUK_DDPRINT("inner function masks ('captures') breakpoint")); - bp_match = 0; - break; - } - funcs++; - } - - if (bp_match) { - /* No need to check for size of bp_active list, - * it's always larger than maximum number of - * breakpoints. - */ - act->flags |= DUK_ACT_FLAG_BREAKPOINT_ACTIVE; - *bp_active = heap->dbg_breakpoints + bp_idx; - bp_active++; - } - } - } - } - - *bp_active = NULL; /* terminate */ - - DUK_DD(DUK_DDPRINT("ACTIVE BREAKPOINTS: %ld", (long) (bp_active - thr->heap->dbg_breakpoints_active))); - - /* Force pause if we were doing "step into" in another activation. */ - if ((thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_FUNC_ENTRY) && thr->heap->dbg_pause_act != thr->callstack_curr) { - DUK_D(DUK_DPRINT("PAUSE TRIGGERED by function entry")); - duk_debug_set_paused(thr->heap); - } - - /* Force interrupt right away if we're paused or in "checked mode". - * Step out is handled by callstack unwind. - */ - if ((act->flags & DUK_ACT_FLAG_BREAKPOINT_ACTIVE) || DUK_HEAP_HAS_DEBUGGER_PAUSED(thr->heap) || - ((thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_LINE_CHANGE) && thr->heap->dbg_pause_act == thr->callstack_curr)) { - /* We'll need to interrupt early so recompute the init - * counter to reflect the number of bytecode instructions - * executed so that step counts for e.g. debugger rate - * limiting are accurate. - */ - DUK_ASSERT(thr->interrupt_counter <= thr->interrupt_init); - thr->interrupt_init = thr->interrupt_init - thr->interrupt_counter; - thr->interrupt_counter = 0; - } -} -#endif /* DUK_USE_DEBUGGER_SUPPORT */ - -/* - * Opcode handlers for opcodes with a lot of code and which are relatively - * rare; NOINLINE to reduce amount of code in main bytecode dispatcher. - */ - -DUK_LOCAL DUK_EXEC_NOINLINE_PERF void duk__handle_op_initset_initget(duk_hthread *thr, duk_uint_fast32_t ins) { - duk_bool_t is_set = (DUK_DEC_OP(ins) == DUK_OP_INITSET); - duk_uint_fast_t idx; - duk_uint_t defprop_flags; - - /* A -> object register (acts as a source) - * BC -> BC+0 contains key, BC+1 closure (value) - */ - - /* INITSET/INITGET are only used to initialize object literal keys. - * There may be a previous propery in ES2015 because duplicate property - * names are allowed. - */ - - /* This could be made more optimal by accessing internals directly. */ - - idx = (duk_uint_fast_t) DUK_DEC_BC(ins); - duk_dup(thr, (duk_idx_t) (idx + 0)); /* key */ - duk_dup(thr, (duk_idx_t) (idx + 1)); /* getter/setter */ - if (is_set) { - defprop_flags = - DUK_DEFPROP_HAVE_SETTER | DUK_DEFPROP_FORCE | DUK_DEFPROP_SET_ENUMERABLE | DUK_DEFPROP_SET_CONFIGURABLE; - } else { - defprop_flags = - DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_FORCE | DUK_DEFPROP_SET_ENUMERABLE | DUK_DEFPROP_SET_CONFIGURABLE; - } - duk_def_prop(thr, (duk_idx_t) DUK_DEC_A(ins), defprop_flags); -} - -DUK_LOCAL DUK_EXEC_NOINLINE_PERF void duk__handle_op_trycatch(duk_hthread *thr, duk_uint_fast32_t ins, duk_instr_t *curr_pc) { - duk_activation *act; - duk_catcher *cat; - duk_tval *tv1; - duk_small_uint_fast_t a; - duk_small_uint_fast_t bc; - - /* A -> flags - * BC -> reg_catch; base register for two registers used both during - * trycatch setup and when catch is triggered - * - * If DUK_BC_TRYCATCH_FLAG_CATCH_BINDING set: - * reg_catch + 0: catch binding variable name (string). - * Automatic declarative environment is established for - * the duration of the 'catch' clause. - * - * If DUK_BC_TRYCATCH_FLAG_WITH_BINDING set: - * reg_catch + 0: with 'target value', which is coerced to - * an object and then used as a bindind object for an - * environment record. The binding is initialized here, for - * the 'try' clause. - * - * Note that a TRYCATCH generated for a 'with' statement has no - * catch or finally parts. - */ - - /* XXX: TRYCATCH handling should be reworked to avoid creating - * an explicit scope unless it is actually needed (e.g. function - * instances or eval is executed inside the catch block). This - * rework is not trivial because the compiler doesn't have an - * intermediate representation. When the rework is done, the - * opcode format can also be made more straightforward. - */ - - /* XXX: side effect handling is quite awkward here */ - - DUK_DDD(DUK_DDDPRINT("TRYCATCH: reg_catch=%ld, have_catch=%ld, " - "have_finally=%ld, catch_binding=%ld, with_binding=%ld (flags=0x%02lx)", - (long) DUK_DEC_BC(ins), - (long) (DUK_DEC_A(ins) & DUK_BC_TRYCATCH_FLAG_HAVE_CATCH ? 1 : 0), - (long) (DUK_DEC_A(ins) & DUK_BC_TRYCATCH_FLAG_HAVE_FINALLY ? 1 : 0), - (long) (DUK_DEC_A(ins) & DUK_BC_TRYCATCH_FLAG_CATCH_BINDING ? 1 : 0), - (long) (DUK_DEC_A(ins) & DUK_BC_TRYCATCH_FLAG_WITH_BINDING ? 1 : 0), - (unsigned long) DUK_DEC_A(ins))); - - a = DUK_DEC_A(ins); - bc = DUK_DEC_BC(ins); - - /* Registers 'bc' and 'bc + 1' are written in longjmp handling - * and if their previous values (which are temporaries) become - * unreachable -and- have a finalizer, there'll be a function - * call during error handling which is not supported now (GH-287). - * Ensure that both 'bc' and 'bc + 1' have primitive values to - * guarantee no finalizer calls in error handling. Scrubbing also - * ensures finalizers for the previous values run here rather than - * later. Error handling related values are also written to 'bc' - * and 'bc + 1' but those values never become unreachable during - * error handling, so there's no side effect problem even if the - * error value has a finalizer. - */ - duk_dup(thr, (duk_idx_t) bc); /* Stabilize value. */ - duk_to_undefined(thr, (duk_idx_t) bc); - duk_to_undefined(thr, (duk_idx_t) (bc + 1)); - - /* Allocate catcher and populate it. Doesn't have to - * be fully atomic, but the catcher must be in a - * consistent state if side effects (such as finalizer - * calls) occur. - */ - - cat = duk_hthread_catcher_alloc(thr); - DUK_ASSERT(cat != NULL); - - cat->flags = DUK_CAT_TYPE_TCF; - cat->h_varname = NULL; - cat->pc_base = (duk_instr_t *) curr_pc; /* pre-incremented, points to first jump slot */ - cat->idx_base = (duk_size_t) (thr->valstack_bottom - thr->valstack) + bc; - - act = thr->callstack_curr; - DUK_ASSERT(act != NULL); - cat->parent = act->cat; - act->cat = cat; - - if (a & DUK_BC_TRYCATCH_FLAG_HAVE_CATCH) { - cat->flags |= DUK_CAT_FLAG_CATCH_ENABLED; - } - if (a & DUK_BC_TRYCATCH_FLAG_HAVE_FINALLY) { - cat->flags |= DUK_CAT_FLAG_FINALLY_ENABLED; - } - if (a & DUK_BC_TRYCATCH_FLAG_CATCH_BINDING) { - DUK_DDD(DUK_DDDPRINT("catch binding flag set to catcher")); - cat->flags |= DUK_CAT_FLAG_CATCH_BINDING_ENABLED; - tv1 = DUK_GET_TVAL_NEGIDX(thr, -1); - DUK_ASSERT(DUK_TVAL_IS_STRING(tv1)); - - /* borrowed reference; although 'tv1' comes from a register, - * its value was loaded using LDCONST so the constant will - * also exist and be reachable. - */ - cat->h_varname = DUK_TVAL_GET_STRING(tv1); - } else if (a & DUK_BC_TRYCATCH_FLAG_WITH_BINDING) { - duk_hobjenv *env; - duk_hobject *target; - - /* Delayed env initialization for activation (if needed). */ - DUK_ASSERT(thr->callstack_top >= 1); - DUK_ASSERT(act == thr->callstack_curr); - DUK_ASSERT(act != NULL); - if (act->lex_env == NULL) { - DUK_DDD(DUK_DDDPRINT("delayed environment initialization")); - DUK_ASSERT(act->var_env == NULL); - - duk_js_init_activation_environment_records_delayed(thr, act); - DUK_ASSERT(act == thr->callstack_curr); - DUK_UNREF(act); /* 'act' is no longer accessed, scanbuild fix */ - } - DUK_ASSERT(act->lex_env != NULL); - DUK_ASSERT(act->var_env != NULL); - - /* Coerce 'with' target. */ - target = duk_to_hobject(thr, -1); - DUK_ASSERT(target != NULL); - - /* Create an object environment; it is not pushed - * so avoid side effects very carefully until it is - * referenced. - */ - env = duk_hobjenv_alloc(thr, DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJENV)); - DUK_ASSERT(env != NULL); - DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) env) == NULL); - env->target = target; /* always provideThis=true */ - DUK_HOBJECT_INCREF(thr, target); - env->has_this = 1; - DUK_HOBJENV_ASSERT_VALID(env); - DUK_DDD(DUK_DDDPRINT("environment for with binding: %!iO", env)); - - DUK_ASSERT(act == thr->callstack_curr); - DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) env) == NULL); - DUK_ASSERT(act->lex_env != NULL); - DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) env, act->lex_env); - act->lex_env = (duk_hobject *) env; /* Now reachable. */ - DUK_HOBJECT_INCREF(thr, (duk_hobject *) env); - /* Net refcount change to act->lex_env is 0: incref for env's - * prototype, decref for act->lex_env overwrite. - */ - - /* Set catcher lex_env active (affects unwind) - * only when the whole setup is complete. - */ - cat = act->cat; /* XXX: better to relookup? not mandatory because 'cat' is stable */ - cat->flags |= DUK_CAT_FLAG_LEXENV_ACTIVE; - } else { - ; - } - - DUK_DDD(DUK_DDDPRINT("TRYCATCH catcher: flags=0x%08lx, pc_base=%ld, " - "idx_base=%ld, h_varname=%!O", - (unsigned long) cat->flags, - (long) cat->pc_base, - (long) cat->idx_base, - (duk_heaphdr *) cat->h_varname)); - - duk_pop_unsafe(thr); -} - -DUK_LOCAL DUK_EXEC_NOINLINE_PERF duk_instr_t *duk__handle_op_endtry(duk_hthread *thr, duk_uint_fast32_t ins) { - duk_activation *act; - duk_catcher *cat; - duk_tval *tv1; - duk_instr_t *pc_base; - - DUK_UNREF(ins); - - DUK_ASSERT(thr->callstack_top >= 1); - act = thr->callstack_curr; - DUK_ASSERT(act != NULL); - cat = act->cat; - DUK_ASSERT(cat != NULL); - DUK_ASSERT(DUK_CAT_GET_TYPE(act->cat) == DUK_CAT_TYPE_TCF); - - DUK_DDD(DUK_DDDPRINT("ENDTRY: clearing catch active flag (regardless of whether it was set or not)")); - DUK_CAT_CLEAR_CATCH_ENABLED(cat); - - pc_base = cat->pc_base; - - if (DUK_CAT_HAS_FINALLY_ENABLED(cat)) { - DUK_DDD(DUK_DDDPRINT("ENDTRY: finally part is active, jump through 2nd jump slot with 'normal continuation'")); - - tv1 = thr->valstack + cat->idx_base; - DUK_ASSERT(tv1 >= thr->valstack && tv1 < thr->valstack_top); - DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv1); /* side effects */ - tv1 = NULL; - - tv1 = thr->valstack + cat->idx_base + 1; - DUK_ASSERT(tv1 >= thr->valstack && tv1 < thr->valstack_top); - DUK_TVAL_SET_U32_UPDREF(thr, tv1, (duk_uint32_t) DUK_LJ_TYPE_NORMAL); /* side effects */ - tv1 = NULL; - - DUK_CAT_CLEAR_FINALLY_ENABLED(cat); - } else { - DUK_DDD( - DUK_DDDPRINT("ENDTRY: no finally part, dismantle catcher, jump through 2nd jump slot (to end of statement)")); - - duk_hthread_catcher_unwind_norz(thr, act); /* lexenv may be set for 'with' binding */ - /* no need to unwind callstack */ - } - - return pc_base + 1; /* new curr_pc value */ -} - -DUK_LOCAL DUK_EXEC_NOINLINE_PERF duk_instr_t *duk__handle_op_endcatch(duk_hthread *thr, duk_uint_fast32_t ins) { - duk_activation *act; - duk_catcher *cat; - duk_tval *tv1; - duk_instr_t *pc_base; - - DUK_UNREF(ins); - - DUK_ASSERT(thr->callstack_top >= 1); - act = thr->callstack_curr; - DUK_ASSERT(act != NULL); - cat = act->cat; - DUK_ASSERT(cat != NULL); - DUK_ASSERT(!DUK_CAT_HAS_CATCH_ENABLED(cat)); /* cleared before entering catch part */ - - if (DUK_CAT_HAS_LEXENV_ACTIVE(cat)) { - duk_hobject *prev_env; - - /* 'with' binding has no catch clause, so can't be here unless a normal try-catch */ - DUK_ASSERT(DUK_CAT_HAS_CATCH_BINDING_ENABLED(cat)); - DUK_ASSERT(act->lex_env != NULL); - - DUK_DDD(DUK_DDDPRINT("ENDCATCH: popping catcher part lexical environment")); - - prev_env = act->lex_env; - DUK_ASSERT(prev_env != NULL); - act->lex_env = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, prev_env); - DUK_CAT_CLEAR_LEXENV_ACTIVE(cat); - DUK_HOBJECT_INCREF(thr, act->lex_env); - DUK_HOBJECT_DECREF(thr, prev_env); /* side effects */ - - DUK_ASSERT(act == thr->callstack_curr); - DUK_ASSERT(act != NULL); - } - - pc_base = cat->pc_base; - - if (DUK_CAT_HAS_FINALLY_ENABLED(cat)) { - DUK_DDD(DUK_DDDPRINT("ENDCATCH: finally part is active, jump through 2nd jump slot with 'normal continuation'")); - - tv1 = thr->valstack + cat->idx_base; - DUK_ASSERT(tv1 >= thr->valstack && tv1 < thr->valstack_top); - DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv1); /* side effects */ - tv1 = NULL; - - tv1 = thr->valstack + cat->idx_base + 1; - DUK_ASSERT(tv1 >= thr->valstack && tv1 < thr->valstack_top); - DUK_TVAL_SET_U32_UPDREF(thr, tv1, (duk_uint32_t) DUK_LJ_TYPE_NORMAL); /* side effects */ - tv1 = NULL; - - DUK_CAT_CLEAR_FINALLY_ENABLED(cat); - } else { - DUK_DDD( - DUK_DDDPRINT("ENDCATCH: no finally part, dismantle catcher, jump through 2nd jump slot (to end of statement)")); - - duk_hthread_catcher_unwind_norz(thr, act); - /* no need to unwind callstack */ - } - - return pc_base + 1; /* new curr_pc value */ -} - -DUK_LOCAL DUK_EXEC_NOINLINE_PERF duk_small_uint_t duk__handle_op_endfin(duk_hthread *thr, - duk_uint_fast32_t ins, - duk_activation *entry_act) { - duk_activation *act; - duk_tval *tv1; - duk_uint_t reg_catch; - duk_small_uint_t cont_type; - duk_small_uint_t ret_result; - - DUK_ASSERT(thr->ptr_curr_pc == NULL); - DUK_ASSERT(thr->callstack_top >= 1); - act = thr->callstack_curr; - DUK_ASSERT(act != NULL); - reg_catch = DUK_DEC_ABC(ins); - - /* CATCH flag may be enabled or disabled here; it may be enabled if - * the statement has a catch block but the try block does not throw - * an error. - */ - - DUK_DDD(DUK_DDDPRINT("ENDFIN: completion value=%!T, type=%!T", - (duk_tval *) (thr->valstack_bottom + reg_catch + 0), - (duk_tval *) (thr->valstack_bottom + reg_catch + 1))); - - tv1 = thr->valstack_bottom + reg_catch + 1; /* type */ - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv1)); -#if defined(DUK_USE_FASTINT) - DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv1)); - cont_type = (duk_small_uint_t) DUK_TVAL_GET_FASTINT_U32(tv1); -#else - cont_type = (duk_small_uint_t) DUK_TVAL_GET_NUMBER(tv1); -#endif - - tv1--; /* value */ - - switch (cont_type) { - case DUK_LJ_TYPE_NORMAL: { - DUK_DDD(DUK_DDDPRINT("ENDFIN: finally part finishing with 'normal' (non-abrupt) completion -> " - "dismantle catcher, resume execution after ENDFIN")); - - duk_hthread_catcher_unwind_norz(thr, act); - /* no need to unwind callstack */ - return 0; /* restart execution */ - } - case DUK_LJ_TYPE_RETURN: { - DUK_DDD(DUK_DDDPRINT("ENDFIN: finally part finishing with 'return' complation -> dismantle " - "catcher, handle return, lj.value1=%!T", - tv1)); - - /* Not necessary to unwind catch stack: return handling will - * do it. The finally flag of 'cat' is no longer set. The - * catch flag may be set, but it's not checked by return handling. - */ - - duk_push_tval(thr, tv1); - ret_result = duk__handle_return(thr, entry_act); - if (ret_result == DUK__RETHAND_RESTART) { - return 0; /* restart execution */ - } - DUK_ASSERT(ret_result == DUK__RETHAND_FINISHED); - - DUK_DDD(DUK_DDDPRINT("exiting executor after ENDFIN and RETURN (pseudo) longjmp type")); - return 1; /* exit executor */ - } - case DUK_LJ_TYPE_BREAK: - case DUK_LJ_TYPE_CONTINUE: { - duk_uint_t label_id; - duk_small_uint_t lj_type; - - /* Not necessary to unwind catch stack: break/continue - * handling will do it. The finally flag of 'cat' is - * no longer set. The catch flag may be set, but it's - * not checked by break/continue handling. - */ - - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv1)); -#if defined(DUK_USE_FASTINT) - DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv1)); - label_id = (duk_small_uint_t) DUK_TVAL_GET_FASTINT_U32(tv1); -#else - label_id = (duk_small_uint_t) DUK_TVAL_GET_NUMBER(tv1); -#endif - lj_type = cont_type; - duk__handle_break_or_continue(thr, label_id, lj_type); - return 0; /* restart execution */ - } - default: { - DUK_DDD(DUK_DDDPRINT("ENDFIN: finally part finishing with abrupt completion, lj_type=%ld -> " - "dismantle catcher, re-throw error", - (long) cont_type)); - - duk_err_setup_ljstate1(thr, (duk_small_uint_t) cont_type, tv1); - /* No debugger Throw notify check on purpose (rethrow). */ - - DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL); /* always in executor */ - duk_err_longjmp(thr); - DUK_UNREACHABLE(); - } - } - - DUK_UNREACHABLE(); - return 0; -} - -DUK_LOCAL DUK_EXEC_NOINLINE_PERF void duk__handle_op_initenum(duk_hthread *thr, duk_uint_fast32_t ins) { - duk_small_uint_t b; - duk_small_uint_t c; - - /* - * Enumeration semantics come from for-in statement, E5 Section 12.6.4. - * If called with 'null' or 'undefined', this opcode returns 'null' as - * the enumerator, which is special cased in NEXTENUM. This simplifies - * the compiler part - */ - - /* B -> register for writing enumerator object - * C -> value to be enumerated (register) - */ - b = DUK_DEC_B(ins); - c = DUK_DEC_C(ins); - - if (duk_is_null_or_undefined(thr, (duk_idx_t) c)) { - duk_push_null(thr); - duk_replace(thr, (duk_idx_t) b); - } else { - duk_dup(thr, (duk_idx_t) c); - duk_to_object(thr, -1); - duk_hobject_enumerator_create(thr, 0 /*enum_flags*/); /* [ ... val ] --> [ ... enum ] */ - duk_replace(thr, (duk_idx_t) b); - } -} - -DUK_LOCAL DUK_EXEC_NOINLINE_PERF duk_small_uint_t duk__handle_op_nextenum(duk_hthread *thr, duk_uint_fast32_t ins) { - duk_small_uint_t b; - duk_small_uint_t c; - duk_small_uint_t pc_skip = 0; - - /* - * NEXTENUM checks whether the enumerator still has unenumerated - * keys. If so, the next key is loaded to the target register - * and the next instruction is skipped. Otherwise the next instruction - * will be executed, jumping out of the enumeration loop. - */ - - /* B -> target register for next key - * C -> enum register - */ - b = DUK_DEC_B(ins); - c = DUK_DEC_C(ins); - - DUK_DDD(DUK_DDDPRINT("NEXTENUM: b->%!T, c->%!T", - (duk_tval *) duk_get_tval(thr, (duk_idx_t) b), - (duk_tval *) duk_get_tval(thr, (duk_idx_t) c))); - - if (duk_is_object(thr, (duk_idx_t) c)) { - /* XXX: assert 'c' is an enumerator */ - duk_dup(thr, (duk_idx_t) c); - if (duk_hobject_enumerator_next(thr, 0 /*get_value*/)) { - /* [ ... enum ] -> [ ... next_key ] */ - DUK_DDD(DUK_DDDPRINT("enum active, next key is %!T, skip jump slot ", (duk_tval *) duk_get_tval(thr, -1))); - pc_skip = 1; - } else { - /* [ ... enum ] -> [ ... ] */ - DUK_DDD(DUK_DDDPRINT("enum finished, execute jump slot")); - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top)); /* valstack policy */ - thr->valstack_top++; - } - duk_replace(thr, (duk_idx_t) b); - } else { - /* 'null' enumerator case -> behave as with an empty enumerator */ - DUK_ASSERT(duk_is_null(thr, (duk_idx_t) c)); - DUK_DDD(DUK_DDDPRINT("enum is null, execute jump slot")); - } - - return pc_skip; -} - -/* - * Call handling helpers. - */ - -DUK_LOCAL duk_bool_t duk__executor_handle_call(duk_hthread *thr, duk_idx_t idx, duk_idx_t nargs, duk_small_uint_t call_flags) { - duk_bool_t rc; - - duk_set_top_unsafe(thr, (duk_idx_t) (idx + nargs + 2)); /* [ ... func this arg1 ... argN ] */ - - /* Attempt an Ecma-to-Ecma call setup. If the call - * target is (directly or indirectly) Reflect.construct(), - * the call may change into a constructor call on the fly. - */ - rc = (duk_bool_t) duk_handle_call_unprotected(thr, idx, call_flags); - if (rc != 0) { - /* Ecma-to-ecma call possible, may or may not - * be a tail call. Avoid C recursion by - * reusing current executor instance. - */ - DUK_DDD(DUK_DDDPRINT("ecma-to-ecma call setup possible, restart execution")); - /* curr_pc synced by duk_handle_call_unprotected() */ - DUK_ASSERT(thr->ptr_curr_pc == NULL); - return rc; - } else { - /* Call was handled inline. */ - } - DUK_ASSERT(thr->ptr_curr_pc != NULL); - return rc; -} - -/* - * ECMAScript bytecode executor. - * - * Resume execution for the current thread from its current activation. - * Returns when execution would return from the entry level activation, - * leaving a single return value on top of the stack. Function calls - * and thread resumptions are handled internally. If an error occurs, - * a longjmp() with type DUK_LJ_TYPE_THROW is called on the entry level - * setjmp() jmpbuf. - * - * ECMAScript function calls and coroutine resumptions are handled - * internally (by the outer executor function) without recursive C calls. - * Other function calls are handled using duk_handle_call(), increasing - * C recursion depth. - * - * Abrupt completions (= long control tranfers) are handled either - * directly by reconfiguring relevant stacks and restarting execution, - * or via a longjmp. Longjmp-free handling is preferable for performance - * (especially Emscripten performance), and is used for: break, continue, - * and return. - * - * For more detailed notes, see doc/execution.rst. - * - * Also see doc/code-issues.rst for discussion of setjmp(), longjmp(), - * and volatile. - */ - -/* Presence of 'fun' is config based, there's a marginal performance - * difference and the best option is architecture dependent. - */ -#if defined(DUK_USE_EXEC_FUN_LOCAL) -#define DUK__FUN() fun -#else -#define DUK__FUN() ((duk_hcompfunc *) DUK_ACT_GET_FUNC((thr)->callstack_curr)) -#endif - -/* Strict flag. */ -#define DUK__STRICT() ((duk_small_uint_t) DUK_HOBJECT_HAS_STRICT((duk_hobject *) DUK__FUN())) - -/* Reg/const access macros: these are very footprint and performance sensitive - * so modify with care. Arguments are sometimes evaluated multiple times which - * is not ideal. - */ -#define DUK__REG(x) (*(thr->valstack_bottom + (x))) -#define DUK__REGP(x) (thr->valstack_bottom + (x)) -#define DUK__CONST(x) (*(consts + (x))) -#define DUK__CONSTP(x) (consts + (x)) - -/* Reg/const access macros which take the 32-bit instruction and avoid an - * explicit field decoding step by using shifts and masks. These must be - * kept in sync with duk_js_bytecode.h. The shift/mask values are chosen - * so that 'ins' can be shifted and masked and used as a -byte- offset - * instead of a duk_tval offset which needs further shifting (which is an - * issue on some, but not all, CPUs). - */ -#define DUK__RCBIT_B DUK_BC_REGCONST_B -#define DUK__RCBIT_C DUK_BC_REGCONST_C -#if defined(DUK_USE_EXEC_REGCONST_OPTIMIZE) -#if defined(DUK_USE_PACKED_TVAL) -#define DUK__TVAL_SHIFT 3 /* sizeof(duk_tval) == 8 */ -#else -#define DUK__TVAL_SHIFT 4 /* sizeof(duk_tval) == 16; not always the case so also asserted for */ -#endif -#define DUK__SHIFT_A (DUK_BC_SHIFT_A - DUK__TVAL_SHIFT) -#define DUK__SHIFT_B (DUK_BC_SHIFT_B - DUK__TVAL_SHIFT) -#define DUK__SHIFT_C (DUK_BC_SHIFT_C - DUK__TVAL_SHIFT) -#define DUK__SHIFT_BC (DUK_BC_SHIFT_BC - DUK__TVAL_SHIFT) -#define DUK__MASK_A (DUK_BC_UNSHIFTED_MASK_A << DUK__TVAL_SHIFT) -#define DUK__MASK_B (DUK_BC_UNSHIFTED_MASK_B << DUK__TVAL_SHIFT) -#define DUK__MASK_C (DUK_BC_UNSHIFTED_MASK_C << DUK__TVAL_SHIFT) -#define DUK__MASK_BC (DUK_BC_UNSHIFTED_MASK_BC << DUK__TVAL_SHIFT) -#define DUK__BYTEOFF_A(ins) (((ins) >> DUK__SHIFT_A) & DUK__MASK_A) -#define DUK__BYTEOFF_B(ins) (((ins) >> DUK__SHIFT_B) & DUK__MASK_B) -#define DUK__BYTEOFF_C(ins) (((ins) >> DUK__SHIFT_C) & DUK__MASK_C) -#define DUK__BYTEOFF_BC(ins) (((ins) >> DUK__SHIFT_BC) & DUK__MASK_BC) - -#define DUK__REGP_A(ins) ((duk_tval *) (void *) ((duk_uint8_t *) thr->valstack_bottom + DUK__BYTEOFF_A((ins)))) -#define DUK__REGP_B(ins) ((duk_tval *) (void *) ((duk_uint8_t *) thr->valstack_bottom + DUK__BYTEOFF_B((ins)))) -#define DUK__REGP_C(ins) ((duk_tval *) (void *) ((duk_uint8_t *) thr->valstack_bottom + DUK__BYTEOFF_C((ins)))) -#define DUK__REGP_BC(ins) ((duk_tval *) (void *) ((duk_uint8_t *) thr->valstack_bottom + DUK__BYTEOFF_BC((ins)))) -#define DUK__CONSTP_A(ins) ((duk_tval *) (void *) ((duk_uint8_t *) consts + DUK__BYTEOFF_A((ins)))) -#define DUK__CONSTP_B(ins) ((duk_tval *) (void *) ((duk_uint8_t *) consts + DUK__BYTEOFF_B((ins)))) -#define DUK__CONSTP_C(ins) ((duk_tval *) (void *) ((duk_uint8_t *) consts + DUK__BYTEOFF_C((ins)))) -#define DUK__CONSTP_BC(ins) ((duk_tval *) (void *) ((duk_uint8_t *) consts + DUK__BYTEOFF_BC((ins)))) -#define DUK__REGCONSTP_B(ins) \ - ((duk_tval *) (void *) ((duk_uint8_t *) (((ins) &DUK__RCBIT_B) ? consts : thr->valstack_bottom) + DUK__BYTEOFF_B((ins)))) -#define DUK__REGCONSTP_C(ins) \ - ((duk_tval *) (void *) ((duk_uint8_t *) (((ins) &DUK__RCBIT_C) ? consts : thr->valstack_bottom) + DUK__BYTEOFF_C((ins)))) -#else /* DUK_USE_EXEC_REGCONST_OPTIMIZE */ -/* Safe alternatives, no assumption about duk_tval size. */ -#define DUK__REGP_A(ins) DUK__REGP(DUK_DEC_A((ins))) -#define DUK__REGP_B(ins) DUK__REGP(DUK_DEC_B((ins))) -#define DUK__REGP_C(ins) DUK__REGP(DUK_DEC_C((ins))) -#define DUK__REGP_BC(ins) DUK__REGP(DUK_DEC_BC((ins))) -#define DUK__CONSTP_A(ins) DUK__CONSTP(DUK_DEC_A((ins))) -#define DUK__CONSTP_B(ins) DUK__CONSTP(DUK_DEC_B((ins))) -#define DUK__CONSTP_C(ins) DUK__CONSTP(DUK_DEC_C((ins))) -#define DUK__CONSTP_BC(ins) DUK__CONSTP(DUK_DEC_BC((ins))) -#define DUK__REGCONSTP_B(ins) ((((ins) &DUK__RCBIT_B) ? consts : thr->valstack_bottom) + DUK_DEC_B((ins))) -#define DUK__REGCONSTP_C(ins) ((((ins) &DUK__RCBIT_C) ? consts : thr->valstack_bottom) + DUK_DEC_C((ins))) -#endif /* DUK_USE_EXEC_REGCONST_OPTIMIZE */ - -#if defined(DUK_USE_VERBOSE_EXECUTOR_ERRORS) -#define DUK__INTERNAL_ERROR(msg) \ - do { \ - DUK_ERROR_ERROR(thr, (msg)); \ - DUK_WO_NORETURN(return;); \ - } while (0) -#else -#define DUK__INTERNAL_ERROR(msg) \ - do { \ - goto internal_error; \ - } while (0) -#endif - -#define DUK__SYNC_CURR_PC() \ - do { \ - duk_activation *duk__act; \ - duk__act = thr->callstack_curr; \ - duk__act->curr_pc = curr_pc; \ - } while (0) -#define DUK__SYNC_AND_NULL_CURR_PC() \ - do { \ - duk_activation *duk__act; \ - duk__act = thr->callstack_curr; \ - duk__act->curr_pc = curr_pc; \ - thr->ptr_curr_pc = NULL; \ - } while (0) - -#if defined(DUK_USE_EXEC_PREFER_SIZE) -#define DUK__LOOKUP_INDIRECT(idx) \ - do { \ - (idx) = (duk_uint_fast_t) duk_get_uint(thr, (duk_idx_t) (idx)); \ - } while (0) -#elif defined(DUK_USE_FASTINT) -#define DUK__LOOKUP_INDIRECT(idx) \ - do { \ - duk_tval *tv_ind; \ - tv_ind = DUK__REGP((idx)); \ - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_ind)); \ - DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv_ind)); /* compiler guarantees */ \ - (idx) = (duk_uint_fast_t) DUK_TVAL_GET_FASTINT_U32(tv_ind); \ - } while (0) -#else -#define DUK__LOOKUP_INDIRECT(idx) \ - do { \ - duk_tval *tv_ind; \ - tv_ind = DUK__REGP(idx); \ - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_ind)); \ - idx = (duk_uint_fast_t) DUK_TVAL_GET_NUMBER(tv_ind); \ - } while (0) -#endif - -DUK_LOCAL void duk__handle_executor_error(duk_heap *heap, - duk_activation *entry_act, - duk_int_t entry_call_recursion_depth, - duk_jmpbuf *entry_jmpbuf_ptr, - volatile duk_bool_t *out_delayed_catch_setup) { - duk_small_uint_t lj_ret; - - /* Longjmp callers are required to sync-and-null thr->ptr_curr_pc - * before longjmp. - */ - DUK_ASSERT(heap->curr_thread != NULL); - DUK_ASSERT(heap->curr_thread->ptr_curr_pc == NULL); - - /* XXX: signalling the need to shrink check (only if unwound) */ - - /* Must be restored here to handle e.g. yields properly. */ - heap->call_recursion_depth = entry_call_recursion_depth; - - /* Switch to caller's setjmp() catcher so that if an error occurs - * during error handling, it is always propagated outwards instead - * of causing an infinite loop in our own handler. - */ - heap->lj.jmpbuf_ptr = (duk_jmpbuf *) entry_jmpbuf_ptr; - - lj_ret = duk__handle_longjmp(heap->curr_thread, entry_act, out_delayed_catch_setup); - - /* Error handling complete, remove side effect protections. - */ -#if defined(DUK_USE_ASSERTIONS) - DUK_ASSERT(heap->error_not_allowed == 1); - heap->error_not_allowed = 0; -#endif - DUK_ASSERT(heap->pf_prevent_count > 0); - heap->pf_prevent_count--; - DUK_DD(DUK_DDPRINT("executor error handled, pf_prevent_count updated to %ld", (long) heap->pf_prevent_count)); - - if (lj_ret == DUK__LONGJMP_RESTART) { - /* Restart bytecode execution, possibly with a changed thread. */ - DUK_REFZERO_CHECK_SLOW(heap->curr_thread); - } else { - /* If an error is propagated, don't run refzero checks here. - * The next catcher will deal with that. Pf_prevent_count - * will be re-bumped by the longjmp. - */ - - DUK_ASSERT(lj_ret == DUK__LONGJMP_RETHROW); /* Rethrow error to calling state. */ - DUK_ASSERT(heap->lj.jmpbuf_ptr == entry_jmpbuf_ptr); /* Longjmp handling has restored jmpbuf_ptr. */ - - /* Thread may have changed, e.g. YIELD converted to THROW. */ - duk_err_longjmp(heap->curr_thread); - DUK_UNREACHABLE(); - } -} - -/* Outer executor with setjmp/longjmp handling. */ -DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { - /* Entry level info. */ - duk_hthread *entry_thread; - duk_activation *entry_act; - duk_int_t entry_call_recursion_depth; - duk_jmpbuf *entry_jmpbuf_ptr; - duk_jmpbuf our_jmpbuf; - duk_heap *heap; - volatile duk_bool_t delayed_catch_setup = 0; - - DUK_ASSERT(exec_thr != NULL); - DUK_ASSERT(exec_thr->heap != NULL); - DUK_ASSERT(exec_thr->heap->curr_thread != NULL); - DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR((duk_heaphdr *) exec_thr); - DUK_ASSERT(exec_thr->callstack_top >= 1); /* at least one activation, ours */ - DUK_ASSERT(exec_thr->callstack_curr != NULL); - DUK_ASSERT(DUK_ACT_GET_FUNC(exec_thr->callstack_curr) != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(exec_thr->callstack_curr))); - - DUK_GC_TORTURE(exec_thr->heap); - - entry_thread = exec_thr; - heap = entry_thread->heap; - entry_act = entry_thread->callstack_curr; - DUK_ASSERT(entry_act != NULL); - entry_call_recursion_depth = entry_thread->heap->call_recursion_depth; - entry_jmpbuf_ptr = entry_thread->heap->lj.jmpbuf_ptr; - - /* - * Note: we currently assume that the setjmp() catchpoint is - * not re-entrant (longjmp() cannot be called more than once - * for a single setjmp()). - * - * See doc/code-issues.rst for notes on variable assignment - * before and after setjmp(). - */ - - for (;;) { - heap->lj.jmpbuf_ptr = &our_jmpbuf; - DUK_ASSERT(heap->lj.jmpbuf_ptr != NULL); - -#if defined(DUK_USE_CPP_EXCEPTIONS) - try { -#else - DUK_ASSERT(heap->lj.jmpbuf_ptr == &our_jmpbuf); - if (DUK_SETJMP(our_jmpbuf.jb) == 0) { -#endif - DUK_DDD(DUK_DDDPRINT("after setjmp, delayed catch setup: %ld\n", (long) delayed_catch_setup)); - - if (DUK_UNLIKELY(delayed_catch_setup != 0)) { - duk_hthread *thr = entry_thread->heap->curr_thread; - - delayed_catch_setup = 0; - duk__handle_catch_part2(thr); - DUK_ASSERT(delayed_catch_setup == 0); - DUK_DDD(DUK_DDDPRINT("top after delayed catch setup: %ld", (long) duk_get_top(entry_thread))); - } - - /* Execute bytecode until returned or longjmp(). */ - duk__js_execute_bytecode_inner(entry_thread, entry_act); - - /* Successful return: restore jmpbuf and return to caller. */ - heap->lj.jmpbuf_ptr = entry_jmpbuf_ptr; - - return; -#if defined(DUK_USE_CPP_EXCEPTIONS) - } catch (duk_internal_exception &exc) { -#else - } else { -#endif -#if defined(DUK_USE_CPP_EXCEPTIONS) - DUK_UNREF(exc); -#endif - DUK_DDD(DUK_DDDPRINT("longjmp caught by bytecode executor")); - DUK_STATS_INC(exec_thr->heap, stats_exec_throw); - - duk__handle_executor_error(heap, - entry_act, - entry_call_recursion_depth, - entry_jmpbuf_ptr, - &delayed_catch_setup); - } -#if defined(DUK_USE_CPP_EXCEPTIONS) - catch (duk_fatal_exception &exc) { - DUK_D(DUK_DPRINT("rethrow duk_fatal_exception")); - DUK_UNREF(exc); - throw; - } catch (std::exception &exc) { - const char *what = exc.what(); - if (!what) { - what = "unknown"; - } - DUK_D(DUK_DPRINT("unexpected c++ std::exception (perhaps thrown by user code)")); - DUK_STATS_INC(exec_thr->heap, stats_exec_throw); - try { - DUK_ASSERT(heap->curr_thread != NULL); - DUK_ERROR_FMT1(heap->curr_thread, - DUK_ERR_TYPE_ERROR, - "caught invalid c++ std::exception '%s' (perhaps thrown by user code)", - what); - DUK_WO_NORETURN(return;); - } catch (duk_internal_exception exc) { - DUK_D(DUK_DPRINT("caught api error thrown from unexpected c++ std::exception")); - DUK_UNREF(exc); - duk__handle_executor_error(heap, - entry_act, - entry_call_recursion_depth, - entry_jmpbuf_ptr, - &delayed_catch_setup); - } - } catch (...) { - DUK_D(DUK_DPRINT("unexpected c++ exception (perhaps thrown by user code)")); - DUK_STATS_INC(exec_thr->heap, stats_exec_throw); - try { - DUK_ASSERT(heap->curr_thread != NULL); - DUK_ERROR_TYPE(heap->curr_thread, "caught invalid c++ exception (perhaps thrown by user code)"); - DUK_WO_NORETURN(return;); - } catch (duk_internal_exception exc) { - DUK_D(DUK_DPRINT("caught api error thrown from unexpected c++ exception")); - DUK_UNREF(exc); - duk__handle_executor_error(heap, - entry_act, - entry_call_recursion_depth, - entry_jmpbuf_ptr, - &delayed_catch_setup); - } - } -#endif - } - - DUK_WO_NORETURN(return;); -} - -/* Inner executor, performance critical. */ -DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread *entry_thread, duk_activation *entry_act) { - /* Current PC, accessed by other functions through thr->ptr_to_curr_pc. - * Critical for performance. It would be safest to make this volatile, - * but that eliminates performance benefits; aliasing guarantees - * should be enough though. - */ - duk_instr_t *curr_pc; /* bytecode has a stable pointer */ - - /* Hot variables for interpretation. Critical for performance, - * but must add sparingly to minimize register shuffling. - */ - duk_hthread *thr; /* stable */ - duk_tval *consts; /* stable */ - duk_uint_fast32_t ins; - /* 'funcs' is quite rarely used, so no local for it */ -#if defined(DUK_USE_EXEC_FUN_LOCAL) - duk_hcompfunc *fun; -#else - /* 'fun' is quite rarely used, so no local for it */ -#endif - -#if defined(DUK_USE_INTERRUPT_COUNTER) - duk_int_t int_ctr; -#endif - -#if defined(DUK_USE_ASSERTIONS) - duk_size_t valstack_top_base; /* valstack top, should match before interpreting each op (no leftovers) */ -#endif - - /* Optimized reg/const access macros assume sizeof(duk_tval) to be - * either 8 or 16. Heap allocation checks this even without asserts - * enabled now because it can't be autodetected in duk_config.h. - */ -#if 1 -#if defined(DUK_USE_PACKED_TVAL) - DUK_ASSERT(sizeof(duk_tval) == 8); -#else - DUK_ASSERT(sizeof(duk_tval) == 16); -#endif -#endif - - DUK_GC_TORTURE(entry_thread->heap); - - /* - * Restart execution by reloading thread state. - * - * Note that 'thr' and any thread configuration may have changed, - * so all local variables are suspect and we need to reinitialize. - * - * The number of local variables should be kept to a minimum: if - * the variables are spilled, they will need to be loaded from - * memory anyway. - * - * Any 'goto restart_execution;' code path in opcode dispatch must - * ensure 'curr_pc' is synced back to act->curr_pc before the goto - * takes place. - * - * The interpreter must be very careful with memory pointers, as - * many pointers are not guaranteed to be 'stable' and may be - * reallocated and relocated on-the-fly quite easily (e.g. by a - * memory allocation or a property access). - * - * The following are assumed to have stable pointers: - * - the current thread - * - the current function - * - the bytecode, constant table, inner function table of the - * current function (as they are a part of the function allocation) - * - * The following are assumed to have semi-stable pointers: - * - the current activation entry: stable as long as callstack - * is not changed (reallocated by growing or shrinking), or - * by any garbage collection invocation (through finalizers) - * - Note in particular that ANY DECREF can invalidate the - * activation pointer, so for the most part a fresh lookup - * is required - * - * The following are not assumed to have stable pointers at all: - * - the value stack (registers) of the current thread - * - * See execution.rst for discussion. - */ - -restart_execution: - - /* Lookup current thread; use the stable 'entry_thread' for this to - * avoid clobber warnings. Any valid, reachable 'thr' value would be - * fine for this, so using 'entry_thread' is just to silence warnings. - */ - thr = entry_thread->heap->curr_thread; - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->callstack_top >= 1); - DUK_ASSERT(thr->callstack_curr != NULL); - DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr))); - - DUK_GC_TORTURE(thr->heap); - - thr->ptr_curr_pc = &curr_pc; - - /* Relookup and initialize dispatch loop variables. Debugger check. */ - { - duk_activation *act; -#if !defined(DUK_USE_EXEC_FUN_LOCAL) - duk_hcompfunc *fun; -#endif - - /* Assume interrupt init/counter are properly initialized here. */ - /* Assume that thr->valstack_bottom has been set-up before getting here. */ - - act = thr->callstack_curr; - DUK_ASSERT(act != NULL); - fun = (duk_hcompfunc *) DUK_ACT_GET_FUNC(act); - DUK_ASSERT(fun != NULL); - DUK_ASSERT(thr->valstack_top - thr->valstack_bottom == fun->nregs); - consts = DUK_HCOMPFUNC_GET_CONSTS_BASE(thr->heap, fun); - DUK_ASSERT(consts != NULL); - -#if defined(DUK_USE_DEBUGGER_SUPPORT) - if (DUK_UNLIKELY(duk_debug_is_attached(thr->heap) && !thr->heap->dbg_processing)) { - duk__executor_recheck_debugger(thr, act, fun); - DUK_ASSERT(act == thr->callstack_curr); - DUK_ASSERT(act != NULL); - } -#endif /* DUK_USE_DEBUGGER_SUPPORT */ - -#if defined(DUK_USE_ASSERTIONS) - valstack_top_base = (duk_size_t) (thr->valstack_top - thr->valstack); -#endif - - /* Set up curr_pc for opcode dispatch. */ - curr_pc = act->curr_pc; - } - - DUK_DD(DUK_DDPRINT("restarting execution, thr %p, act idx %ld, fun %p," - "consts %p, funcs %p, lev %ld, regbot %ld, regtop %ld, " - "preventcount=%ld", - (void *) thr, - (long) (thr->callstack_top - 1), - (void *) DUK__FUN(), - (void *) DUK_HCOMPFUNC_GET_CONSTS_BASE(thr->heap, DUK__FUN()), - (void *) DUK_HCOMPFUNC_GET_FUNCS_BASE(thr->heap, DUK__FUN()), - (long) (thr->callstack_top - 1), - (long) (thr->valstack_bottom - thr->valstack), - (long) (thr->valstack_top - thr->valstack), - (long) thr->callstack_preventcount)); - - /* Dispatch loop. */ - - for (;;) { - duk_uint8_t op; - - DUK_ASSERT(thr->callstack_top >= 1); - DUK_ASSERT(thr->valstack_top - thr->valstack_bottom == DUK__FUN()->nregs); - DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack) == valstack_top_base); - - /* Executor interrupt counter check, used to implement breakpoints, - * debugging interface, execution timeouts, etc. The counter is heap - * specific but is maintained in the current thread to make the check - * as fast as possible. The counter is copied back to the heap struct - * whenever a thread switch occurs by the DUK_HEAP_SWITCH_THREAD() macro. - */ -#if defined(DUK_USE_INTERRUPT_COUNTER) - int_ctr = thr->interrupt_counter; - if (DUK_LIKELY(int_ctr > 0)) { - thr->interrupt_counter = int_ctr - 1; - } else { - /* Trigger at zero or below */ - duk_small_uint_t exec_int_ret; - - DUK_STATS_INC(thr->heap, stats_exec_interrupt); - - /* Write curr_pc back for the debugger. */ - { - duk_activation *act; - DUK_ASSERT(thr->callstack_top > 0); - act = thr->callstack_curr; - DUK_ASSERT(act != NULL); - act->curr_pc = (duk_instr_t *) curr_pc; - } - - /* Forced restart caused by a function return; must recheck - * debugger breakpoints before checking line transitions, - * see GH-303. Restart and then handle interrupt_counter - * zero again. - */ -#if defined(DUK_USE_DEBUGGER_SUPPORT) - if (thr->heap->dbg_force_restart) { - DUK_DD(DUK_DDPRINT("dbg_force_restart flag forced restart execution")); /* GH-303 */ - thr->heap->dbg_force_restart = 0; - goto restart_execution; - } -#endif - - exec_int_ret = duk__executor_interrupt(thr); - if (exec_int_ret == DUK__INT_RESTART) { - /* curr_pc synced back above */ - goto restart_execution; - } - } -#endif /* DUK_USE_INTERRUPT_COUNTER */ -#if defined(DUK_USE_INTERRUPT_COUNTER) && defined(DUK_USE_DEBUG) - /* For cross-checking during development: ensure dispatch count - * matches cumulative interrupt counter init value sums. - */ - thr->heap->inst_count_exec++; -#endif - -#if defined(DUK_USE_ASSERTIONS) || defined(DUK_USE_DEBUG) - { - duk_activation *act; - act = thr->callstack_curr; - DUK_ASSERT(curr_pc >= DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, DUK__FUN())); - DUK_ASSERT(curr_pc < DUK_HCOMPFUNC_GET_CODE_END(thr->heap, DUK__FUN())); - DUK_UNREF(act); /* if debugging disabled */ - - DUK_DDD(DUK_DDDPRINT( - "executing bytecode: pc=%ld, ins=0x%08lx, op=%ld, valstack_top=%ld/%ld, nregs=%ld --> %!I", - (long) (curr_pc - DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, DUK__FUN())), - (unsigned long) *curr_pc, - (long) DUK_DEC_OP(*curr_pc), - (long) (thr->valstack_top - thr->valstack), - (long) (thr->valstack_end - thr->valstack), - (long) (DUK__FUN() ? DUK__FUN()->nregs : -1), - (duk_instr_t) *curr_pc)); - } -#endif - -#if defined(DUK_USE_ASSERTIONS) - /* Quite heavy assert: check valstack policy. Improper - * shuffle instructions can write beyond valstack_top/end - * so this check catches them in the act. - */ - { - duk_tval *tv; - tv = thr->valstack_top; - while (tv != thr->valstack_end) { - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv)); - tv++; - } - } -#endif - - ins = *curr_pc++; - DUK_STATS_INC(thr->heap, stats_exec_opcodes); - - /* Typing: use duk_small_(u)int_fast_t when decoding small - * opcode fields (op, A, B, C, BC) which fit into 16 bits - * and duk_(u)int_fast_t when decoding larger fields (e.g. - * ABC). Use unsigned variant by default, signed when the - * value is used in signed arithmetic. Using variable names - * such as 'a', 'b', 'c', 'bc', etc makes it easier to spot - * typing mismatches. - */ - - /* Switch based on opcode. Cast to 8-bit unsigned value and - * use a fully populated case clauses so that the compiler - * will (at least usually) omit a bounds check. - */ - op = (duk_uint8_t) DUK_DEC_OP(ins); - switch (op) { - /* Some useful macros. These access inner executor variables - * directly so they only apply within the executor. - */ -#if defined(DUK_USE_EXEC_PREFER_SIZE) -#define DUK__REPLACE_TOP_A_BREAK() \ - { goto replace_top_a; } -#define DUK__REPLACE_TOP_BC_BREAK() \ - { goto replace_top_bc; } -#define DUK__REPLACE_BOOL_A_BREAK(bval) \ - { \ - duk_bool_t duk__bval; \ - duk__bval = (bval); \ - DUK_ASSERT(duk__bval == 0 || duk__bval == 1); \ - duk_push_boolean(thr, duk__bval); \ - DUK__REPLACE_TOP_A_BREAK(); \ - } -#else -#define DUK__REPLACE_TOP_A_BREAK() \ - { \ - DUK__REPLACE_TO_TVPTR(thr, DUK__REGP_A(ins)); \ - break; \ - } -#define DUK__REPLACE_TOP_BC_BREAK() \ - { \ - DUK__REPLACE_TO_TVPTR(thr, DUK__REGP_BC(ins)); \ - break; \ - } -#define DUK__REPLACE_BOOL_A_BREAK(bval) \ - { \ - duk_bool_t duk__bval; \ - duk_tval *duk__tvdst; \ - duk__bval = (bval); \ - DUK_ASSERT(duk__bval == 0 || duk__bval == 1); \ - duk__tvdst = DUK__REGP_A(ins); \ - DUK_TVAL_SET_BOOLEAN_UPDREF(thr, duk__tvdst, duk__bval); \ - break; \ - } -#endif - - /* XXX: 12 + 12 bit variant might make sense too, for both reg and - * const loads. - */ - - /* For LDREG, STREG, LDCONST footprint optimized variants would just - * duk_dup() + duk_replace(), but because they're used quite a lot - * they're currently intentionally not size optimized. - */ - case DUK_OP_LDREG: { - duk_tval *tv1, *tv2; - - tv1 = DUK__REGP_A(ins); - tv2 = DUK__REGP_BC(ins); - DUK_TVAL_SET_TVAL_UPDREF_FAST(thr, tv1, tv2); /* side effects */ - break; - } - - case DUK_OP_STREG: { - duk_tval *tv1, *tv2; - - tv1 = DUK__REGP_A(ins); - tv2 = DUK__REGP_BC(ins); - DUK_TVAL_SET_TVAL_UPDREF_FAST(thr, tv2, tv1); /* side effects */ - break; - } - - case DUK_OP_LDCONST: { - duk_tval *tv1, *tv2; - - tv1 = DUK__REGP_A(ins); - tv2 = DUK__CONSTP_BC(ins); - DUK_TVAL_SET_TVAL_UPDREF_FAST(thr, tv1, tv2); /* side effects */ - break; - } - - /* LDINT and LDINTX are intended to load an arbitrary signed - * 32-bit value. Only an LDINT+LDINTX sequence is supported. - * This also guarantees all values remain fastints. - */ -#if defined(DUK_USE_EXEC_PREFER_SIZE) - case DUK_OP_LDINT: { - duk_int32_t val; - - val = (duk_int32_t) DUK_DEC_BC(ins) - (duk_int32_t) DUK_BC_LDINT_BIAS; - duk_push_int(thr, val); - DUK__REPLACE_TOP_A_BREAK(); - } - case DUK_OP_LDINTX: { - duk_int32_t val; - - val = (duk_int32_t) duk_get_int(thr, DUK_DEC_A(ins)); - val = (val << DUK_BC_LDINTX_SHIFT) + (duk_int32_t) DUK_DEC_BC(ins); /* no bias */ - duk_push_int(thr, val); - DUK__REPLACE_TOP_A_BREAK(); - } -#else /* DUK_USE_EXEC_PREFER_SIZE */ - case DUK_OP_LDINT: { - duk_tval *tv1; - duk_int32_t val; - - val = (duk_int32_t) DUK_DEC_BC(ins) - (duk_int32_t) DUK_BC_LDINT_BIAS; - tv1 = DUK__REGP_A(ins); - DUK_TVAL_SET_I32_UPDREF(thr, tv1, val); /* side effects */ - break; - } - case DUK_OP_LDINTX: { - duk_tval *tv1; - duk_int32_t val; - - tv1 = DUK__REGP_A(ins); - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv1)); -#if defined(DUK_USE_FASTINT) - DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv1)); - val = DUK_TVAL_GET_FASTINT_I32(tv1); -#else - /* XXX: fast double-to-int conversion, we know number is integer in [-0x80000000,0xffffffff]. */ - val = (duk_int32_t) DUK_TVAL_GET_NUMBER(tv1); -#endif - val = - (duk_int32_t) ((duk_uint32_t) val << DUK_BC_LDINTX_SHIFT) + (duk_int32_t) DUK_DEC_BC(ins); /* no bias */ - DUK_TVAL_SET_I32_UPDREF(thr, tv1, val); /* side effects */ - break; - } -#endif /* DUK_USE_EXEC_PREFER_SIZE */ - -#if defined(DUK_USE_EXEC_PREFER_SIZE) - case DUK_OP_LDTHIS: { - duk_push_this(thr); - DUK__REPLACE_TOP_BC_BREAK(); - } - case DUK_OP_LDUNDEF: { - duk_to_undefined(thr, (duk_idx_t) DUK_DEC_BC(ins)); - break; - } - case DUK_OP_LDNULL: { - duk_to_null(thr, (duk_idx_t) DUK_DEC_BC(ins)); - break; - } - case DUK_OP_LDTRUE: { - duk_push_true(thr); - DUK__REPLACE_TOP_BC_BREAK(); - } - case DUK_OP_LDFALSE: { - duk_push_false(thr); - DUK__REPLACE_TOP_BC_BREAK(); - } -#else /* DUK_USE_EXEC_PREFER_SIZE */ - case DUK_OP_LDTHIS: { - /* Note: 'this' may be bound to any value, not just an object */ - duk_tval *tv1, *tv2; - - tv1 = DUK__REGP_BC(ins); - tv2 = thr->valstack_bottom - 1; /* 'this binding' is just under bottom */ - DUK_ASSERT(tv2 >= thr->valstack); - DUK_TVAL_SET_TVAL_UPDREF_FAST(thr, tv1, tv2); /* side effects */ - break; - } - case DUK_OP_LDUNDEF: { - duk_tval *tv1; - - tv1 = DUK__REGP_BC(ins); - DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv1); /* side effects */ - break; - } - case DUK_OP_LDNULL: { - duk_tval *tv1; - - tv1 = DUK__REGP_BC(ins); - DUK_TVAL_SET_NULL_UPDREF(thr, tv1); /* side effects */ - break; - } - case DUK_OP_LDTRUE: { - duk_tval *tv1; - - tv1 = DUK__REGP_BC(ins); - DUK_TVAL_SET_BOOLEAN_UPDREF(thr, tv1, 1); /* side effects */ - break; - } - case DUK_OP_LDFALSE: { - duk_tval *tv1; - - tv1 = DUK__REGP_BC(ins); - DUK_TVAL_SET_BOOLEAN_UPDREF(thr, tv1, 0); /* side effects */ - break; - } -#endif /* DUK_USE_EXEC_PREFER_SIZE */ - - case DUK_OP_BNOT: { - duk__vm_bitwise_not(thr, DUK_DEC_BC(ins), DUK_DEC_A(ins)); - break; - } - - case DUK_OP_LNOT: { - duk__vm_logical_not(thr, DUK_DEC_BC(ins), DUK_DEC_A(ins)); - break; - } - -#if defined(DUK_USE_EXEC_PREFER_SIZE) - case DUK_OP_UNM: - case DUK_OP_UNP: { - duk__vm_arith_unary_op(thr, DUK_DEC_BC(ins), DUK_DEC_A(ins), op); - break; - } -#else /* DUK_USE_EXEC_PREFER_SIZE */ - case DUK_OP_UNM: { - duk__vm_arith_unary_op(thr, DUK_DEC_BC(ins), DUK_DEC_A(ins), DUK_OP_UNM); - break; - } - case DUK_OP_UNP: { - duk__vm_arith_unary_op(thr, DUK_DEC_BC(ins), DUK_DEC_A(ins), DUK_OP_UNP); - break; - } -#endif /* DUK_USE_EXEC_PREFER_SIZE */ - -#if defined(DUK_USE_EXEC_PREFER_SIZE) - case DUK_OP_TYPEOF: { - duk_small_uint_t stridx; - - stridx = duk_js_typeof_stridx(DUK__REGP_BC(ins)); - DUK_ASSERT_STRIDX_VALID(stridx); - duk_push_hstring_stridx(thr, stridx); - DUK__REPLACE_TOP_A_BREAK(); - } -#else /* DUK_USE_EXEC_PREFER_SIZE */ - case DUK_OP_TYPEOF: { - duk_tval *tv; - duk_small_uint_t stridx; - duk_hstring *h_str; - - tv = DUK__REGP_BC(ins); - stridx = duk_js_typeof_stridx(tv); - DUK_ASSERT_STRIDX_VALID(stridx); - h_str = DUK_HTHREAD_GET_STRING(thr, stridx); - tv = DUK__REGP_A(ins); - DUK_TVAL_SET_STRING_UPDREF(thr, tv, h_str); - break; - } -#endif /* DUK_USE_EXEC_PREFER_SIZE */ - - case DUK_OP_TYPEOFID: { - duk_small_uint_t stridx; -#if !defined(DUK_USE_EXEC_PREFER_SIZE) - duk_hstring *h_str; -#endif - duk_activation *act; - duk_hstring *name; - duk_tval *tv; - - /* A -> target register - * BC -> constant index of identifier name - */ - - tv = DUK__CONSTP_BC(ins); - DUK_ASSERT(DUK_TVAL_IS_STRING(tv)); - name = DUK_TVAL_GET_STRING(tv); - tv = NULL; /* lookup has side effects */ - act = thr->callstack_curr; - if (duk_js_getvar_activation(thr, act, name, 0 /*throw*/)) { - /* -> [... val this] */ - tv = DUK_GET_TVAL_NEGIDX(thr, -2); - stridx = duk_js_typeof_stridx(tv); - tv = NULL; /* no longer needed */ - duk_pop_2_unsafe(thr); - } else { - /* unresolvable, no stack changes */ - stridx = DUK_STRIDX_LC_UNDEFINED; - } - DUK_ASSERT_STRIDX_VALID(stridx); -#if defined(DUK_USE_EXEC_PREFER_SIZE) - duk_push_hstring_stridx(thr, stridx); - DUK__REPLACE_TOP_A_BREAK(); -#else /* DUK_USE_EXEC_PREFER_SIZE */ - h_str = DUK_HTHREAD_GET_STRING(thr, stridx); - tv = DUK__REGP_A(ins); - DUK_TVAL_SET_STRING_UPDREF(thr, tv, h_str); - break; -#endif /* DUK_USE_EXEC_PREFER_SIZE */ - } - - /* Equality: E5 Sections 11.9.1, 11.9.3 */ - -#define DUK__EQ_BODY(barg, carg) \ - { \ - duk_bool_t tmp; \ - tmp = duk_js_equals(thr, (barg), (carg)); \ - DUK_ASSERT(tmp == 0 || tmp == 1); \ - DUK__REPLACE_BOOL_A_BREAK(tmp); \ - } -#define DUK__NEQ_BODY(barg, carg) \ - { \ - duk_bool_t tmp; \ - tmp = duk_js_equals(thr, (barg), (carg)); \ - DUK_ASSERT(tmp == 0 || tmp == 1); \ - tmp ^= 1; \ - DUK__REPLACE_BOOL_A_BREAK(tmp); \ - } -#define DUK__SEQ_BODY(barg, carg) \ - { \ - duk_bool_t tmp; \ - tmp = duk_js_strict_equals((barg), (carg)); \ - DUK_ASSERT(tmp == 0 || tmp == 1); \ - DUK__REPLACE_BOOL_A_BREAK(tmp); \ - } -#define DUK__SNEQ_BODY(barg, carg) \ - { \ - duk_bool_t tmp; \ - tmp = duk_js_strict_equals((barg), (carg)); \ - DUK_ASSERT(tmp == 0 || tmp == 1); \ - tmp ^= 1; \ - DUK__REPLACE_BOOL_A_BREAK(tmp); \ - } -#if defined(DUK_USE_EXEC_PREFER_SIZE) - case DUK_OP_EQ_RR: - case DUK_OP_EQ_CR: - case DUK_OP_EQ_RC: - case DUK_OP_EQ_CC: - DUK__EQ_BODY(DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins)); - case DUK_OP_NEQ_RR: - case DUK_OP_NEQ_CR: - case DUK_OP_NEQ_RC: - case DUK_OP_NEQ_CC: - DUK__NEQ_BODY(DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins)); - case DUK_OP_SEQ_RR: - case DUK_OP_SEQ_CR: - case DUK_OP_SEQ_RC: - case DUK_OP_SEQ_CC: - DUK__SEQ_BODY(DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins)); - case DUK_OP_SNEQ_RR: - case DUK_OP_SNEQ_CR: - case DUK_OP_SNEQ_RC: - case DUK_OP_SNEQ_CC: - DUK__SNEQ_BODY(DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins)); -#else /* DUK_USE_EXEC_PREFER_SIZE */ - case DUK_OP_EQ_RR: - DUK__EQ_BODY(DUK__REGP_B(ins), DUK__REGP_C(ins)); - case DUK_OP_EQ_CR: - DUK__EQ_BODY(DUK__CONSTP_B(ins), DUK__REGP_C(ins)); - case DUK_OP_EQ_RC: - DUK__EQ_BODY(DUK__REGP_B(ins), DUK__CONSTP_C(ins)); - case DUK_OP_EQ_CC: - DUK__EQ_BODY(DUK__CONSTP_B(ins), DUK__CONSTP_C(ins)); - case DUK_OP_NEQ_RR: - DUK__NEQ_BODY(DUK__REGP_B(ins), DUK__REGP_C(ins)); - case DUK_OP_NEQ_CR: - DUK__NEQ_BODY(DUK__CONSTP_B(ins), DUK__REGP_C(ins)); - case DUK_OP_NEQ_RC: - DUK__NEQ_BODY(DUK__REGP_B(ins), DUK__CONSTP_C(ins)); - case DUK_OP_NEQ_CC: - DUK__NEQ_BODY(DUK__CONSTP_B(ins), DUK__CONSTP_C(ins)); - case DUK_OP_SEQ_RR: - DUK__SEQ_BODY(DUK__REGP_B(ins), DUK__REGP_C(ins)); - case DUK_OP_SEQ_CR: - DUK__SEQ_BODY(DUK__CONSTP_B(ins), DUK__REGP_C(ins)); - case DUK_OP_SEQ_RC: - DUK__SEQ_BODY(DUK__REGP_B(ins), DUK__CONSTP_C(ins)); - case DUK_OP_SEQ_CC: - DUK__SEQ_BODY(DUK__CONSTP_B(ins), DUK__CONSTP_C(ins)); - case DUK_OP_SNEQ_RR: - DUK__SNEQ_BODY(DUK__REGP_B(ins), DUK__REGP_C(ins)); - case DUK_OP_SNEQ_CR: - DUK__SNEQ_BODY(DUK__CONSTP_B(ins), DUK__REGP_C(ins)); - case DUK_OP_SNEQ_RC: - DUK__SNEQ_BODY(DUK__REGP_B(ins), DUK__CONSTP_C(ins)); - case DUK_OP_SNEQ_CC: - DUK__SNEQ_BODY(DUK__CONSTP_B(ins), DUK__CONSTP_C(ins)); -#endif /* DUK_USE_EXEC_PREFER_SIZE */ - -#define DUK__COMPARE_BODY(arg1, arg2, flags) \ - { \ - duk_bool_t tmp; \ - tmp = duk_js_compare_helper(thr, (arg1), (arg2), (flags)); \ - DUK_ASSERT(tmp == 0 || tmp == 1); \ - DUK__REPLACE_BOOL_A_BREAK(tmp); \ - } -#define DUK__GT_BODY(barg, carg) DUK__COMPARE_BODY((carg), (barg), 0) -#define DUK__GE_BODY(barg, carg) DUK__COMPARE_BODY((barg), (carg), DUK_COMPARE_FLAG_EVAL_LEFT_FIRST | DUK_COMPARE_FLAG_NEGATE) -#define DUK__LT_BODY(barg, carg) DUK__COMPARE_BODY((barg), (carg), DUK_COMPARE_FLAG_EVAL_LEFT_FIRST) -#define DUK__LE_BODY(barg, carg) DUK__COMPARE_BODY((carg), (barg), DUK_COMPARE_FLAG_NEGATE) -#if defined(DUK_USE_EXEC_PREFER_SIZE) - case DUK_OP_GT_RR: - case DUK_OP_GT_CR: - case DUK_OP_GT_RC: - case DUK_OP_GT_CC: - DUK__GT_BODY(DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins)); - case DUK_OP_GE_RR: - case DUK_OP_GE_CR: - case DUK_OP_GE_RC: - case DUK_OP_GE_CC: - DUK__GE_BODY(DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins)); - case DUK_OP_LT_RR: - case DUK_OP_LT_CR: - case DUK_OP_LT_RC: - case DUK_OP_LT_CC: - DUK__LT_BODY(DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins)); - case DUK_OP_LE_RR: - case DUK_OP_LE_CR: - case DUK_OP_LE_RC: - case DUK_OP_LE_CC: - DUK__LE_BODY(DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins)); -#else /* DUK_USE_EXEC_PREFER_SIZE */ - case DUK_OP_GT_RR: - DUK__GT_BODY(DUK__REGP_B(ins), DUK__REGP_C(ins)); - case DUK_OP_GT_CR: - DUK__GT_BODY(DUK__CONSTP_B(ins), DUK__REGP_C(ins)); - case DUK_OP_GT_RC: - DUK__GT_BODY(DUK__REGP_B(ins), DUK__CONSTP_C(ins)); - case DUK_OP_GT_CC: - DUK__GT_BODY(DUK__CONSTP_B(ins), DUK__CONSTP_C(ins)); - case DUK_OP_GE_RR: - DUK__GE_BODY(DUK__REGP_B(ins), DUK__REGP_C(ins)); - case DUK_OP_GE_CR: - DUK__GE_BODY(DUK__CONSTP_B(ins), DUK__REGP_C(ins)); - case DUK_OP_GE_RC: - DUK__GE_BODY(DUK__REGP_B(ins), DUK__CONSTP_C(ins)); - case DUK_OP_GE_CC: - DUK__GE_BODY(DUK__CONSTP_B(ins), DUK__CONSTP_C(ins)); - case DUK_OP_LT_RR: - DUK__LT_BODY(DUK__REGP_B(ins), DUK__REGP_C(ins)); - case DUK_OP_LT_CR: - DUK__LT_BODY(DUK__CONSTP_B(ins), DUK__REGP_C(ins)); - case DUK_OP_LT_RC: - DUK__LT_BODY(DUK__REGP_B(ins), DUK__CONSTP_C(ins)); - case DUK_OP_LT_CC: - DUK__LT_BODY(DUK__CONSTP_B(ins), DUK__CONSTP_C(ins)); - case DUK_OP_LE_RR: - DUK__LE_BODY(DUK__REGP_B(ins), DUK__REGP_C(ins)); - case DUK_OP_LE_CR: - DUK__LE_BODY(DUK__CONSTP_B(ins), DUK__REGP_C(ins)); - case DUK_OP_LE_RC: - DUK__LE_BODY(DUK__REGP_B(ins), DUK__CONSTP_C(ins)); - case DUK_OP_LE_CC: - DUK__LE_BODY(DUK__CONSTP_B(ins), DUK__CONSTP_C(ins)); -#endif /* DUK_USE_EXEC_PREFER_SIZE */ - - /* No size optimized variant at present for IF. */ - case DUK_OP_IFTRUE_R: { - if (duk_js_toboolean(DUK__REGP_BC(ins)) != 0) { - curr_pc++; - } - break; - } - case DUK_OP_IFTRUE_C: { - if (duk_js_toboolean(DUK__CONSTP_BC(ins)) != 0) { - curr_pc++; - } - break; - } - case DUK_OP_IFFALSE_R: { - if (duk_js_toboolean(DUK__REGP_BC(ins)) == 0) { - curr_pc++; - } - break; - } - case DUK_OP_IFFALSE_C: { - if (duk_js_toboolean(DUK__CONSTP_BC(ins)) == 0) { - curr_pc++; - } - break; - } - -#if defined(DUK_USE_EXEC_PREFER_SIZE) - case DUK_OP_ADD_RR: - case DUK_OP_ADD_CR: - case DUK_OP_ADD_RC: - case DUK_OP_ADD_CC: { - /* XXX: could leave value on stack top and goto replace_top_a; */ - duk__vm_arith_add(thr, DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins), DUK_DEC_A(ins)); - break; - } -#else /* DUK_USE_EXEC_PREFER_SIZE */ - case DUK_OP_ADD_RR: { - duk__vm_arith_add(thr, DUK__REGP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins)); - break; - } - case DUK_OP_ADD_CR: { - duk__vm_arith_add(thr, DUK__CONSTP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins)); - break; - } - case DUK_OP_ADD_RC: { - duk__vm_arith_add(thr, DUK__REGP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins)); - break; - } - case DUK_OP_ADD_CC: { - duk__vm_arith_add(thr, DUK__CONSTP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins)); - break; - } -#endif /* DUK_USE_EXEC_PREFER_SIZE */ - -#if defined(DUK_USE_EXEC_PREFER_SIZE) - case DUK_OP_SUB_RR: - case DUK_OP_SUB_CR: - case DUK_OP_SUB_RC: - case DUK_OP_SUB_CC: - case DUK_OP_MUL_RR: - case DUK_OP_MUL_CR: - case DUK_OP_MUL_RC: - case DUK_OP_MUL_CC: - case DUK_OP_DIV_RR: - case DUK_OP_DIV_CR: - case DUK_OP_DIV_RC: - case DUK_OP_DIV_CC: - case DUK_OP_MOD_RR: - case DUK_OP_MOD_CR: - case DUK_OP_MOD_RC: - case DUK_OP_MOD_CC: -#if defined(DUK_USE_ES7_EXP_OPERATOR) - case DUK_OP_EXP_RR: - case DUK_OP_EXP_CR: - case DUK_OP_EXP_RC: - case DUK_OP_EXP_CC: -#endif /* DUK_USE_ES7_EXP_OPERATOR */ - { - /* XXX: could leave value on stack top and goto replace_top_a; */ - duk__vm_arith_binary_op(thr, DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins), DUK_DEC_A(ins), op); - break; - } -#else /* DUK_USE_EXEC_PREFER_SIZE */ - case DUK_OP_SUB_RR: { - duk__vm_arith_binary_op(thr, DUK__REGP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_SUB); - break; - } - case DUK_OP_SUB_CR: { - duk__vm_arith_binary_op(thr, DUK__CONSTP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_SUB); - break; - } - case DUK_OP_SUB_RC: { - duk__vm_arith_binary_op(thr, DUK__REGP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_SUB); - break; - } - case DUK_OP_SUB_CC: { - duk__vm_arith_binary_op(thr, DUK__CONSTP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_SUB); - break; - } - case DUK_OP_MUL_RR: { - duk__vm_arith_binary_op(thr, DUK__REGP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_MUL); - break; - } - case DUK_OP_MUL_CR: { - duk__vm_arith_binary_op(thr, DUK__CONSTP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_MUL); - break; - } - case DUK_OP_MUL_RC: { - duk__vm_arith_binary_op(thr, DUK__REGP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_MUL); - break; - } - case DUK_OP_MUL_CC: { - duk__vm_arith_binary_op(thr, DUK__CONSTP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_MUL); - break; - } - case DUK_OP_DIV_RR: { - duk__vm_arith_binary_op(thr, DUK__REGP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_DIV); - break; - } - case DUK_OP_DIV_CR: { - duk__vm_arith_binary_op(thr, DUK__CONSTP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_DIV); - break; - } - case DUK_OP_DIV_RC: { - duk__vm_arith_binary_op(thr, DUK__REGP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_DIV); - break; - } - case DUK_OP_DIV_CC: { - duk__vm_arith_binary_op(thr, DUK__CONSTP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_DIV); - break; - } - case DUK_OP_MOD_RR: { - duk__vm_arith_binary_op(thr, DUK__REGP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_MOD); - break; - } - case DUK_OP_MOD_CR: { - duk__vm_arith_binary_op(thr, DUK__CONSTP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_MOD); - break; - } - case DUK_OP_MOD_RC: { - duk__vm_arith_binary_op(thr, DUK__REGP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_MOD); - break; - } - case DUK_OP_MOD_CC: { - duk__vm_arith_binary_op(thr, DUK__CONSTP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_MOD); - break; - } -#if defined(DUK_USE_ES7_EXP_OPERATOR) - case DUK_OP_EXP_RR: { - duk__vm_arith_binary_op(thr, DUK__REGP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_EXP); - break; - } - case DUK_OP_EXP_CR: { - duk__vm_arith_binary_op(thr, DUK__CONSTP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_EXP); - break; - } - case DUK_OP_EXP_RC: { - duk__vm_arith_binary_op(thr, DUK__REGP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_EXP); - break; - } - case DUK_OP_EXP_CC: { - duk__vm_arith_binary_op(thr, DUK__CONSTP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_EXP); - break; - } -#endif /* DUK_USE_ES7_EXP_OPERATOR */ -#endif /* DUK_USE_EXEC_PREFER_SIZE */ - -#if defined(DUK_USE_EXEC_PREFER_SIZE) - case DUK_OP_BAND_RR: - case DUK_OP_BAND_CR: - case DUK_OP_BAND_RC: - case DUK_OP_BAND_CC: - case DUK_OP_BOR_RR: - case DUK_OP_BOR_CR: - case DUK_OP_BOR_RC: - case DUK_OP_BOR_CC: - case DUK_OP_BXOR_RR: - case DUK_OP_BXOR_CR: - case DUK_OP_BXOR_RC: - case DUK_OP_BXOR_CC: - case DUK_OP_BASL_RR: - case DUK_OP_BASL_CR: - case DUK_OP_BASL_RC: - case DUK_OP_BASL_CC: - case DUK_OP_BLSR_RR: - case DUK_OP_BLSR_CR: - case DUK_OP_BLSR_RC: - case DUK_OP_BLSR_CC: - case DUK_OP_BASR_RR: - case DUK_OP_BASR_CR: - case DUK_OP_BASR_RC: - case DUK_OP_BASR_CC: { - /* XXX: could leave value on stack top and goto replace_top_a; */ - duk__vm_bitwise_binary_op(thr, DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins), DUK_DEC_A(ins), op); - break; - } -#else /* DUK_USE_EXEC_PREFER_SIZE */ - case DUK_OP_BAND_RR: { - duk__vm_bitwise_binary_op(thr, DUK__REGP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_BAND); - break; - } - case DUK_OP_BAND_CR: { - duk__vm_bitwise_binary_op(thr, DUK__CONSTP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_BAND); - break; - } - case DUK_OP_BAND_RC: { - duk__vm_bitwise_binary_op(thr, DUK__REGP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_BAND); - break; - } - case DUK_OP_BAND_CC: { - duk__vm_bitwise_binary_op(thr, DUK__CONSTP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_BAND); - break; - } - case DUK_OP_BOR_RR: { - duk__vm_bitwise_binary_op(thr, DUK__REGP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_BOR); - break; - } - case DUK_OP_BOR_CR: { - duk__vm_bitwise_binary_op(thr, DUK__CONSTP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_BOR); - break; - } - case DUK_OP_BOR_RC: { - duk__vm_bitwise_binary_op(thr, DUK__REGP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_BOR); - break; - } - case DUK_OP_BOR_CC: { - duk__vm_bitwise_binary_op(thr, DUK__CONSTP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_BOR); - break; - } - case DUK_OP_BXOR_RR: { - duk__vm_bitwise_binary_op(thr, DUK__REGP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_BXOR); - break; - } - case DUK_OP_BXOR_CR: { - duk__vm_bitwise_binary_op(thr, DUK__CONSTP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_BXOR); - break; - } - case DUK_OP_BXOR_RC: { - duk__vm_bitwise_binary_op(thr, DUK__REGP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_BXOR); - break; - } - case DUK_OP_BXOR_CC: { - duk__vm_bitwise_binary_op(thr, DUK__CONSTP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_BXOR); - break; - } - case DUK_OP_BASL_RR: { - duk__vm_bitwise_binary_op(thr, DUK__REGP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_BASL); - break; - } - case DUK_OP_BASL_CR: { - duk__vm_bitwise_binary_op(thr, DUK__CONSTP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_BASL); - break; - } - case DUK_OP_BASL_RC: { - duk__vm_bitwise_binary_op(thr, DUK__REGP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_BASL); - break; - } - case DUK_OP_BASL_CC: { - duk__vm_bitwise_binary_op(thr, DUK__CONSTP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_BASL); - break; - } - case DUK_OP_BLSR_RR: { - duk__vm_bitwise_binary_op(thr, DUK__REGP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_BLSR); - break; - } - case DUK_OP_BLSR_CR: { - duk__vm_bitwise_binary_op(thr, DUK__CONSTP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_BLSR); - break; - } - case DUK_OP_BLSR_RC: { - duk__vm_bitwise_binary_op(thr, DUK__REGP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_BLSR); - break; - } - case DUK_OP_BLSR_CC: { - duk__vm_bitwise_binary_op(thr, DUK__CONSTP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_BLSR); - break; - } - case DUK_OP_BASR_RR: { - duk__vm_bitwise_binary_op(thr, DUK__REGP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_BASR); - break; - } - case DUK_OP_BASR_CR: { - duk__vm_bitwise_binary_op(thr, DUK__CONSTP_B(ins), DUK__REGP_C(ins), DUK_DEC_A(ins), DUK_OP_BASR); - break; - } - case DUK_OP_BASR_RC: { - duk__vm_bitwise_binary_op(thr, DUK__REGP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_BASR); - break; - } - case DUK_OP_BASR_CC: { - duk__vm_bitwise_binary_op(thr, DUK__CONSTP_B(ins), DUK__CONSTP_C(ins), DUK_DEC_A(ins), DUK_OP_BASR); - break; - } -#endif /* DUK_USE_EXEC_PREFER_SIZE */ - - /* For INSTOF and IN, B is always a register. */ -#define DUK__INSTOF_BODY(barg, carg) \ - { \ - duk_bool_t tmp; \ - tmp = duk_js_instanceof(thr, (barg), (carg)); \ - DUK_ASSERT(tmp == 0 || tmp == 1); \ - DUK__REPLACE_BOOL_A_BREAK(tmp); \ - } -#define DUK__IN_BODY(barg, carg) \ - { \ - duk_bool_t tmp; \ - tmp = duk_js_in(thr, (barg), (carg)); \ - DUK_ASSERT(tmp == 0 || tmp == 1); \ - DUK__REPLACE_BOOL_A_BREAK(tmp); \ - } -#if defined(DUK_USE_EXEC_PREFER_SIZE) - case DUK_OP_INSTOF_RR: - case DUK_OP_INSTOF_CR: - case DUK_OP_INSTOF_RC: - case DUK_OP_INSTOF_CC: - DUK__INSTOF_BODY(DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins)); - case DUK_OP_IN_RR: - case DUK_OP_IN_CR: - case DUK_OP_IN_RC: - case DUK_OP_IN_CC: - DUK__IN_BODY(DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins)); -#else /* DUK_USE_EXEC_PREFER_SIZE */ - case DUK_OP_INSTOF_RR: - DUK__INSTOF_BODY(DUK__REGP_B(ins), DUK__REGP_C(ins)); - case DUK_OP_INSTOF_CR: - DUK__INSTOF_BODY(DUK__CONSTP_B(ins), DUK__REGP_C(ins)); - case DUK_OP_INSTOF_RC: - DUK__INSTOF_BODY(DUK__REGP_B(ins), DUK__CONSTP_C(ins)); - case DUK_OP_INSTOF_CC: - DUK__INSTOF_BODY(DUK__CONSTP_B(ins), DUK__CONSTP_C(ins)); - case DUK_OP_IN_RR: - DUK__IN_BODY(DUK__REGP_B(ins), DUK__REGP_C(ins)); - case DUK_OP_IN_CR: - DUK__IN_BODY(DUK__CONSTP_B(ins), DUK__REGP_C(ins)); - case DUK_OP_IN_RC: - DUK__IN_BODY(DUK__REGP_B(ins), DUK__CONSTP_C(ins)); - case DUK_OP_IN_CC: - DUK__IN_BODY(DUK__CONSTP_B(ins), DUK__CONSTP_C(ins)); -#endif /* DUK_USE_EXEC_PREFER_SIZE */ - - /* Pre/post inc/dec for register variables, important for loops. */ -#if defined(DUK_USE_EXEC_PREFER_SIZE) - case DUK_OP_PREINCR: - case DUK_OP_PREDECR: - case DUK_OP_POSTINCR: - case DUK_OP_POSTDECR: { - duk__prepost_incdec_reg_helper(thr, DUK__REGP_A(ins), DUK__REGP_BC(ins), op); - break; - } - case DUK_OP_PREINCV: - case DUK_OP_PREDECV: - case DUK_OP_POSTINCV: - case DUK_OP_POSTDECV: { - duk__prepost_incdec_var_helper(thr, DUK_DEC_A(ins), DUK__CONSTP_BC(ins), op, DUK__STRICT()); - break; - } -#else /* DUK_USE_EXEC_PREFER_SIZE */ - case DUK_OP_PREINCR: { - duk__prepost_incdec_reg_helper(thr, DUK__REGP_A(ins), DUK__REGP_BC(ins), DUK_OP_PREINCR); - break; - } - case DUK_OP_PREDECR: { - duk__prepost_incdec_reg_helper(thr, DUK__REGP_A(ins), DUK__REGP_BC(ins), DUK_OP_PREDECR); - break; - } - case DUK_OP_POSTINCR: { - duk__prepost_incdec_reg_helper(thr, DUK__REGP_A(ins), DUK__REGP_BC(ins), DUK_OP_POSTINCR); - break; - } - case DUK_OP_POSTDECR: { - duk__prepost_incdec_reg_helper(thr, DUK__REGP_A(ins), DUK__REGP_BC(ins), DUK_OP_POSTDECR); - break; - } - case DUK_OP_PREINCV: { - duk__prepost_incdec_var_helper(thr, DUK_DEC_A(ins), DUK__CONSTP_BC(ins), DUK_OP_PREINCV, DUK__STRICT()); - break; - } - case DUK_OP_PREDECV: { - duk__prepost_incdec_var_helper(thr, DUK_DEC_A(ins), DUK__CONSTP_BC(ins), DUK_OP_PREDECV, DUK__STRICT()); - break; - } - case DUK_OP_POSTINCV: { - duk__prepost_incdec_var_helper(thr, DUK_DEC_A(ins), DUK__CONSTP_BC(ins), DUK_OP_POSTINCV, DUK__STRICT()); - break; - } - case DUK_OP_POSTDECV: { - duk__prepost_incdec_var_helper(thr, DUK_DEC_A(ins), DUK__CONSTP_BC(ins), DUK_OP_POSTDECV, DUK__STRICT()); - break; - } -#endif /* DUK_USE_EXEC_PREFER_SIZE */ - - /* XXX: Move to separate helper, optimize for perf/size separately. */ - /* Preinc/predec for object properties. */ - case DUK_OP_PREINCP_RR: - case DUK_OP_PREINCP_CR: - case DUK_OP_PREINCP_RC: - case DUK_OP_PREINCP_CC: - case DUK_OP_PREDECP_RR: - case DUK_OP_PREDECP_CR: - case DUK_OP_PREDECP_RC: - case DUK_OP_PREDECP_CC: - case DUK_OP_POSTINCP_RR: - case DUK_OP_POSTINCP_CR: - case DUK_OP_POSTINCP_RC: - case DUK_OP_POSTINCP_CC: - case DUK_OP_POSTDECP_RR: - case DUK_OP_POSTDECP_CR: - case DUK_OP_POSTDECP_RC: - case DUK_OP_POSTDECP_CC: { - duk_tval *tv_obj; - duk_tval *tv_key; - duk_tval *tv_val; - duk_bool_t rc; - duk_double_t x, y, z; -#if !defined(DUK_USE_EXEC_PREFER_SIZE) - duk_tval *tv_dst; -#endif /* DUK_USE_EXEC_PREFER_SIZE */ - - /* A -> target reg - * B -> object reg/const (may be const e.g. in "'foo'[1]") - * C -> key reg/const - */ - - /* Opcode bits 0-1 are used to distinguish reg/const variants. - * Opcode bits 2-3 are used to distinguish inc/dec variants: - * Bit 2 = inc(0)/dec(1), bit 3 = pre(0)/post(1). - */ - DUK_ASSERT((DUK_OP_PREINCP_RR & 0x0c) == 0x00); - DUK_ASSERT((DUK_OP_PREDECP_RR & 0x0c) == 0x04); - DUK_ASSERT((DUK_OP_POSTINCP_RR & 0x0c) == 0x08); - DUK_ASSERT((DUK_OP_POSTDECP_RR & 0x0c) == 0x0c); - - tv_obj = DUK__REGCONSTP_B(ins); - tv_key = DUK__REGCONSTP_C(ins); - rc = duk_hobject_getprop(thr, tv_obj, tv_key); /* -> [val] */ - DUK_UNREF(rc); /* ignore */ - tv_obj = NULL; /* invalidated */ - tv_key = NULL; /* invalidated */ - - /* XXX: Fastint fast path would be useful here. Also fastints - * now lose their fastint status in current handling which is - * not intuitive. - */ - - x = duk_to_number_m1(thr); - duk_pop_unsafe(thr); - if (ins & DUK_BC_INCDECP_FLAG_DEC) { - y = x - 1.0; - } else { - y = x + 1.0; - } - - duk_push_number(thr, y); - tv_val = DUK_GET_TVAL_NEGIDX(thr, -1); - DUK_ASSERT(tv_val != NULL); - tv_obj = DUK__REGCONSTP_B(ins); - tv_key = DUK__REGCONSTP_C(ins); - rc = duk_hobject_putprop(thr, tv_obj, tv_key, tv_val, DUK__STRICT()); - DUK_UNREF(rc); /* ignore */ - tv_obj = NULL; /* invalidated */ - tv_key = NULL; /* invalidated */ - duk_pop_unsafe(thr); - - z = (ins & DUK_BC_INCDECP_FLAG_POST) ? x : y; -#if defined(DUK_USE_EXEC_PREFER_SIZE) - duk_push_number(thr, z); - DUK__REPLACE_TOP_A_BREAK(); -#else - tv_dst = DUK__REGP_A(ins); - DUK_TVAL_SET_NUMBER_UPDREF(thr, tv_dst, z); - break; -#endif - } - - /* XXX: GETPROP where object is 'this', GETPROPT? - * Occurs relatively often in object oriented code. - */ - -#define DUK__GETPROP_BODY(barg, carg) \ - { \ - /* A -> target reg \ - * B -> object reg/const (may be const e.g. in "'foo'[1]") \ - * C -> key reg/const \ - */ \ - (void) duk_hobject_getprop(thr, (barg), (carg)); \ - DUK__REPLACE_TOP_A_BREAK(); \ - } -#define DUK__GETPROPC_BODY(barg, carg) \ - { \ - /* Same as GETPROP but callability check for property-based calls. */ \ - duk_tval *tv__targ; \ - (void) duk_hobject_getprop(thr, (barg), (carg)); \ - DUK_GC_TORTURE(thr->heap); \ - tv__targ = DUK_GET_TVAL_NEGIDX(thr, -1); \ - if (DUK_UNLIKELY(!duk_is_callable_tval(thr, tv__targ))) { \ - /* Here we intentionally re-evaluate the macro \ - * arguments to deal with potentially changed \ - * valstack base pointer! \ - */ \ - duk_call_setup_propcall_error(thr, (barg), (carg)); \ - } \ - DUK__REPLACE_TOP_A_BREAK(); \ - } -#define DUK__PUTPROP_BODY(aarg, barg, carg) \ - { \ - /* A -> object reg \ - * B -> key reg/const \ - * C -> value reg/const \ - * \ - * Note: intentional difference to register arrangement \ - * of e.g. GETPROP; 'A' must contain a register-only value. \ - */ \ - (void) duk_hobject_putprop(thr, (aarg), (barg), (carg), DUK__STRICT()); \ - break; \ - } -#define DUK__DELPROP_BODY(barg, carg) \ - { \ - /* A -> result reg \ - * B -> object reg \ - * C -> key reg/const \ - */ \ - duk_bool_t rc; \ - rc = duk_hobject_delprop(thr, (barg), (carg), DUK__STRICT()); \ - DUK_ASSERT(rc == 0 || rc == 1); \ - DUK__REPLACE_BOOL_A_BREAK(rc); \ - } -#if defined(DUK_USE_EXEC_PREFER_SIZE) - case DUK_OP_GETPROP_RR: - case DUK_OP_GETPROP_CR: - case DUK_OP_GETPROP_RC: - case DUK_OP_GETPROP_CC: - DUK__GETPROP_BODY(DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins)); -#if defined(DUK_USE_VERBOSE_ERRORS) - case DUK_OP_GETPROPC_RR: - case DUK_OP_GETPROPC_CR: - case DUK_OP_GETPROPC_RC: - case DUK_OP_GETPROPC_CC: - DUK__GETPROPC_BODY(DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins)); -#endif - case DUK_OP_PUTPROP_RR: - case DUK_OP_PUTPROP_CR: - case DUK_OP_PUTPROP_RC: - case DUK_OP_PUTPROP_CC: - DUK__PUTPROP_BODY(DUK__REGP_A(ins), DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins)); - case DUK_OP_DELPROP_RR: - case DUK_OP_DELPROP_RC: /* B is always reg */ - DUK__DELPROP_BODY(DUK__REGP_B(ins), DUK__REGCONSTP_C(ins)); -#else /* DUK_USE_EXEC_PREFER_SIZE */ - case DUK_OP_GETPROP_RR: - DUK__GETPROP_BODY(DUK__REGP_B(ins), DUK__REGP_C(ins)); - case DUK_OP_GETPROP_CR: - DUK__GETPROP_BODY(DUK__CONSTP_B(ins), DUK__REGP_C(ins)); - case DUK_OP_GETPROP_RC: - DUK__GETPROP_BODY(DUK__REGP_B(ins), DUK__CONSTP_C(ins)); - case DUK_OP_GETPROP_CC: - DUK__GETPROP_BODY(DUK__CONSTP_B(ins), DUK__CONSTP_C(ins)); -#if defined(DUK_USE_VERBOSE_ERRORS) - case DUK_OP_GETPROPC_RR: - DUK__GETPROPC_BODY(DUK__REGP_B(ins), DUK__REGP_C(ins)); - case DUK_OP_GETPROPC_CR: - DUK__GETPROPC_BODY(DUK__CONSTP_B(ins), DUK__REGP_C(ins)); - case DUK_OP_GETPROPC_RC: - DUK__GETPROPC_BODY(DUK__REGP_B(ins), DUK__CONSTP_C(ins)); - case DUK_OP_GETPROPC_CC: - DUK__GETPROPC_BODY(DUK__CONSTP_B(ins), DUK__CONSTP_C(ins)); -#endif - case DUK_OP_PUTPROP_RR: - DUK__PUTPROP_BODY(DUK__REGP_A(ins), DUK__REGP_B(ins), DUK__REGP_C(ins)); - case DUK_OP_PUTPROP_CR: - DUK__PUTPROP_BODY(DUK__REGP_A(ins), DUK__CONSTP_B(ins), DUK__REGP_C(ins)); - case DUK_OP_PUTPROP_RC: - DUK__PUTPROP_BODY(DUK__REGP_A(ins), DUK__REGP_B(ins), DUK__CONSTP_C(ins)); - case DUK_OP_PUTPROP_CC: - DUK__PUTPROP_BODY(DUK__REGP_A(ins), DUK__CONSTP_B(ins), DUK__CONSTP_C(ins)); - case DUK_OP_DELPROP_RR: /* B is always reg */ - DUK__DELPROP_BODY(DUK__REGP_B(ins), DUK__REGP_C(ins)); - case DUK_OP_DELPROP_RC: - DUK__DELPROP_BODY(DUK__REGP_B(ins), DUK__CONSTP_C(ins)); -#endif /* DUK_USE_EXEC_PREFER_SIZE */ - - /* No fast path for DECLVAR now, it's quite a rare instruction. */ - case DUK_OP_DECLVAR_RR: - case DUK_OP_DECLVAR_CR: - case DUK_OP_DECLVAR_RC: - case DUK_OP_DECLVAR_CC: { - duk_activation *act; - duk_small_uint_fast_t a = DUK_DEC_A(ins); - duk_tval *tv1; - duk_hstring *name; - duk_small_uint_t prop_flags; - duk_bool_t is_func_decl; - - tv1 = DUK__REGCONSTP_B(ins); - DUK_ASSERT(DUK_TVAL_IS_STRING(tv1)); - name = DUK_TVAL_GET_STRING(tv1); - DUK_ASSERT(name != NULL); - - is_func_decl = ((a & DUK_BC_DECLVAR_FLAG_FUNC_DECL) != 0); - - /* XXX: declvar takes an duk_tval pointer, which is awkward and - * should be reworked. - */ - - /* Compiler is responsible for selecting property flags (configurability, - * writability, etc). - */ - prop_flags = a & DUK_PROPDESC_FLAGS_MASK; - - if (is_func_decl) { - duk_push_tval(thr, DUK__REGCONSTP_C(ins)); - } else { - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top)); /* valstack policy */ - thr->valstack_top++; - } - tv1 = DUK_GET_TVAL_NEGIDX(thr, -1); - - act = thr->callstack_curr; - if (duk_js_declvar_activation(thr, act, name, tv1, prop_flags, is_func_decl)) { - if (is_func_decl) { - /* Already declared, update value. */ - tv1 = DUK_GET_TVAL_NEGIDX(thr, -1); - duk_js_putvar_activation(thr, act, name, tv1, DUK__STRICT()); - } else { - /* Already declared but no initializer value - * (e.g. 'var xyz;'), no-op. - */ - } - } - - duk_pop_unsafe(thr); - break; - } - -#if defined(DUK_USE_REGEXP_SUPPORT) - /* The compiler should never emit DUK_OP_REGEXP if there is no - * regexp support. - */ - case DUK_OP_REGEXP_RR: - case DUK_OP_REGEXP_CR: - case DUK_OP_REGEXP_RC: - case DUK_OP_REGEXP_CC: { - /* A -> target register - * B -> bytecode (also contains flags) - * C -> escaped source - */ - - duk_push_tval(thr, DUK__REGCONSTP_C(ins)); - duk_push_tval(thr, DUK__REGCONSTP_B(ins)); /* -> [ ... escaped_source bytecode ] */ - duk_regexp_create_instance(thr); /* -> [ ... regexp_instance ] */ - DUK__REPLACE_TOP_A_BREAK(); - } -#endif /* DUK_USE_REGEXP_SUPPORT */ - - /* XXX: 'c' is unused, use whole BC, etc. */ - case DUK_OP_CSVAR_RR: - case DUK_OP_CSVAR_CR: - case DUK_OP_CSVAR_RC: - case DUK_OP_CSVAR_CC: { - /* The speciality of calling through a variable binding is that the - * 'this' value may be provided by the variable lookup: E5 Section 6.b.i. - * - * The only (standard) case where the 'this' binding is non-null is when - * (1) the variable is found in an object environment record, and - * (2) that object environment record is a 'with' block. - */ - - duk_activation *act; - duk_uint_fast_t idx; - duk_tval *tv1; - duk_hstring *name; - - /* A -> target registers (A, A + 1) for call setup - * B -> identifier name, usually constant but can be a register due to shuffling - */ - - tv1 = DUK__REGCONSTP_B(ins); - DUK_ASSERT(DUK_TVAL_IS_STRING(tv1)); - name = DUK_TVAL_GET_STRING(tv1); - DUK_ASSERT(name != NULL); - act = thr->callstack_curr; - (void) duk_js_getvar_activation(thr, act, name, 1 /*throw*/); /* -> [... val this] */ - - idx = (duk_uint_fast_t) DUK_DEC_A(ins); - - /* Could add direct value stack handling. */ - duk_replace(thr, (duk_idx_t) (idx + 1)); /* 'this' binding */ - duk_replace(thr, (duk_idx_t) idx); /* variable value (function, we hope, not checked here) */ - break; - } - - case DUK_OP_CLOSURE: { - duk_activation *act; - duk_hcompfunc *fun_act; - duk_small_uint_fast_t bc = DUK_DEC_BC(ins); - duk_hobject *fun_temp; - - /* A -> target reg - * BC -> inner function index - */ - - DUK_DDD(DUK_DDDPRINT("CLOSURE to target register %ld, fnum %ld (count %ld)", - (long) DUK_DEC_A(ins), - (long) DUK_DEC_BC(ins), - (long) DUK_HCOMPFUNC_GET_FUNCS_COUNT(thr->heap, DUK__FUN()))); - - DUK_ASSERT_DISABLE(bc >= 0); /* unsigned */ - DUK_ASSERT((duk_uint_t) bc < (duk_uint_t) DUK_HCOMPFUNC_GET_FUNCS_COUNT(thr->heap, DUK__FUN())); - - act = thr->callstack_curr; - fun_act = (duk_hcompfunc *) DUK_ACT_GET_FUNC(act); - fun_temp = DUK_HCOMPFUNC_GET_FUNCS_BASE(thr->heap, fun_act)[bc]; - DUK_ASSERT(fun_temp != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(fun_temp)); - - DUK_DDD( - DUK_DDDPRINT("CLOSURE: function template is: %p -> %!O", (void *) fun_temp, (duk_heaphdr *) fun_temp)); - - if (act->lex_env == NULL) { - DUK_ASSERT(act->var_env == NULL); - duk_js_init_activation_environment_records_delayed(thr, act); - act = thr->callstack_curr; - } - DUK_ASSERT(act->lex_env != NULL); - DUK_ASSERT(act->var_env != NULL); - - /* functions always have a NEWENV flag, i.e. they get a - * new variable declaration environment, so only lex_env - * matters here. - */ - duk_js_push_closure(thr, (duk_hcompfunc *) fun_temp, act->var_env, act->lex_env, 1 /*add_auto_proto*/); - DUK__REPLACE_TOP_A_BREAK(); - } - - case DUK_OP_GETVAR: { - duk_activation *act; - duk_tval *tv1; - duk_hstring *name; - - tv1 = DUK__CONSTP_BC(ins); - DUK_ASSERT(DUK_TVAL_IS_STRING(tv1)); - name = DUK_TVAL_GET_STRING(tv1); - DUK_ASSERT(name != NULL); - act = thr->callstack_curr; - DUK_ASSERT(act != NULL); - (void) duk_js_getvar_activation(thr, act, name, 1 /*throw*/); /* -> [... val this] */ - duk_pop_unsafe(thr); /* 'this' binding is not needed here */ - DUK__REPLACE_TOP_A_BREAK(); - } - - case DUK_OP_PUTVAR: { - duk_activation *act; - duk_tval *tv1; - duk_hstring *name; - - tv1 = DUK__CONSTP_BC(ins); - DUK_ASSERT(DUK_TVAL_IS_STRING(tv1)); - name = DUK_TVAL_GET_STRING(tv1); - DUK_ASSERT(name != NULL); - - /* XXX: putvar takes a duk_tval pointer, which is awkward and - * should be reworked. - */ - - tv1 = DUK__REGP_A(ins); /* val */ - act = thr->callstack_curr; - duk_js_putvar_activation(thr, act, name, tv1, DUK__STRICT()); - break; - } - - case DUK_OP_DELVAR: { - duk_activation *act; - duk_tval *tv1; - duk_hstring *name; - duk_bool_t rc; - - tv1 = DUK__CONSTP_BC(ins); - DUK_ASSERT(DUK_TVAL_IS_STRING(tv1)); - name = DUK_TVAL_GET_STRING(tv1); - DUK_ASSERT(name != NULL); - act = thr->callstack_curr; - rc = duk_js_delvar_activation(thr, act, name); - DUK__REPLACE_BOOL_A_BREAK(rc); - } - - case DUK_OP_JUMP: { - /* Note: without explicit cast to signed, MSVC will - * apparently generate a large positive jump when the - * bias-corrected value would normally be negative. - */ - curr_pc += (duk_int_fast_t) DUK_DEC_ABC(ins) - (duk_int_fast_t) DUK_BC_JUMP_BIAS; - break; - } - -#define DUK__RETURN_SHARED() \ - do { \ - duk_small_uint_t ret_result; \ - /* duk__handle_return() is guaranteed never to throw, except \ - * for potential out-of-memory situations which will then \ - * propagate out of the executor longjmp handler. \ - */ \ - DUK_ASSERT(thr->ptr_curr_pc == NULL); \ - ret_result = duk__handle_return(thr, entry_act); \ - if (ret_result == DUK__RETHAND_RESTART) { \ - goto restart_execution; \ - } \ - DUK_ASSERT(ret_result == DUK__RETHAND_FINISHED); \ - return; \ - } while (0) -#if defined(DUK_USE_EXEC_PREFER_SIZE) - case DUK_OP_RETREG: - case DUK_OP_RETCONST: - case DUK_OP_RETCONSTN: - case DUK_OP_RETUNDEF: { - /* BC -> return value reg/const */ - - DUK__SYNC_AND_NULL_CURR_PC(); - - if (op == DUK_OP_RETREG) { - duk_push_tval(thr, DUK__REGP_BC(ins)); - } else if (op == DUK_OP_RETUNDEF) { - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top)); /* valstack policy */ - thr->valstack_top++; - } else { - DUK_ASSERT(op == DUK_OP_RETCONST || op == DUK_OP_RETCONSTN); - duk_push_tval(thr, DUK__CONSTP_BC(ins)); - } - - DUK__RETURN_SHARED(); - } -#else /* DUK_USE_EXEC_PREFER_SIZE */ - case DUK_OP_RETREG: { - duk_tval *tv; - - DUK__SYNC_AND_NULL_CURR_PC(); - tv = DUK__REGP_BC(ins); - DUK_TVAL_SET_TVAL(thr->valstack_top, tv); - DUK_TVAL_INCREF(thr, tv); - thr->valstack_top++; - DUK__RETURN_SHARED(); - } - /* This will be unused without refcounting. */ - case DUK_OP_RETCONST: { - duk_tval *tv; - - DUK__SYNC_AND_NULL_CURR_PC(); - tv = DUK__CONSTP_BC(ins); - DUK_TVAL_SET_TVAL(thr->valstack_top, tv); - DUK_TVAL_INCREF(thr, tv); - thr->valstack_top++; - DUK__RETURN_SHARED(); - } - case DUK_OP_RETCONSTN: { - duk_tval *tv; - - DUK__SYNC_AND_NULL_CURR_PC(); - tv = DUK__CONSTP_BC(ins); - DUK_TVAL_SET_TVAL(thr->valstack_top, tv); -#if defined(DUK_USE_REFERENCE_COUNTING) - /* Without refcounting only RETCONSTN is used. */ - DUK_ASSERT(!DUK_TVAL_IS_HEAP_ALLOCATED(tv)); /* no INCREF for this constant */ -#endif - thr->valstack_top++; - DUK__RETURN_SHARED(); - } - case DUK_OP_RETUNDEF: { - DUK__SYNC_AND_NULL_CURR_PC(); - thr->valstack_top++; /* value at valstack top is already undefined by valstack policy */ - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top)); - DUK__RETURN_SHARED(); - } -#endif /* DUK_USE_EXEC_PREFER_SIZE */ - - case DUK_OP_LABEL: { - duk_activation *act; - duk_catcher *cat; - duk_small_uint_fast_t bc = DUK_DEC_BC(ins); - - /* Allocate catcher and populate it (must be atomic). */ - - cat = duk_hthread_catcher_alloc(thr); - DUK_ASSERT(cat != NULL); - - cat->flags = (duk_uint32_t) (DUK_CAT_TYPE_LABEL | (bc << DUK_CAT_LABEL_SHIFT)); - cat->pc_base = (duk_instr_t *) curr_pc; /* pre-incremented, points to first jump slot */ - cat->idx_base = 0; /* unused for label */ - cat->h_varname = NULL; - - act = thr->callstack_curr; - DUK_ASSERT(act != NULL); - cat->parent = act->cat; - act->cat = cat; - - DUK_DDD(DUK_DDDPRINT("LABEL catcher: flags=0x%08lx, pc_base=%ld, " - "idx_base=%ld, h_varname=%!O, label_id=%ld", - (long) cat->flags, - (long) cat->pc_base, - (long) cat->idx_base, - (duk_heaphdr *) cat->h_varname, - (long) DUK_CAT_GET_LABEL(cat))); - - curr_pc += 2; /* skip jump slots */ - break; - } - - case DUK_OP_ENDLABEL: { - duk_activation *act; -#if (defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2)) || defined(DUK_USE_ASSERTIONS) - duk_small_uint_fast_t bc = DUK_DEC_BC(ins); -#endif -#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2) - DUK_DDD(DUK_DDDPRINT("ENDLABEL %ld", (long) bc)); -#endif - - act = thr->callstack_curr; - DUK_ASSERT(act->cat != NULL); - DUK_ASSERT(DUK_CAT_GET_TYPE(act->cat) == DUK_CAT_TYPE_LABEL); - DUK_ASSERT((duk_uint_fast_t) DUK_CAT_GET_LABEL(act->cat) == bc); - duk_hthread_catcher_unwind_nolexenv_norz(thr, act); - - /* no need to unwind callstack */ - break; - } - - case DUK_OP_BREAK: { - duk_small_uint_fast_t bc = DUK_DEC_BC(ins); - - DUK__SYNC_AND_NULL_CURR_PC(); - duk__handle_break_or_continue(thr, (duk_uint_t) bc, DUK_LJ_TYPE_BREAK); - goto restart_execution; - } - - case DUK_OP_CONTINUE: { - duk_small_uint_fast_t bc = DUK_DEC_BC(ins); - - DUK__SYNC_AND_NULL_CURR_PC(); - duk__handle_break_or_continue(thr, (duk_uint_t) bc, DUK_LJ_TYPE_CONTINUE); - goto restart_execution; - } - - /* XXX: move to helper, too large to be inline here */ - case DUK_OP_TRYCATCH: { - duk__handle_op_trycatch(thr, ins, curr_pc); - curr_pc += 2; /* skip jump slots */ - break; - } - - case DUK_OP_ENDTRY: { - curr_pc = duk__handle_op_endtry(thr, ins); - break; - } - - case DUK_OP_ENDCATCH: { - duk__handle_op_endcatch(thr, ins); - break; - } - - case DUK_OP_ENDFIN: { - /* Sync and NULL early. */ - DUK__SYNC_AND_NULL_CURR_PC(); - - if (duk__handle_op_endfin(thr, ins, entry_act) != 0) { - return; - } - - /* Must restart because we NULLed out curr_pc. */ - goto restart_execution; - } - - case DUK_OP_THROW: { - duk_small_uint_fast_t bc = DUK_DEC_BC(ins); - - /* Note: errors are augmented when they are created, not - * when they are thrown. So, don't augment here, it would - * break re-throwing for instance. - */ - - /* Sync so that augmentation sees up-to-date activations, NULL - * thr->ptr_curr_pc so that it's not used if side effects occur - * in augmentation or longjmp handling. - */ - DUK__SYNC_AND_NULL_CURR_PC(); - - duk_dup(thr, (duk_idx_t) bc); - DUK_DDD(DUK_DDDPRINT("THROW ERROR (BYTECODE): %!dT (before throw augment)", - (duk_tval *) duk_get_tval(thr, -1))); -#if defined(DUK_USE_AUGMENT_ERROR_THROW) - duk_err_augment_error_throw(thr); - DUK_DDD( - DUK_DDDPRINT("THROW ERROR (BYTECODE): %!dT (after throw augment)", (duk_tval *) duk_get_tval(thr, -1))); -#endif - - duk_err_setup_ljstate1(thr, DUK_LJ_TYPE_THROW, DUK_GET_TVAL_NEGIDX(thr, -1)); -#if defined(DUK_USE_DEBUGGER_SUPPORT) - duk_err_check_debugger_integration(thr); -#endif - - DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL); /* always in executor */ - duk_err_longjmp(thr); - DUK_UNREACHABLE(); - break; - } - - case DUK_OP_CSREG: { - /* - * Assuming a register binds to a variable declared within this - * function (a declarative binding), the 'this' for the call - * setup is always 'undefined'. E5 Section 10.2.1.1.6. - */ - - duk_small_uint_fast_t a = DUK_DEC_A(ins); - duk_small_uint_fast_t bc = DUK_DEC_BC(ins); - - /* A -> register containing target function (not type checked here) - * BC -> target registers (BC, BC + 1) for call setup - */ - -#if defined(DUK_USE_PREFER_SIZE) - duk_dup(thr, (duk_idx_t) a); - duk_replace(thr, (duk_idx_t) bc); - duk_to_undefined(thr, (duk_idx_t) (bc + 1)); -#else - duk_tval *tv1; - duk_tval *tv2; - duk_tval *tv3; - duk_tval tv_tmp1; - duk_tval tv_tmp2; - - tv1 = DUK__REGP(bc); - tv2 = tv1 + 1; - DUK_TVAL_SET_TVAL(&tv_tmp1, tv1); - DUK_TVAL_SET_TVAL(&tv_tmp2, tv2); - tv3 = DUK__REGP(a); - DUK_TVAL_SET_TVAL(tv1, tv3); - DUK_TVAL_INCREF(thr, tv1); /* no side effects */ - DUK_TVAL_SET_UNDEFINED(tv2); /* no need for incref */ - DUK_TVAL_DECREF(thr, &tv_tmp1); - DUK_TVAL_DECREF(thr, &tv_tmp2); -#endif - break; - } - - /* XXX: in some cases it's faster NOT to reuse the value - * stack but rather copy the arguments on top of the stack - * (mainly when the calling value stack is large and the value - * stack resize would be large). - */ - - case DUK_OP_CALL0: - case DUK_OP_CALL1: - case DUK_OP_CALL2: - case DUK_OP_CALL3: - case DUK_OP_CALL4: - case DUK_OP_CALL5: - case DUK_OP_CALL6: - case DUK_OP_CALL7: { - /* Opcode packs 4 flag bits: 1 for indirect, 3 map - * 1:1 to three lowest call handling flags. - * - * A -> nargs or register with nargs (indirect) - * BC -> base register for call (base -> func, base+1 -> this, base+2 -> arg1 ... base+2+N-1 -> argN) - */ - - duk_idx_t nargs; - duk_idx_t idx; - duk_small_uint_t call_flags; -#if !defined(DUK_USE_EXEC_FUN_LOCAL) - duk_hcompfunc *fun; -#endif - - DUK_ASSERT((DUK_OP_CALL0 & 0x0fU) == 0); - DUK_ASSERT((ins & DUK_BC_CALL_FLAG_INDIRECT) == 0); - - nargs = (duk_idx_t) DUK_DEC_A(ins); - call_flags = (ins & 0x07U) | DUK_CALL_FLAG_ALLOW_ECMATOECMA; - idx = (duk_idx_t) DUK_DEC_BC(ins); - - if (duk__executor_handle_call(thr, idx, nargs, call_flags)) { - /* curr_pc synced by duk_handle_call_unprotected() */ - DUK_ASSERT(thr->ptr_curr_pc == NULL); - goto restart_execution; - } - DUK_ASSERT(thr->ptr_curr_pc != NULL); - - /* duk_js_call.c is required to restore the stack reserve - * so we only need to reset the top. - */ -#if !defined(DUK_USE_EXEC_FUN_LOCAL) - fun = DUK__FUN(); -#endif - duk_set_top_unsafe(thr, (duk_idx_t) fun->nregs); - - /* No need to reinit setjmp() catchpoint, as call handling - * will store and restore our state. - * - * When debugger is enabled, we need to recheck the activation - * status after returning. This is now handled by call handling - * and heap->dbg_force_restart. - */ - break; - } - - case DUK_OP_CALL8: - case DUK_OP_CALL9: - case DUK_OP_CALL10: - case DUK_OP_CALL11: - case DUK_OP_CALL12: - case DUK_OP_CALL13: - case DUK_OP_CALL14: - case DUK_OP_CALL15: { - /* Indirect variant. */ - duk_uint_fast_t nargs; - duk_idx_t idx; - duk_small_uint_t call_flags; -#if !defined(DUK_USE_EXEC_FUN_LOCAL) - duk_hcompfunc *fun; -#endif - - DUK_ASSERT((DUK_OP_CALL0 & 0x0fU) == 0); - DUK_ASSERT((ins & DUK_BC_CALL_FLAG_INDIRECT) != 0); - - nargs = (duk_uint_fast_t) DUK_DEC_A(ins); - DUK__LOOKUP_INDIRECT(nargs); - call_flags = (ins & 0x07U) | DUK_CALL_FLAG_ALLOW_ECMATOECMA; - idx = (duk_idx_t) DUK_DEC_BC(ins); - - if (duk__executor_handle_call(thr, idx, (duk_idx_t) nargs, call_flags)) { - DUK_ASSERT(thr->ptr_curr_pc == NULL); - goto restart_execution; - } - DUK_ASSERT(thr->ptr_curr_pc != NULL); - -#if !defined(DUK_USE_EXEC_FUN_LOCAL) - fun = DUK__FUN(); -#endif - duk_set_top_unsafe(thr, (duk_idx_t) fun->nregs); - break; - } - - case DUK_OP_NEWOBJ: { - duk_push_object(thr); -#if defined(DUK_USE_ASSERTIONS) - { - duk_hobject *h; - h = duk_require_hobject(thr, -1); - DUK_ASSERT(DUK_HOBJECT_GET_ESIZE(h) == 0); - DUK_ASSERT(DUK_HOBJECT_GET_ENEXT(h) == 0); - DUK_ASSERT(DUK_HOBJECT_GET_ASIZE(h) == 0); - DUK_ASSERT(DUK_HOBJECT_GET_HSIZE(h) == 0); - } -#endif -#if !defined(DUK_USE_PREFER_SIZE) - /* XXX: could do a direct props realloc, but need hash size */ - duk_hobject_resize_entrypart(thr, duk_known_hobject(thr, -1), DUK_DEC_A(ins)); -#endif - DUK__REPLACE_TOP_BC_BREAK(); - } - - case DUK_OP_NEWARR: { - duk_push_array(thr); -#if defined(DUK_USE_ASSERTIONS) - { - duk_hobject *h; - h = duk_require_hobject(thr, -1); - DUK_ASSERT(DUK_HOBJECT_GET_ESIZE(h) == 0); - DUK_ASSERT(DUK_HOBJECT_GET_ENEXT(h) == 0); - DUK_ASSERT(DUK_HOBJECT_GET_ASIZE(h) == 0); - DUK_ASSERT(DUK_HOBJECT_GET_HSIZE(h) == 0); - DUK_ASSERT(DUK_HOBJECT_HAS_ARRAY_PART(h)); - } -#endif -#if !defined(DUK_USE_PREFER_SIZE) - duk_hobject_realloc_props(thr, - duk_known_hobject(thr, -1), - 0 /*new_e_size*/, - DUK_DEC_A(ins) /*new_a_size*/, - 0 /*new_h_size*/, - 0 /*abandon_array*/); -#if 0 - duk_hobject_resize_arraypart(thr, duk_known_hobject(thr, -1), DUK_DEC_A(ins)); -#endif -#endif - DUK__REPLACE_TOP_BC_BREAK(); - } - - case DUK_OP_MPUTOBJ: - case DUK_OP_MPUTOBJI: { - duk_idx_t obj_idx; - duk_uint_fast_t idx, idx_end; - duk_small_uint_fast_t count; - - /* A -> register of target object - * B -> first register of key/value pair list - * or register containing first register number if indirect - * C -> number of key/value pairs * 2 - * (= number of value stack indices used starting from 'B') - */ - - obj_idx = DUK_DEC_A(ins); - DUK_ASSERT(duk_is_object(thr, obj_idx)); - - idx = (duk_uint_fast_t) DUK_DEC_B(ins); - if (DUK_DEC_OP(ins) == DUK_OP_MPUTOBJI) { - DUK__LOOKUP_INDIRECT(idx); - } - - count = (duk_small_uint_fast_t) DUK_DEC_C(ins); - DUK_ASSERT(count > 0); /* compiler guarantees */ - idx_end = idx + count; - -#if defined(DUK_USE_EXEC_INDIRECT_BOUND_CHECK) - if (DUK_UNLIKELY(idx_end > (duk_uint_fast_t) duk_get_top(thr))) { - /* XXX: use duk_is_valid_index() instead? */ - /* XXX: improve check; check against nregs, not against top */ - DUK__INTERNAL_ERROR("MPUTOBJ out of bounds"); - } -#endif - - /* Use 'force' flag to duk_def_prop() to ensure that any - * inherited properties don't prevent the operation. - * With ES2015 duplicate properties are allowed, so that we - * must overwrite any previous data or accessor property. - * - * With ES2015 computed property names the literal keys - * may be arbitrary values and need to be ToPropertyKey() - * coerced at runtime. - */ - do { - /* XXX: faster initialization (direct access or better primitives) */ - duk_dup(thr, (duk_idx_t) idx); - duk_dup(thr, (duk_idx_t) (idx + 1)); - duk_def_prop(thr, - obj_idx, - DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_FORCE | DUK_DEFPROP_SET_WRITABLE | - DUK_DEFPROP_SET_ENUMERABLE | DUK_DEFPROP_SET_CONFIGURABLE); - idx += 2; - } while (idx < idx_end); - break; - } - - case DUK_OP_INITSET: - case DUK_OP_INITGET: { - duk__handle_op_initset_initget(thr, ins); - break; - } - - case DUK_OP_MPUTARR: - case DUK_OP_MPUTARRI: { - duk_idx_t obj_idx; - duk_uint_fast_t idx, idx_end; - duk_small_uint_fast_t count; - duk_tval *tv1; - duk_uint32_t arr_idx; - - /* A -> register of target object - * B -> first register of value data (start_index, value1, value2, ..., valueN) - * or register containing first register number if indirect - * C -> number of key/value pairs (N) - */ - - obj_idx = DUK_DEC_A(ins); - DUK_ASSERT(duk_is_object(thr, obj_idx)); - - idx = (duk_uint_fast_t) DUK_DEC_B(ins); - if (DUK_DEC_OP(ins) == DUK_OP_MPUTARRI) { - DUK__LOOKUP_INDIRECT(idx); - } - - count = (duk_small_uint_fast_t) DUK_DEC_C(ins); - DUK_ASSERT(count > 0 + 1); /* compiler guarantees */ - idx_end = idx + count; - -#if defined(DUK_USE_EXEC_INDIRECT_BOUND_CHECK) - if (idx_end > (duk_uint_fast_t) duk_get_top(thr)) { - /* XXX: use duk_is_valid_index() instead? */ - /* XXX: improve check; check against nregs, not against top */ - DUK__INTERNAL_ERROR("MPUTARR out of bounds"); - } -#endif - - tv1 = DUK__REGP(idx); - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv1)); -#if defined(DUK_USE_FASTINT) - DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv1)); - arr_idx = (duk_uint32_t) DUK_TVAL_GET_FASTINT_U32(tv1); -#else - arr_idx = (duk_uint32_t) DUK_TVAL_GET_NUMBER(tv1); -#endif - idx++; - - do { - /* duk_xdef_prop() will define an own property without any array - * special behaviors. We'll need to set the array length explicitly - * in the end. For arrays with elisions, the compiler will emit an - * explicit SETALEN which will update the length. - */ - - /* XXX: because we're dealing with 'own' properties of a fresh array, - * the array initializer should just ensure that the array has a large - * enough array part and write the values directly into array part, - * and finally set 'length' manually in the end (as already happens now). - */ - - duk_dup(thr, (duk_idx_t) idx); - duk_xdef_prop_index_wec(thr, obj_idx, arr_idx); - - idx++; - arr_idx++; - } while (idx < idx_end); - - /* XXX: E5.1 Section 11.1.4 coerces the final length through - * ToUint32() which is odd but happens now as a side effect of - * 'arr_idx' type. - */ - duk_set_length(thr, obj_idx, (duk_size_t) (duk_uarridx_t) arr_idx); - break; - } - - case DUK_OP_SETALEN: { - duk_tval *tv1; - duk_hobject *h; - duk_uint32_t len; - - tv1 = DUK__REGP_A(ins); - DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv1)); - h = DUK_TVAL_GET_OBJECT(tv1); - DUK_ASSERT(DUK_HOBJECT_IS_ARRAY(h)); - - tv1 = DUK__REGP_BC(ins); - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv1)); -#if defined(DUK_USE_FASTINT) - DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv1)); - len = (duk_uint32_t) DUK_TVAL_GET_FASTINT_U32(tv1); -#else - len = (duk_uint32_t) DUK_TVAL_GET_NUMBER(tv1); -#endif - ((duk_harray *) h)->length = len; - break; - } - - case DUK_OP_INITENUM: { - duk__handle_op_initenum(thr, ins); - break; - } - - case DUK_OP_NEXTENUM: { - curr_pc += duk__handle_op_nextenum(thr, ins); - break; - } - - case DUK_OP_INVLHS: { - DUK_ERROR_REFERENCE(thr, DUK_STR_INVALID_LVALUE); - DUK_WO_NORETURN(return;); - break; - } - - case DUK_OP_DEBUGGER: { - /* Opcode only emitted by compiler when debugger - * support is enabled. Ignore it silently without - * debugger support, in case it has been loaded - * from precompiled bytecode. - */ -#if defined(DUK_USE_DEBUGGER_SUPPORT) - if (duk_debug_is_attached(thr->heap)) { - DUK_D(DUK_DPRINT("DEBUGGER statement encountered, halt execution")); - DUK__SYNC_AND_NULL_CURR_PC(); - duk_debug_halt_execution(thr, 1 /*use_prev_pc*/); - DUK_D(DUK_DPRINT("DEBUGGER statement finished, resume execution")); - goto restart_execution; - } else { - DUK_D(DUK_DPRINT("DEBUGGER statement ignored, debugger not attached")); - } -#else - DUK_D(DUK_DPRINT("DEBUGGER statement ignored, no debugger support")); -#endif - break; - } - - case DUK_OP_NOP: { - /* Nop, ignored, but ABC fields may carry a value e.g. - * for indirect opcode handling. - */ - break; - } - - case DUK_OP_INVALID: { - DUK_ERROR_FMT1(thr, DUK_ERR_ERROR, "INVALID opcode (%ld)", (long) DUK_DEC_ABC(ins)); - DUK_WO_NORETURN(return;); - break; - } - -#if defined(DUK_USE_ES6) - case DUK_OP_NEWTARGET: { - duk_push_new_target(thr); - DUK__REPLACE_TOP_BC_BREAK(); - } -#endif /* DUK_USE_ES6 */ - -#if !defined(DUK_USE_EXEC_PREFER_SIZE) -#if !defined(DUK_USE_ES7_EXP_OPERATOR) - case DUK_OP_EXP_RR: - case DUK_OP_EXP_CR: - case DUK_OP_EXP_RC: - case DUK_OP_EXP_CC: -#endif -#if !defined(DUK_USE_ES6) - case DUK_OP_NEWTARGET: -#endif -#if !defined(DUK_USE_VERBOSE_ERRORS) - case DUK_OP_GETPROPC_RR: - case DUK_OP_GETPROPC_CR: - case DUK_OP_GETPROPC_RC: - case DUK_OP_GETPROPC_CC: -#endif - case DUK_OP_UNUSED207: - case DUK_OP_UNUSED212: - case DUK_OP_UNUSED213: - case DUK_OP_UNUSED214: - case DUK_OP_UNUSED215: - case DUK_OP_UNUSED216: - case DUK_OP_UNUSED217: - case DUK_OP_UNUSED218: - case DUK_OP_UNUSED219: - case DUK_OP_UNUSED220: - case DUK_OP_UNUSED221: - case DUK_OP_UNUSED222: - case DUK_OP_UNUSED223: - case DUK_OP_UNUSED224: - case DUK_OP_UNUSED225: - case DUK_OP_UNUSED226: - case DUK_OP_UNUSED227: - case DUK_OP_UNUSED228: - case DUK_OP_UNUSED229: - case DUK_OP_UNUSED230: - case DUK_OP_UNUSED231: - case DUK_OP_UNUSED232: - case DUK_OP_UNUSED233: - case DUK_OP_UNUSED234: - case DUK_OP_UNUSED235: - case DUK_OP_UNUSED236: - case DUK_OP_UNUSED237: - case DUK_OP_UNUSED238: - case DUK_OP_UNUSED239: - case DUK_OP_UNUSED240: - case DUK_OP_UNUSED241: - case DUK_OP_UNUSED242: - case DUK_OP_UNUSED243: - case DUK_OP_UNUSED244: - case DUK_OP_UNUSED245: - case DUK_OP_UNUSED246: - case DUK_OP_UNUSED247: - case DUK_OP_UNUSED248: - case DUK_OP_UNUSED249: - case DUK_OP_UNUSED250: - case DUK_OP_UNUSED251: - case DUK_OP_UNUSED252: - case DUK_OP_UNUSED253: - case DUK_OP_UNUSED254: - case DUK_OP_UNUSED255: - /* Force all case clauses to map to an actual handler - * so that the compiler can emit a jump without a bounds - * check: the switch argument is a duk_uint8_t so that - * the compiler may be able to figure it out. This is - * a small detail and obviously compiler dependent. - */ - /* default: clause omitted on purpose */ -#else /* DUK_USE_EXEC_PREFER_SIZE */ - default: -#endif /* DUK_USE_EXEC_PREFER_SIZE */ - { - /* Default case catches invalid/unsupported opcodes. */ - DUK_D(DUK_DPRINT("invalid opcode: %ld - %!I", (long) op, ins)); - DUK__INTERNAL_ERROR("invalid opcode"); - break; - } - - } /* end switch */ - - continue; - - /* Some shared exit paths for opcode handling below. These - * are mostly useful to reduce code footprint when multiple - * opcodes have a similar epilogue (like replacing stack top - * with index 'a'). - */ - -#if defined(DUK_USE_EXEC_PREFER_SIZE) - replace_top_a: - DUK__REPLACE_TO_TVPTR(thr, DUK__REGP_A(ins)); - continue; - replace_top_bc: - DUK__REPLACE_TO_TVPTR(thr, DUK__REGP_BC(ins)); - continue; -#endif - } - DUK_WO_NORETURN(return;); - -#if !defined(DUK_USE_VERBOSE_EXECUTOR_ERRORS) -internal_error: - DUK_ERROR_INTERNAL(thr); - DUK_WO_NORETURN(return;); -#endif -} - -/* automatic undefs */ -#undef DUK__BYTEOFF_A -#undef DUK__BYTEOFF_B -#undef DUK__BYTEOFF_BC -#undef DUK__BYTEOFF_C -#undef DUK__COMPARE_BODY -#undef DUK__CONST -#undef DUK__CONSTP -#undef DUK__CONSTP_A -#undef DUK__CONSTP_B -#undef DUK__CONSTP_BC -#undef DUK__CONSTP_C -#undef DUK__DELPROP_BODY -#undef DUK__EQ_BODY -#undef DUK__FUN -#undef DUK__GETPROPC_BODY -#undef DUK__GETPROP_BODY -#undef DUK__GE_BODY -#undef DUK__GT_BODY -#undef DUK__INSTOF_BODY -#undef DUK__INTERNAL_ERROR -#undef DUK__INT_NOACTION -#undef DUK__INT_RESTART -#undef DUK__IN_BODY -#undef DUK__LE_BODY -#undef DUK__LONGJMP_RESTART -#undef DUK__LONGJMP_RETHROW -#undef DUK__LOOKUP_INDIRECT -#undef DUK__LT_BODY -#undef DUK__MASK_A -#undef DUK__MASK_B -#undef DUK__MASK_BC -#undef DUK__MASK_C -#undef DUK__NEQ_BODY -#undef DUK__PUTPROP_BODY -#undef DUK__RCBIT_B -#undef DUK__RCBIT_C -#undef DUK__REG -#undef DUK__REGCONSTP_B -#undef DUK__REGCONSTP_C -#undef DUK__REGP -#undef DUK__REGP_A -#undef DUK__REGP_B -#undef DUK__REGP_BC -#undef DUK__REGP_C -#undef DUK__REPLACE_BOOL_A_BREAK -#undef DUK__REPLACE_TOP_A_BREAK -#undef DUK__REPLACE_TOP_BC_BREAK -#undef DUK__REPLACE_TO_TVPTR -#undef DUK__RETHAND_FINISHED -#undef DUK__RETHAND_RESTART -#undef DUK__RETURN_SHARED -#undef DUK__SEQ_BODY -#undef DUK__SHIFT_A -#undef DUK__SHIFT_B -#undef DUK__SHIFT_BC -#undef DUK__SHIFT_C -#undef DUK__SNEQ_BODY -#undef DUK__STRICT -#undef DUK__SYNC_AND_NULL_CURR_PC -#undef DUK__SYNC_CURR_PC -#undef DUK__TVAL_SHIFT -/* - * ECMAScript specification algorithm and conversion helpers. - * - * These helpers encapsulate the primitive ECMAScript operation semantics, - * and are used by the bytecode executor and the API (among other places). - * Some primitives are only implemented as part of the API and have no - * "internal" helper. This is the case when an internal helper would not - * really be useful; e.g. the operation is rare, uses value stack heavily, - * etc. - * - * The operation arguments depend on what is required to implement - * the operation: - * - * - If an operation is simple and stateless, and has no side - * effects, it won't take an duk_hthread argument and its - * arguments may be duk_tval pointers (which are safe as long - * as no side effects take place). - * - * - If complex coercions are required (e.g. a "ToNumber" coercion) - * or errors may be thrown, the operation takes an duk_hthread - * argument. This also implies that the operation may have - * arbitrary side effects, invalidating any duk_tval pointers. - * - * - For operations with potential side effects, arguments can be - * taken in several ways: - * - * a) as duk_tval pointers, which makes sense if the "common case" - * can be resolved without side effects (e.g. coercion); the - * arguments are pushed to the valstack for coercion if - * necessary - * - * b) as duk_tval values - * - * c) implicitly on value stack top - * - * d) as indices to the value stack - * - * Future work: - * - * - Argument styles may not be the most sensible in every case now. - * - * - In-place coercions might be useful for several operations, if - * in-place coercion is OK for the bytecode executor and the API. - */ - -/* #include duk_internal.h -> already included */ - -/* - * ToPrimitive() (E5 Section 9.1) - * - * ==> implemented in the API. - */ - -/* - * ToBoolean() (E5 Section 9.2) - */ - -DUK_INTERNAL duk_bool_t duk_js_toboolean(duk_tval *tv) { - switch (DUK_TVAL_GET_TAG(tv)) { - case DUK_TAG_UNDEFINED: - case DUK_TAG_NULL: - return 0; - case DUK_TAG_BOOLEAN: - DUK_ASSERT(DUK_TVAL_GET_BOOLEAN(tv) == 0 || DUK_TVAL_GET_BOOLEAN(tv) == 1); - return DUK_TVAL_GET_BOOLEAN(tv); - case DUK_TAG_STRING: { - /* Symbols ToBoolean() coerce to true, regardless of their - * description. This happens with no explicit check because - * of the symbol representation byte prefix. - */ - duk_hstring *h = DUK_TVAL_GET_STRING(tv); - DUK_ASSERT(h != NULL); - return (DUK_HSTRING_GET_BYTELEN(h) > 0 ? 1 : 0); - } - case DUK_TAG_OBJECT: { - return 1; - } - case DUK_TAG_BUFFER: { - /* Mimic Uint8Array semantics: objects coerce true, regardless - * of buffer length (zero or not) or context. - */ - return 1; - } - case DUK_TAG_POINTER: { - void *p = DUK_TVAL_GET_POINTER(tv); - return (p != NULL ? 1 : 0); - } - case DUK_TAG_LIGHTFUNC: { - return 1; - } -#if defined(DUK_USE_FASTINT) - case DUK_TAG_FASTINT: - if (DUK_TVAL_GET_FASTINT(tv) != 0) { - return 1; - } else { - return 0; - } -#endif - default: { - /* number */ - duk_double_t d; -#if defined(DUK_USE_PREFER_SIZE) - int c; -#endif - DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv)); - DUK_ASSERT(DUK_TVAL_IS_DOUBLE(tv)); - d = DUK_TVAL_GET_DOUBLE(tv); -#if defined(DUK_USE_PREFER_SIZE) - c = DUK_FPCLASSIFY((double) d); - if (c == DUK_FP_ZERO || c == DUK_FP_NAN) { - return 0; - } else { - return 1; - } -#else - DUK_ASSERT(duk_double_is_nan_or_zero(d) == 0 || duk_double_is_nan_or_zero(d) == 1); - return duk_double_is_nan_or_zero(d) ^ 1; -#endif - } - } - DUK_UNREACHABLE(); - DUK_WO_UNREACHABLE(return 0;); -} - -/* - * ToNumber() (E5 Section 9.3) - * - * Value to convert must be on stack top, and is popped before exit. - * - * See: http://www.cs.indiana.edu/~burger/FP-Printing-PLDI96.pdf - * http://www.cs.indiana.edu/~burger/fp/index.html - * - * Notes on the conversion: - * - * - There are specific requirements on the accuracy of the conversion - * through a "Mathematical Value" (MV), so this conversion is not - * trivial. - * - * - Quick rejects (e.g. based on first char) are difficult because - * the grammar allows leading and trailing white space. - * - * - Quick reject based on string length is difficult even after - * accounting for white space; there may be arbitrarily many - * decimal digits. - * - * - Standard grammar allows decimal values ("123"), hex values - * ("0x123") and infinities - * - * - Unlike source code literals, ToNumber() coerces empty strings - * and strings with only whitespace to zero (not NaN). However, - * while '' coerces to 0, '+' and '-' coerce to NaN. - */ - -/* E5 Section 9.3.1 */ -DUK_LOCAL duk_double_t duk__tonumber_string_raw(duk_hthread *thr) { - duk_small_uint_t s2n_flags; - duk_double_t d; - - DUK_ASSERT(duk_is_string(thr, -1)); - - /* Quite lenient, e.g. allow empty as zero, but don't allow trailing - * garbage. - */ - s2n_flags = DUK_S2N_FLAG_TRIM_WHITE | DUK_S2N_FLAG_ALLOW_EXP | DUK_S2N_FLAG_ALLOW_PLUS | DUK_S2N_FLAG_ALLOW_MINUS | - DUK_S2N_FLAG_ALLOW_INF | DUK_S2N_FLAG_ALLOW_FRAC | DUK_S2N_FLAG_ALLOW_NAKED_FRAC | - DUK_S2N_FLAG_ALLOW_EMPTY_FRAC | DUK_S2N_FLAG_ALLOW_EMPTY_AS_ZERO | DUK_S2N_FLAG_ALLOW_LEADING_ZERO | - DUK_S2N_FLAG_ALLOW_AUTO_HEX_INT | DUK_S2N_FLAG_ALLOW_AUTO_OCT_INT | DUK_S2N_FLAG_ALLOW_AUTO_BIN_INT; - - duk_numconv_parse(thr, 10 /*radix*/, s2n_flags); - -#if defined(DUK_USE_PREFER_SIZE) - d = duk_get_number(thr, -1); - duk_pop_unsafe(thr); -#else - thr->valstack_top--; - DUK_ASSERT(DUK_TVAL_IS_NUMBER(thr->valstack_top)); - DUK_ASSERT(DUK_TVAL_IS_DOUBLE(thr->valstack_top)); /* no fastint conversion in numconv now */ - DUK_ASSERT(!DUK_TVAL_NEEDS_REFCOUNT_UPDATE(thr->valstack_top)); - d = DUK_TVAL_GET_DOUBLE(thr->valstack_top); /* assumes not a fastint */ - DUK_TVAL_SET_UNDEFINED(thr->valstack_top); -#endif - - return d; -} - -DUK_INTERNAL duk_double_t duk_js_tonumber(duk_hthread *thr, duk_tval *tv) { - DUK_ASSERT(thr != NULL); - DUK_ASSERT(tv != NULL); - - switch (DUK_TVAL_GET_TAG(tv)) { - case DUK_TAG_UNDEFINED: { - /* return a specific NaN (although not strictly necessary) */ - duk_double_union du; - DUK_DBLUNION_SET_NAN(&du); - DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du)); - return du.d; - } - case DUK_TAG_NULL: { - /* +0.0 */ - return 0.0; - } - case DUK_TAG_BOOLEAN: { - if (DUK_TVAL_IS_BOOLEAN_TRUE(tv)) { - return 1.0; - } - return 0.0; - } - case DUK_TAG_STRING: { - /* For Symbols ToNumber() is always a TypeError. */ - duk_hstring *h = DUK_TVAL_GET_STRING(tv); - if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) { - DUK_ERROR_TYPE(thr, DUK_STR_CANNOT_NUMBER_COERCE_SYMBOL); - DUK_WO_NORETURN(return 0.0;); - } - duk_push_hstring(thr, h); - return duk__tonumber_string_raw(thr); - } - case DUK_TAG_BUFFER: /* plain buffer treated like object */ - case DUK_TAG_OBJECT: { - duk_double_t d; - duk_push_tval(thr, tv); - duk_to_primitive(thr, -1, DUK_HINT_NUMBER); /* 'tv' becomes invalid */ - - /* recursive call for a primitive value (guaranteed not to cause second - * recursion). - */ - DUK_ASSERT(duk_get_tval(thr, -1) != NULL); - d = duk_js_tonumber(thr, duk_get_tval(thr, -1)); - - duk_pop_unsafe(thr); - return d; - } - case DUK_TAG_POINTER: { - /* Coerce like boolean */ - void *p = DUK_TVAL_GET_POINTER(tv); - return (p != NULL ? 1.0 : 0.0); - } - case DUK_TAG_LIGHTFUNC: { - /* +(function(){}) -> NaN */ - return DUK_DOUBLE_NAN; - } -#if defined(DUK_USE_FASTINT) - case DUK_TAG_FASTINT: - return (duk_double_t) DUK_TVAL_GET_FASTINT(tv); -#endif - default: { - /* number */ - DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv)); - DUK_ASSERT(DUK_TVAL_IS_DOUBLE(tv)); - return DUK_TVAL_GET_DOUBLE(tv); - } - } - - DUK_UNREACHABLE(); - DUK_WO_UNREACHABLE(return 0.0;); -} - -/* - * ToInteger() (E5 Section 9.4) - */ - -/* exposed, used by e.g. duk_bi_date.c */ -DUK_INTERNAL duk_double_t duk_js_tointeger_number(duk_double_t x) { -#if defined(DUK_USE_PREFER_SIZE) - duk_small_int_t c = (duk_small_int_t) DUK_FPCLASSIFY(x); - - if (DUK_UNLIKELY(c == DUK_FP_NAN)) { - return 0.0; - } else if (DUK_UNLIKELY(c == DUK_FP_INFINITE)) { - return x; - } else { - /* Finite, including neg/pos zero. Neg zero sign must be - * preserved. - */ - return duk_double_trunc_towards_zero(x); - } -#else /* DUK_USE_PREFER_SIZE */ - /* NaN and Infinity have the same exponent so it's a cheap - * initial check for the rare path. - */ - if (DUK_UNLIKELY(duk_double_is_nan_or_inf(x) != 0U)) { - if (duk_double_is_nan(x)) { - return 0.0; - } else { - return x; - } - } else { - return duk_double_trunc_towards_zero(x); - } -#endif /* DUK_USE_PREFER_SIZE */ -} - -DUK_INTERNAL duk_double_t duk_js_tointeger(duk_hthread *thr, duk_tval *tv) { - /* XXX: fastint */ - duk_double_t d = duk_js_tonumber(thr, tv); /* invalidates tv */ - return duk_js_tointeger_number(d); -} - -/* - * ToInt32(), ToUint32(), ToUint16() (E5 Sections 9.5, 9.6, 9.7) - */ - -/* combined algorithm matching E5 Sections 9.5 and 9.6 */ -DUK_LOCAL duk_double_t duk__toint32_touint32_helper(duk_double_t x, duk_bool_t is_toint32) { -#if defined(DUK_USE_PREFER_SIZE) - duk_small_int_t c; -#endif - -#if defined(DUK_USE_PREFER_SIZE) - c = (duk_small_int_t) DUK_FPCLASSIFY(x); - if (c == DUK_FP_NAN || c == DUK_FP_ZERO || c == DUK_FP_INFINITE) { - return 0.0; - } -#else - if (duk_double_is_nan_zero_inf(x)) { - return 0.0; - } -#endif - - /* x = sign(x) * floor(abs(x)), i.e. truncate towards zero, keep sign */ - x = duk_double_trunc_towards_zero(x); - - /* NOTE: fmod(x) result sign is same as sign of x, which - * differs from what Javascript wants (see Section 9.6). - */ - - x = DUK_FMOD(x, DUK_DOUBLE_2TO32); /* -> x in ]-2**32, 2**32[ */ - - if (x < 0.0) { - x += DUK_DOUBLE_2TO32; - } - DUK_ASSERT(x >= 0 && x < DUK_DOUBLE_2TO32); /* -> x in [0, 2**32[ */ - - if (is_toint32) { - if (x >= DUK_DOUBLE_2TO31) { - /* x in [2**31, 2**32[ */ - - x -= DUK_DOUBLE_2TO32; /* -> x in [-2**31,2**31[ */ - } - } - - return x; -} - -DUK_INTERNAL duk_int32_t duk_js_toint32(duk_hthread *thr, duk_tval *tv) { - duk_double_t d; - -#if defined(DUK_USE_FASTINT) - if (DUK_TVAL_IS_FASTINT(tv)) { - return DUK_TVAL_GET_FASTINT_I32(tv); - } -#endif - - d = duk_js_tonumber(thr, tv); /* invalidates tv */ - d = duk__toint32_touint32_helper(d, 1); - DUK_ASSERT(DUK_FPCLASSIFY(d) == DUK_FP_ZERO || DUK_FPCLASSIFY(d) == DUK_FP_NORMAL); - DUK_ASSERT(d >= -2147483648.0 && d <= 2147483647.0); /* [-0x80000000,0x7fffffff] */ - DUK_ASSERT(duk_double_equals(d, (duk_double_t) ((duk_int32_t) d))); /* whole, won't clip */ - return (duk_int32_t) d; -} - -DUK_INTERNAL duk_uint32_t duk_js_touint32(duk_hthread *thr, duk_tval *tv) { - duk_double_t d; - -#if defined(DUK_USE_FASTINT) - if (DUK_TVAL_IS_FASTINT(tv)) { - return DUK_TVAL_GET_FASTINT_U32(tv); - } -#endif - - d = duk_js_tonumber(thr, tv); /* invalidates tv */ - d = duk__toint32_touint32_helper(d, 0); - DUK_ASSERT(DUK_FPCLASSIFY(d) == DUK_FP_ZERO || DUK_FPCLASSIFY(d) == DUK_FP_NORMAL); - DUK_ASSERT(d >= 0.0 && d <= 4294967295.0); /* [0x00000000, 0xffffffff] */ - DUK_ASSERT(duk_double_equals(d, (duk_double_t) ((duk_uint32_t) d))); /* whole, won't clip */ - return (duk_uint32_t) d; -} - -DUK_INTERNAL duk_uint16_t duk_js_touint16(duk_hthread *thr, duk_tval *tv) { - /* should be a safe way to compute this */ - return (duk_uint16_t) (duk_js_touint32(thr, tv) & 0x0000ffffU); -} - -/* - * ToString() (E5 Section 9.8) - * ToObject() (E5 Section 9.9) - * CheckObjectCoercible() (E5 Section 9.10) - * IsCallable() (E5 Section 9.11) - * - * ==> implemented in the API. - */ - -/* - * Loose equality, strict equality, and SameValue (E5 Sections 11.9.1, 11.9.4, - * 9.12). These have much in common so they can share some helpers. - * - * Future work notes: - * - * - Current implementation (and spec definition) has recursion; this should - * be fixed if possible. - * - * - String-to-number coercion should be possible without going through the - * value stack (and be more compact) if a shared helper is invoked. - */ - -/* Note that this is the same operation for strict and loose equality: - * - E5 Section 11.9.3, step 1.c (loose) - * - E5 Section 11.9.6, step 4 (strict) - */ - -DUK_LOCAL duk_bool_t duk__js_equals_number(duk_double_t x, duk_double_t y) { -#if defined(DUK_USE_PARANOID_MATH) - /* Straightforward algorithm, makes fewer compiler assumptions. */ - duk_small_int_t cx = (duk_small_int_t) DUK_FPCLASSIFY(x); - duk_small_int_t cy = (duk_small_int_t) DUK_FPCLASSIFY(y); - if (cx == DUK_FP_NAN || cy == DUK_FP_NAN) { - return 0; - } - if (cx == DUK_FP_ZERO && cy == DUK_FP_ZERO) { - return 1; - } - if (x == y) { - return 1; - } - return 0; -#else /* DUK_USE_PARANOID_MATH */ - /* Better equivalent algorithm. If the compiler is compliant, C and - * ECMAScript semantics are identical for this particular comparison. - * In particular, NaNs must never compare equal and zeroes must compare - * equal regardless of sign. Could also use a macro, but this inlines - * already nicely (no difference on gcc, for instance). - */ - if (duk_double_equals(x, y)) { - /* IEEE requires that NaNs compare false */ - DUK_ASSERT(DUK_FPCLASSIFY(x) != DUK_FP_NAN); - DUK_ASSERT(DUK_FPCLASSIFY(y) != DUK_FP_NAN); - return 1; - } else { - /* IEEE requires that zeros compare the same regardless - * of their signed, so if both x and y are zeroes, they - * are caught above. - */ - DUK_ASSERT(!(DUK_FPCLASSIFY(x) == DUK_FP_ZERO && DUK_FPCLASSIFY(y) == DUK_FP_ZERO)); - return 0; - } -#endif /* DUK_USE_PARANOID_MATH */ -} - -DUK_LOCAL duk_bool_t duk__js_samevalue_number(duk_double_t x, duk_double_t y) { -#if defined(DUK_USE_PARANOID_MATH) - duk_small_int_t cx = (duk_small_int_t) DUK_FPCLASSIFY(x); - duk_small_int_t cy = (duk_small_int_t) DUK_FPCLASSIFY(y); - - if (cx == DUK_FP_NAN && cy == DUK_FP_NAN) { - /* SameValue(NaN, NaN) = true, regardless of NaN sign or extra bits */ - return 1; - } - if (cx == DUK_FP_ZERO && cy == DUK_FP_ZERO) { - /* Note: cannot assume that a non-zero return value of signbit() would - * always be the same -- hence cannot (portably) use something like: - * - * signbit(x) == signbit(y) - */ - duk_small_int_t sx = DUK_SIGNBIT(x) ? 1 : 0; - duk_small_int_t sy = DUK_SIGNBIT(y) ? 1 : 0; - return (sx == sy); - } - - /* normal comparison; known: - * - both x and y are not NaNs (but one of them can be) - * - both x and y are not zero (but one of them can be) - * - x and y may be denormal or infinite - */ - - return (x == y); -#else /* DUK_USE_PARANOID_MATH */ - duk_small_int_t cx = (duk_small_int_t) DUK_FPCLASSIFY(x); - duk_small_int_t cy = (duk_small_int_t) DUK_FPCLASSIFY(y); - - if (duk_double_equals(x, y)) { - /* IEEE requires that NaNs compare false */ - DUK_ASSERT(DUK_FPCLASSIFY(x) != DUK_FP_NAN); - DUK_ASSERT(DUK_FPCLASSIFY(y) != DUK_FP_NAN); - - /* Using classification has smaller footprint than direct comparison. */ - if (DUK_UNLIKELY(cx == DUK_FP_ZERO && cy == DUK_FP_ZERO)) { - /* Note: cannot assume that a non-zero return value of signbit() would - * always be the same -- hence cannot (portably) use something like: - * - * signbit(x) == signbit(y) - */ - return duk_double_same_sign(x, y); - } - return 1; - } else { - /* IEEE requires that zeros compare the same regardless - * of their sign, so if both x and y are zeroes, they - * are caught above. - */ - DUK_ASSERT(!(DUK_FPCLASSIFY(x) == DUK_FP_ZERO && DUK_FPCLASSIFY(y) == DUK_FP_ZERO)); - - /* Difference to non-strict/strict comparison is that NaNs compare - * equal and signed zero signs matter. - */ - if (DUK_UNLIKELY(cx == DUK_FP_NAN && cy == DUK_FP_NAN)) { - /* SameValue(NaN, NaN) = true, regardless of NaN sign or extra bits */ - return 1; - } - return 0; - } -#endif /* DUK_USE_PARANOID_MATH */ -} - -DUK_INTERNAL duk_bool_t duk_js_equals_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_uint_t flags) { - duk_uint_t type_mask_x; - duk_uint_t type_mask_y; - - /* If flags != 0 (strict or SameValue), thr can be NULL. For loose - * equals comparison it must be != NULL. - */ - DUK_ASSERT(flags != 0 || thr != NULL); - - /* - * Same type? - * - * Note: since number values have no explicit tag in the 8-byte - * representation, need the awkward if + switch. - */ - -#if defined(DUK_USE_FASTINT) - if (DUK_TVAL_IS_FASTINT(tv_x) && DUK_TVAL_IS_FASTINT(tv_y)) { - if (DUK_TVAL_GET_FASTINT(tv_x) == DUK_TVAL_GET_FASTINT(tv_y)) { - return 1; - } else { - return 0; - } - } else -#endif - if (DUK_TVAL_IS_NUMBER(tv_x) && DUK_TVAL_IS_NUMBER(tv_y)) { - duk_double_t d1, d2; - - /* Catches both doubles and cases where only one argument is - * a fastint so can't assume a double. - */ - d1 = DUK_TVAL_GET_NUMBER(tv_x); - d2 = DUK_TVAL_GET_NUMBER(tv_y); - if (DUK_UNLIKELY((flags & DUK_EQUALS_FLAG_SAMEVALUE) != 0)) { - /* SameValue */ - return duk__js_samevalue_number(d1, d2); - } else { - /* equals and strict equals */ - return duk__js_equals_number(d1, d2); - } - } else if (DUK_TVAL_GET_TAG(tv_x) == DUK_TVAL_GET_TAG(tv_y)) { - switch (DUK_TVAL_GET_TAG(tv_x)) { - case DUK_TAG_UNDEFINED: - case DUK_TAG_NULL: { - return 1; - } - case DUK_TAG_BOOLEAN: { - return DUK_TVAL_GET_BOOLEAN(tv_x) == DUK_TVAL_GET_BOOLEAN(tv_y); - } - case DUK_TAG_POINTER: { - return DUK_TVAL_GET_POINTER(tv_x) == DUK_TVAL_GET_POINTER(tv_y); - } - case DUK_TAG_STRING: - case DUK_TAG_OBJECT: { - /* Heap pointer comparison suffices for strings and objects. - * Symbols compare equal if they have the same internal - * representation; again heap pointer comparison suffices. - */ - return DUK_TVAL_GET_HEAPHDR(tv_x) == DUK_TVAL_GET_HEAPHDR(tv_y); - } - case DUK_TAG_BUFFER: { - /* In Duktape 2.x plain buffers mimic Uint8Array objects - * so always compare by heap pointer. In Duktape 1.x - * strict comparison would compare heap pointers and - * non-strict would compare contents. - */ - return DUK_TVAL_GET_HEAPHDR(tv_x) == DUK_TVAL_GET_HEAPHDR(tv_y); - } - case DUK_TAG_LIGHTFUNC: { - /* At least 'magic' has a significant impact on function - * identity. - */ - duk_small_uint_t lf_flags_x; - duk_small_uint_t lf_flags_y; - duk_c_function func_x; - duk_c_function func_y; - - DUK_TVAL_GET_LIGHTFUNC(tv_x, func_x, lf_flags_x); - DUK_TVAL_GET_LIGHTFUNC(tv_y, func_y, lf_flags_y); - return ((func_x == func_y) && (lf_flags_x == lf_flags_y)) ? 1 : 0; - } -#if defined(DUK_USE_FASTINT) - case DUK_TAG_FASTINT: -#endif - default: { - DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv_x)); - DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv_y)); - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_x)); - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_y)); - DUK_UNREACHABLE(); - DUK_WO_UNREACHABLE(return 0;); - } - } - } - - if ((flags & (DUK_EQUALS_FLAG_STRICT | DUK_EQUALS_FLAG_SAMEVALUE)) != 0) { - return 0; - } - - DUK_ASSERT(flags == 0); /* non-strict equality from here on */ - - /* - * Types are different; various cases for non-strict comparison - * - * Since comparison is symmetric, we use a "swap trick" to reduce - * code size. - */ - - type_mask_x = duk_get_type_mask_tval(tv_x); - type_mask_y = duk_get_type_mask_tval(tv_y); - - /* Undefined/null are considered equal (e.g. "null == undefined" -> true). */ - if ((type_mask_x & (DUK_TYPE_MASK_UNDEFINED | DUK_TYPE_MASK_NULL)) && - (type_mask_y & (DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_UNDEFINED))) { - return 1; - } - - /* Number/string -> coerce string to number (e.g. "'1.5' == 1.5" -> true). */ - if ((type_mask_x & DUK_TYPE_MASK_NUMBER) && (type_mask_y & DUK_TYPE_MASK_STRING)) { - if (!DUK_TVAL_STRING_IS_SYMBOL(tv_y)) { - duk_double_t d1, d2; - d1 = DUK_TVAL_GET_NUMBER(tv_x); - d2 = duk_to_number_tval(thr, tv_y); - return duk__js_equals_number(d1, d2); - } - } - if ((type_mask_x & DUK_TYPE_MASK_STRING) && (type_mask_y & DUK_TYPE_MASK_NUMBER)) { - if (!DUK_TVAL_STRING_IS_SYMBOL(tv_x)) { - duk_double_t d1, d2; - d1 = DUK_TVAL_GET_NUMBER(tv_y); - d2 = duk_to_number_tval(thr, tv_x); - return duk__js_equals_number(d1, d2); - } - } - - /* Boolean/any -> coerce boolean to number and try again. If boolean is - * compared to a pointer, the final comparison after coercion now always - * yields false (as pointer vs. number compares to false), but this is - * not special cased. - * - * ToNumber(bool) is +1.0 or 0.0. Tagged boolean value is always 0 or 1. - */ - if (type_mask_x & DUK_TYPE_MASK_BOOLEAN) { - DUK_ASSERT(DUK_TVAL_GET_BOOLEAN(tv_x) == 0 || DUK_TVAL_GET_BOOLEAN(tv_x) == 1); - duk_push_uint(thr, DUK_TVAL_GET_BOOLEAN(tv_x)); - duk_push_tval(thr, tv_y); - goto recursive_call; - } - if (type_mask_y & DUK_TYPE_MASK_BOOLEAN) { - DUK_ASSERT(DUK_TVAL_GET_BOOLEAN(tv_y) == 0 || DUK_TVAL_GET_BOOLEAN(tv_y) == 1); - duk_push_tval(thr, tv_x); - duk_push_uint(thr, DUK_TVAL_GET_BOOLEAN(tv_y)); - goto recursive_call; - } - - /* String-number-symbol/object -> coerce object to primitive (apparently without hint), then try again. */ - if ((type_mask_x & (DUK_TYPE_MASK_STRING | DUK_TYPE_MASK_NUMBER)) && (type_mask_y & DUK_TYPE_MASK_OBJECT)) { - /* No symbol check needed because symbols and strings are accepted. */ - duk_push_tval(thr, tv_x); - duk_push_tval(thr, tv_y); - duk_to_primitive(thr, -1, DUK_HINT_NONE); /* apparently no hint? */ - goto recursive_call; - } - if ((type_mask_x & DUK_TYPE_MASK_OBJECT) && (type_mask_y & (DUK_TYPE_MASK_STRING | DUK_TYPE_MASK_NUMBER))) { - /* No symbol check needed because symbols and strings are accepted. */ - duk_push_tval(thr, tv_x); - duk_push_tval(thr, tv_y); - duk_to_primitive(thr, -2, DUK_HINT_NONE); /* apparently no hint? */ - goto recursive_call; - } - - /* Nothing worked -> not equal. */ - return 0; - -recursive_call: - /* Shared code path to call the helper again with arguments on stack top. */ - { - duk_bool_t rc; - rc = duk_js_equals_helper(thr, DUK_GET_TVAL_NEGIDX(thr, -2), DUK_GET_TVAL_NEGIDX(thr, -1), 0 /*flags:nonstrict*/); - duk_pop_2_unsafe(thr); - return rc; - } -} - -/* - * Comparisons (x >= y, x > y, x <= y, x < y) - * - * E5 Section 11.8.5: implement 'x < y' and then use negate and eval_left_first - * flags to get the rest. - */ - -/* XXX: this should probably just operate on the stack top, because it - * needs to push stuff on the stack anyway... - */ - -DUK_INTERNAL duk_small_int_t duk_js_data_compare(const duk_uint8_t *buf1, - const duk_uint8_t *buf2, - duk_size_t len1, - duk_size_t len2) { - duk_size_t prefix_len; - duk_small_int_t rc; - - prefix_len = (len1 <= len2 ? len1 : len2); - - /* duk_memcmp() is guaranteed to return zero (equal) for zero length - * inputs. - */ - rc = duk_memcmp_unsafe((const void *) buf1, (const void *) buf2, (size_t) prefix_len); - - if (rc < 0) { - return -1; - } else if (rc > 0) { - return 1; - } - - /* prefix matches, lengths matter now */ - if (len1 < len2) { - /* e.g. "x" < "xx" */ - return -1; - } else if (len1 > len2) { - return 1; - } - - return 0; -} - -DUK_INTERNAL duk_small_int_t duk_js_string_compare(duk_hstring *h1, duk_hstring *h2) { - /* - * String comparison (E5 Section 11.8.5, step 4), which - * needs to compare codepoint by codepoint. - * - * However, UTF-8 allows us to use strcmp directly: the shared - * prefix will be encoded identically (UTF-8 has unique encoding) - * and the first differing character can be compared with a simple - * unsigned byte comparison (which strcmp does). - * - * This will not work properly for non-xutf-8 strings, but this - * is not an issue for compliance. - */ - - DUK_ASSERT(h1 != NULL); - DUK_ASSERT(h2 != NULL); - - return duk_js_data_compare((const duk_uint8_t *) DUK_HSTRING_GET_DATA(h1), - (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h2), - (duk_size_t) DUK_HSTRING_GET_BYTELEN(h1), - (duk_size_t) DUK_HSTRING_GET_BYTELEN(h2)); -} - -#if 0 /* unused */ -DUK_INTERNAL duk_small_int_t duk_js_buffer_compare(duk_heap *heap, duk_hbuffer *h1, duk_hbuffer *h2) { - /* Similar to String comparison. */ - - DUK_ASSERT(h1 != NULL); - DUK_ASSERT(h2 != NULL); - DUK_UNREF(heap); - - return duk_js_data_compare((const duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(heap, h1), - (const duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(heap, h2), - (duk_size_t) DUK_HBUFFER_GET_SIZE(h1), - (duk_size_t) DUK_HBUFFER_GET_SIZE(h2)); -} -#endif - -#if defined(DUK_USE_FASTINT) -DUK_LOCAL duk_bool_t duk__compare_fastint(duk_bool_t retval, duk_int64_t v1, duk_int64_t v2) { - DUK_ASSERT(retval == 0 || retval == 1); - if (v1 < v2) { - return retval ^ 1; - } else { - return retval; - } -} -#endif - -#if defined(DUK_USE_PARANOID_MATH) -DUK_LOCAL duk_bool_t duk__compare_number(duk_bool_t retval, duk_double_t d1, duk_double_t d2) { - duk_small_int_t c1, s1, c2, s2; - - DUK_ASSERT(retval == 0 || retval == 1); - c1 = (duk_small_int_t) DUK_FPCLASSIFY(d1); - s1 = (duk_small_int_t) DUK_SIGNBIT(d1); - c2 = (duk_small_int_t) DUK_FPCLASSIFY(d2); - s2 = (duk_small_int_t) DUK_SIGNBIT(d2); - - if (c1 == DUK_FP_NAN || c2 == DUK_FP_NAN) { - return 0; /* Always false, regardless of negation. */ - } - - if (c1 == DUK_FP_ZERO && c2 == DUK_FP_ZERO) { - /* For all combinations: +0 < +0, +0 < -0, -0 < +0, -0 < -0, - * steps e, f, and g. - */ - return retval; /* false */ - } - - if (d1 == d2) { - return retval; /* false */ - } - - if (c1 == DUK_FP_INFINITE && s1 == 0) { - /* x == +Infinity */ - return retval; /* false */ - } - - if (c2 == DUK_FP_INFINITE && s2 == 0) { - /* y == +Infinity */ - return retval ^ 1; /* true */ - } - - if (c2 == DUK_FP_INFINITE && s2 != 0) { - /* y == -Infinity */ - return retval; /* false */ - } - - if (c1 == DUK_FP_INFINITE && s1 != 0) { - /* x == -Infinity */ - return retval ^ 1; /* true */ - } - - if (d1 < d2) { - return retval ^ 1; /* true */ - } - - return retval; /* false */ -} -#else /* DUK_USE_PARANOID_MATH */ -DUK_LOCAL duk_bool_t duk__compare_number(duk_bool_t retval, duk_double_t d1, duk_double_t d2) { - /* This comparison tree relies doesn't match the exact steps in - * E5 Section 11.8.5 but should produce the same results. The - * steps rely on exact IEEE semantics for NaNs, etc. - */ - - DUK_ASSERT(retval == 0 || retval == 1); - if (d1 < d2) { - /* In no case should both (d1 < d2) and (d2 < d1) be true. - * It's possible that neither is true though, and that's - * handled below. - */ - DUK_ASSERT(!(d2 < d1)); - - /* - d1 < d2, both d1/d2 are normals (not Infinity, not NaN) - * - d2 is +Infinity, d1 != +Infinity and NaN - * - d1 is -Infinity, d2 != -Infinity and NaN - */ - return retval ^ 1; - } else { - if (d2 < d1) { - /* - !(d1 < d2), both d1/d2 are normals (not Infinity, not NaN) - * - d1 is +Infinity, d2 != +Infinity and NaN - * - d2 is -Infinity, d1 != -Infinity and NaN - */ - return retval; - } else { - /* - d1 and/or d2 is NaN - * - d1 and d2 are both +/- 0 - * - d1 == d2 (including infinities) - */ - if (duk_double_is_nan(d1) || duk_double_is_nan(d2)) { - /* Note: undefined from Section 11.8.5 always - * results in false return (see e.g. Section - * 11.8.3) - hence special treatment here. - */ - return 0; /* zero regardless of negation */ - } else { - return retval; - } - } - } -} -#endif /* DUK_USE_PARANOID_MATH */ - -DUK_INTERNAL duk_bool_t duk_js_compare_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_uint_t flags) { - duk_double_t d1, d2; - duk_small_int_t rc; - duk_bool_t retval; - - DUK_ASSERT(DUK_COMPARE_FLAG_NEGATE == 1); /* Rely on this flag being lowest. */ - retval = flags & DUK_COMPARE_FLAG_NEGATE; - DUK_ASSERT(retval == 0 || retval == 1); - - /* Fast path for fastints */ -#if defined(DUK_USE_FASTINT) - if (DUK_LIKELY(DUK_TVAL_IS_FASTINT(tv_x) && DUK_TVAL_IS_FASTINT(tv_y))) { - return duk__compare_fastint(retval, DUK_TVAL_GET_FASTINT(tv_x), DUK_TVAL_GET_FASTINT(tv_y)); - } -#endif /* DUK_USE_FASTINT */ - - /* Fast path for numbers (one of which may be a fastint) */ -#if !defined(DUK_USE_PREFER_SIZE) - if (DUK_LIKELY(DUK_TVAL_IS_NUMBER(tv_x) && DUK_TVAL_IS_NUMBER(tv_y))) { - return duk__compare_number(retval, DUK_TVAL_GET_NUMBER(tv_x), DUK_TVAL_GET_NUMBER(tv_y)); - } -#endif - - /* Slow path */ - - duk_push_tval(thr, tv_x); - duk_push_tval(thr, tv_y); - - if (flags & DUK_COMPARE_FLAG_EVAL_LEFT_FIRST) { - duk_to_primitive(thr, -2, DUK_HINT_NUMBER); - duk_to_primitive(thr, -1, DUK_HINT_NUMBER); - } else { - duk_to_primitive(thr, -1, DUK_HINT_NUMBER); - duk_to_primitive(thr, -2, DUK_HINT_NUMBER); - } - - /* Note: reuse variables */ - tv_x = DUK_GET_TVAL_NEGIDX(thr, -2); - tv_y = DUK_GET_TVAL_NEGIDX(thr, -1); - - if (DUK_TVAL_IS_STRING(tv_x) && DUK_TVAL_IS_STRING(tv_y)) { - duk_hstring *h1 = DUK_TVAL_GET_STRING(tv_x); - duk_hstring *h2 = DUK_TVAL_GET_STRING(tv_y); - DUK_ASSERT(h1 != NULL); - DUK_ASSERT(h2 != NULL); - - if (DUK_LIKELY(!DUK_HSTRING_HAS_SYMBOL(h1) && !DUK_HSTRING_HAS_SYMBOL(h2))) { - rc = duk_js_string_compare(h1, h2); - duk_pop_2_unsafe(thr); - if (rc < 0) { - return retval ^ 1; - } else { - return retval; - } - } - - /* One or both are Symbols: fall through to handle in the - * generic path. Concretely, ToNumber() will fail. - */ - } - - /* Ordering should not matter (E5 Section 11.8.5, step 3.a). */ -#if 0 - if (flags & DUK_COMPARE_FLAG_EVAL_LEFT_FIRST) { - d1 = duk_to_number_m2(thr); - d2 = duk_to_number_m1(thr); - } else { - d2 = duk_to_number_m1(thr); - d1 = duk_to_number_m2(thr); - } -#endif - d1 = duk_to_number_m2(thr); - d2 = duk_to_number_m1(thr); - - /* We want to duk_pop_2_unsafe(thr); because the values are numbers - * no decref check is needed. - */ -#if defined(DUK_USE_PREFER_SIZE) - duk_pop_2_nodecref_unsafe(thr); -#else - DUK_ASSERT(!DUK_TVAL_NEEDS_REFCOUNT_UPDATE(duk_get_tval(thr, -2))); - DUK_ASSERT(!DUK_TVAL_NEEDS_REFCOUNT_UPDATE(duk_get_tval(thr, -1))); - DUK_ASSERT(duk_get_top(thr) >= 2); - thr->valstack_top -= 2; - tv_x = thr->valstack_top; - tv_y = tv_x + 1; - DUK_TVAL_SET_UNDEFINED(tv_x); /* Value stack policy */ - DUK_TVAL_SET_UNDEFINED(tv_y); -#endif - - return duk__compare_number(retval, d1, d2); -} - -/* - * instanceof - */ - -/* - * ES2015 Section 7.3.19 describes the OrdinaryHasInstance() algorithm - * which covers both bound and non-bound functions; in effect the algorithm - * includes E5 Sections 11.8.6, 15.3.5.3, and 15.3.4.5.3. - * - * ES2015 Section 12.9.4 describes the instanceof operator which first - * checks @@hasInstance well-known symbol and falls back to - * OrdinaryHasInstance(). - * - * Limited Proxy support: don't support 'getPrototypeOf' trap but - * continue lookup in Proxy target if the value is a Proxy. - */ - -DUK_LOCAL duk_bool_t duk__js_instanceof_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_bool_t skip_sym_check) { - duk_hobject *func; - duk_hobject *val; - duk_hobject *proto; - duk_tval *tv; - duk_bool_t skip_first; - duk_uint_t sanity; - - /* - * Get the values onto the stack first. It would be possible to cover - * some normal cases without resorting to the value stack. - * - * The right hand side could be a light function (as they generally - * behave like objects). Light functions never have a 'prototype' - * property so E5.1 Section 15.3.5.3 step 3 always throws a TypeError. - * Using duk_require_hobject() is thus correct (except for error msg). - */ - - duk_push_tval(thr, tv_x); - duk_push_tval(thr, tv_y); - func = duk_require_hobject(thr, -1); - DUK_ASSERT(func != NULL); - -#if defined(DUK_USE_SYMBOL_BUILTIN) - /* - * @@hasInstance check, ES2015 Section 12.9.4, Steps 2-4. - */ - if (!skip_sym_check) { - if (duk_get_method_stridx(thr, -1, DUK_STRIDX_WELLKNOWN_SYMBOL_HAS_INSTANCE)) { - /* [ ... lhs rhs func ] */ - duk_insert(thr, -3); /* -> [ ... func lhs rhs ] */ - duk_swap_top(thr, -2); /* -> [ ... func rhs(this) lhs ] */ - duk_call_method(thr, 1); - return duk_to_boolean_top_pop(thr); - } - } -#else - DUK_UNREF(skip_sym_check); -#endif - - /* - * For bound objects, [[HasInstance]] just calls the target function - * [[HasInstance]]. If that is again a bound object, repeat until - * we find a non-bound Function object. - * - * The bound function chain is now "collapsed" so there can be only - * one bound function in the chain. - */ - - if (!DUK_HOBJECT_IS_CALLABLE(func)) { - /* - * Note: of native ECMAScript objects, only Function instances - * have a [[HasInstance]] internal property. Custom objects might - * also have it, but not in current implementation. - * - * XXX: add a separate flag, DUK_HOBJECT_FLAG_ALLOW_INSTANCEOF? - */ - goto error_invalid_rval; - } - - if (DUK_HOBJECT_HAS_BOUNDFUNC(func)) { - duk_push_tval(thr, &((duk_hboundfunc *) (void *) func)->target); - duk_replace(thr, -2); - func = duk_require_hobject(thr, -1); /* lightfunc throws */ - - /* Rely on Function.prototype.bind() never creating bound - * functions whose target is not proper. - */ - DUK_ASSERT(func != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_CALLABLE(func)); - } - - /* - * 'func' is now a non-bound object which supports [[HasInstance]] - * (which here just means DUK_HOBJECT_FLAG_CALLABLE). Move on - * to execute E5 Section 15.3.5.3. - */ - - DUK_ASSERT(func != NULL); - DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func)); - DUK_ASSERT(DUK_HOBJECT_IS_CALLABLE(func)); - - /* [ ... lval rval(func) ] */ - - /* For lightfuncs, buffers, and pointers start the comparison directly - * from the virtual prototype object. - */ - skip_first = 0; - tv = DUK_GET_TVAL_NEGIDX(thr, -2); - switch (DUK_TVAL_GET_TAG(tv)) { - case DUK_TAG_LIGHTFUNC: - val = thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]; - DUK_ASSERT(val != NULL); - break; - case DUK_TAG_BUFFER: - val = thr->builtins[DUK_BIDX_UINT8ARRAY_PROTOTYPE]; - DUK_ASSERT(val != NULL); - break; - case DUK_TAG_POINTER: - val = thr->builtins[DUK_BIDX_POINTER_PROTOTYPE]; - DUK_ASSERT(val != NULL); - break; - case DUK_TAG_OBJECT: - skip_first = 1; /* Ignore object itself on first round. */ - val = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(val != NULL); - break; - default: - goto pop2_and_false; - } - DUK_ASSERT(val != NULL); /* Loop doesn't actually rely on this. */ - - /* Look up .prototype of rval. Leave it on the value stack in case it - * has been virtualized (e.g. getter, Proxy trap). - */ - duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_PROTOTYPE); /* -> [ ... lval rval rval.prototype ] */ -#if defined(DUK_USE_VERBOSE_ERRORS) - proto = duk_get_hobject(thr, -1); - if (proto == NULL) { - goto error_invalid_rval_noproto; - } -#else - proto = duk_require_hobject(thr, -1); -#endif - - sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY; - do { - /* - * Note: prototype chain is followed BEFORE first comparison. This - * means that the instanceof lval is never itself compared to the - * rval.prototype property. This is apparently intentional, see E5 - * Section 15.3.5.3, step 4.a. - * - * Also note: - * - * js> (function() {}) instanceof Function - * true - * js> Function instanceof Function - * true - * - * For the latter, h_proto will be Function.prototype, which is the - * built-in Function prototype. Because Function.[[Prototype]] is - * also the built-in Function prototype, the result is true. - */ - - if (!val) { - goto pop3_and_false; - } - - DUK_ASSERT(val != NULL); -#if defined(DUK_USE_ES6_PROXY) - val = duk_hobject_resolve_proxy_target(val); -#endif - - if (skip_first) { - skip_first = 0; - } else if (val == proto) { - goto pop3_and_true; - } - - DUK_ASSERT(val != NULL); - val = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, val); - } while (--sanity > 0); - - DUK_ASSERT(sanity == 0); - DUK_ERROR_RANGE(thr, DUK_STR_PROTOTYPE_CHAIN_LIMIT); - DUK_WO_NORETURN(return 0;); - -pop2_and_false: - duk_pop_2_unsafe(thr); - return 0; - -pop3_and_false: - duk_pop_3_unsafe(thr); - return 0; - -pop3_and_true: - duk_pop_3_unsafe(thr); - return 1; - -error_invalid_rval: - DUK_ERROR_TYPE(thr, DUK_STR_INVALID_INSTANCEOF_RVAL); - DUK_WO_NORETURN(return 0;); - -#if defined(DUK_USE_VERBOSE_ERRORS) -error_invalid_rval_noproto: - DUK_ERROR_TYPE(thr, DUK_STR_INVALID_INSTANCEOF_RVAL_NOPROTO); - DUK_WO_NORETURN(return 0;); -#endif -} - -#if defined(DUK_USE_SYMBOL_BUILTIN) -DUK_INTERNAL duk_bool_t duk_js_instanceof_ordinary(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y) { - return duk__js_instanceof_helper(thr, tv_x, tv_y, 1 /*skip_sym_check*/); -} -#endif - -DUK_INTERNAL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y) { - return duk__js_instanceof_helper(thr, tv_x, tv_y, 0 /*skip_sym_check*/); -} - -/* - * in - */ - -/* - * E5 Sections 11.8.7, 8.12.6. - * - * Basically just a property existence check using [[HasProperty]]. - */ - -DUK_INTERNAL duk_bool_t duk_js_in(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y) { - duk_bool_t retval; - - /* - * Get the values onto the stack first. It would be possible to cover - * some normal cases without resorting to the value stack (e.g. if - * lval is already a string). - */ - - /* XXX: The ES5/5.1/6 specifications require that the key in 'key in obj' - * must be string coerced before the internal HasProperty() algorithm is - * invoked. A fast path skipping coercion could be safely implemented for - * numbers (as number-to-string coercion has no side effects). For ES2015 - * proxy behavior, the trap 'key' argument must be in a string coerced - * form (which is a shame). - */ - - /* TypeError if rval is not an object or object like (e.g. lightfunc - * or plain buffer). - */ - duk_push_tval(thr, tv_x); - duk_push_tval(thr, tv_y); - duk_require_type_mask(thr, -1, DUK_TYPE_MASK_OBJECT | DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER); - - (void) duk_to_property_key_hstring(thr, -2); - - retval = duk_hobject_hasprop(thr, DUK_GET_TVAL_NEGIDX(thr, -1), DUK_GET_TVAL_NEGIDX(thr, -2)); - - duk_pop_2_unsafe(thr); - return retval; -} - -/* - * typeof - * - * E5 Section 11.4.3. - * - * Very straightforward. The only question is what to return for our - * non-standard tag / object types. - * - * There is an unfortunate string constant define naming problem with - * typeof return values for e.g. "Object" and "object"; careful with - * the built-in string defines. The LC_XXX defines are used for the - * lowercase variants now. - */ - -DUK_INTERNAL duk_small_uint_t duk_js_typeof_stridx(duk_tval *tv_x) { - duk_small_uint_t stridx = 0; - - switch (DUK_TVAL_GET_TAG(tv_x)) { - case DUK_TAG_UNDEFINED: { - stridx = DUK_STRIDX_LC_UNDEFINED; - break; - } - case DUK_TAG_NULL: { - /* Note: not a typo, "object" is returned for a null value. */ - stridx = DUK_STRIDX_LC_OBJECT; - break; - } - case DUK_TAG_BOOLEAN: { - stridx = DUK_STRIDX_LC_BOOLEAN; - break; - } - case DUK_TAG_POINTER: { - /* Implementation specific. */ - stridx = DUK_STRIDX_LC_POINTER; - break; - } - case DUK_TAG_STRING: { - duk_hstring *str; - - /* All internal keys are identified as Symbols. */ - str = DUK_TVAL_GET_STRING(tv_x); - DUK_ASSERT(str != NULL); - if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(str))) { - stridx = DUK_STRIDX_LC_SYMBOL; - } else { - stridx = DUK_STRIDX_LC_STRING; - } - break; - } - case DUK_TAG_OBJECT: { - duk_hobject *obj = DUK_TVAL_GET_OBJECT(tv_x); - DUK_ASSERT(obj != NULL); - if (DUK_HOBJECT_IS_CALLABLE(obj)) { - stridx = DUK_STRIDX_LC_FUNCTION; - } else { - stridx = DUK_STRIDX_LC_OBJECT; - } - break; - } - case DUK_TAG_BUFFER: { - /* Implementation specific. In Duktape 1.x this would be - * 'buffer', in Duktape 2.x changed to 'object' because plain - * buffers now mimic Uint8Array objects. - */ - stridx = DUK_STRIDX_LC_OBJECT; - break; - } - case DUK_TAG_LIGHTFUNC: { - stridx = DUK_STRIDX_LC_FUNCTION; - break; - } -#if defined(DUK_USE_FASTINT) - case DUK_TAG_FASTINT: -#endif - default: { - /* number */ - DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv_x)); - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_x)); - stridx = DUK_STRIDX_LC_NUMBER; - break; - } - } - - DUK_ASSERT_STRIDX_VALID(stridx); - return stridx; -} - -/* - * IsArray() - */ - -DUK_INTERNAL duk_bool_t duk_js_isarray_hobject(duk_hobject *h) { - DUK_ASSERT(h != NULL); -#if defined(DUK_USE_ES6_PROXY) - h = duk_hobject_resolve_proxy_target(h); -#endif - return (DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_ARRAY ? 1 : 0); -} - -DUK_INTERNAL duk_bool_t duk_js_isarray(duk_tval *tv) { - DUK_ASSERT(tv != NULL); - if (DUK_TVAL_IS_OBJECT(tv)) { - return duk_js_isarray_hobject(DUK_TVAL_GET_OBJECT(tv)); - } - return 0; -} - -/* - * Array index and length - * - * Array index: E5 Section 15.4 - * Array length: E5 Section 15.4.5.1 steps 3.c - 3.d (array length write) - */ - -/* Compure array index from string context, or return a "not array index" - * indicator. - */ -DUK_INTERNAL duk_uarridx_t duk_js_to_arrayindex_string(const duk_uint8_t *str, duk_uint32_t blen) { - duk_uarridx_t res; - - /* Only strings with byte length 1-10 can be 32-bit array indices. - * Leading zeroes (except '0' alone), plus/minus signs are not allowed. - * We could do a lot of prechecks here, but since most strings won't - * start with any digits, it's simpler to just parse the number and - * fail quickly. - */ - - res = 0; - if (blen == 0) { - goto parse_fail; - } - do { - duk_uarridx_t dig; - dig = (duk_uarridx_t) (*str++) - DUK_ASC_0; - - if (dig <= 9U) { - /* Careful overflow handling. When multiplying by 10: - * - 0x19999998 x 10 = 0xfffffff0: no overflow, and adding - * 0...9 is safe. - * - 0x19999999 x 10 = 0xfffffffa: no overflow, adding - * 0...5 is safe, 6...9 overflows. - * - 0x1999999a x 10 = 0x100000004: always overflow. - */ - if (DUK_UNLIKELY(res >= 0x19999999UL)) { - if (res >= 0x1999999aUL) { - /* Always overflow. */ - goto parse_fail; - } - DUK_ASSERT(res == 0x19999999UL); - if (dig >= 6U) { - goto parse_fail; - } - res = 0xfffffffaUL + dig; - DUK_ASSERT(res >= 0xfffffffaUL); - DUK_ASSERT_DISABLE(res <= 0xffffffffUL); /* range */ - } else { - res = res * 10U + dig; - if (DUK_UNLIKELY(res == 0)) { - /* If 'res' is 0, previous 'res' must - * have been 0 and we scanned in a zero. - * This is only allowed if blen == 1, - * i.e. the exact string '0'. - */ - if (blen == (duk_uint32_t) 1) { - return 0; - } - goto parse_fail; - } - } - } else { - /* Because 'dig' is unsigned, catches both values - * above '9' and below '0'. - */ - goto parse_fail; - } - } while (--blen > 0); - - return res; - -parse_fail: - return DUK_HSTRING_NO_ARRAY_INDEX; -} - -#if !defined(DUK_USE_HSTRING_ARRIDX) -/* Get array index for a string which is known to be an array index. This helper - * is needed when duk_hstring doesn't concretely store the array index, but strings - * are flagged as array indices at intern time. - */ -DUK_INTERNAL duk_uarridx_t duk_js_to_arrayindex_hstring_fast_known(duk_hstring *h) { - const duk_uint8_t *p; - duk_uarridx_t res; - duk_uint8_t t; - - DUK_ASSERT(h != NULL); - DUK_ASSERT(DUK_HSTRING_HAS_ARRIDX(h)); - - p = DUK_HSTRING_GET_DATA(h); - res = 0; - for (;;) { - t = *p++; - if (DUK_UNLIKELY(t == 0)) { - /* Scanning to NUL is always safe for interned strings. */ - break; - } - DUK_ASSERT(t >= (duk_uint8_t) DUK_ASC_0 && t <= (duk_uint8_t) DUK_ASC_9); - res = res * 10U + (duk_uarridx_t) t - (duk_uarridx_t) DUK_ASC_0; - } - return res; -} - -DUK_INTERNAL duk_uarridx_t duk_js_to_arrayindex_hstring_fast(duk_hstring *h) { - DUK_ASSERT(h != NULL); - if (!DUK_HSTRING_HAS_ARRIDX(h)) { - return DUK_HSTRING_NO_ARRAY_INDEX; - } - return duk_js_to_arrayindex_hstring_fast_known(h); -} -#endif /* DUK_USE_HSTRING_ARRIDX */ -/* - * Identifier access and function closure handling. - * - * Provides the primitives for slow path identifier accesses: GETVAR, - * PUTVAR, DELVAR, etc. The fast path, direct register accesses, should - * be used for most identifier accesses. Consequently, these slow path - * primitives should be optimized for maximum compactness. - * - * ECMAScript environment records (declarative and object) are represented - * as internal objects with control keys. Environment records have a - * parent record ("outer environment reference") which is represented by - * the implicit prototype for technical reasons (in other words, it is a - * convenient field). The prototype chain is not followed in the ordinary - * sense for variable lookups. - * - * See identifier-handling.rst for more details on the identifier algorithms - * and the internal representation. See function-objects.rst for details on - * what function templates and instances are expected to look like. - * - * Care must be taken to avoid duk_tval pointer invalidation caused by - * e.g. value stack or object resizing. - * - * TODO: properties for function instances could be initialized much more - * efficiently by creating a property allocation for a certain size and - * filling in keys and values directly (and INCREFing both with "bulk incref" - * primitives. - * - * XXX: duk_hobject_getprop() and duk_hobject_putprop() calls are a bit - * awkward (especially because they follow the prototype chain); rework - * if "raw" own property helpers are added. - */ - -/* #include duk_internal.h -> already included */ - -/* - * Local result type for duk__get_identifier_reference() lookup. - */ - -typedef struct { - duk_hobject *env; - duk_hobject *holder; /* for object-bound identifiers */ - duk_tval *value; /* for register-bound and declarative env identifiers */ - duk_uint_t attrs; /* property attributes for identifier (relevant if value != NULL) */ - duk_bool_t has_this; /* for object-bound identifiers: provide 'this' binding */ -} duk__id_lookup_result; - -/* - * Create a new function object based on a "template function" which contains - * compiled bytecode, constants, etc, but lacks a lexical environment. - * - * ECMAScript requires that each created closure is a separate object, with - * its own set of editable properties. However, structured property values - * (such as the formal arguments list and the variable map) are shared. - * Also the bytecode, constants, and inner functions are shared. - * - * See E5 Section 13.2 for detailed requirements on the function objects; - * there are no similar requirements for function "templates" which are an - * implementation dependent internal feature. Also see function-objects.rst - * for a discussion on the function instance properties provided by this - * implementation. - * - * Notes: - * - * * Order of internal properties should match frequency of use, since the - * properties will be linearly scanned on lookup (functions usually don't - * have enough properties to warrant a hash part). - * - * * The created closure is independent of its template; they do share the - * same 'data' buffer object, but the template object itself can be freed - * even if the closure object remains reachable. - */ - -DUK_LOCAL void duk__inc_data_inner_refcounts(duk_hthread *thr, duk_hcompfunc *f) { - duk_tval *tv, *tv_end; - duk_hobject **funcs, **funcs_end; - - DUK_UNREF(thr); - - /* If function creation fails due to out-of-memory, the data buffer - * pointer may be NULL in some cases. That's actually possible for - * GC code, but shouldn't be possible here because the incomplete - * function will be unwound from the value stack and never instantiated. - */ - DUK_ASSERT(DUK_HCOMPFUNC_GET_DATA(thr->heap, f) != NULL); - - tv = DUK_HCOMPFUNC_GET_CONSTS_BASE(thr->heap, f); - tv_end = DUK_HCOMPFUNC_GET_CONSTS_END(thr->heap, f); - while (tv < tv_end) { - DUK_TVAL_INCREF(thr, tv); - tv++; - } - - funcs = DUK_HCOMPFUNC_GET_FUNCS_BASE(thr->heap, f); - funcs_end = DUK_HCOMPFUNC_GET_FUNCS_END(thr->heap, f); - while (funcs < funcs_end) { - DUK_HEAPHDR_INCREF(thr, (duk_heaphdr *) *funcs); - funcs++; - } -} - -/* Push a new closure on the stack. - * - * Note: if fun_temp has NEWENV, i.e. a new lexical and variable declaration - * is created when the function is called, only outer_lex_env matters - * (outer_var_env is ignored and may or may not be same as outer_lex_env). - */ - -DUK_LOCAL const duk_uint16_t duk__closure_copy_proplist[] = { - /* order: most frequent to least frequent */ - DUK_STRIDX_INT_VARMAP, - DUK_STRIDX_INT_FORMALS, -#if defined(DUK_USE_PC2LINE) - DUK_STRIDX_INT_PC2LINE, -#endif -#if defined(DUK_USE_FUNC_FILENAME_PROPERTY) - DUK_STRIDX_FILE_NAME, -#endif -#if defined(DUK_USE_NONSTD_FUNC_SOURCE_PROPERTY) - DUK_STRIDX_INT_SOURCE -#endif -}; - -DUK_INTERNAL -void duk_js_push_closure(duk_hthread *thr, - duk_hcompfunc *fun_temp, - duk_hobject *outer_var_env, - duk_hobject *outer_lex_env, - duk_bool_t add_auto_proto) { - duk_hcompfunc *fun_clos; - duk_harray *formals; - duk_small_uint_t i; - duk_uint_t len_value; - - DUK_ASSERT(fun_temp != NULL); - DUK_ASSERT(DUK_HCOMPFUNC_GET_DATA(thr->heap, fun_temp) != NULL); - DUK_ASSERT(DUK_HCOMPFUNC_GET_FUNCS(thr->heap, fun_temp) != NULL); - DUK_ASSERT(DUK_HCOMPFUNC_GET_BYTECODE(thr->heap, fun_temp) != NULL); - DUK_ASSERT(outer_var_env != NULL); - DUK_ASSERT(outer_lex_env != NULL); - DUK_UNREF(len_value); - - DUK_STATS_INC(thr->heap, stats_envrec_pushclosure); - - fun_clos = duk_push_hcompfunc(thr); - DUK_ASSERT(fun_clos != NULL); - DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) fun_clos) == thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]); - - duk_push_hobject(thr, &fun_temp->obj); /* -> [ ... closure template ] */ - - DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC((duk_hobject *) fun_clos)); - DUK_ASSERT(DUK_HCOMPFUNC_GET_DATA(thr->heap, fun_clos) == NULL); - DUK_ASSERT(DUK_HCOMPFUNC_GET_FUNCS(thr->heap, fun_clos) == NULL); - DUK_ASSERT(DUK_HCOMPFUNC_GET_BYTECODE(thr->heap, fun_clos) == NULL); - - DUK_HCOMPFUNC_SET_DATA(thr->heap, fun_clos, DUK_HCOMPFUNC_GET_DATA(thr->heap, fun_temp)); - DUK_HCOMPFUNC_SET_FUNCS(thr->heap, fun_clos, DUK_HCOMPFUNC_GET_FUNCS(thr->heap, fun_temp)); - DUK_HCOMPFUNC_SET_BYTECODE(thr->heap, fun_clos, DUK_HCOMPFUNC_GET_BYTECODE(thr->heap, fun_temp)); - - /* Note: all references inside 'data' need to get their refcounts - * upped too. This is the case because refcounts are decreased - * through every function referencing 'data' independently. - */ - - DUK_HBUFFER_INCREF(thr, DUK_HCOMPFUNC_GET_DATA(thr->heap, fun_clos)); - duk__inc_data_inner_refcounts(thr, fun_temp); - - fun_clos->nregs = fun_temp->nregs; - fun_clos->nargs = fun_temp->nargs; -#if defined(DUK_USE_DEBUGGER_SUPPORT) - fun_clos->start_line = fun_temp->start_line; - fun_clos->end_line = fun_temp->end_line; -#endif - - DUK_ASSERT(DUK_HCOMPFUNC_GET_DATA(thr->heap, fun_clos) != NULL); - DUK_ASSERT(DUK_HCOMPFUNC_GET_FUNCS(thr->heap, fun_clos) != NULL); - DUK_ASSERT(DUK_HCOMPFUNC_GET_BYTECODE(thr->heap, fun_clos) != NULL); - - /* XXX: Could also copy from template, but there's no way to have any - * other value here now (used code has no access to the template). - * Prototype is set by duk_push_hcompfunc(). - */ - DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, &fun_clos->obj) == thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]); -#if 0 - DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, &fun_clos->obj, thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]); -#endif - - /* Copy duk_hobject flags as is from the template using a mask. - * Leave out duk_heaphdr owned flags just in case (e.g. if there's - * some GC flag or similar). Some flags can then be adjusted - * separately if necessary. - */ - - /* DUK_HEAPHDR_SET_FLAGS() masks changes to non-duk_heaphdr flags only. */ - DUK_HEAPHDR_SET_FLAGS((duk_heaphdr *) fun_clos, DUK_HEAPHDR_GET_FLAGS_RAW((duk_heaphdr *) fun_temp)); - DUK_DD(DUK_DDPRINT("fun_temp heaphdr flags: 0x%08lx, fun_clos heaphdr flags: 0x%08lx", - (unsigned long) DUK_HEAPHDR_GET_FLAGS_RAW((duk_heaphdr *) fun_temp), - (unsigned long) DUK_HEAPHDR_GET_FLAGS_RAW((duk_heaphdr *) fun_clos))); - - DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(&fun_clos->obj)); - DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(&fun_clos->obj)); - DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(&fun_clos->obj)); - DUK_ASSERT(!DUK_HOBJECT_HAS_NATFUNC(&fun_clos->obj)); - DUK_ASSERT(!DUK_HOBJECT_IS_THREAD(&fun_clos->obj)); - /* DUK_HOBJECT_FLAG_ARRAY_PART: don't care */ - /* DUK_HOBJECT_FLAG_NEWENV: handled below */ - DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARRAY(&fun_clos->obj)); - DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(&fun_clos->obj)); - DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(&fun_clos->obj)); - - if (!DUK_HOBJECT_HAS_CONSTRUCTABLE(&fun_clos->obj)) { - /* If the template is not constructable don't add an automatic - * .prototype property. This is the case for e.g. ES2015 object - * literal getters/setters and method definitions. - */ - add_auto_proto = 0; - } - - /* - * Setup environment record properties based on the template and - * its flags. - * - * If DUK_HOBJECT_HAS_NEWENV(fun_temp) is true, the environment - * records represent identifiers "outside" the function; the - * "inner" environment records are created on demand. Otherwise, - * the environment records are those that will be directly used - * (e.g. for declarations). - * - * _Lexenv is always set; _Varenv defaults to _Lexenv if missing, - * so _Varenv is only set if _Lexenv != _Varenv. - * - * This is relatively complex, see doc/identifier-handling.rst. - */ - - if (DUK_HOBJECT_HAS_NEWENV(&fun_clos->obj)) { -#if defined(DUK_USE_FUNC_NAME_PROPERTY) - if (DUK_HOBJECT_HAS_NAMEBINDING(&fun_clos->obj)) { - duk_hobject *proto; - duk_hdecenv *new_env; - - /* - * Named function expression, name needs to be bound - * in an intermediate environment record. The "outer" - * lexical/variable environment will thus be: - * - * a) { funcname: , __prototype: outer_lex_env } - * b) { funcname: , __prototype: } (if outer_lex_env missing) - */ - - if (outer_lex_env) { - proto = outer_lex_env; - } else { - proto = thr->builtins[DUK_BIDX_GLOBAL_ENV]; - } - - /* -> [ ... closure template env ] */ - new_env = - duk_hdecenv_alloc(thr, - DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV)); - DUK_ASSERT(new_env != NULL); - duk_push_hobject(thr, (duk_hobject *) new_env); - - DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) new_env) == NULL); - DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) new_env, proto); - DUK_HOBJECT_INCREF_ALLOWNULL(thr, proto); - - DUK_ASSERT(new_env->thread == NULL); /* Closed. */ - DUK_ASSERT(new_env->varmap == NULL); - - /* It's important that duk_xdef_prop() is a 'raw define' so that any - * properties in an ancestor are never an issue (they should never be - * e.g. non-writable, but just in case). - * - * Because template objects are not visible to user code, the case - * where .name is missing shouldn't happen in practice. It it does, - * the name 'undefined' gets bound and maps to the closure (which is - * a bit odd, but safe). - */ - (void) duk_get_prop_stridx_short(thr, -2, DUK_STRIDX_NAME); - /* -> [ ... closure template env funcname ] */ - duk_dup_m4(thr); /* -> [ ... closure template env funcname closure ] */ - duk_xdef_prop(thr, -3, DUK_PROPDESC_FLAGS_NONE); /* -> [ ... closure template env ] */ - /* env[funcname] = closure */ - - /* [ ... closure template env ] */ - - DUK_HCOMPFUNC_SET_LEXENV(thr->heap, fun_clos, (duk_hobject *) new_env); - DUK_HCOMPFUNC_SET_VARENV(thr->heap, fun_clos, (duk_hobject *) new_env); - DUK_HOBJECT_INCREF(thr, (duk_hobject *) new_env); - DUK_HOBJECT_INCREF(thr, (duk_hobject *) new_env); - duk_pop_unsafe(thr); - - /* [ ... closure template ] */ - } else -#endif /* DUK_USE_FUNC_NAME_PROPERTY */ - { - /* - * Other cases (function declaration, anonymous function expression, - * strict direct eval code). The "outer" environment will be whatever - * the caller gave us. - */ - - DUK_HCOMPFUNC_SET_LEXENV(thr->heap, fun_clos, outer_lex_env); - DUK_HCOMPFUNC_SET_VARENV(thr->heap, fun_clos, outer_lex_env); - DUK_HOBJECT_INCREF(thr, outer_lex_env); - DUK_HOBJECT_INCREF(thr, outer_lex_env); - - /* [ ... closure template ] */ - } - } else { - /* - * Function gets no new environment when called. This is the - * case for global code, indirect eval code, and non-strict - * direct eval code. There is no direct correspondence to the - * E5 specification, as global/eval code is not exposed as a - * function. - */ - - DUK_ASSERT(!DUK_HOBJECT_HAS_NAMEBINDING(&fun_temp->obj)); - - DUK_HCOMPFUNC_SET_LEXENV(thr->heap, fun_clos, outer_lex_env); - DUK_HCOMPFUNC_SET_VARENV(thr->heap, fun_clos, outer_var_env); - DUK_HOBJECT_INCREF(thr, outer_lex_env); /* NULLs not allowed; asserted on entry */ - DUK_HOBJECT_INCREF(thr, outer_var_env); - } - DUK_DDD(DUK_DDDPRINT("closure varenv -> %!ipO, lexenv -> %!ipO", - (duk_heaphdr *) fun_clos->var_env, - (duk_heaphdr *) fun_clos->lex_env)); - - /* Call handling assumes this for all callable closures. */ - DUK_ASSERT(DUK_HCOMPFUNC_GET_LEXENV(thr->heap, fun_clos) != NULL); - DUK_ASSERT(DUK_HCOMPFUNC_GET_VARENV(thr->heap, fun_clos) != NULL); - - /* - * Copy some internal properties directly - * - * The properties will be non-writable and non-enumerable, but - * configurable. - * - * Function templates are bare objects, so inheritance of internal - * Symbols is not an issue here even when using ordinary property - * reads. The function instance created is not bare, so internal - * Symbols must be defined without inheritance checks. - */ - - /* [ ... closure template ] */ - - DUK_DDD(DUK_DDDPRINT("copying properties: closure=%!iT, template=%!iT", - (duk_tval *) duk_get_tval(thr, -2), - (duk_tval *) duk_get_tval(thr, -1))); - - for (i = 0; i < (duk_small_uint_t) (sizeof(duk__closure_copy_proplist) / sizeof(duk_uint16_t)); i++) { - duk_small_int_t stridx = (duk_small_int_t) duk__closure_copy_proplist[i]; - if (duk_xget_owndataprop_stridx_short(thr, -1, stridx)) { - /* [ ... closure template val ] */ - DUK_DDD(DUK_DDDPRINT("copying property, stridx=%ld -> found", (long) stridx)); - duk_xdef_prop_stridx_short(thr, -3, stridx, DUK_PROPDESC_FLAGS_C); - } else { - DUK_DDD(DUK_DDDPRINT("copying property, stridx=%ld -> not found", (long) stridx)); - duk_pop_unsafe(thr); - } - } - - /* - * "length" maps to number of formals (E5 Section 13.2) for function - * declarations/expressions (non-bound functions). Note that 'nargs' - * is NOT necessarily equal to the number of arguments. Use length - * of _Formals; if missing, assume nargs matches .length. - */ - - /* [ ... closure template ] */ - - formals = duk_hobject_get_formals(thr, (duk_hobject *) fun_temp); - if (formals) { - len_value = (duk_uint_t) formals->length; - DUK_DD(DUK_DDPRINT("closure length from _Formals -> %ld", (long) len_value)); - } else { - len_value = fun_temp->nargs; - DUK_DD(DUK_DDPRINT("closure length defaulted from nargs -> %ld", (long) len_value)); - } - - duk_push_uint(thr, len_value); /* [ ... closure template len_value ] */ - duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_C); - - /* - * "prototype" is, by default, a fresh object with the "constructor" - * property. - * - * Note that this creates a circular reference for every function - * instance (closure) which prevents refcount-based collection of - * function instances. - * - * XXX: Try to avoid creating the default prototype object, because - * many functions are not used as constructors and the default - * prototype is unnecessary. Perhaps it could be created on-demand - * when it is first accessed? - */ - - /* [ ... closure template ] */ - - if (add_auto_proto) { - duk_push_object(thr); /* -> [ ... closure template newobj ] */ - duk_dup_m3(thr); /* -> [ ... closure template newobj closure ] */ - duk_xdef_prop_stridx_short(thr, - -2, - DUK_STRIDX_CONSTRUCTOR, - DUK_PROPDESC_FLAGS_WC); /* -> [ ... closure template newobj ] */ - duk_compact(thr, -1); /* compact the prototype */ - duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_PROTOTYPE, DUK_PROPDESC_FLAGS_W); /* -> [ ... closure template ] */ - } - - /* - * "arguments" and "caller" must be mapped to throwers for strict - * mode and bound functions (E5 Section 15.3.5). - * - * XXX: This is expensive to have for every strict function instance. - * Try to implement as virtual properties or on-demand created properties. - */ - - /* [ ... closure template ] */ - - if (DUK_HOBJECT_HAS_STRICT(&fun_clos->obj)) { - duk_xdef_prop_stridx_thrower(thr, -2, DUK_STRIDX_CALLER); - duk_xdef_prop_stridx_thrower(thr, -2, DUK_STRIDX_LC_ARGUMENTS); - } else { -#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY) - DUK_DDD(DUK_DDDPRINT("function is non-strict and non-standard 'caller' property in use, add initial 'null' value")); - duk_push_null(thr); - duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_CALLER, DUK_PROPDESC_FLAGS_NONE); -#else - DUK_DDD(DUK_DDDPRINT("function is non-strict and non-standard 'caller' property not used")); -#endif - } - - /* - * "name" used to be non-standard but is now defined by ES2015. - * In ES2015/ES2016 the .name property is configurable. - */ - - /* [ ... closure template ] */ - -#if defined(DUK_USE_FUNC_NAME_PROPERTY) - /* XXX: Look for own property only; doesn't matter much because - * templates are bare objects. - */ - if (duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_NAME)) { - /* [ ... closure template name ] */ - DUK_ASSERT(duk_is_string(thr, -1)); - DUK_DD(DUK_DDPRINT("setting function instance name to %!T", duk_get_tval(thr, -1))); - duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C); /* -> [ ... closure template ] */ - } else { - /* Anonymous functions don't have a .name in ES2015, so don't set - * it on the instance either. The instance will then inherit - * it from Function.prototype.name. - */ - DUK_DD(DUK_DDPRINT("not setting function instance .name")); - duk_pop_unsafe(thr); - } -#endif - - /* - * Compact the closure, in most cases no properties will be added later. - * Also, without this the closures end up having unused property slots - * (e.g. in Duktape 0.9.0, 8 slots would be allocated and only 7 used). - * A better future solution would be to allocate the closure directly - * to correct size (and setup the properties directly without going - * through the API). - */ - - duk_compact(thr, -2); - - /* - * Some assertions (E5 Section 13.2). - */ - - DUK_ASSERT(DUK_HOBJECT_GET_CLASS_NUMBER(&fun_clos->obj) == DUK_HOBJECT_CLASS_FUNCTION); - DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, &fun_clos->obj) == thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]); - DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(&fun_clos->obj)); - DUK_ASSERT(duk_has_prop_stridx(thr, -2, DUK_STRIDX_LENGTH) != 0); - DUK_ASSERT(add_auto_proto == 0 || duk_has_prop_stridx(thr, -2, DUK_STRIDX_PROTOTYPE) != 0); - /* May be missing .name */ - DUK_ASSERT(!DUK_HOBJECT_HAS_STRICT(&fun_clos->obj) || duk_has_prop_stridx(thr, -2, DUK_STRIDX_CALLER) != 0); - DUK_ASSERT(!DUK_HOBJECT_HAS_STRICT(&fun_clos->obj) || duk_has_prop_stridx(thr, -2, DUK_STRIDX_LC_ARGUMENTS) != 0); - - /* - * Finish - */ - - /* [ ... closure template ] */ - - DUK_DDD(DUK_DDDPRINT("created function instance: template=%!iT -> closure=%!iT", - (duk_tval *) duk_get_tval(thr, -1), - (duk_tval *) duk_get_tval(thr, -2))); - - duk_pop_unsafe(thr); - - /* [ ... closure ] */ -} - -/* - * Delayed activation environment record initialization (for functions - * with NEWENV). - * - * The non-delayed initialization is handled by duk_handle_call(). - */ - -DUK_LOCAL void duk__preallocate_env_entries(duk_hthread *thr, duk_hobject *varmap, duk_hobject *env) { - duk_uint_fast32_t i; - - for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(varmap); i++) { - duk_hstring *key; - - key = DUK_HOBJECT_E_GET_KEY(thr->heap, varmap, i); - DUK_ASSERT(key != NULL); /* assume keys are compact in _Varmap */ - DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, varmap, i)); /* assume plain values */ - - /* Predefine as 'undefined' to reserve a property slot. - * This makes the unwind process (where register values - * are copied to the env object) safe against throwing. - * - * XXX: This could be made much faster by creating the - * property table directly. - */ - duk_push_undefined(thr); - DUK_DDD(DUK_DDDPRINT("preallocate env entry for key %!O", key)); - duk_hobject_define_property_internal(thr, env, key, DUK_PROPDESC_FLAGS_WE); - } -} - -/* shared helper */ -DUK_INTERNAL -duk_hobject *duk_create_activation_environment_record(duk_hthread *thr, duk_hobject *func, duk_size_t bottom_byteoff) { - duk_hdecenv *env; - duk_hobject *parent; - duk_hcompfunc *f; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(func != NULL); - - DUK_STATS_INC(thr->heap, stats_envrec_create); - - f = (duk_hcompfunc *) func; - parent = DUK_HCOMPFUNC_GET_LEXENV(thr->heap, f); - if (!parent) { - parent = thr->builtins[DUK_BIDX_GLOBAL_ENV]; - } - - env = duk_hdecenv_alloc(thr, DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV)); - DUK_ASSERT(env != NULL); - duk_push_hobject(thr, (duk_hobject *) env); - - DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) env) == NULL); - DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) env, parent); - DUK_HOBJECT_INCREF_ALLOWNULL(thr, parent); /* parent env is the prototype */ - - /* open scope information, for compiled functions only */ - - DUK_ASSERT(env->thread == NULL); - DUK_ASSERT(env->varmap == NULL); - DUK_ASSERT(env->regbase_byteoff == 0); - if (DUK_HOBJECT_IS_COMPFUNC(func)) { - duk_hobject *varmap; - - varmap = duk_hobject_get_varmap(thr, func); - if (varmap != NULL) { - env->varmap = varmap; - DUK_HOBJECT_INCREF(thr, varmap); - env->thread = thr; - DUK_HTHREAD_INCREF(thr, thr); - env->regbase_byteoff = bottom_byteoff; - - /* Preallocate env property table to avoid potential - * for out-of-memory on unwind when the env is closed. - */ - duk__preallocate_env_entries(thr, varmap, (duk_hobject *) env); - } else { - /* If function has no _Varmap, leave the environment closed. */ - DUK_ASSERT(env->thread == NULL); - DUK_ASSERT(env->varmap == NULL); - DUK_ASSERT(env->regbase_byteoff == 0); - } - } - - return (duk_hobject *) env; -} - -DUK_INTERNAL -void duk_js_init_activation_environment_records_delayed(duk_hthread *thr, duk_activation *act) { - duk_hobject *func; - duk_hobject *env; - - DUK_ASSERT(thr != NULL); - func = DUK_ACT_GET_FUNC(act); - DUK_ASSERT(func != NULL); - DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func)); /* bound functions are never in act 'func' */ - - /* - * Delayed initialization only occurs for 'NEWENV' functions. - */ - - DUK_ASSERT(DUK_HOBJECT_HAS_NEWENV(func)); - DUK_ASSERT(act->lex_env == NULL); - DUK_ASSERT(act->var_env == NULL); - - DUK_STATS_INC(thr->heap, stats_envrec_delayedcreate); - - env = duk_create_activation_environment_record(thr, func, act->bottom_byteoff); - DUK_ASSERT(env != NULL); - /* 'act' is a stable pointer, so still OK. */ - - DUK_DDD(DUK_DDDPRINT("created delayed fresh env: %!ipO", (duk_heaphdr *) env)); -#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2) - { - duk_hobject *p = env; - while (p) { - DUK_DDD(DUK_DDDPRINT(" -> %!ipO", (duk_heaphdr *) p)); - p = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, p); - } - } -#endif - - act->lex_env = env; - act->var_env = env; - DUK_HOBJECT_INCREF(thr, env); /* XXX: incref by count (here 2 times) */ - DUK_HOBJECT_INCREF(thr, env); - - duk_pop_unsafe(thr); -} - -/* - * Closing environment records. - * - * The environment record MUST be closed with the thread where its activation - * is; i.e. if 'env' is open, 'thr' must match env->thread, and the regbase - * and varmap must still be valid. On entry, 'env' must be reachable. - */ - -DUK_INTERNAL void duk_js_close_environment_record(duk_hthread *thr, duk_hobject *env) { - duk_uint_fast32_t i; - duk_hobject *varmap; - duk_hstring *key; - duk_tval *tv; - duk_uint_t regnum; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(env != NULL); - - if (DUK_UNLIKELY(!DUK_HOBJECT_IS_DECENV(env))) { - DUK_DDD(DUK_DDDPRINT("env not a declarative record: %!iO", (duk_heaphdr *) env)); - return; - } - - varmap = ((duk_hdecenv *) env)->varmap; - if (varmap == NULL) { - DUK_DDD(DUK_DDDPRINT("env already closed: %!iO", (duk_heaphdr *) env)); - - return; - } - DUK_ASSERT(((duk_hdecenv *) env)->thread != NULL); - DUK_HDECENV_ASSERT_VALID((duk_hdecenv *) env); - - DUK_DDD(DUK_DDDPRINT("closing env: %!iO", (duk_heaphdr *) env)); - DUK_DDD(DUK_DDDPRINT("varmap: %!O", (duk_heaphdr *) varmap)); - - /* Env must be closed in the same thread as where it runs. */ - DUK_ASSERT(((duk_hdecenv *) env)->thread == thr); - - /* XXX: additional conditions when to close variables? we don't want to do it - * unless the environment may have "escaped" (referenced in a function closure). - * With delayed environments, the existence is probably good enough of a check. - */ - - /* Note: we rely on the _Varmap having a bunch of nice properties, like: - * - being compacted and unmodified during this process - * - not containing an array part - * - having correct value types - */ - - DUK_DDD(DUK_DDDPRINT("copying bound register values, %ld bound regs", (long) DUK_HOBJECT_GET_ENEXT(varmap))); - - /* Copy over current variable values from value stack to the - * environment record. The scope object is empty but may - * inherit from another scope which has conflicting names. - */ - - /* XXX: Do this using a once allocated entry area, no side effects. - * Hash part would need special treatment however (maybe copy, and - * then realloc with hash part if large enough). - */ - for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(varmap); i++) { - duk_size_t regbase_byteoff; - - key = DUK_HOBJECT_E_GET_KEY(thr->heap, varmap, i); - DUK_ASSERT(key != NULL); /* assume keys are compact in _Varmap */ - DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, varmap, i)); /* assume plain values */ - - tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, varmap, i); - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); - DUK_ASSERT(DUK_TVAL_GET_NUMBER(tv) <= (duk_double_t) DUK_UINT32_MAX); /* limits */ -#if defined(DUK_USE_FASTINT) - DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv)); - regnum = (duk_uint_t) DUK_TVAL_GET_FASTINT_U32(tv); -#else - regnum = (duk_uint_t) DUK_TVAL_GET_NUMBER(tv); -#endif - - regbase_byteoff = ((duk_hdecenv *) env)->regbase_byteoff; - DUK_ASSERT((duk_uint8_t *) thr->valstack + regbase_byteoff + sizeof(duk_tval) * regnum >= - (duk_uint8_t *) thr->valstack); - DUK_ASSERT((duk_uint8_t *) thr->valstack + regbase_byteoff + sizeof(duk_tval) * regnum < - (duk_uint8_t *) thr->valstack_top); - - /* Write register value into env as named properties. - * If property already exists, overwrites silently. - * Property is writable, but not deletable (not configurable - * in terms of property attributes). - * - * This property write must not throw because we're unwinding - * and unwind code is not allowed to throw at present. The - * call itself has no such guarantees, but we've preallocated - * entries for each property when the env was created, so no - * out-of-memory error should be possible. If this guarantee - * is not provided, problems like GH-476 may happen. - */ - duk_push_tval(thr, - (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + regbase_byteoff + sizeof(duk_tval) * regnum)); - DUK_DDD(DUK_DDDPRINT("closing identifier %!O -> reg %ld, value %!T", - (duk_heaphdr *) key, - (long) regnum, - (duk_tval *) duk_get_tval(thr, -1))); - duk_hobject_define_property_internal(thr, env, key, DUK_PROPDESC_FLAGS_WE); - } - - /* NULL atomically to avoid inconsistent state + side effects. */ - DUK_HOBJECT_DECREF_NORZ(thr, ((duk_hdecenv *) env)->thread); - DUK_HOBJECT_DECREF_NORZ(thr, ((duk_hdecenv *) env)->varmap); - ((duk_hdecenv *) env)->thread = NULL; - ((duk_hdecenv *) env)->varmap = NULL; - - DUK_DDD(DUK_DDDPRINT("env after closing: %!O", (duk_heaphdr *) env)); -} - -/* - * GETIDREF: a GetIdentifierReference-like helper. - * - * Provides a parent traversing lookup and a single level lookup - * (for HasBinding). - * - * Instead of returning the value, returns a bunch of values allowing - * the caller to read, write, or delete the binding. Value pointers - * are duk_tval pointers which can be mutated directly as long as - * refcounts are properly updated. Note that any operation which may - * reallocate valstacks or compact objects may invalidate the returned - * duk_tval (but not object) pointers, so caller must be very careful. - * - * If starting environment record 'env' is given, 'act' is ignored. - * However, if 'env' is NULL, the caller may identify, in 'act', an - * activation which hasn't had its declarative environment initialized - * yet. The activation registers are then looked up, and its parent - * traversed normally. - * - * The 'out' structure values are only valid if the function returns - * success (non-zero). - */ - -/* lookup name from an open declarative record's registers */ -DUK_LOCAL -duk_bool_t duk__getid_open_decl_env_regs(duk_hthread *thr, duk_hstring *name, duk_hdecenv *env, duk__id_lookup_result *out) { - duk_tval *tv; - duk_size_t reg_rel; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(name != NULL); - DUK_ASSERT(env != NULL); - DUK_ASSERT(out != NULL); - - DUK_ASSERT(DUK_HOBJECT_IS_DECENV((duk_hobject *) env)); - DUK_HDECENV_ASSERT_VALID(env); - - if (env->thread == NULL) { - /* already closed */ - return 0; - } - DUK_ASSERT(env->varmap != NULL); - - tv = duk_hobject_find_entry_tval_ptr(thr->heap, env->varmap, name); - if (DUK_UNLIKELY(tv == NULL)) { - return 0; - } - - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); - DUK_ASSERT(DUK_TVAL_GET_NUMBER(tv) <= (duk_double_t) DUK_UINT32_MAX); /* limits */ -#if defined(DUK_USE_FASTINT) - DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv)); - reg_rel = (duk_size_t) DUK_TVAL_GET_FASTINT_U32(tv); -#else - reg_rel = (duk_size_t) DUK_TVAL_GET_NUMBER(tv); -#endif - DUK_ASSERT_DISABLE(reg_rel >= 0); /* unsigned */ - - tv = (duk_tval *) (void *) ((duk_uint8_t *) env->thread->valstack + env->regbase_byteoff + sizeof(duk_tval) * reg_rel); - DUK_ASSERT(tv >= env->thread->valstack && tv < env->thread->valstack_end); /* XXX: more accurate? */ - - out->value = tv; - out->attrs = DUK_PROPDESC_FLAGS_W; /* registers are mutable, non-deletable */ - out->env = (duk_hobject *) env; - out->holder = NULL; - out->has_this = 0; - return 1; -} - -/* lookup name from current activation record's functions' registers */ -DUK_LOCAL -duk_bool_t duk__getid_activation_regs(duk_hthread *thr, duk_hstring *name, duk_activation *act, duk__id_lookup_result *out) { - duk_tval *tv; - duk_hobject *func; - duk_hobject *varmap; - duk_size_t reg_rel; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(name != NULL); - DUK_ASSERT(act != NULL); - DUK_ASSERT(out != NULL); - - func = DUK_ACT_GET_FUNC(act); - DUK_ASSERT(func != NULL); - DUK_ASSERT(DUK_HOBJECT_HAS_NEWENV(func)); - - if (!DUK_HOBJECT_IS_COMPFUNC(func)) { - return 0; - } - - /* XXX: move varmap to duk_hcompfunc struct field? */ - varmap = duk_hobject_get_varmap(thr, func); - if (!varmap) { - return 0; - } - - tv = duk_hobject_find_entry_tval_ptr(thr->heap, varmap, name); - if (!tv) { - return 0; - } - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); - reg_rel = (duk_size_t) DUK_TVAL_GET_NUMBER(tv); - DUK_ASSERT_DISABLE(reg_rel >= 0); - DUK_ASSERT(reg_rel < ((duk_hcompfunc *) func)->nregs); - - tv = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + act->bottom_byteoff); - tv += reg_rel; - - out->value = tv; - out->attrs = DUK_PROPDESC_FLAGS_W; /* registers are mutable, non-deletable */ - out->env = NULL; - out->holder = NULL; - out->has_this = 0; - return 1; -} - -DUK_LOCAL -duk_bool_t duk__get_identifier_reference(duk_hthread *thr, - duk_hobject *env, - duk_hstring *name, - duk_activation *act, - duk_bool_t parents, - duk__id_lookup_result *out) { - duk_tval *tv; - duk_uint_t sanity; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(env != NULL || act != NULL); - DUK_ASSERT(name != NULL); - DUK_ASSERT(out != NULL); - - DUK_ASSERT(!env || DUK_HOBJECT_IS_ENV(env)); - DUK_ASSERT(!env || !DUK_HOBJECT_HAS_ARRAY_PART(env)); - - /* - * Conceptually, we look for the identifier binding by starting from - * 'env' and following to chain of environment records (represented - * by the prototype chain). - * - * If 'env' is NULL, the current activation does not yet have an - * allocated declarative environment record; this should be treated - * exactly as if the environment record existed but had no bindings - * other than register bindings. - * - * Note: we assume that with the DUK_HOBJECT_FLAG_NEWENV cleared - * the environment will always be initialized immediately; hence - * a NULL 'env' should only happen with the flag set. This is the - * case for: (1) function calls, and (2) strict, direct eval calls. - */ - - if (env == NULL && act != NULL) { - duk_hobject *func; - duk_hcompfunc *f; - - DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference: env is NULL, activation is non-NULL -> " - "delayed env case, look up activation regs first")); - - /* - * Try registers - */ - - if (duk__getid_activation_regs(thr, name, act, out)) { - DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference successful: " - "name=%!O -> value=%!T, attrs=%ld, has_this=%ld, env=%!O, holder=%!O " - "(found from register bindings when env=NULL)", - (duk_heaphdr *) name, - (duk_tval *) out->value, - (long) out->attrs, - (long) out->has_this, - (duk_heaphdr *) out->env, - (duk_heaphdr *) out->holder)); - return 1; - } - - DUK_DDD(DUK_DDDPRINT("not found in current activation regs")); - - /* - * Not found in registers, proceed to the parent record. - * Here we need to determine what the parent would be, - * if 'env' was not NULL (i.e. same logic as when initializing - * the record). - * - * Note that environment initialization is only deferred when - * DUK_HOBJECT_HAS_NEWENV is set, and this only happens for: - * - Function code - * - Strict eval code - * - * We only need to check _Lexenv here; _Varenv exists only if it - * differs from _Lexenv (and thus _Lexenv will also be present). - */ - - if (!parents) { - DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference failed, no parent traversal " - "(not found from register bindings when env=NULL)")); - goto fail_not_found; - } - - func = DUK_ACT_GET_FUNC(act); - DUK_ASSERT(func != NULL); - DUK_ASSERT(DUK_HOBJECT_HAS_NEWENV(func)); - f = (duk_hcompfunc *) func; - - env = DUK_HCOMPFUNC_GET_LEXENV(thr->heap, f); - if (!env) { - env = thr->builtins[DUK_BIDX_GLOBAL_ENV]; - } - - DUK_DDD(DUK_DDDPRINT("continue lookup from env: %!iO", (duk_heaphdr *) env)); - } - - /* - * Prototype walking starting from 'env'. - * - * ('act' is not needed anywhere here.) - */ - - sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY; - while (env != NULL) { - duk_small_uint_t cl; - duk_uint_t attrs; - - DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference, name=%!O, considering env=%p -> %!iO", - (duk_heaphdr *) name, - (void *) env, - (duk_heaphdr *) env)); - - DUK_ASSERT(env != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_ENV(env)); - DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(env)); - - cl = DUK_HOBJECT_GET_CLASS_NUMBER(env); - DUK_ASSERT(cl == DUK_HOBJECT_CLASS_OBJENV || cl == DUK_HOBJECT_CLASS_DECENV); - if (cl == DUK_HOBJECT_CLASS_DECENV) { - /* - * Declarative environment record. - * - * Identifiers can never be stored in ancestors and are - * always plain values, so we can use an internal helper - * and access the value directly with an duk_tval ptr. - * - * A closed environment is only indicated by it missing - * the "book-keeping" properties required for accessing - * register-bound variables. - */ - - DUK_HDECENV_ASSERT_VALID((duk_hdecenv *) env); - if (duk__getid_open_decl_env_regs(thr, name, (duk_hdecenv *) env, out)) { - DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference successful: " - "name=%!O -> value=%!T, attrs=%ld, has_this=%ld, env=%!O, holder=%!O " - "(declarative environment record, scope open, found in regs)", - (duk_heaphdr *) name, - (duk_tval *) out->value, - (long) out->attrs, - (long) out->has_this, - (duk_heaphdr *) out->env, - (duk_heaphdr *) out->holder)); - return 1; - } - - tv = duk_hobject_find_entry_tval_ptr_and_attrs(thr->heap, env, name, &attrs); - if (tv) { - out->value = tv; - out->attrs = attrs; - out->env = env; - out->holder = env; - out->has_this = 0; - - DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference successful: " - "name=%!O -> value=%!T, attrs=%ld, has_this=%ld, env=%!O, holder=%!O " - "(declarative environment record, found in properties)", - (duk_heaphdr *) name, - (duk_tval *) out->value, - (long) out->attrs, - (long) out->has_this, - (duk_heaphdr *) out->env, - (duk_heaphdr *) out->holder)); - return 1; - } - } else { - /* - * Object environment record. - * - * Binding (target) object is an external, uncontrolled object. - * Identifier may be bound in an ancestor property, and may be - * an accessor. Target can also be a Proxy which we must support - * here. - */ - - /* XXX: we could save space by using _Target OR _This. If _Target, assume - * this binding is undefined. If _This, assumes this binding is _This, and - * target is also _This. One property would then be enough. - */ - - duk_hobject *target; - duk_bool_t found; - - DUK_ASSERT(cl == DUK_HOBJECT_CLASS_OBJENV); - DUK_HOBJENV_ASSERT_VALID((duk_hobjenv *) env); - - target = ((duk_hobjenv *) env)->target; - DUK_ASSERT(target != NULL); - - /* Target may be a Proxy or property may be an accessor, so we must - * use an actual, Proxy-aware hasprop check here. - * - * out->holder is NOT set to the actual duk_hobject where the - * property is found, but rather the object binding target object. - */ - -#if defined(DUK_USE_ES6_PROXY) - if (DUK_UNLIKELY(DUK_HOBJECT_IS_PROXY(target))) { - duk_tval tv_name; - duk_tval tv_target_tmp; - - DUK_ASSERT(name != NULL); - DUK_TVAL_SET_STRING(&tv_name, name); - DUK_TVAL_SET_OBJECT(&tv_target_tmp, target); - - found = duk_hobject_hasprop(thr, &tv_target_tmp, &tv_name); - } else -#endif /* DUK_USE_ES6_PROXY */ - { - /* XXX: duk_hobject_hasprop() would be correct for - * non-Proxy objects too, but it is about ~20-25% - * slower at present so separate code paths for - * Proxy and non-Proxy now. - */ - found = duk_hobject_hasprop_raw(thr, target, name); - } - - if (found) { - out->value = NULL; /* can't get value, may be accessor */ - out->attrs = 0; /* irrelevant when out->value == NULL */ - out->env = env; - out->holder = target; - out->has_this = ((duk_hobjenv *) env)->has_this; - - DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference successful: " - "name=%!O -> value=%!T, attrs=%ld, has_this=%ld, env=%!O, holder=%!O " - "(object environment record)", - (duk_heaphdr *) name, - (duk_tval *) out->value, - (long) out->attrs, - (long) out->has_this, - (duk_heaphdr *) out->env, - (duk_heaphdr *) out->holder)); - return 1; - } - } - - if (!parents) { - DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference failed, no parent traversal " - "(not found from first traversed env)")); - goto fail_not_found; - } - - if (DUK_UNLIKELY(sanity-- == 0)) { - DUK_ERROR_RANGE(thr, DUK_STR_PROTOTYPE_CHAIN_LIMIT); - DUK_WO_NORETURN(return 0;); - } - env = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, env); - } - - /* - * Not found (even in global object) - */ - -fail_not_found: - return 0; -} - -/* - * HASVAR: check identifier binding from a given environment record - * without traversing its parents. - * - * This primitive is not exposed to user code as such, but is used - * internally for e.g. declaration binding instantiation. - * - * See E5 Sections: - * 10.2.1.1.1 HasBinding(N) - * 10.2.1.2.1 HasBinding(N) - * - * Note: strictness has no bearing on this check. Hence we don't take - * a 'strict' parameter. - */ - -#if 0 /*unused*/ -DUK_INTERNAL -duk_bool_t duk_js_hasvar_envrec(duk_hthread *thr, - duk_hobject *env, - duk_hstring *name) { - duk__id_lookup_result ref; - duk_bool_t parents; - - DUK_DDD(DUK_DDDPRINT("hasvar: thr=%p, env=%p, name=%!O " - "(env -> %!dO)", - (void *) thr, (void *) env, (duk_heaphdr *) name, - (duk_heaphdr *) env)); - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(env != NULL); - DUK_ASSERT(name != NULL); - - DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(env); - DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(name); - - DUK_ASSERT(DUK_HOBJECT_IS_ENV(env)); - DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(env)); - - /* lookup results is ignored */ - parents = 0; - return duk__get_identifier_reference(thr, env, name, NULL, parents, &ref); -} -#endif - -/* - * GETVAR - * - * See E5 Sections: - * 11.1.2 Identifier Reference - * 10.3.1 Identifier Resolution - * 11.13.1 Simple Assignment [example of where the Reference is GetValue'd] - * 8.7.1 GetValue (V) - * 8.12.1 [[GetOwnProperty]] (P) - * 8.12.2 [[GetProperty]] (P) - * 8.12.3 [[Get]] (P) - * - * If 'throw' is true, always leaves two values on top of stack: [val this]. - * - * If 'throw' is false, returns 0 if identifier cannot be resolved, and the - * stack will be unaffected in this case. If identifier is resolved, returns - * 1 and leaves [val this] on top of stack. - * - * Note: the 'strict' flag of a reference returned by GetIdentifierReference - * is ignored by GetValue. Hence we don't take a 'strict' parameter. - * - * The 'throw' flag is needed for implementing 'typeof' for an unreferenced - * identifier. An unreference identifier in other contexts generates a - * ReferenceError. - */ - -DUK_LOCAL -duk_bool_t duk__getvar_helper(duk_hthread *thr, duk_hobject *env, duk_activation *act, duk_hstring *name, duk_bool_t throw_flag) { - duk__id_lookup_result ref; - duk_tval tv_tmp_obj; - duk_tval tv_tmp_key; - duk_bool_t parents; - - DUK_DDD(DUK_DDDPRINT("getvar: thr=%p, env=%p, act=%p, name=%!O " - "(env -> %!dO)", - (void *) thr, - (void *) env, - (void *) act, - (duk_heaphdr *) name, - (duk_heaphdr *) env)); - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(name != NULL); - /* env and act may be NULL */ - - DUK_STATS_INC(thr->heap, stats_getvar_all); - - DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(env); - DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(name); - - parents = 1; /* follow parent chain */ - if (duk__get_identifier_reference(thr, env, name, act, parents, &ref)) { - if (ref.value) { - duk_push_tval(thr, ref.value); - duk_push_undefined(thr); - } else { - DUK_ASSERT(ref.holder != NULL); - - /* ref.holder is safe across the getprop call (even - * with side effects) because 'env' is reachable and - * ref.holder is a direct heap pointer. - */ - - DUK_TVAL_SET_OBJECT(&tv_tmp_obj, ref.holder); - DUK_TVAL_SET_STRING(&tv_tmp_key, name); - (void) duk_hobject_getprop(thr, &tv_tmp_obj, &tv_tmp_key); /* [value] */ - - if (ref.has_this) { - duk_push_hobject(thr, ref.holder); - } else { - duk_push_undefined(thr); - } - - /* [value this] */ - } - - return 1; - } else { - if (throw_flag) { - DUK_ERROR_FMT1(thr, - DUK_ERR_REFERENCE_ERROR, - "identifier '%s' undefined", - (const char *) DUK_HSTRING_GET_DATA(name)); - DUK_WO_NORETURN(return 0;); - } - - return 0; - } -} - -DUK_INTERNAL -duk_bool_t duk_js_getvar_envrec(duk_hthread *thr, duk_hobject *env, duk_hstring *name, duk_bool_t throw_flag) { - return duk__getvar_helper(thr, env, NULL, name, throw_flag); -} - -DUK_INTERNAL -duk_bool_t duk_js_getvar_activation(duk_hthread *thr, duk_activation *act, duk_hstring *name, duk_bool_t throw_flag) { - DUK_ASSERT(act != NULL); - return duk__getvar_helper(thr, act->lex_env, act, name, throw_flag); -} - -/* - * PUTVAR - * - * See E5 Sections: - * 11.1.2 Identifier Reference - * 10.3.1 Identifier Resolution - * 11.13.1 Simple Assignment [example of where the Reference is PutValue'd] - * 8.7.2 PutValue (V,W) [see especially step 3.b, undefined -> automatic global in non-strict mode] - * 8.12.4 [[CanPut]] (P) - * 8.12.5 [[Put]] (P) - * - * Note: may invalidate any valstack (or object) duk_tval pointers because - * putting a value may reallocate any object or any valstack. Caller beware. - */ - -DUK_LOCAL -void duk__putvar_helper(duk_hthread *thr, - duk_hobject *env, - duk_activation *act, - duk_hstring *name, - duk_tval *val, - duk_bool_t strict) { - duk__id_lookup_result ref; - duk_tval tv_tmp_val; - duk_tval tv_tmp_obj; - duk_tval tv_tmp_key; - duk_bool_t parents; - - DUK_STATS_INC(thr->heap, stats_putvar_all); - - DUK_DDD(DUK_DDDPRINT("putvar: thr=%p, env=%p, act=%p, name=%!O, val=%p, strict=%ld " - "(env -> %!dO, val -> %!T)", - (void *) thr, - (void *) env, - (void *) act, - (duk_heaphdr *) name, - (void *) val, - (long) strict, - (duk_heaphdr *) env, - (duk_tval *) val)); - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(name != NULL); - DUK_ASSERT(val != NULL); - /* env and act may be NULL */ - - DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(env); - DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(name); - DUK_ASSERT_REFCOUNT_NONZERO_TVAL(val); - - DUK_TVAL_SET_TVAL(&tv_tmp_val, val); /* Stabilize. */ - val = NULL; - - /* - * In strict mode E5 protects 'eval' and 'arguments' from being - * assigned to (or even declared anywhere). Attempt to do so - * should result in a compile time SyntaxError. See the internal - * design documentation for details. - * - * Thus, we should never come here, run-time, for strict code, - * and name 'eval' or 'arguments'. - */ - - DUK_ASSERT(!strict || (name != DUK_HTHREAD_STRING_EVAL(thr) && name != DUK_HTHREAD_STRING_LC_ARGUMENTS(thr))); - - /* - * Lookup variable and update in-place if found. - */ - - parents = 1; /* follow parent chain */ - - if (duk__get_identifier_reference(thr, env, name, act, parents, &ref)) { - if (ref.value && (ref.attrs & DUK_PROPDESC_FLAG_WRITABLE)) { - /* Update duk_tval in-place if pointer provided and the - * property is writable. If the property is not writable - * (immutable binding), use duk_hobject_putprop() which - * will respect mutability. - */ - duk_tval *tv_val; - - tv_val = ref.value; - DUK_ASSERT(tv_val != NULL); - DUK_TVAL_SET_TVAL_UPDREF(thr, tv_val, &tv_tmp_val); /* side effects */ - - /* ref.value invalidated here */ - } else { - DUK_ASSERT(ref.holder != NULL); - - DUK_TVAL_SET_OBJECT(&tv_tmp_obj, ref.holder); - DUK_TVAL_SET_STRING(&tv_tmp_key, name); - (void) duk_hobject_putprop(thr, &tv_tmp_obj, &tv_tmp_key, &tv_tmp_val, strict); - - /* ref.value invalidated here */ - } - - return; - } - - /* - * Not found: write to global object (non-strict) or ReferenceError - * (strict); see E5 Section 8.7.2, step 3. - */ - - if (strict) { - DUK_DDD(DUK_DDDPRINT("identifier binding not found, strict => reference error")); - DUK_ERROR_FMT1(thr, - DUK_ERR_REFERENCE_ERROR, - "identifier '%s' undefined", - (const char *) DUK_HSTRING_GET_DATA(name)); - DUK_WO_NORETURN(return;); - } - - DUK_DDD(DUK_DDDPRINT("identifier binding not found, not strict => set to global")); - - DUK_TVAL_SET_OBJECT(&tv_tmp_obj, thr->builtins[DUK_BIDX_GLOBAL]); - DUK_TVAL_SET_STRING(&tv_tmp_key, name); - (void) duk_hobject_putprop(thr, &tv_tmp_obj, &tv_tmp_key, &tv_tmp_val, 0); /* 0 = no throw */ - - /* NB: 'val' may be invalidated here because put_value may realloc valstack, - * caller beware. - */ -} - -DUK_INTERNAL -void duk_js_putvar_envrec(duk_hthread *thr, duk_hobject *env, duk_hstring *name, duk_tval *val, duk_bool_t strict) { - duk__putvar_helper(thr, env, NULL, name, val, strict); -} - -DUK_INTERNAL -void duk_js_putvar_activation(duk_hthread *thr, duk_activation *act, duk_hstring *name, duk_tval *val, duk_bool_t strict) { - DUK_ASSERT(act != NULL); - duk__putvar_helper(thr, act->lex_env, act, name, val, strict); -} - -/* - * DELVAR - * - * See E5 Sections: - * 11.4.1 The delete operator - * 10.2.1.1.5 DeleteBinding (N) [declarative environment record] - * 10.2.1.2.5 DeleteBinding (N) [object environment record] - * - * Variable bindings established inside eval() are deletable (configurable), - * other bindings are not, including variables declared in global level. - * Registers are always non-deletable, and the deletion of other bindings - * is controlled by the configurable flag. - * - * For strict mode code, the 'delete' operator should fail with a compile - * time SyntaxError if applied to identifiers. Hence, no strict mode - * run-time deletion of identifiers should ever happen. This function - * should never be called from strict mode code! - */ - -DUK_LOCAL -duk_bool_t duk__delvar_helper(duk_hthread *thr, duk_hobject *env, duk_activation *act, duk_hstring *name) { - duk__id_lookup_result ref; - duk_bool_t parents; - - DUK_DDD(DUK_DDDPRINT("delvar: thr=%p, env=%p, act=%p, name=%!O " - "(env -> %!dO)", - (void *) thr, - (void *) env, - (void *) act, - (duk_heaphdr *) name, - (duk_heaphdr *) env)); - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(name != NULL); - /* env and act may be NULL */ - - DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(name); - - parents = 1; /* follow parent chain */ - - if (duk__get_identifier_reference(thr, env, name, act, parents, &ref)) { - if (ref.value && !(ref.attrs & DUK_PROPDESC_FLAG_CONFIGURABLE)) { - /* Identifier found in registers (always non-deletable) - * or declarative environment record and non-configurable. - */ - return 0; - } - DUK_ASSERT(ref.holder != NULL); - - return duk_hobject_delprop_raw(thr, ref.holder, name, 0); - } - - /* - * Not found (even in global object). - * - * In non-strict mode this is a silent SUCCESS (!), see E5 Section 11.4.1, - * step 3.b. In strict mode this case is a compile time SyntaxError so - * we should not come here. - */ - - DUK_DDD(DUK_DDDPRINT("identifier to be deleted not found: name=%!O " - "(treated as silent success)", - (duk_heaphdr *) name)); - return 1; -} - -#if 0 /*unused*/ -DUK_INTERNAL -duk_bool_t duk_js_delvar_envrec(duk_hthread *thr, - duk_hobject *env, - duk_hstring *name) { - return duk__delvar_helper(thr, env, NULL, name); -} -#endif - -DUK_INTERNAL -duk_bool_t duk_js_delvar_activation(duk_hthread *thr, duk_activation *act, duk_hstring *name) { - DUK_ASSERT(act != NULL); - return duk__delvar_helper(thr, act->lex_env, act, name); -} - -/* - * DECLVAR - * - * See E5 Sections: - * 10.4.3 Entering Function Code - * 10.5 Declaration Binding Instantion - * 12.2 Variable Statement - * 11.1.2 Identifier Reference - * 10.3.1 Identifier Resolution - * - * Variable declaration behavior is mainly discussed in Section 10.5, - * and is not discussed in the execution semantics (Sections 11-13). - * - * Conceptually declarations happen when code (global, eval, function) - * is entered, before any user code is executed. In practice, register- - * bound identifiers are 'declared' automatically (by virtue of being - * allocated to registers with the initial value 'undefined'). Other - * identifiers are declared in the function prologue with this primitive. - * - * Since non-register bindings eventually back to an internal object's - * properties, the 'prop_flags' argument is used to specify binding - * type: - * - * - Immutable binding: set DUK_PROPDESC_FLAG_WRITABLE to false - * - Non-deletable binding: set DUK_PROPDESC_FLAG_CONFIGURABLE to false - * - The flag DUK_PROPDESC_FLAG_ENUMERABLE should be set, although it - * doesn't really matter for internal objects - * - * All bindings are non-deletable mutable bindings except: - * - * - Declarations in eval code (mutable, deletable) - * - 'arguments' binding in strict function code (immutable) - * - Function name binding of a function expression (immutable) - * - * Declarations may go to declarative environment records (always - * so for functions), but may also go to object environment records - * (e.g. global code). The global object environment has special - * behavior when re-declaring a function (but not a variable); see - * E5.1 specification, Section 10.5, step 5.e. - * - * Declarations always go to the 'top-most' environment record, i.e. - * we never check the record chain. It's not an error even if a - * property (even an immutable or non-deletable one) of the same name - * already exists. - * - * If a declared variable already exists, its value needs to be updated - * (if possible). Returns 1 if a PUTVAR needs to be done by the caller; - * otherwise returns 0. - */ - -DUK_LOCAL -duk_bool_t duk__declvar_helper(duk_hthread *thr, - duk_hobject *env, - duk_hstring *name, - duk_tval *val, - duk_small_uint_t prop_flags, - duk_bool_t is_func_decl) { - duk_hobject *holder; - duk_bool_t parents; - duk__id_lookup_result ref; - duk_tval *tv; - - DUK_DDD(DUK_DDDPRINT("declvar: thr=%p, env=%p, name=%!O, val=%!T, prop_flags=0x%08lx, is_func_decl=%ld " - "(env -> %!iO)", - (void *) thr, - (void *) env, - (duk_heaphdr *) name, - (duk_tval *) val, - (unsigned long) prop_flags, - (unsigned int) is_func_decl, - (duk_heaphdr *) env)); - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(env != NULL); - DUK_ASSERT(name != NULL); - DUK_ASSERT(val != NULL); - - /* Note: in strict mode the compiler should reject explicit - * declaration of 'eval' or 'arguments'. However, internal - * bytecode may declare 'arguments' in the function prologue. - * We don't bother checking (or asserting) for these now. - */ - - /* Note: val is a stable duk_tval pointer. The caller makes - * a value copy into its stack frame, so 'tv_val' is not subject - * to side effects here. - */ - - /* - * Check whether already declared. - * - * We need to check whether the binding exists in the environment - * without walking its parents. However, we still need to check - * register-bound identifiers and the prototype chain of an object - * environment target object. - */ - - parents = 0; /* just check 'env' */ - if (duk__get_identifier_reference(thr, env, name, NULL, parents, &ref)) { - duk_int_t e_idx; - duk_int_t h_idx; - duk_small_uint_t flags; - - /* - * Variable already declared, ignore re-declaration. - * The only exception is the updated behavior of E5.1 for - * global function declarations, E5.1 Section 10.5, step 5.e. - * This behavior does not apply to global variable declarations. - */ - - if (!(is_func_decl && env == thr->builtins[DUK_BIDX_GLOBAL_ENV])) { - DUK_DDD(DUK_DDDPRINT("re-declare a binding, ignoring")); - return 1; /* 1 -> needs a PUTVAR */ - } - - /* - * Special behavior in E5.1. - * - * Note that even though parents == 0, the conflicting property - * may be an inherited property (currently our global object's - * prototype is Object.prototype). Step 5.e first operates on - * the existing property (which is potentially in an ancestor) - * and then defines a new property in the global object (and - * never modifies the ancestor). - * - * Also note that this logic would become even more complicated - * if the conflicting property might be a virtual one. Object - * prototype has no virtual properties, though. - * - * XXX: this is now very awkward, rework. - */ - - DUK_DDD(DUK_DDDPRINT("re-declare a function binding in global object, " - "updated E5.1 processing")); - - DUK_ASSERT(ref.holder != NULL); - holder = ref.holder; - - /* holder will be set to the target object, not the actual object - * where the property was found (see duk__get_identifier_reference()). - */ - DUK_ASSERT(DUK_HOBJECT_GET_CLASS_NUMBER(holder) == DUK_HOBJECT_CLASS_GLOBAL); - DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARRAY(holder)); /* global object doesn't have array part */ - - /* XXX: use a helper for prototype traversal; no loop check here */ - /* must be found: was found earlier, and cannot be inherited */ - for (;;) { - DUK_ASSERT(holder != NULL); - if (duk_hobject_find_entry(thr->heap, holder, name, &e_idx, &h_idx)) { - DUK_ASSERT(e_idx >= 0); - break; - } - /* SCANBUILD: NULL pointer dereference, doesn't actually trigger, - * asserted above. - */ - holder = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, holder); - } - DUK_ASSERT(holder != NULL); - DUK_ASSERT(e_idx >= 0); - /* SCANBUILD: scan-build produces a NULL pointer dereference warning - * below; it never actually triggers because holder is actually never - * NULL. - */ - - /* ref.holder is global object, holder is the object with the - * conflicting property. - */ - - flags = DUK_HOBJECT_E_GET_FLAGS(thr->heap, holder, e_idx); - if (!(flags & DUK_PROPDESC_FLAG_CONFIGURABLE)) { - if (flags & DUK_PROPDESC_FLAG_ACCESSOR) { - DUK_DDD(DUK_DDDPRINT("existing property is a non-configurable " - "accessor -> reject")); - goto fail_existing_attributes; - } - if (!((flags & DUK_PROPDESC_FLAG_WRITABLE) && (flags & DUK_PROPDESC_FLAG_ENUMERABLE))) { - DUK_DDD(DUK_DDDPRINT("existing property is a non-configurable " - "plain property which is not writable and " - "enumerable -> reject")); - goto fail_existing_attributes; - } - - DUK_DDD(DUK_DDDPRINT("existing property is not configurable but " - "is plain, enumerable, and writable -> " - "allow redeclaration")); - } - - if (holder == ref.holder) { - /* XXX: if duk_hobject_define_property_internal() was updated - * to handle a pre-existing accessor property, this would be - * a simple call (like for the ancestor case). - */ - DUK_DDD(DUK_DDDPRINT("redefine, offending property in global object itself")); - - if (flags & DUK_PROPDESC_FLAG_ACCESSOR) { - duk_hobject *tmp; - - tmp = DUK_HOBJECT_E_GET_VALUE_GETTER(thr->heap, holder, e_idx); - DUK_HOBJECT_E_SET_VALUE_GETTER(thr->heap, holder, e_idx, NULL); - DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp); - DUK_UNREF(tmp); - tmp = DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, holder, e_idx); - DUK_HOBJECT_E_SET_VALUE_SETTER(thr->heap, holder, e_idx, NULL); - DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp); - DUK_UNREF(tmp); - } else { - tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, holder, e_idx); - DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv); - } - - /* Here val would be potentially invalid if we didn't make - * a value copy at the caller. - */ - - tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, holder, e_idx); - DUK_TVAL_SET_TVAL(tv, val); - DUK_TVAL_INCREF(thr, tv); - DUK_HOBJECT_E_SET_FLAGS(thr->heap, holder, e_idx, prop_flags); - - DUK_DDD(DUK_DDDPRINT("updated global binding, final result: " - "value -> %!T, prop_flags=0x%08lx", - (duk_tval *) DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, holder, e_idx), - (unsigned long) prop_flags)); - } else { - DUK_DDD(DUK_DDDPRINT("redefine, offending property in ancestor")); - - DUK_ASSERT(ref.holder == thr->builtins[DUK_BIDX_GLOBAL]); - duk_push_tval(thr, val); - duk_hobject_define_property_internal(thr, ref.holder, name, prop_flags); - } - - return 0; - } - - /* - * Not found (in registers or record objects). Declare - * to current variable environment. - */ - - /* - * Get holder object - */ - - if (DUK_HOBJECT_IS_DECENV(env)) { - DUK_HDECENV_ASSERT_VALID((duk_hdecenv *) env); - holder = env; - } else { - DUK_HOBJENV_ASSERT_VALID((duk_hobjenv *) env); - holder = ((duk_hobjenv *) env)->target; - DUK_ASSERT(holder != NULL); - } - - /* - * Define new property - * - * Note: this may fail if the holder is not extensible. - */ - - /* XXX: this is awkward as we use an internal method which doesn't handle - * extensibility etc correctly. Basically we'd want to do a [[DefineOwnProperty]] - * or Object.defineProperty() here. - */ - - if (!DUK_HOBJECT_HAS_EXTENSIBLE(holder)) { - goto fail_not_extensible; - } - - duk_push_hobject(thr, holder); - duk_push_hstring(thr, name); - duk_push_tval(thr, val); - duk_xdef_prop(thr, -3, prop_flags); /* [holder name val] -> [holder] */ - duk_pop_unsafe(thr); - - return 0; - -fail_existing_attributes: -fail_not_extensible: - DUK_ERROR_TYPE(thr, "declaration failed"); - DUK_WO_NORETURN(return 0;); -} - -DUK_INTERNAL -duk_bool_t duk_js_declvar_activation(duk_hthread *thr, - duk_activation *act, - duk_hstring *name, - duk_tval *val, - duk_small_uint_t prop_flags, - duk_bool_t is_func_decl) { - duk_hobject *env; - duk_tval tv_val_copy; - - DUK_ASSERT(act != NULL); - - /* - * Make a value copy of the input val. This ensures that - * side effects cannot invalidate the pointer. - */ - - DUK_TVAL_SET_TVAL(&tv_val_copy, val); - val = &tv_val_copy; - - /* - * Delayed env creation check - */ - - if (!act->var_env) { - DUK_ASSERT(act->lex_env == NULL); - duk_js_init_activation_environment_records_delayed(thr, act); - /* 'act' is a stable pointer, so still OK. */ - } - DUK_ASSERT(act->lex_env != NULL); - DUK_ASSERT(act->var_env != NULL); - - env = act->var_env; - DUK_ASSERT(env != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_ENV(env)); - - return duk__declvar_helper(thr, env, name, val, prop_flags, is_func_decl); -} -/* - * Lexer for source files, ToNumber() string conversions, RegExp expressions, - * and JSON. - * - * Provides a stream of ECMAScript tokens from an UTF-8/CESU-8 buffer. The - * caller can also rewind the token stream into a certain position which is - * needed by the compiler part for multi-pass scanning. Tokens are - * represented as duk_token structures, and contain line number information. - * Token types are identified with DUK_TOK_* defines. - * - * Characters are decoded into a fixed size lookup window consisting of - * decoded Unicode code points, with window positions past the end of the - * input filled with an invalid codepoint (-1). The tokenizer can thus - * perform multiple character lookups efficiently and with few sanity - * checks (such as access outside the end of the input), which keeps the - * tokenization code small at the cost of performance. - * - * Character data in tokens, such as identifier names and string literals, - * is encoded into CESU-8 format on-the-fly while parsing the token in - * question. The string data is made reachable to garbage collection by - * placing the token-related values in value stack entries allocated for - * this purpose by the caller. The characters exist in Unicode code point - * form only in the fixed size lookup window, which keeps character data - * expansion (of especially ASCII data) low. - * - * Token parsing supports the full range of Unicode characters as described - * in the E5 specification. Parsing has been optimized for ASCII characters - * because ordinary ECMAScript code consists almost entirely of ASCII - * characters. Matching of complex Unicode codepoint sets (such as in the - * IdentifierStart and IdentifierPart productions) is optimized for size, - * and is done using a linear scan of a bit-packed list of ranges. This is - * very slow, but should never be entered unless the source code actually - * contains Unicode characters. - * - * ECMAScript tokenization is partially context sensitive. First, - * additional future reserved words are recognized in strict mode (see E5 - * Section 7.6.1.2). Second, a forward slash character ('/') can be - * recognized either as starting a RegExp literal or as a division operator, - * depending on context. The caller must provide necessary context flags - * when requesting a new token. - * - * Future work: - * - * * Make line number tracking optional, as it consumes space. - * - * * Add a feature flag for disabling UTF-8 decoding of input, as most - * source code is ASCII. Because of Unicode escapes written in ASCII, - * this does not allow Unicode support to be removed from e.g. - * duk_unicode_is_identifier_start() nor does it allow removal of CESU-8 - * encoding of e.g. string literals. - * - * * Add a feature flag for disabling Unicode compliance of e.g. identifier - * names. This allows for a build more than a kilobyte smaller, because - * Unicode ranges needed by duk_unicode_is_identifier_start() and - * duk_unicode_is_identifier_part() can be dropped. String literals - * should still be allowed to contain escaped Unicode, so this still does - * not allow removal of CESU-8 encoding of e.g. string literals. - * - * * Character lookup tables for codepoints above BMP could be stripped. - * - * * Strictly speaking, E5 specification requires that source code consists - * of 16-bit code units, and if not, must be conceptually converted to - * that format first. The current lexer processes Unicode code points - * and allows characters outside the BMP. These should be converted to - * surrogate pairs while reading the source characters into the window, - * not after tokens have been formed (as is done now). However, the fix - * is not trivial because two characters are decoded from one codepoint. - * - * * Optimize for speed as well as size. Large if-else ladders are (at - * least potentially) slow. - */ - -/* #include duk_internal.h -> already included */ - -/* - * Various defines and file specific helper macros - */ - -#define DUK__MAX_RE_DECESC_DIGITS 9 -#define DUK__MAX_RE_QUANT_DIGITS 9 /* Does not allow e.g. 2**31-1, but one more would allow overflows of u32. */ - -/* whether to use macros or helper function depends on call count */ -#define DUK__ISDIGIT(x) ((x) >= DUK_ASC_0 && (x) <= DUK_ASC_9) -#define DUK__ISHEXDIGIT(x) duk__is_hex_digit((x)) -#define DUK__ISOCTDIGIT(x) ((x) >= DUK_ASC_0 && (x) <= DUK_ASC_7) -#define DUK__ISDIGIT03(x) ((x) >= DUK_ASC_0 && (x) <= DUK_ASC_3) -#define DUK__ISDIGIT47(x) ((x) >= DUK_ASC_4 && (x) <= DUK_ASC_7) - -/* lexer character window helpers */ -#define DUK__LOOKUP(lex_ctx, idx) ((lex_ctx)->window[(idx)].codepoint) -#define DUK__ADVANCECHARS(lex_ctx, count) duk__advance_chars((lex_ctx), (count)) -#define DUK__ADVANCEBYTES(lex_ctx, count) duk__advance_bytes((lex_ctx), (count)) -#define DUK__INITBUFFER(lex_ctx) duk__initbuffer((lex_ctx)) -#define DUK__APPENDBUFFER(lex_ctx, x) duk__appendbuffer((lex_ctx), (duk_codepoint_t) (x)) -#define DUK__APPENDBUFFER_ASCII(lex_ctx, x) duk__appendbuffer_ascii((lex_ctx), (duk_codepoint_t) (x)) - -/* lookup shorthands (note: assume context variable is named 'lex_ctx') */ -#define DUK__L0() DUK__LOOKUP(lex_ctx, 0) -#define DUK__L1() DUK__LOOKUP(lex_ctx, 1) -#define DUK__L2() DUK__LOOKUP(lex_ctx, 2) -#define DUK__L3() DUK__LOOKUP(lex_ctx, 3) -#define DUK__L4() DUK__LOOKUP(lex_ctx, 4) -#define DUK__L5() DUK__LOOKUP(lex_ctx, 5) - -/* packed advance/token number macro used by multiple functions */ -#define DUK__ADVTOK(advbytes, tok) ((((advbytes) * sizeof(duk_lexer_codepoint)) << 8) + (tok)) - -/* - * Advance lookup window by N characters, filling in new characters as - * necessary. After returning caller is guaranteed a character window of - * at least DUK_LEXER_WINDOW_SIZE characters. - * - * The main function duk__advance_bytes() is called at least once per every - * token so it has a major lexer/compiler performance impact. There are two - * variants for the main duk__advance_bytes() algorithm: a sliding window - * approach which is slightly faster at the cost of larger code footprint, - * and a simple copying one. - * - * Decoding directly from the source string would be another lexing option. - * But the lookup window based approach has the advantage of hiding the - * source string and its encoding effectively which gives more flexibility - * going forward to e.g. support chunked streaming of source from flash. - * - * Decodes UTF-8/CESU-8 leniently with support for code points from U+0000 to - * U+10FFFF, causing an error if the input is unparseable. Leniency means: - * - * * Unicode code point validation is intentionally not performed, - * except to check that the codepoint does not exceed 0x10ffff. - * - * * In particular, surrogate pairs are allowed and not combined, which - * allows source files to represent all SourceCharacters with CESU-8. - * Broken surrogate pairs are allowed, as ECMAScript does not mandate - * their validation. - * - * * Allow non-shortest UTF-8 encodings. - * - * Leniency here causes few security concerns because all character data is - * decoded into Unicode codepoints before lexer processing, and is then - * re-encoded into CESU-8. The source can be parsed as strict UTF-8 with - * a compiler option. However, ECMAScript source characters include -all- - * 16-bit unsigned integer codepoints, so leniency seems to be appropriate. - * - * Note that codepoints above the BMP are not strictly SourceCharacters, - * but the lexer still accepts them as such. Before ending up in a string - * or an identifier name, codepoints above BMP are converted into surrogate - * pairs and then CESU-8 encoded, resulting in 16-bit Unicode data as - * expected by ECMAScript. - * - * An alternative approach to dealing with invalid or partial sequences - * would be to skip them and replace them with e.g. the Unicode replacement - * character U+FFFD. This has limited utility because a replacement character - * will most likely cause a parse error, unless it occurs inside a string. - * Further, ECMAScript source is typically pure ASCII. - * - * See: - * - * http://en.wikipedia.org/wiki/UTF-8 - * http://en.wikipedia.org/wiki/CESU-8 - * http://tools.ietf.org/html/rfc3629 - * http://en.wikipedia.org/wiki/UTF-8#Invalid_byte_sequences - * - * Future work: - * - * * Reject other invalid Unicode sequences (see Wikipedia entry for examples) - * in strict UTF-8 mode. - * - * * Size optimize. An attempt to use a 16-byte lookup table for the first - * byte resulted in a code increase though. - * - * * Is checking against maximum 0x10ffff really useful? 4-byte encoding - * imposes a certain limit anyway. - * - * * Support chunked streaming of source code. Can be implemented either - * by streaming chunks of bytes or chunks of codepoints. - */ - -#if defined(DUK_USE_LEXER_SLIDING_WINDOW) -DUK_LOCAL void duk__fill_lexer_buffer(duk_lexer_ctx *lex_ctx, duk_small_uint_t start_offset_bytes) { - duk_lexer_codepoint *cp, *cp_end; - duk_ucodepoint_t x; - duk_small_uint_t contlen; - const duk_uint8_t *p, *p_end; -#if defined(DUK_USE_STRICT_UTF8_SOURCE) - duk_ucodepoint_t mincp; -#endif - duk_int_t input_line; - - /* Use temporaries and update lex_ctx only when finished. */ - input_line = lex_ctx->input_line; - p = lex_ctx->input + lex_ctx->input_offset; - p_end = lex_ctx->input + lex_ctx->input_length; - - cp = (duk_lexer_codepoint *) (void *) ((duk_uint8_t *) lex_ctx->buffer + start_offset_bytes); - cp_end = lex_ctx->buffer + DUK_LEXER_BUFFER_SIZE; - - for (; cp != cp_end; cp++) { - cp->offset = (duk_size_t) (p - lex_ctx->input); - cp->line = input_line; - - /* XXX: potential issue with signed pointers, p_end < p. */ - if (DUK_UNLIKELY(p >= p_end)) { - /* If input_offset were assigned a negative value, it would - * result in a large positive value. Most likely it would be - * larger than input_length and be caught here. In any case - * no memory unsafe behavior would happen. - */ - cp->codepoint = -1; - continue; - } - - x = (duk_ucodepoint_t) (*p++); - - /* Fast path. */ - - if (DUK_LIKELY(x < 0x80UL)) { - DUK_ASSERT(x != 0x2028UL && x != 0x2029UL); /* not LS/PS */ - if (DUK_UNLIKELY(x <= 0x000dUL)) { - if ((x == 0x000aUL) || ((x == 0x000dUL) && (p >= p_end || *p != 0x000aUL))) { - /* lookup for 0x000a above assumes shortest encoding now */ - - /* E5 Section 7.3, treat the following as newlines: - * LF - * CR [not followed by LF] - * LS - * PS - * - * For CR LF, CR is ignored if it is followed by LF, and the LF will bump - * the line number. - */ - input_line++; - } - } - - cp->codepoint = (duk_codepoint_t) x; - continue; - } - - /* Slow path. */ - - if (x < 0xc0UL) { - /* 10xx xxxx -> invalid */ - goto error_encoding; - } else if (x < 0xe0UL) { - /* 110x xxxx 10xx xxxx */ - contlen = 1; -#if defined(DUK_USE_STRICT_UTF8_SOURCE) - mincp = 0x80UL; -#endif - x = x & 0x1fUL; - } else if (x < 0xf0UL) { - /* 1110 xxxx 10xx xxxx 10xx xxxx */ - contlen = 2; -#if defined(DUK_USE_STRICT_UTF8_SOURCE) - mincp = 0x800UL; -#endif - x = x & 0x0fUL; - } else if (x < 0xf8UL) { - /* 1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx */ - contlen = 3; -#if defined(DUK_USE_STRICT_UTF8_SOURCE) - mincp = 0x10000UL; -#endif - x = x & 0x07UL; - } else { - /* no point in supporting encodings of 5 or more bytes */ - goto error_encoding; - } - - DUK_ASSERT(p_end >= p); - if ((duk_size_t) contlen > (duk_size_t) (p_end - p)) { - goto error_clipped; - } - - while (contlen > 0) { - duk_small_uint_t y; - y = *p++; - if ((y & 0xc0U) != 0x80U) { - /* check that byte has the form 10xx xxxx */ - goto error_encoding; - } - x = x << 6; - x += y & 0x3fUL; - contlen--; - } - - /* check final character validity */ - - if (x > 0x10ffffUL) { - goto error_encoding; - } -#if defined(DUK_USE_STRICT_UTF8_SOURCE) - if (x < mincp || (x >= 0xd800UL && x <= 0xdfffUL) || x == 0xfffeUL) { - goto error_encoding; - } -#endif - - DUK_ASSERT(x != 0x000aUL && x != 0x000dUL); - if ((x == 0x2028UL) || (x == 0x2029UL)) { - input_line++; - } - - cp->codepoint = (duk_codepoint_t) x; - } - - lex_ctx->input_offset = (duk_size_t) (p - lex_ctx->input); - lex_ctx->input_line = input_line; - return; - -error_clipped: /* clipped codepoint */ -error_encoding: /* invalid codepoint encoding or codepoint */ - lex_ctx->input_offset = (duk_size_t) (p - lex_ctx->input); - lex_ctx->input_line = input_line; - - DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_SOURCE_DECODE_FAILED); - DUK_WO_NORETURN(return;); -} - -DUK_LOCAL void duk__advance_bytes(duk_lexer_ctx *lex_ctx, duk_small_uint_t count_bytes) { - duk_small_uint_t used_bytes, avail_bytes; - - DUK_ASSERT_DISABLE(count_bytes >= 0); /* unsigned */ - DUK_ASSERT(count_bytes <= (duk_small_uint_t) (DUK_LEXER_WINDOW_SIZE * sizeof(duk_lexer_codepoint))); - DUK_ASSERT(lex_ctx->window >= lex_ctx->buffer); - DUK_ASSERT(lex_ctx->window < lex_ctx->buffer + DUK_LEXER_BUFFER_SIZE); - DUK_ASSERT((duk_uint8_t *) lex_ctx->window + count_bytes <= - (duk_uint8_t *) lex_ctx->buffer + DUK_LEXER_BUFFER_SIZE * sizeof(duk_lexer_codepoint)); - - /* Zero 'count' is also allowed to make call sites easier. - * Arithmetic in bytes generates better code in GCC. - */ - - lex_ctx->window = (duk_lexer_codepoint *) (void *) ((duk_uint8_t *) lex_ctx->window + count_bytes); /* avoid multiply */ - used_bytes = (duk_small_uint_t) ((duk_uint8_t *) lex_ctx->window - (duk_uint8_t *) lex_ctx->buffer); - avail_bytes = DUK_LEXER_BUFFER_SIZE * sizeof(duk_lexer_codepoint) - used_bytes; - if (avail_bytes < (duk_small_uint_t) (DUK_LEXER_WINDOW_SIZE * sizeof(duk_lexer_codepoint))) { - /* Not enough data to provide a full window, so "scroll" window to - * start of buffer and fill up the rest. - */ - duk_memmove((void *) lex_ctx->buffer, (const void *) lex_ctx->window, (size_t) avail_bytes); - lex_ctx->window = lex_ctx->buffer; - duk__fill_lexer_buffer(lex_ctx, avail_bytes); - } -} - -DUK_LOCAL void duk__init_lexer_window(duk_lexer_ctx *lex_ctx) { - lex_ctx->window = lex_ctx->buffer; - duk__fill_lexer_buffer(lex_ctx, 0); -} -#else /* DUK_USE_LEXER_SLIDING_WINDOW */ -DUK_LOCAL duk_codepoint_t duk__read_char(duk_lexer_ctx *lex_ctx) { - duk_ucodepoint_t x; - duk_small_uint_t len; - duk_small_uint_t i; - const duk_uint8_t *p; -#if defined(DUK_USE_STRICT_UTF8_SOURCE) - duk_ucodepoint_t mincp; -#endif - duk_size_t input_offset; - - input_offset = lex_ctx->input_offset; - if (DUK_UNLIKELY(input_offset >= lex_ctx->input_length)) { - /* If input_offset were assigned a negative value, it would - * result in a large positive value. Most likely it would be - * larger than input_length and be caught here. In any case - * no memory unsafe behavior would happen. - */ - return -1; - } - - p = lex_ctx->input + input_offset; - x = (duk_ucodepoint_t) (*p); - - if (DUK_LIKELY(x < 0x80UL)) { - /* 0xxx xxxx -> fast path */ - - /* input offset tracking */ - lex_ctx->input_offset++; - - DUK_ASSERT(x != 0x2028UL && x != 0x2029UL); /* not LS/PS */ - if (DUK_UNLIKELY(x <= 0x000dUL)) { - if ((x == 0x000aUL) || ((x == 0x000dUL) && (lex_ctx->input_offset >= lex_ctx->input_length || - lex_ctx->input[lex_ctx->input_offset] != 0x000aUL))) { - /* lookup for 0x000a above assumes shortest encoding now */ - - /* E5 Section 7.3, treat the following as newlines: - * LF - * CR [not followed by LF] - * LS - * PS - * - * For CR LF, CR is ignored if it is followed by LF, and the LF will bump - * the line number. - */ - lex_ctx->input_line++; - } - } - - return (duk_codepoint_t) x; - } - - /* Slow path. */ - - if (x < 0xc0UL) { - /* 10xx xxxx -> invalid */ - goto error_encoding; - } else if (x < 0xe0UL) { - /* 110x xxxx 10xx xxxx */ - len = 2; -#if defined(DUK_USE_STRICT_UTF8_SOURCE) - mincp = 0x80UL; -#endif - x = x & 0x1fUL; - } else if (x < 0xf0UL) { - /* 1110 xxxx 10xx xxxx 10xx xxxx */ - len = 3; -#if defined(DUK_USE_STRICT_UTF8_SOURCE) - mincp = 0x800UL; -#endif - x = x & 0x0fUL; - } else if (x < 0xf8UL) { - /* 1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx */ - len = 4; -#if defined(DUK_USE_STRICT_UTF8_SOURCE) - mincp = 0x10000UL; -#endif - x = x & 0x07UL; - } else { - /* no point in supporting encodings of 5 or more bytes */ - goto error_encoding; - } - - DUK_ASSERT(lex_ctx->input_length >= lex_ctx->input_offset); - if ((duk_size_t) len > (duk_size_t) (lex_ctx->input_length - lex_ctx->input_offset)) { - goto error_clipped; - } - - p++; - for (i = 1; i < len; i++) { - duk_small_uint_t y; - y = *p++; - if ((y & 0xc0U) != 0x80U) { - /* check that byte has the form 10xx xxxx */ - goto error_encoding; - } - x = x << 6; - x += y & 0x3fUL; - } - - /* check final character validity */ - - if (x > 0x10ffffUL) { - goto error_encoding; - } -#if defined(DUK_USE_STRICT_UTF8_SOURCE) - if (x < mincp || (x >= 0xd800UL && x <= 0xdfffUL) || x == 0xfffeUL) { - goto error_encoding; - } -#endif - - /* input offset tracking */ - lex_ctx->input_offset += len; - - /* line tracking */ - DUK_ASSERT(x != 0x000aUL && x != 0x000dUL); - if ((x == 0x2028UL) || (x == 0x2029UL)) { - lex_ctx->input_line++; - } - - return (duk_codepoint_t) x; - -error_clipped: /* clipped codepoint */ -error_encoding: /* invalid codepoint encoding or codepoint */ - DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_SOURCE_DECODE_FAILED); - DUK_WO_NORETURN(return 0;); -} - -DUK_LOCAL void duk__advance_bytes(duk_lexer_ctx *lex_ctx, duk_small_uint_t count_bytes) { - duk_small_uint_t keep_bytes; - duk_lexer_codepoint *cp, *cp_end; - - DUK_ASSERT_DISABLE(count_bytes >= 0); /* unsigned */ - DUK_ASSERT(count_bytes <= (duk_small_uint_t) (DUK_LEXER_WINDOW_SIZE * sizeof(duk_lexer_codepoint))); - - /* Zero 'count' is also allowed to make call sites easier. */ - - keep_bytes = DUK_LEXER_WINDOW_SIZE * sizeof(duk_lexer_codepoint) - count_bytes; - duk_memmove((void *) lex_ctx->window, (const void *) ((duk_uint8_t *) lex_ctx->window + count_bytes), (size_t) keep_bytes); - - cp = (duk_lexer_codepoint *) ((duk_uint8_t *) lex_ctx->window + keep_bytes); - cp_end = lex_ctx->window + DUK_LEXER_WINDOW_SIZE; - for (; cp != cp_end; cp++) { - cp->offset = lex_ctx->input_offset; - cp->line = lex_ctx->input_line; - cp->codepoint = duk__read_char(lex_ctx); - } -} - -DUK_LOCAL void duk__init_lexer_window(duk_lexer_ctx *lex_ctx) { - /* Call with count == DUK_LEXER_WINDOW_SIZE to fill buffer initially. */ - duk__advance_bytes(lex_ctx, DUK_LEXER_WINDOW_SIZE * sizeof(duk_lexer_codepoint)); /* fill window */ -} -#endif /* DUK_USE_LEXER_SLIDING_WINDOW */ - -DUK_LOCAL void duk__advance_chars(duk_lexer_ctx *lex_ctx, duk_small_uint_t count_chars) { - duk__advance_bytes(lex_ctx, count_chars * sizeof(duk_lexer_codepoint)); -} - -/* - * (Re)initialize the temporary byte buffer. May be called extra times - * with little impact. - */ - -DUK_LOCAL void duk__initbuffer(duk_lexer_ctx *lex_ctx) { - /* Reuse buffer as is unless buffer has grown large. */ - if (DUK_HBUFFER_DYNAMIC_GET_SIZE(lex_ctx->buf) < DUK_LEXER_TEMP_BUF_LIMIT) { - /* Keep current size */ - } else { - duk_hbuffer_resize(lex_ctx->thr, lex_ctx->buf, DUK_LEXER_TEMP_BUF_LIMIT); - } - - DUK_BW_INIT_WITHBUF(lex_ctx->thr, &lex_ctx->bw, lex_ctx->buf); -} - -/* - * Append a Unicode codepoint to the temporary byte buffer. Performs - * CESU-8 surrogate pair encoding for codepoints above the BMP. - * Existing surrogate pairs are allowed and also encoded into CESU-8. - */ - -DUK_LOCAL void duk__appendbuffer(duk_lexer_ctx *lex_ctx, duk_codepoint_t x) { - /* - * Since character data is only generated by decoding the source or by - * the compiler itself, we rely on the input codepoints being correct - * and avoid a check here. - * - * Character data can also come here through decoding of Unicode - * escapes ("\udead\ubeef") so all 16-but unsigned values can be - * present, even when the source file itself is strict UTF-8. - */ - DUK_ASSERT(x >= 0 && x <= 0x10ffffL); - - DUK_BW_WRITE_ENSURE_CESU8(lex_ctx->thr, &lex_ctx->bw, (duk_ucodepoint_t) x); -} - -DUK_LOCAL void duk__appendbuffer_ascii(duk_lexer_ctx *lex_ctx, duk_codepoint_t x) { - /* ASCII characters can be emitted as a single byte without encoding - * which matters for some fast paths. - */ - DUK_ASSERT(x >= 0 && x <= 0x7f); - - DUK_BW_WRITE_ENSURE_U8(lex_ctx->thr, &lex_ctx->bw, (duk_uint8_t) x); -} - -/* - * Intern the temporary byte buffer into a valstack slot - * (in practice, slot1 or slot2). - */ - -DUK_LOCAL duk_hstring *duk__internbuffer(duk_lexer_ctx *lex_ctx, duk_idx_t valstack_idx) { - DUK_ASSERT(valstack_idx == lex_ctx->slot1_idx || valstack_idx == lex_ctx->slot2_idx); - - DUK_BW_PUSH_AS_STRING(lex_ctx->thr, &lex_ctx->bw); - duk_replace(lex_ctx->thr, valstack_idx); - return duk_known_hstring(lex_ctx->thr, valstack_idx); -} - -/* - * Init lexer context - */ - -DUK_INTERNAL void duk_lexer_initctx(duk_lexer_ctx *lex_ctx) { - DUK_ASSERT(lex_ctx != NULL); - - duk_memzero(lex_ctx, sizeof(*lex_ctx)); -#if defined(DUK_USE_EXPLICIT_NULL_INIT) -#if defined(DUK_USE_LEXER_SLIDING_WINDOW) - lex_ctx->window = NULL; -#endif - lex_ctx->thr = NULL; - lex_ctx->input = NULL; - lex_ctx->buf = NULL; -#endif -} - -/* - * Set lexer input position and reinitialize lookup window. - */ - -DUK_INTERNAL void duk_lexer_getpoint(duk_lexer_ctx *lex_ctx, duk_lexer_point *pt) { - pt->offset = lex_ctx->window[0].offset; - pt->line = lex_ctx->window[0].line; -} - -DUK_INTERNAL void duk_lexer_setpoint(duk_lexer_ctx *lex_ctx, duk_lexer_point *pt) { - DUK_ASSERT_DISABLE(pt->offset >= 0); /* unsigned */ - DUK_ASSERT(pt->line >= 1); - lex_ctx->input_offset = pt->offset; - lex_ctx->input_line = pt->line; - duk__init_lexer_window(lex_ctx); -} - -/* - * Lexing helpers - */ - -/* Numeric value of a hex digit (also covers octal and decimal digits) or - * -1 if not a valid hex digit. - */ -DUK_LOCAL duk_codepoint_t duk__hexval_validate(duk_codepoint_t x) { - duk_small_int_t t; - - /* Here 'x' is a Unicode codepoint */ - if (DUK_LIKELY(x >= 0 && x <= 0xff)) { - t = duk_hex_dectab[x]; - if (DUK_LIKELY(t >= 0)) { - return t; - } - } - - return -1; -} - -/* Just a wrapper for call sites where 'x' is known to be valid so - * we assert for it before decoding. - */ -DUK_LOCAL duk_codepoint_t duk__hexval(duk_codepoint_t x) { - duk_codepoint_t ret; - - DUK_ASSERT((x >= DUK_ASC_0 && x <= DUK_ASC_9) || (x >= DUK_ASC_LC_A && x <= DUK_ASC_LC_F) || - (x >= DUK_ASC_UC_A && x <= DUK_ASC_UC_F)); - ret = duk__hexval_validate(x); - DUK_ASSERT(ret >= 0 && ret <= 15); - return ret; -} - -/* having this as a separate function provided a size benefit */ -DUK_LOCAL duk_bool_t duk__is_hex_digit(duk_codepoint_t x) { - if (DUK_LIKELY(x >= 0 && x <= 0xff)) { - return (duk_hex_dectab[x] >= 0); - } - return 0; -} - -/* Parse a Unicode escape of the form \xHH, \uHHHH, or \u{H+}. Shared by - * source and RegExp parsing. - */ -DUK_LOCAL duk_codepoint_t duk__lexer_parse_escape(duk_lexer_ctx *lex_ctx, duk_bool_t allow_es6) { - duk_small_int_t digits; /* Initial value 2 or 4 for fixed length escapes, 0 for ES2015 \u{H+}. */ - duk_codepoint_t escval; - duk_codepoint_t x; - duk_small_uint_t adv; - - DUK_ASSERT(DUK__L0() == DUK_ASC_BACKSLASH); /* caller responsibilities */ - DUK_ASSERT(DUK__L1() == DUK_ASC_LC_X || DUK__L1() == DUK_ASC_LC_U); - DUK_UNREF(allow_es6); - - adv = 2; - digits = 2; - if (DUK__L1() == DUK_ASC_LC_U) { - digits = 4; -#if defined(DUK_USE_ES6_UNICODE_ESCAPE) - if (DUK__L2() == DUK_ASC_LCURLY && allow_es6) { - digits = 0; - adv = 3; - } -#endif - } - DUK__ADVANCECHARS(lex_ctx, adv); - - escval = 0; - for (;;) { - /* One of the escape forms: \xHH, \uHHHH, \u{H+}. - * The 'digits' variable tracks parsing state and is - * initialized to: - * - * \xHH 2 - * \uHH 4 - * \u{H+} 0 first time, updated to -1 to indicate - * at least one digit has been parsed - * - * Octal parsing is handled separately because it can be - * done with fixed lookahead and also has validation - * rules which depend on the escape length (which is - * variable). - * - * We don't need a specific check for x < 0 (end of - * input) or duk_unicode_is_line_terminator(x) - * because the 'dig' decode will fail and lead to a - * SyntaxError. - */ - duk_codepoint_t dig; - - x = DUK__L0(); - DUK__ADVANCECHARS(lex_ctx, 1); - - dig = duk__hexval_validate(x); - if (digits > 0) { - digits--; - if (dig < 0) { - goto fail_escape; - } - DUK_ASSERT(dig >= 0x00 && dig <= 0x0f); - escval = (escval << 4) + dig; - if (digits == 0) { - DUK_ASSERT(escval >= 0 && escval <= 0xffffL); - break; - } - } else { -#if defined(DUK_USE_ES6_UNICODE_ESCAPE) - DUK_ASSERT(digits == 0 /* first time */ || digits == -1 /* others */); - if (dig >= 0) { - DUK_ASSERT(dig >= 0x00 && dig <= 0x0f); - escval = (escval << 4) + dig; - if (escval > 0x10ffffL) { - goto fail_escape; - } - } else if (x == DUK_ASC_RCURLY) { - if (digits == 0) { - /* Empty escape, \u{}. */ - goto fail_escape; - } - DUK_ASSERT(escval >= 0 && escval <= 0x10ffffL); - break; - } else { - goto fail_escape; - } - digits = -1; /* Indicate we have at least one digit. */ -#else /* DUK_USE_ES6_UNICODE_ESCAPE */ - DUK_ASSERT(0); /* Never happens if \u{H+} support disabled. */ -#endif /* DUK_USE_ES6_UNICODE_ESCAPE */ - } - } - - return escval; - -fail_escape: - DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_ESCAPE); - DUK_WO_NORETURN(return 0;); -} - -/* Parse legacy octal escape of the form \N{1,3}, e.g. \0, \5, \0377. Maximum - * allowed value is \0377 (U+00FF), longest match is used. Used for both string - * RegExp octal escape parsing. Window[0] must be the slash '\' and the first - * digit must already be validated to be in [0-9] by the caller. - */ -DUK_LOCAL duk_codepoint_t duk__lexer_parse_legacy_octal(duk_lexer_ctx *lex_ctx, - duk_small_uint_t *out_adv, - duk_bool_t reject_annex_b) { - duk_codepoint_t cp; - duk_small_uint_t lookup_idx; - duk_small_uint_t adv; - duk_codepoint_t tmp; - - DUK_ASSERT(out_adv != NULL); - DUK_ASSERT(DUK__LOOKUP(lex_ctx, 0) == DUK_ASC_BACKSLASH); - DUK_ASSERT(DUK__LOOKUP(lex_ctx, 1) >= DUK_ASC_0 && DUK__LOOKUP(lex_ctx, 1) <= DUK_ASC_9); - - cp = 0; - tmp = 0; - for (lookup_idx = 1; lookup_idx <= 3; lookup_idx++) { - DUK_DDD(DUK_DDDPRINT("lookup_idx=%ld, cp=%ld", (long) lookup_idx, (long) cp)); - tmp = DUK__LOOKUP(lex_ctx, lookup_idx); - if (tmp < DUK_ASC_0 || tmp > DUK_ASC_7) { - /* No more valid digits. */ - break; - } - tmp = (cp << 3) + (tmp - DUK_ASC_0); - if (tmp > 0xff) { - /* Three digit octal escapes above \377 (= 0xff) - * are not allowed. - */ - break; - } - cp = tmp; - } - DUK_DDD(DUK_DDDPRINT("final lookup_idx=%ld, cp=%ld", (long) lookup_idx, (long) cp)); - - adv = lookup_idx; - if (lookup_idx == 1) { - DUK_DDD(DUK_DDDPRINT("\\8 or \\9 -> treat as literal, accept in strict mode too")); - DUK_ASSERT(tmp == DUK_ASC_8 || tmp == DUK_ASC_9); - cp = tmp; - adv++; /* correction to above, eat offending character */ - } else if (lookup_idx == 2 && cp == 0) { - /* Note: 'foo\0bar' is OK in strict mode, but 'foo\00bar' is not. - * It won't be interpreted as 'foo\u{0}0bar' but as a SyntaxError. - */ - DUK_DDD(DUK_DDDPRINT("\\0 -> accept in strict mode too")); - } else { - /* This clause also handles non-shortest zero, e.g. \00. */ - if (reject_annex_b) { - DUK_DDD(DUK_DDDPRINT("non-zero octal literal %ld -> reject in strict-mode", (long) cp)); - cp = -1; - } else { - DUK_DDD(DUK_DDDPRINT("non-zero octal literal %ld -> accepted", (long) cp)); - DUK_ASSERT(cp >= 0 && cp <= 0xff); - } - } - - *out_adv = adv; - - DUK_ASSERT((cp >= 0 && cp <= 0xff) || (cp == -1 && reject_annex_b)); - return cp; -} - -/* XXX: move strict mode to lex_ctx? */ -DUK_LOCAL void duk__lexer_parse_string_literal(duk_lexer_ctx *lex_ctx, - duk_token *out_token, - duk_small_int_t quote, - duk_bool_t strict_mode) { - duk_small_uint_t adv; - - for (adv = 1 /* initial quote */;;) { - duk_codepoint_t x; - - DUK__ADVANCECHARS(lex_ctx, adv); /* eat opening quote on first loop */ - x = DUK__L0(); - - adv = 1; - if (x == quote) { - DUK__ADVANCECHARS(lex_ctx, 1); /* eat closing quote */ - break; - } else if (x == '\\') { - /* DUK__L0 -> '\' char - * DUK__L1 ... DUK__L5 -> more lookup - */ - duk_small_int_t emitcp = -1; - - x = DUK__L1(); - - /* How much to advance before next loop. */ - adv = 2; /* note: long live range */ - - switch (x) { - case '\'': - emitcp = 0x0027; - break; - case '"': - emitcp = 0x0022; - break; - case '\\': - emitcp = 0x005c; - break; - case 'b': - emitcp = 0x0008; - break; - case 'f': - emitcp = 0x000c; - break; - case 'n': - emitcp = 0x000a; - break; - case 'r': - emitcp = 0x000d; - break; - case 't': - emitcp = 0x0009; - break; - case 'v': - emitcp = 0x000b; - break; - case 'x': - case 'u': { - duk_codepoint_t esc_cp; - esc_cp = duk__lexer_parse_escape(lex_ctx, 1 /*allow_es6*/); - DUK__APPENDBUFFER(lex_ctx, esc_cp); - adv = 0; - break; - } - default: { - if (duk_unicode_is_line_terminator(x)) { - /* line continuation */ - if (x == 0x000d && DUK__L2() == 0x000a) { - /* CR LF again a special case */ - adv = 3; /* line terminator, CR, LF */ - } - } else if (DUK__ISDIGIT(x)) { - /* - * Octal escape or zero escape: - * \0 (lookahead not OctalDigit) - * \1 ... \7 (lookahead not OctalDigit) - * \ZeroToThree OctalDigit (lookahead not OctalDigit) - * \FourToSeven OctalDigit (no lookahead restrictions) - * \ZeroToThree OctalDigit OctalDigit (no lookahead restrictions) - * - * Zero escape is part of the standard syntax. Octal escapes are - * defined in E5 Section B.1.2, and are only allowed in non-strict mode. - * Any other productions starting with a decimal digit are invalid - * but are in practice treated like identity escapes. - * - * Parse octal (up to 3 digits) from the lookup window. - */ - - emitcp = duk__lexer_parse_legacy_octal(lex_ctx, &adv, strict_mode /*reject_annex_b*/); - if (emitcp < 0) { - goto fail_escape; - } - } else if (x < 0) { - goto fail_unterminated; - } else { - /* escaped NonEscapeCharacter */ - DUK__APPENDBUFFER(lex_ctx, x); - } - } /* end default clause */ - } /* end switch */ - - /* Shared handling for single codepoint escapes. */ - if (emitcp >= 0) { - DUK__APPENDBUFFER(lex_ctx, emitcp); - } - - /* Track number of escapes; count not really needed but directive - * prologues need to detect whether there were any escapes or line - * continuations or not. - */ - out_token->num_escapes++; - } else if (x >= 0x20 && x <= 0x7f) { - /* Fast path for ASCII case, avoids line terminator - * check and CESU-8 encoding. - */ - DUK_ASSERT(x >= 0); - DUK_ASSERT(!duk_unicode_is_line_terminator(x)); - DUK_ASSERT(x != quote); - DUK_ASSERT(x != DUK_ASC_BACKSLASH); - DUK__APPENDBUFFER_ASCII(lex_ctx, x); - } else if (x < 0 || duk_unicode_is_line_terminator(x)) { - goto fail_unterminated; - } else { - /* Character which is part of the string but wasn't handled - * by the fast path. - */ - DUK__APPENDBUFFER(lex_ctx, x); - } - } /* string parse loop */ - - return; - -fail_escape: - DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_ESCAPE); - DUK_WO_NORETURN(return;); - -fail_unterminated: - DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_UNTERMINATED_STRING); - DUK_WO_NORETURN(return;); -} - -/* Skip to end-of-line (or end-of-file), used for single line comments. */ -DUK_LOCAL void duk__lexer_skip_to_endofline(duk_lexer_ctx *lex_ctx) { - for (;;) { - duk_codepoint_t x; - - x = DUK__L0(); - if (x < 0 || duk_unicode_is_line_terminator(x)) { - break; - } - DUK__ADVANCECHARS(lex_ctx, 1); - } -} - -/* - * Parse ECMAScript source InputElementDiv or InputElementRegExp - * (E5 Section 7), skipping whitespace, comments, and line terminators. - * - * Possible results are: - * (1) a token - * (2) a line terminator (skipped) - * (3) a comment (skipped) - * (4) EOF - * - * White space is automatically skipped from the current position (but - * not after the input element). If input has already ended, returns - * DUK_TOK_EOF indefinitely. If a parse error occurs, uses an DUK_ERROR() - * macro call (and hence a longjmp through current heap longjmp context). - * Comments and line terminator tokens are automatically skipped. - * - * The input element being matched is determined by regexp_mode; if set, - * parses a InputElementRegExp, otherwise a InputElementDiv. The - * difference between these are handling of productions starting with a - * forward slash. - * - * If strict_mode is set, recognizes additional future reserved words - * specific to strict mode, and refuses to parse octal literals. - * - * The matching strategy below is to (currently) use a six character - * lookup window to quickly determine which production is the -longest- - * matching one, and then parse that. The top-level if-else clauses - * match the first character, and the code blocks for each clause - * handle -all- alternatives for that first character. ECMAScript - * specification uses the "longest match wins" semantics, so the order - * of the if-clauses matters. - * - * Misc notes: - * - * * ECMAScript numeric literals do not accept a sign character. - * Consequently e.g. "-1.0" is parsed as two tokens: a negative - * sign and a positive numeric literal. The compiler performs - * the negation during compilation, so this has no adverse impact. - * - * * There is no token for "undefined": it is just a value available - * from the global object (or simply established by doing a reference - * to an undefined value). - * - * * Some contexts want Identifier tokens, which are IdentifierNames - * excluding reserved words, while some contexts want IdentifierNames - * directly. In the latter case e.g. "while" is interpreted as an - * identifier name, not a DUK_TOK_WHILE token. The solution here is - * to provide both token types: DUK_TOK_WHILE goes to 't' while - * DUK_TOK_IDENTIFIER goes to 't_nores', and 'slot1' always contains - * the identifier / keyword name. - * - * * Directive prologue needs to identify string literals such as - * "use strict" and 'use strict', which are sensitive to line - * continuations and escape sequences. For instance, "use\u0020strict" - * is a valid directive but is distinct from "use strict". The solution - * here is to decode escapes while tokenizing, but to keep track of the - * number of escapes. Directive detection can then check that the - * number of escapes is zero. - * - * * Multi-line comments with one or more internal LineTerminator are - * treated like a line terminator to comply with automatic semicolon - * insertion. - */ - -DUK_INTERNAL -void duk_lexer_parse_js_input_element(duk_lexer_ctx *lex_ctx, - duk_token *out_token, - duk_bool_t strict_mode, - duk_bool_t regexp_mode) { - duk_codepoint_t x; /* temporary, must be signed and 32-bit to hold Unicode code points */ - duk_small_uint_t advtok = 0; /* (advance << 8) + token_type, updated at function end, - * init is unnecessary but suppresses "may be used uninitialized" warnings. - */ - duk_bool_t got_lineterm = 0; /* got lineterm preceding non-whitespace, non-lineterm token */ - - if (++lex_ctx->token_count >= lex_ctx->token_limit) { - goto fail_token_limit; - } - - out_token->t = DUK_TOK_EOF; - out_token->t_nores = DUK_TOK_INVALID; /* marker: copy t if not changed */ -#if 0 /* not necessary to init, disabled for faster parsing */ - out_token->num = DUK_DOUBLE_NAN; - out_token->str1 = NULL; - out_token->str2 = NULL; -#endif - out_token->num_escapes = 0; - /* out_token->lineterm set by caller */ - - /* This would be nice, but parsing is faster without resetting the - * value slots. The only side effect is that references to temporary - * string values may linger until lexing is finished; they're then - * freed normally. - */ -#if 0 - duk_to_undefined(lex_ctx->thr, lex_ctx->slot1_idx); - duk_to_undefined(lex_ctx->thr, lex_ctx->slot2_idx); -#endif - - /* 'advtok' indicates how much to advance and which token id to assign - * at the end. This shared functionality minimizes code size. All - * code paths are required to set 'advtok' to some value, so no default - * init value is used. Code paths calling DUK_ERROR() never return so - * they don't need to set advtok. - */ - - /* - * Matching order: - * - * Punctuator first chars, also covers comments, regexps - * LineTerminator - * Identifier or reserved word, also covers null/true/false literals - * NumericLiteral - * StringLiteral - * EOF - * - * The order does not matter as long as the longest match is - * always correctly identified. There are order dependencies - * in the clauses, so it's not trivial to convert to a switch. - */ - -restart_lineupdate: - out_token->start_line = lex_ctx->window[0].line; - -restart: - out_token->start_offset = lex_ctx->window[0].offset; - - x = DUK__L0(); - - switch (x) { - case DUK_ASC_SPACE: - case DUK_ASC_HT: /* fast paths for space and tab */ - DUK__ADVANCECHARS(lex_ctx, 1); - goto restart; - case DUK_ASC_LF: /* LF line terminator; CR LF and Unicode lineterms are handled in slow path */ - DUK__ADVANCECHARS(lex_ctx, 1); - got_lineterm = 1; - goto restart_lineupdate; -#if defined(DUK_USE_SHEBANG_COMMENTS) - case DUK_ASC_HASH: /* '#' */ - if (DUK__L1() == DUK_ASC_EXCLAMATION && lex_ctx->window[0].offset == 0 && (lex_ctx->flags & DUK_COMPILE_SHEBANG)) { - /* "Shebang" comment ('#! ...') on first line. */ - /* DUK__ADVANCECHARS(lex_ctx, 2) would be correct here, but not necessary */ - duk__lexer_skip_to_endofline(lex_ctx); - goto restart; /* line terminator will be handled on next round */ - } - goto fail_token; -#endif /* DUK_USE_SHEBANG_COMMENTS */ - case DUK_ASC_SLASH: /* '/' */ - if (DUK__L1() == DUK_ASC_SLASH) { - /* - * E5 Section 7.4, allow SourceCharacter (which is any 16-bit - * code point). - */ - - /* DUK__ADVANCECHARS(lex_ctx, 2) would be correct here, but not necessary */ - duk__lexer_skip_to_endofline(lex_ctx); - goto restart; /* line terminator will be handled on next round */ - } else if (DUK__L1() == DUK_ASC_STAR) { - /* - * E5 Section 7.4. If the multi-line comment contains a newline, - * it is treated like a single line terminator for automatic - * semicolon insertion. - */ - - duk_bool_t last_asterisk = 0; - DUK__ADVANCECHARS(lex_ctx, 2); - for (;;) { - x = DUK__L0(); - if (x < 0) { - goto fail_unterm_comment; - } - DUK__ADVANCECHARS(lex_ctx, 1); - if (last_asterisk && x == DUK_ASC_SLASH) { - break; - } - if (duk_unicode_is_line_terminator(x)) { - got_lineterm = 1; - } - last_asterisk = (x == DUK_ASC_STAR); - } - goto restart_lineupdate; - } else if (regexp_mode) { -#if defined(DUK_USE_REGEXP_SUPPORT) - /* - * "/" followed by something in regexp mode. See E5 Section 7.8.5. - * - * RegExp parsing is a bit complex. First, the regexp body is delimited - * by forward slashes, but the body may also contain forward slashes as - * part of an escape sequence or inside a character class (delimited by - * square brackets). A mini state machine is used to implement these. - * - * Further, an early (parse time) error must be thrown if the regexp - * would cause a run-time error when used in the expression new RegExp(...). - * Parsing here simply extracts the (candidate) regexp, and also accepts - * invalid regular expressions (which are delimited properly). The caller - * (compiler) must perform final validation and regexp compilation. - * - * RegExp first char may not be '/' (single line comment) or '*' (multi- - * line comment). These have already been checked above, so there is no - * need below for special handling of the first regexp character as in - * the E5 productions. - * - * About unicode escapes within regexp literals: - * - * E5 Section 7.8.5 grammar does NOT accept \uHHHH escapes. - * However, Section 6 states that regexps accept the escapes, - * see paragraph starting with "In string literals...". - * The regexp grammar, which sees the decoded regexp literal - * (after lexical parsing) DOES have a \uHHHH unicode escape. - * So, for instance: - * - * /\u1234/ - * - * should first be parsed by the lexical grammar as: - * - * '\' 'u' RegularExpressionBackslashSequence - * '1' RegularExpressionNonTerminator - * '2' RegularExpressionNonTerminator - * '3' RegularExpressionNonTerminator - * '4' RegularExpressionNonTerminator - * - * and the escape itself is then parsed by the regexp engine. - * This is the current implementation. - * - * Minor spec inconsistency: - * - * E5 Section 7.8.5 RegularExpressionBackslashSequence is: - * - * \ RegularExpressionNonTerminator - * - * while Section A.1 RegularExpressionBackslashSequence is: - * - * \ NonTerminator - * - * The latter is not normative and a typo. - * - */ - - /* first, parse regexp body roughly */ - - duk_small_int_t state = 0; /* 0=base, 1=esc, 2=class, 3=class+esc */ - - DUK__INITBUFFER(lex_ctx); - for (;;) { - DUK__ADVANCECHARS(lex_ctx, 1); /* skip opening slash on first loop */ - x = DUK__L0(); - if (x < 0 || duk_unicode_is_line_terminator(x)) { - goto fail_unterm_regexp; - } - x = DUK__L0(); /* re-read to avoid spill / fetch */ - if (state == 0) { - if (x == DUK_ASC_SLASH) { - DUK__ADVANCECHARS(lex_ctx, 1); /* eat closing slash */ - break; - } else if (x == DUK_ASC_BACKSLASH) { - state = 1; - } else if (x == DUK_ASC_LBRACKET) { - state = 2; - } - } else if (state == 1) { - state = 0; - } else if (state == 2) { - if (x == DUK_ASC_RBRACKET) { - state = 0; - } else if (x == DUK_ASC_BACKSLASH) { - state = 3; - } - } else { /* state == 3 */ - state = 2; - } - DUK__APPENDBUFFER(lex_ctx, x); - } - out_token->str1 = duk__internbuffer(lex_ctx, lex_ctx->slot1_idx); - - /* second, parse flags */ - - DUK__INITBUFFER(lex_ctx); - for (;;) { - x = DUK__L0(); - if (!duk_unicode_is_identifier_part(x)) { - break; - } - x = DUK__L0(); /* re-read to avoid spill / fetch */ - DUK__APPENDBUFFER(lex_ctx, x); - DUK__ADVANCECHARS(lex_ctx, 1); - } - out_token->str2 = duk__internbuffer(lex_ctx, lex_ctx->slot2_idx); - - DUK__INITBUFFER(lex_ctx); /* free some memory */ - - /* validation of the regexp is caller's responsibility */ - - advtok = DUK__ADVTOK(0, DUK_TOK_REGEXP); -#else /* DUK_USE_REGEXP_SUPPORT */ - goto fail_regexp_support; -#endif /* DUK_USE_REGEXP_SUPPORT */ - } else if (DUK__L1() == DUK_ASC_EQUALS) { - /* "/=" and not in regexp mode */ - advtok = DUK__ADVTOK(2, DUK_TOK_DIV_EQ); - } else { - /* "/" and not in regexp mode */ - advtok = DUK__ADVTOK(1, DUK_TOK_DIV); - } - break; - case DUK_ASC_LCURLY: /* '{' */ - advtok = DUK__ADVTOK(1, DUK_TOK_LCURLY); - break; - case DUK_ASC_RCURLY: /* '}' */ - advtok = DUK__ADVTOK(1, DUK_TOK_RCURLY); - break; - case DUK_ASC_LPAREN: /* '(' */ - advtok = DUK__ADVTOK(1, DUK_TOK_LPAREN); - break; - case DUK_ASC_RPAREN: /* ')' */ - advtok = DUK__ADVTOK(1, DUK_TOK_RPAREN); - break; - case DUK_ASC_LBRACKET: /* '[' */ - advtok = DUK__ADVTOK(1, DUK_TOK_LBRACKET); - break; - case DUK_ASC_RBRACKET: /* ']' */ - advtok = DUK__ADVTOK(1, DUK_TOK_RBRACKET); - break; - case DUK_ASC_PERIOD: /* '.' */ - if (DUK__ISDIGIT(DUK__L1())) { - /* Period followed by a digit can only start DecimalLiteral - * (handled in slow path). We could jump straight into the - * DecimalLiteral handling but should avoid goto to inside - * a block. - */ - goto slow_path; - } - advtok = DUK__ADVTOK(1, DUK_TOK_PERIOD); - break; - case DUK_ASC_SEMICOLON: /* ';' */ - advtok = DUK__ADVTOK(1, DUK_TOK_SEMICOLON); - break; - case DUK_ASC_COMMA: /* ',' */ - advtok = DUK__ADVTOK(1, DUK_TOK_COMMA); - break; - case DUK_ASC_LANGLE: /* '<' */ -#if defined(DUK_USE_HTML_COMMENTS) - if (DUK__L1() == DUK_ASC_EXCLAMATION && DUK__L2() == DUK_ASC_MINUS && DUK__L3() == DUK_ASC_MINUS) { - /* - * ES2015: B.1.3, handle "" SingleLineHTMLCloseComment - * Only allowed: - * - on new line - * - preceded only by whitespace - * - preceded by end of multiline comment and optional whitespace - * - * Since whitespace generates no tokens, and multiline comments - * are treated as a line ending, consulting `got_lineterm` is - * sufficient to test for these three options. - */ - - /* DUK__ADVANCECHARS(lex_ctx, 3) would be correct here, but not necessary */ - duk__lexer_skip_to_endofline(lex_ctx); - goto restart; /* line terminator will be handled on next round */ - } else -#endif /* DUK_USE_HTML_COMMENTS */ - if (DUK__L1() == DUK_ASC_MINUS) { - advtok = DUK__ADVTOK(2, DUK_TOK_DECREMENT); - } else if (DUK__L1() == DUK_ASC_EQUALS) { - advtok = DUK__ADVTOK(2, DUK_TOK_SUB_EQ); - } else { - advtok = DUK__ADVTOK(1, DUK_TOK_SUB); - } - break; - case DUK_ASC_STAR: /* '*' */ -#if defined(DUK_USE_ES7_EXP_OPERATOR) - if (DUK__L1() == DUK_ASC_STAR && DUK__L2() == DUK_ASC_EQUALS) { - advtok = DUK__ADVTOK(3, DUK_TOK_EXP_EQ); - } else if (DUK__L1() == DUK_ASC_STAR) { - advtok = DUK__ADVTOK(2, DUK_TOK_EXP); - } else -#endif - if (DUK__L1() == DUK_ASC_EQUALS) { - advtok = DUK__ADVTOK(2, DUK_TOK_MUL_EQ); - } else { - advtok = DUK__ADVTOK(1, DUK_TOK_MUL); - } - break; - case DUK_ASC_PERCENT: /* '%' */ - if (DUK__L1() == DUK_ASC_EQUALS) { - advtok = DUK__ADVTOK(2, DUK_TOK_MOD_EQ); - } else { - advtok = DUK__ADVTOK(1, DUK_TOK_MOD); - } - break; - case DUK_ASC_AMP: /* '&' */ - if (DUK__L1() == DUK_ASC_AMP) { - advtok = DUK__ADVTOK(2, DUK_TOK_LAND); - } else if (DUK__L1() == DUK_ASC_EQUALS) { - advtok = DUK__ADVTOK(2, DUK_TOK_BAND_EQ); - } else { - advtok = DUK__ADVTOK(1, DUK_TOK_BAND); - } - break; - case DUK_ASC_PIPE: /* '|' */ - if (DUK__L1() == DUK_ASC_PIPE) { - advtok = DUK__ADVTOK(2, DUK_TOK_LOR); - } else if (DUK__L1() == DUK_ASC_EQUALS) { - advtok = DUK__ADVTOK(2, DUK_TOK_BOR_EQ); - } else { - advtok = DUK__ADVTOK(1, DUK_TOK_BOR); - } - break; - case DUK_ASC_CARET: /* '^' */ - if (DUK__L1() == DUK_ASC_EQUALS) { - advtok = DUK__ADVTOK(2, DUK_TOK_BXOR_EQ); - } else { - advtok = DUK__ADVTOK(1, DUK_TOK_BXOR); - } - break; - case DUK_ASC_TILDE: /* '~' */ - advtok = DUK__ADVTOK(1, DUK_TOK_BNOT); - break; - case DUK_ASC_QUESTION: /* '?' */ - advtok = DUK__ADVTOK(1, DUK_TOK_QUESTION); - break; - case DUK_ASC_COLON: /* ':' */ - advtok = DUK__ADVTOK(1, DUK_TOK_COLON); - break; - case DUK_ASC_DOUBLEQUOTE: /* '"' */ - case DUK_ASC_SINGLEQUOTE: { /* '\'' */ - DUK__INITBUFFER(lex_ctx); - duk__lexer_parse_string_literal(lex_ctx, out_token, x /*quote*/, strict_mode); - duk__internbuffer(lex_ctx, lex_ctx->slot1_idx); - out_token->str1 = duk_known_hstring(lex_ctx->thr, lex_ctx->slot1_idx); - - DUK__INITBUFFER(lex_ctx); /* free some memory */ - - advtok = DUK__ADVTOK(0, DUK_TOK_STRING); - break; - } - default: - goto slow_path; - } /* switch */ - - goto skip_slow_path; - -slow_path: - if (duk_unicode_is_line_terminator(x)) { - if (x == 0x000d && DUK__L1() == 0x000a) { - /* - * E5 Section 7.3: CR LF is detected as a single line terminator for - * line numbers. Here we also detect it as a single line terminator - * token. - */ - DUK__ADVANCECHARS(lex_ctx, 2); - } else { - DUK__ADVANCECHARS(lex_ctx, 1); - } - got_lineterm = 1; - goto restart_lineupdate; - } else if (duk_unicode_is_identifier_start(x) || x == DUK_ASC_BACKSLASH) { - /* - * Parse an identifier and then check whether it is: - * - reserved word (keyword or other reserved word) - * - "null" (NullLiteral) - * - "true" (BooleanLiteral) - * - "false" (BooleanLiteral) - * - anything else => identifier - * - * This does not follow the E5 productions cleanly, but is - * useful and compact. - * - * Note that identifiers may contain Unicode escapes, - * see E5 Sections 6 and 7.6. They must be decoded first, - * and the result checked against allowed characters. - * The above if-clause accepts an identifier start and an - * '\' character -- no other token can begin with a '\'. - * - * Note that "get" and "set" are not reserved words in E5 - * specification so they are recognized as plain identifiers - * (the tokens DUK_TOK_GET and DUK_TOK_SET are actually not - * used now). The compiler needs to work around this. - * - * Strictly speaking, following ECMAScript longest match - * specification, an invalid escape for the first character - * should cause a syntax error. However, an invalid escape - * for IdentifierParts should just terminate the identifier - * early (longest match), and let the next tokenization - * fail. For instance Rhino croaks with 'foo\z' when - * parsing the identifier. This has little practical impact. - */ - - duk_small_uint_t i, i_end; - duk_bool_t first = 1; - duk_hstring *str; - - DUK__INITBUFFER(lex_ctx); - for (;;) { - /* re-lookup first char on first loop */ - if (DUK__L0() == DUK_ASC_BACKSLASH) { - duk_codepoint_t esc_cp; - if (DUK__L1() != DUK_ASC_LC_U) { - goto fail_escape; - } - esc_cp = duk__lexer_parse_escape(lex_ctx, 1 /*allow_es6*/); - DUK__APPENDBUFFER(lex_ctx, esc_cp); - - /* IdentifierStart is stricter than IdentifierPart, so if the first - * character is escaped, must have a stricter check here. - */ - if (!(first ? duk_unicode_is_identifier_start(esc_cp) : duk_unicode_is_identifier_part(esc_cp))) { - goto fail_escape; - } - - /* Track number of escapes: necessary for proper keyword - * detection. - */ - out_token->num_escapes++; - } else { - /* Note: first character is checked against this. But because - * IdentifierPart includes all IdentifierStart characters, and - * the first character (if unescaped) has already been checked - * in the if condition, this is OK. - */ - if (!duk_unicode_is_identifier_part(DUK__L0())) { - break; - } - DUK__APPENDBUFFER(lex_ctx, DUK__L0()); - DUK__ADVANCECHARS(lex_ctx, 1); - } - first = 0; - } - - out_token->str1 = duk__internbuffer(lex_ctx, lex_ctx->slot1_idx); - str = out_token->str1; - out_token->t_nores = DUK_TOK_IDENTIFIER; - - DUK__INITBUFFER(lex_ctx); /* free some memory */ - - /* - * Interned identifier is compared against reserved words, which are - * currently interned into the heap context. See genbuiltins.py. - * - * Note that an escape in the identifier disables recognition of - * keywords; e.g. "\u0069f = 1;" is a valid statement (assigns to - * identifier named "if"). This is not necessarily compliant, - * see test-dec-escaped-char-in-keyword.js. - * - * Note: "get" and "set" are awkward. They are not officially - * ReservedWords (and indeed e.g. "var set = 1;" is valid), and - * must come out as DUK_TOK_IDENTIFIER. The compiler needs to - * work around this a bit. - */ - - /* XXX: optimize by adding the token numbers directly into the - * always interned duk_hstring objects (there should be enough - * flag bits free for that)? - */ - - i_end = (strict_mode ? DUK_STRIDX_END_RESERVED : DUK_STRIDX_START_STRICT_RESERVED); - - advtok = DUK__ADVTOK(0, DUK_TOK_IDENTIFIER); - if (out_token->num_escapes == 0) { - for (i = DUK_STRIDX_START_RESERVED; i < i_end; i++) { - DUK_ASSERT_DISABLE(i >= 0); /* unsigned */ - DUK_ASSERT(i < DUK_HEAP_NUM_STRINGS); - if (DUK_HTHREAD_GET_STRING(lex_ctx->thr, i) == str) { - advtok = DUK__ADVTOK(0, DUK_STRIDX_TO_TOK(i)); - break; - } - } - } - } else if (DUK__ISDIGIT(x) || (x == DUK_ASC_PERIOD)) { - /* Note: decimal number may start with a period, but must be followed by a digit */ - - /* - * Pre-parsing for decimal, hex, octal (both legacy and ES2015), - * and binary literals, followed by an actual parser step - * provided by numconv. - * - * Note: the leading sign character ('+' or '-') is -not- part of - * the production in E5 grammar, and that the a DecimalLiteral - * starting with a '0' must be followed by a non-digit. - * - * XXX: the two step parsing process is quite awkward, it would - * be more straightforward to allow numconv to parse the longest - * valid prefix (it already does that, it only needs to indicate - * where the input ended). However, the lexer decodes characters - * using a limited lookup window, so this is not a trivial change. - */ - - /* XXX: because of the final check below (that the literal is not - * followed by a digit), this could maybe be simplified, if we bail - * out early from a leading zero (and if there are no periods etc). - * Maybe too complex. - */ - - duk_double_t val; - duk_bool_t legacy_oct = 0; - duk_small_int_t state; /* 0=before period/exp, - * 1=after period, before exp - * 2=after exp, allow '+' or '-' - * 3=after exp and exp sign - */ - duk_small_uint_t s2n_flags; - duk_codepoint_t y, z; - duk_small_int_t s2n_radix = 10; - duk_small_uint_t pre_adv = 0; - - DUK__INITBUFFER(lex_ctx); - y = DUK__L1(); - - if (x == DUK_ASC_0) { - z = DUK_LOWERCASE_CHAR_ASCII(y); - - pre_adv = 2; /* default for 0xNNN, 0oNNN, 0bNNN. */ - if (z == DUK_ASC_LC_X) { - s2n_radix = 16; - } else if (z == DUK_ASC_LC_O) { - s2n_radix = 8; - } else if (z == DUK_ASC_LC_B) { - s2n_radix = 2; - } else { - pre_adv = 0; - if (DUK__ISDIGIT(y)) { - if (strict_mode) { - /* Reject octal like \07 but also octal-lookalike - * decimal like \08 in strict mode. - */ - goto fail_number_literal; - } else { - /* Legacy OctalIntegerLiteral or octal-lookalice - * decimal. Deciding between the two happens below - * in digit scanning. - */ - DUK__APPENDBUFFER(lex_ctx, x); - pre_adv = 1; - legacy_oct = 1; - s2n_radix = 8; /* tentative unless conflicting digits found */ - } - } - } - } - - DUK__ADVANCECHARS(lex_ctx, pre_adv); - - /* XXX: we could parse integers here directly, and fall back - * to numconv only when encountering a fractional expression - * or when an octal literal turned out to be decimal (0778 etc). - */ - state = 0; - for (;;) { - x = DUK__L0(); /* re-lookup curr char on first round */ - if (DUK__ISDIGIT(x)) { - /* Note: intentionally allow leading zeroes here, as the - * actual parser will check for them. - */ - if (state == 0 && legacy_oct && (x == DUK_ASC_8 || x == DUK_ASC_9)) { - /* Started out as an octal-lookalike - * but interpreted as decimal, e.g. - * '0779' -> 779. This also means - * that fractions are allowed, e.g. - * '0779.123' is allowed but '0777.123' - * is not! - */ - s2n_radix = 10; - } - if (state == 2) { - state = 3; - } - } else if (s2n_radix == 16 && DUK__ISHEXDIGIT(x)) { - /* Note: 'e' and 'E' are also accepted here. */ - ; - } else if (x == DUK_ASC_PERIOD) { - if (state >= 1 || s2n_radix != 10) { - break; - } else { - state = 1; - } - } else if (x == DUK_ASC_LC_E || x == DUK_ASC_UC_E) { - if (state >= 2 || s2n_radix != 10) { - break; - } else { - state = 2; - } - } else if (x == DUK_ASC_MINUS || x == DUK_ASC_PLUS) { - if (state != 2) { - break; - } else { - state = 3; - } - } else { - break; - } - DUK__APPENDBUFFER(lex_ctx, x); - DUK__ADVANCECHARS(lex_ctx, 1); - } - - /* XXX: better coercion */ - (void) duk__internbuffer(lex_ctx, lex_ctx->slot1_idx); - - if (s2n_radix != 10) { - /* For bases other than 10, integer only. */ - s2n_flags = DUK_S2N_FLAG_ALLOW_LEADING_ZERO; - } else { - s2n_flags = DUK_S2N_FLAG_ALLOW_EXP | DUK_S2N_FLAG_ALLOW_FRAC | DUK_S2N_FLAG_ALLOW_NAKED_FRAC | - DUK_S2N_FLAG_ALLOW_EMPTY_FRAC | DUK_S2N_FLAG_ALLOW_LEADING_ZERO; - } - - duk_dup(lex_ctx->thr, lex_ctx->slot1_idx); - duk_numconv_parse(lex_ctx->thr, s2n_radix, s2n_flags); - val = duk_to_number_m1(lex_ctx->thr); - if (DUK_ISNAN(val)) { - goto fail_number_literal; - } - duk_replace(lex_ctx->thr, lex_ctx->slot1_idx); /* could also just pop? */ - - DUK__INITBUFFER(lex_ctx); /* free some memory */ - - /* Section 7.8.3 (note): NumericLiteral must be followed by something other than - * IdentifierStart or DecimalDigit. - */ - - if (DUK__ISDIGIT(DUK__L0()) || duk_unicode_is_identifier_start(DUK__L0())) { - goto fail_number_literal; - } - - out_token->num = val; - advtok = DUK__ADVTOK(0, DUK_TOK_NUMBER); - } else if (duk_unicode_is_whitespace(DUK__LOOKUP(lex_ctx, 0))) { - DUK__ADVANCECHARS(lex_ctx, 1); - goto restart; - } else if (x < 0) { - advtok = DUK__ADVTOK(0, DUK_TOK_EOF); - } else { - goto fail_token; - } -skip_slow_path: - - /* - * Shared exit path - */ - - DUK__ADVANCEBYTES(lex_ctx, advtok >> 8); - out_token->t = advtok & 0xff; - if (out_token->t_nores == DUK_TOK_INVALID) { - out_token->t_nores = out_token->t; - } - out_token->lineterm = got_lineterm; - - /* Automatic semicolon insertion is allowed if a token is preceded - * by line terminator(s), or terminates a statement list (right curly - * or EOF). - */ - if (got_lineterm || out_token->t == DUK_TOK_RCURLY || out_token->t == DUK_TOK_EOF) { - out_token->allow_auto_semi = 1; - } else { - out_token->allow_auto_semi = 0; - } - - return; - -fail_token_limit: - DUK_ERROR_RANGE(lex_ctx->thr, DUK_STR_TOKEN_LIMIT); - DUK_WO_NORETURN(return;); - -fail_token: - DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_TOKEN); - DUK_WO_NORETURN(return;); - -fail_number_literal: - DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_NUMBER_LITERAL); - DUK_WO_NORETURN(return;); - -fail_escape: - DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_ESCAPE); - DUK_WO_NORETURN(return;); - -fail_unterm_regexp: - DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_UNTERMINATED_REGEXP); - DUK_WO_NORETURN(return;); - -fail_unterm_comment: - DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_UNTERMINATED_COMMENT); - DUK_WO_NORETURN(return;); - -#if !defined(DUK_USE_REGEXP_SUPPORT) -fail_regexp_support: - DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_REGEXP_SUPPORT_DISABLED); - DUK_WO_NORETURN(return;); -#endif -} - -#if defined(DUK_USE_REGEXP_SUPPORT) - -/* - * Parse a RegExp token. The grammar is described in E5 Section 15.10. - * Terminal constructions (such as quantifiers) are parsed directly here. - * - * 0xffffffffU is used as a marker for "infinity" in quantifiers. Further, - * DUK__MAX_RE_QUANT_DIGITS limits the maximum number of digits that - * will be accepted for a quantifier. - */ - -DUK_INTERNAL void duk_lexer_parse_re_token(duk_lexer_ctx *lex_ctx, duk_re_token *out_token) { - duk_small_uint_t advtok = 0; /* init is unnecessary but suppresses "may be used uninitialized" warnings */ - duk_codepoint_t x, y; - - if (++lex_ctx->token_count >= lex_ctx->token_limit) { - goto fail_token_limit; - } - - duk_memzero(out_token, sizeof(*out_token)); - - x = DUK__L0(); - y = DUK__L1(); - - DUK_DDD(DUK_DDDPRINT("parsing regexp token, L0=%ld, L1=%ld", (long) x, (long) y)); - - switch (x) { - case DUK_ASC_PIPE: { - advtok = DUK__ADVTOK(1, DUK_RETOK_DISJUNCTION); - break; - } - case DUK_ASC_CARET: { - advtok = DUK__ADVTOK(1, DUK_RETOK_ASSERT_START); - break; - } - case DUK_ASC_DOLLAR: { - advtok = DUK__ADVTOK(1, DUK_RETOK_ASSERT_END); - break; - } - case DUK_ASC_QUESTION: { - out_token->qmin = 0; - out_token->qmax = 1; - if (y == DUK_ASC_QUESTION) { - advtok = DUK__ADVTOK(2, DUK_RETOK_QUANTIFIER); - out_token->greedy = 0; - } else { - advtok = DUK__ADVTOK(1, DUK_RETOK_QUANTIFIER); - out_token->greedy = 1; - } - break; - } - case DUK_ASC_STAR: { - out_token->qmin = 0; - out_token->qmax = DUK_RE_QUANTIFIER_INFINITE; - if (y == DUK_ASC_QUESTION) { - advtok = DUK__ADVTOK(2, DUK_RETOK_QUANTIFIER); - out_token->greedy = 0; - } else { - advtok = DUK__ADVTOK(1, DUK_RETOK_QUANTIFIER); - out_token->greedy = 1; - } - break; - } - case DUK_ASC_PLUS: { - out_token->qmin = 1; - out_token->qmax = DUK_RE_QUANTIFIER_INFINITE; - if (y == DUK_ASC_QUESTION) { - advtok = DUK__ADVTOK(2, DUK_RETOK_QUANTIFIER); - out_token->greedy = 0; - } else { - advtok = DUK__ADVTOK(1, DUK_RETOK_QUANTIFIER); - out_token->greedy = 1; - } - break; - } - case DUK_ASC_LCURLY: { - /* Production allows 'DecimalDigits', including leading zeroes */ - duk_uint32_t val1 = 0; - duk_uint32_t val2 = DUK_RE_QUANTIFIER_INFINITE; - duk_small_int_t digits = 0; -#if defined(DUK_USE_ES6_REGEXP_SYNTAX) - duk_lexer_point lex_pt; -#endif - -#if defined(DUK_USE_ES6_REGEXP_SYNTAX) - /* Store lexer position, restoring if quantifier is invalid. */ - DUK_LEXER_GETPOINT(lex_ctx, &lex_pt); -#endif - - for (;;) { - DUK__ADVANCECHARS(lex_ctx, 1); /* eat '{' on entry */ - x = DUK__L0(); - if (DUK__ISDIGIT(x)) { - digits++; - val1 = val1 * 10 + (duk_uint32_t) duk__hexval(x); - } else if (x == DUK_ASC_COMMA) { - if (digits > DUK__MAX_RE_QUANT_DIGITS) { - goto invalid_quantifier; - } - if (val2 != DUK_RE_QUANTIFIER_INFINITE) { - goto invalid_quantifier; - } - if (DUK__L1() == DUK_ASC_RCURLY) { - /* form: { DecimalDigits , }, val1 = min count */ - if (digits == 0) { - goto invalid_quantifier; - } - out_token->qmin = val1; - out_token->qmax = DUK_RE_QUANTIFIER_INFINITE; - DUK__ADVANCECHARS(lex_ctx, 2); - break; - } - val2 = val1; - val1 = 0; - digits = 0; /* not strictly necessary because of lookahead '}' above */ - } else if (x == DUK_ASC_RCURLY) { - if (digits > DUK__MAX_RE_QUANT_DIGITS) { - goto invalid_quantifier; - } - if (digits == 0) { - goto invalid_quantifier; - } - if (val2 != DUK_RE_QUANTIFIER_INFINITE) { - /* val2 = min count, val1 = max count */ - out_token->qmin = val2; - out_token->qmax = val1; - } else { - /* val1 = count */ - out_token->qmin = val1; - out_token->qmax = val1; - } - DUK__ADVANCECHARS(lex_ctx, 1); - break; - } else { - goto invalid_quantifier; - } - } - if (DUK__L0() == DUK_ASC_QUESTION) { - out_token->greedy = 0; - DUK__ADVANCECHARS(lex_ctx, 1); - } else { - out_token->greedy = 1; - } - advtok = DUK__ADVTOK(0, DUK_RETOK_QUANTIFIER); - break; - invalid_quantifier: -#if defined(DUK_USE_ES6_REGEXP_SYNTAX) - /* Failed to match the quantifier, restore lexer and parse - * opening brace as a literal. - */ - DUK_LEXER_SETPOINT(lex_ctx, &lex_pt); - advtok = DUK__ADVTOK(1, DUK_RETOK_ATOM_CHAR); - out_token->num = DUK_ASC_LCURLY; -#else - goto fail_quantifier; -#endif - break; - } - case DUK_ASC_PERIOD: { - advtok = DUK__ADVTOK(1, DUK_RETOK_ATOM_PERIOD); - break; - } - case DUK_ASC_BACKSLASH: { - /* The E5.1 specification does not seem to allow IdentifierPart characters - * to be used as identity escapes. Unfortunately this includes '$', which - * cannot be escaped as '\$'; it needs to be escaped e.g. as '\u0024'. - * Many other implementations (including V8 and Rhino, for instance) do - * accept '\$' as a valid identity escape, which is quite pragmatic, and - * ES2015 Annex B relaxes the rules to allow these (and other) real world forms. - */ - - advtok = DUK__ADVTOK(2, DUK_RETOK_ATOM_CHAR); /* default: char escape (two chars) */ - if (y == DUK_ASC_LC_B) { - advtok = DUK__ADVTOK(2, DUK_RETOK_ASSERT_WORD_BOUNDARY); - } else if (y == DUK_ASC_UC_B) { - advtok = DUK__ADVTOK(2, DUK_RETOK_ASSERT_NOT_WORD_BOUNDARY); - } else if (y == DUK_ASC_LC_F) { - out_token->num = 0x000c; - } else if (y == DUK_ASC_LC_N) { - out_token->num = 0x000a; - } else if (y == DUK_ASC_LC_T) { - out_token->num = 0x0009; - } else if (y == DUK_ASC_LC_R) { - out_token->num = 0x000d; - } else if (y == DUK_ASC_LC_V) { - out_token->num = 0x000b; - } else if (y == DUK_ASC_LC_C) { - x = DUK__L2(); - if ((x >= DUK_ASC_LC_A && x <= DUK_ASC_LC_Z) || (x >= DUK_ASC_UC_A && x <= DUK_ASC_UC_Z)) { - out_token->num = (duk_uint32_t) (x % 32); - advtok = DUK__ADVTOK(3, DUK_RETOK_ATOM_CHAR); - } else { - goto fail_escape; - } - } else if (y == DUK_ASC_LC_X || y == DUK_ASC_LC_U) { - /* The token value is the Unicode codepoint without - * it being decode into surrogate pair characters - * here. The \u{H+} is only allowed in Unicode mode - * which we don't support yet. - */ - out_token->num = (duk_uint32_t) duk__lexer_parse_escape(lex_ctx, 0 /*allow_es6*/); - advtok = DUK__ADVTOK(0, DUK_RETOK_ATOM_CHAR); - } else if (y == DUK_ASC_LC_D) { - advtok = DUK__ADVTOK(2, DUK_RETOK_ATOM_DIGIT); - } else if (y == DUK_ASC_UC_D) { - advtok = DUK__ADVTOK(2, DUK_RETOK_ATOM_NOT_DIGIT); - } else if (y == DUK_ASC_LC_S) { - advtok = DUK__ADVTOK(2, DUK_RETOK_ATOM_WHITE); - } else if (y == DUK_ASC_UC_S) { - advtok = DUK__ADVTOK(2, DUK_RETOK_ATOM_NOT_WHITE); - } else if (y == DUK_ASC_LC_W) { - advtok = DUK__ADVTOK(2, DUK_RETOK_ATOM_WORD_CHAR); - } else if (y == DUK_ASC_UC_W) { - advtok = DUK__ADVTOK(2, DUK_RETOK_ATOM_NOT_WORD_CHAR); - } else if (DUK__ISDIGIT(y)) { - /* E5 Section 15.10.2.11 */ - if (y == DUK_ASC_0) { - if (DUK__ISDIGIT(DUK__L2())) { - goto fail_escape; - } - out_token->num = 0x0000; - advtok = DUK__ADVTOK(2, DUK_RETOK_ATOM_CHAR); - } else { - /* XXX: shared parsing? */ - duk_uint32_t val = 0; - duk_small_int_t i; - for (i = 0;; i++) { - if (i >= DUK__MAX_RE_DECESC_DIGITS) { - goto fail_escape; - } - DUK__ADVANCECHARS(lex_ctx, 1); /* eat backslash on entry */ - x = DUK__L0(); - if (!DUK__ISDIGIT(x)) { - break; - } - val = val * 10 + (duk_uint32_t) duk__hexval(x); - } - /* DUK__L0() cannot be a digit, because the loop doesn't terminate if it is */ - advtok = DUK__ADVTOK(0, DUK_RETOK_ATOM_BACKREFERENCE); - out_token->num = val; - } -#if defined(DUK_USE_ES6_REGEXP_SYNTAX) - } else if (y >= 0) { - /* For ES2015 Annex B, accept any source character as identity - * escape except 'c' which is used for control characters. - * http://www.ecma-international.org/ecma-262/6.0/#sec-regular-expressions-patterns - * Careful not to match end-of-buffer (<0) here. - * This is not yet full ES2015 Annex B because cases above - * (like hex escape) won't backtrack. - */ - DUK_ASSERT(y != DUK_ASC_LC_C); /* covered above */ -#else /* DUK_USE_ES6_REGEXP_SYNTAX */ - } else if ((y >= 0 && !duk_unicode_is_identifier_part(y)) || y == DUK_UNICODE_CP_ZWNJ || y == DUK_UNICODE_CP_ZWJ) { - /* For ES5.1 identity escapes are not allowed for identifier - * parts. This conflicts with a lot of real world code as this - * doesn't e.g. allow escaping a dollar sign as /\$/, see - * test-regexp-identity-escape-dollar.js. - */ -#endif /* DUK_USE_ES6_REGEXP_SYNTAX */ - out_token->num = (duk_uint32_t) y; - } else { - goto fail_escape; - } - break; - } - case DUK_ASC_LPAREN: { - /* XXX: naming is inconsistent: ATOM_END_GROUP ends an ASSERT_START_LOOKAHEAD */ - - if (y == DUK_ASC_QUESTION) { - if (DUK__L2() == DUK_ASC_EQUALS) { - /* (?= */ - advtok = DUK__ADVTOK(3, DUK_RETOK_ASSERT_START_POS_LOOKAHEAD); - } else if (DUK__L2() == DUK_ASC_EXCLAMATION) { - /* (?! */ - advtok = DUK__ADVTOK(3, DUK_RETOK_ASSERT_START_NEG_LOOKAHEAD); - } else if (DUK__L2() == DUK_ASC_COLON) { - /* (?: */ - advtok = DUK__ADVTOK(3, DUK_RETOK_ATOM_START_NONCAPTURE_GROUP); - } else { - goto fail_group; - } - } else { - /* ( */ - advtok = DUK__ADVTOK(1, DUK_RETOK_ATOM_START_CAPTURE_GROUP); - } - break; - } - case DUK_ASC_RPAREN: { - advtok = DUK__ADVTOK(1, DUK_RETOK_ATOM_END_GROUP); - break; - } - case DUK_ASC_LBRACKET: { - /* - * To avoid creating a heavy intermediate value for the list of ranges, - * only the start token ('[' or '[^') is parsed here. The regexp - * compiler parses the ranges itself. - */ - - /* XXX: with DUK_USE_ES6_REGEXP_SYNTAX we should allow left bracket - * literal too, but it's not easy to parse without backtracking. - */ - - advtok = DUK__ADVTOK(1, DUK_RETOK_ATOM_START_CHARCLASS); - if (y == DUK_ASC_CARET) { - advtok = DUK__ADVTOK(2, DUK_RETOK_ATOM_START_CHARCLASS_INVERTED); - } - break; - } -#if !defined(DUK_USE_ES6_REGEXP_SYNTAX) - case DUK_ASC_RCURLY: - case DUK_ASC_RBRACKET: { - /* Although these could be parsed as PatternCharacters unambiguously (here), - * E5 Section 15.10.1 grammar explicitly forbids these as PatternCharacters. - */ - goto fail_invalid_char; - break; - } -#endif - case -1: { - /* EOF */ - advtok = DUK__ADVTOK(0, DUK_TOK_EOF); - break; - } - default: { - /* PatternCharacter, all excluded characters are matched by cases above */ - advtok = DUK__ADVTOK(1, DUK_RETOK_ATOM_CHAR); - out_token->num = (duk_uint32_t) x; - break; - } - } - - /* - * Shared exit path - */ - - DUK__ADVANCEBYTES(lex_ctx, advtok >> 8); - out_token->t = advtok & 0xff; - return; - -fail_token_limit: - DUK_ERROR_RANGE(lex_ctx->thr, DUK_STR_TOKEN_LIMIT); - DUK_WO_NORETURN(return;); - -fail_escape: - DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_REGEXP_ESCAPE); - DUK_WO_NORETURN(return;); - -fail_group: - DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_REGEXP_GROUP); - DUK_WO_NORETURN(return;); - -#if !defined(DUK_USE_ES6_REGEXP_SYNTAX) -fail_invalid_char: - DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_REGEXP_CHARACTER); - DUK_WO_NORETURN(return;); - -fail_quantifier: - DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_QUANTIFIER); - DUK_WO_NORETURN(return;); -#endif -} - -/* - * Special parser for character classes; calls callback for every - * range parsed and returns the number of ranges present. - */ - -/* XXX: this duplicates functionality in duk_regexp.c where a similar loop is - * required anyway. We could use that BUT we need to update the regexp compiler - * 'nranges' too. Work this out a bit more cleanly to save space. - */ - -/* XXX: the handling of character range detection is a bit convoluted. - * Try to simplify and make smaller. - */ - -/* XXX: logic for handling character ranges is now incorrect, it will accept - * e.g. [\d-z] whereas it should croak from it? SMJS accepts this too, though. - * - * Needs a read through and a lot of additional tests. - */ - -DUK_LOCAL -void duk__emit_u16_direct_ranges(duk_lexer_ctx *lex_ctx, - duk_re_range_callback gen_range, - void *userdata, - const duk_uint16_t *ranges, - duk_small_int_t num) { - const duk_uint16_t *ranges_end; - - DUK_UNREF(lex_ctx); - - ranges_end = ranges + num; - while (ranges < ranges_end) { - /* mark range 'direct', bypass canonicalization (see Wiki) */ - gen_range(userdata, (duk_codepoint_t) ranges[0], (duk_codepoint_t) ranges[1], 1); - ranges += 2; - } -} - -DUK_INTERNAL void duk_lexer_parse_re_ranges(duk_lexer_ctx *lex_ctx, duk_re_range_callback gen_range, void *userdata) { - duk_codepoint_t start = -1; - duk_codepoint_t ch; - duk_codepoint_t x; - duk_bool_t dash = 0; - duk_small_uint_t adv = 0; - - DUK_DD(DUK_DDPRINT("parsing regexp ranges")); - - for (;;) { - DUK__ADVANCECHARS(lex_ctx, adv); - adv = 1; - - x = DUK__L0(); - - ch = -1; /* not strictly necessary, but avoids "uninitialized variable" warnings */ - DUK_UNREF(ch); - - if (x < 0) { - goto fail_unterm_charclass; - } else if (x == DUK_ASC_RBRACKET) { - if (start >= 0) { - gen_range(userdata, start, start, 0); - } - DUK__ADVANCECHARS(lex_ctx, 1); /* eat ']' before finishing */ - break; - } else if (x == DUK_ASC_MINUS) { - if (start >= 0 && !dash && DUK__L1() != DUK_ASC_RBRACKET) { - /* '-' as a range indicator */ - dash = 1; - continue; - } else { - /* '-' verbatim */ - ch = x; - } - } else if (x == DUK_ASC_BACKSLASH) { - /* - * The escapes are same as outside a character class, except that \b has a - * different meaning, and \B and backreferences are prohibited (see E5 - * Section 15.10.2.19). However, it's difficult to share code because we - * handle e.g. "\n" very differently: here we generate a single character - * range for it. - */ - - /* XXX: ES2015 surrogate pair handling. */ - - x = DUK__L1(); - - adv = 2; - - if (x == DUK_ASC_LC_B) { - /* Note: '\b' in char class is different than outside (assertion), - * '\B' is not allowed and is caught by the duk_unicode_is_identifier_part() - * check below. - */ - ch = 0x0008; - } else if (x == DUK_ASC_LC_F) { - ch = 0x000c; - } else if (x == DUK_ASC_LC_N) { - ch = 0x000a; - } else if (x == DUK_ASC_LC_T) { - ch = 0x0009; - } else if (x == DUK_ASC_LC_R) { - ch = 0x000d; - } else if (x == DUK_ASC_LC_V) { - ch = 0x000b; - } else if (x == DUK_ASC_LC_C) { - x = DUK__L2(); - adv = 3; - if ((x >= DUK_ASC_LC_A && x <= DUK_ASC_LC_Z) || (x >= DUK_ASC_UC_A && x <= DUK_ASC_UC_Z)) { - ch = (x % 32); - } else { - goto fail_escape; - } - } else if (x == DUK_ASC_LC_X || x == DUK_ASC_LC_U) { - /* The \u{H+} form is only allowed in Unicode mode which - * we don't support yet. - */ - ch = duk__lexer_parse_escape(lex_ctx, 0 /*allow_es6*/); - adv = 0; - } else if (x == DUK_ASC_LC_D) { - duk__emit_u16_direct_ranges(lex_ctx, - gen_range, - userdata, - duk_unicode_re_ranges_digit, - sizeof(duk_unicode_re_ranges_digit) / sizeof(duk_uint16_t)); - ch = -1; - } else if (x == DUK_ASC_UC_D) { - duk__emit_u16_direct_ranges(lex_ctx, - gen_range, - userdata, - duk_unicode_re_ranges_not_digit, - sizeof(duk_unicode_re_ranges_not_digit) / sizeof(duk_uint16_t)); - ch = -1; - } else if (x == DUK_ASC_LC_S) { - duk__emit_u16_direct_ranges(lex_ctx, - gen_range, - userdata, - duk_unicode_re_ranges_white, - sizeof(duk_unicode_re_ranges_white) / sizeof(duk_uint16_t)); - ch = -1; - } else if (x == DUK_ASC_UC_S) { - duk__emit_u16_direct_ranges(lex_ctx, - gen_range, - userdata, - duk_unicode_re_ranges_not_white, - sizeof(duk_unicode_re_ranges_not_white) / sizeof(duk_uint16_t)); - ch = -1; - } else if (x == DUK_ASC_LC_W) { - duk__emit_u16_direct_ranges(lex_ctx, - gen_range, - userdata, - duk_unicode_re_ranges_wordchar, - sizeof(duk_unicode_re_ranges_wordchar) / sizeof(duk_uint16_t)); - ch = -1; - } else if (x == DUK_ASC_UC_W) { - duk__emit_u16_direct_ranges(lex_ctx, - gen_range, - userdata, - duk_unicode_re_ranges_not_wordchar, - sizeof(duk_unicode_re_ranges_not_wordchar) / sizeof(duk_uint16_t)); - ch = -1; - } else if (DUK__ISDIGIT(x)) { - /* DecimalEscape, only \0 is allowed, no leading - * zeroes are allowed. - * - * ES2015 Annex B also allows (maximal match) legacy - * octal escapes up to \377 and \8 and \9 are - * accepted as literal '8' and '9', also in strict mode. - */ - -#if defined(DUK_USE_ES6_REGEXP_SYNTAX) - ch = duk__lexer_parse_legacy_octal(lex_ctx, &adv, 0 /*reject_annex_b*/); - DUK_ASSERT(ch >= 0); /* no rejections */ -#else - if (x == DUK_ASC_0 && !DUK__ISDIGIT(DUK__L2())) { - ch = 0x0000; - } else { - goto fail_escape; - } -#endif -#if defined(DUK_USE_ES6_REGEXP_SYNTAX) - } else if (x >= 0) { - /* IdentityEscape: ES2015 Annex B allows almost all - * source characters here. Match anything except - * EOF here. - */ - ch = x; -#else /* DUK_USE_ES6_REGEXP_SYNTAX */ - } else if (!duk_unicode_is_identifier_part(x)) { - /* IdentityEscape: ES5.1 doesn't allow identity escape - * for identifier part characters, which conflicts with - * some real world code. For example, it doesn't allow - * /[\$]/ which is awkward. - */ - ch = x; -#endif /* DUK_USE_ES6_REGEXP_SYNTAX */ - } else { - goto fail_escape; - } - } else { - /* character represents itself */ - ch = x; - } - - /* ch is a literal character here or -1 if parsed entity was - * an escape such as "\s". - */ - - if (ch < 0) { - /* multi-character sets not allowed as part of ranges, see - * E5 Section 15.10.2.15, abstract operation CharacterRange. - */ - if (start >= 0) { - if (dash) { - goto fail_range; - } else { - gen_range(userdata, start, start, 0); - start = -1; - /* dash is already 0 */ - } - } - } else { - if (start >= 0) { - if (dash) { - if (start > ch) { - goto fail_range; - } - gen_range(userdata, start, ch, 0); - start = -1; - dash = 0; - } else { - gen_range(userdata, start, start, 0); - start = ch; - /* dash is already 0 */ - } - } else { - start = ch; - } - } - } - - return; - -fail_escape: - DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_REGEXP_ESCAPE); - DUK_WO_NORETURN(return;); - -fail_range: - DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_RANGE); - DUK_WO_NORETURN(return;); - -fail_unterm_charclass: - DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_UNTERMINATED_CHARCLASS); - DUK_WO_NORETURN(return;); -} - -#endif /* DUK_USE_REGEXP_SUPPORT */ - -/* automatic undefs */ -#undef DUK__ADVANCEBYTES -#undef DUK__ADVANCECHARS -#undef DUK__ADVTOK -#undef DUK__APPENDBUFFER -#undef DUK__APPENDBUFFER_ASCII -#undef DUK__INITBUFFER -#undef DUK__ISDIGIT -#undef DUK__ISDIGIT03 -#undef DUK__ISDIGIT47 -#undef DUK__ISHEXDIGIT -#undef DUK__ISOCTDIGIT -#undef DUK__L0 -#undef DUK__L1 -#undef DUK__L2 -#undef DUK__L3 -#undef DUK__L4 -#undef DUK__L5 -#undef DUK__LOOKUP -#undef DUK__MAX_RE_DECESC_DIGITS -#undef DUK__MAX_RE_QUANT_DIGITS -/* - * Number-to-string and string-to-number conversions. - * - * Slow path number-to-string and string-to-number conversion is based on - * a Dragon4 variant, with fast paths for small integers. Big integer - * arithmetic is needed for guaranteeing that the conversion is correct - * and uses a minimum number of digits. The big number arithmetic has a - * fixed maximum size and does not require dynamic allocations. - * - * See: doc/number-conversion.rst. - */ - -/* #include duk_internal.h -> already included */ - -#define DUK__IEEE_DOUBLE_EXP_BIAS 1023 -#define DUK__IEEE_DOUBLE_EXP_MIN (-1022) /* biased exp == 0 -> denormal, exp -1022 */ - -#define DUK__DIGITCHAR(x) duk_lc_digits[(x)] - -/* - * Tables generated with util/gennumdigits.py. - * - * duk__str2num_digits_for_radix indicates, for each radix, how many input - * digits should be considered significant for string-to-number conversion. - * The input is also padded to this many digits to give the Dragon4 - * conversion enough (apparent) precision to work with. - * - * duk__str2num_exp_limits indicates, for each radix, the radix-specific - * minimum/maximum exponent values (for a Dragon4 integer mantissa) - * below and above which the number is guaranteed to underflow to zero - * or overflow to Infinity. This allows parsing to keep bigint values - * bounded. - */ - -DUK_LOCAL const duk_uint8_t duk__str2num_digits_for_radix[] = { - 69, 44, 35, 30, 27, 25, 23, 22, 20, 20, /* 2 to 11 */ - 20, 19, 19, 18, 18, 17, 17, 17, 16, 16, /* 12 to 21 */ - 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, /* 22 to 31 */ - 14, 14, 14, 14, 14 /* 31 to 36 */ -}; - -typedef struct { - duk_int16_t upper; - duk_int16_t lower; -} duk__exp_limits; - -DUK_LOCAL const duk__exp_limits duk__str2num_exp_limits[] = { - { 957, -1147 }, { 605, -725 }, { 479, -575 }, { 414, -496 }, { 372, -446 }, { 342, -411 }, { 321, -384 }, - { 304, -364 }, { 291, -346 }, { 279, -334 }, { 268, -323 }, { 260, -312 }, { 252, -304 }, { 247, -296 }, - { 240, -289 }, { 236, -283 }, { 231, -278 }, { 227, -273 }, { 223, -267 }, { 220, -263 }, { 216, -260 }, - { 213, -256 }, { 210, -253 }, { 208, -249 }, { 205, -246 }, { 203, -244 }, { 201, -241 }, { 198, -239 }, - { 196, -237 }, { 195, -234 }, { 193, -232 }, { 191, -230 }, { 190, -228 }, { 188, -226 }, { 187, -225 }, -}; - -/* - * Limited functionality bigint implementation. - * - * Restricted to non-negative numbers with less than 32 * DUK__BI_MAX_PARTS bits, - * with the caller responsible for ensuring this is never exceeded. No memory - * allocation (except stack) is needed for bigint computation. Operations - * have been tailored for number conversion needs. - * - * Argument order is "assignment order", i.e. target first, then arguments: - * x <- y * z --> duk__bi_mul(x, y, z); - */ - -/* This upper value has been experimentally determined; debug build will check - * bigint size with assertions. - */ -#define DUK__BI_MAX_PARTS 37 /* 37x32 = 1184 bits */ - -#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2) -#define DUK__BI_PRINT(name, x) duk__bi_print((name), (x)) -#else -#define DUK__BI_PRINT(name, x) -#endif - -/* Current size is about 152 bytes. */ -typedef struct { - duk_small_int_t n; - duk_uint32_t v[DUK__BI_MAX_PARTS]; /* low to high */ -} duk__bigint; - -#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2) -DUK_LOCAL void duk__bi_print(const char *name, duk__bigint *x) { - /* Overestimate required size; debug code so not critical to be tight. */ - char buf[DUK__BI_MAX_PARTS * 9 + 64]; - char *p = buf; - duk_small_int_t i; - - /* No NUL term checks in this debug code. */ - p += DUK_SPRINTF(p, "%p n=%ld", (void *) x, (long) x->n); - if (x->n == 0) { - p += DUK_SPRINTF(p, " 0"); - } - for (i = x->n - 1; i >= 0; i--) { - p += DUK_SPRINTF(p, " %08lx", (unsigned long) x->v[i]); - } - - DUK_DDD(DUK_DDDPRINT("%s: %s", (const char *) name, (const char *) buf)); -} -#endif - -#if defined(DUK_USE_ASSERTIONS) -DUK_LOCAL duk_small_int_t duk__bi_is_valid(duk__bigint *x) { - return (duk_small_int_t) (((x->n >= 0) && (x->n <= DUK__BI_MAX_PARTS)) /* is valid size */ && - ((x->n == 0) || (x->v[x->n - 1] != 0)) /* is normalized */); -} -#endif - -DUK_LOCAL void duk__bi_normalize(duk__bigint *x) { - duk_small_int_t i; - - for (i = x->n - 1; i >= 0; i--) { - if (x->v[i] != 0) { - break; - } - } - - /* Note: if 'x' is zero, x->n becomes 0 here */ - x->n = i + 1; - DUK_ASSERT(duk__bi_is_valid(x)); -} - -/* x <- y */ -DUK_LOCAL void duk__bi_copy(duk__bigint *x, duk__bigint *y) { - duk_small_int_t n; - - n = y->n; - x->n = n; - /* No need to special case n == 0. */ - duk_memcpy((void *) x->v, (const void *) y->v, (size_t) (sizeof(duk_uint32_t) * (size_t) n)); -} - -DUK_LOCAL void duk__bi_set_small(duk__bigint *x, duk_uint32_t v) { - if (v == 0U) { - x->n = 0; - } else { - x->n = 1; - x->v[0] = v; - } - DUK_ASSERT(duk__bi_is_valid(x)); -} - -/* Return value: <0 <=> x < y - * 0 <=> x == y - * >0 <=> x > y - */ -DUK_LOCAL int duk__bi_compare(duk__bigint *x, duk__bigint *y) { - duk_small_int_t i, nx, ny; - duk_uint32_t tx, ty; - - DUK_ASSERT(duk__bi_is_valid(x)); - DUK_ASSERT(duk__bi_is_valid(y)); - - nx = x->n; - ny = y->n; - if (nx > ny) { - goto ret_gt; - } - if (nx < ny) { - goto ret_lt; - } - for (i = nx - 1; i >= 0; i--) { - tx = x->v[i]; - ty = y->v[i]; - - if (tx > ty) { - goto ret_gt; - } - if (tx < ty) { - goto ret_lt; - } - } - - return 0; - -ret_gt: - return 1; - -ret_lt: - return -1; -} - -/* x <- y + z */ -#if defined(DUK_USE_64BIT_OPS) -DUK_LOCAL void duk__bi_add(duk__bigint *x, duk__bigint *y, duk__bigint *z) { - duk_uint64_t tmp; - duk_small_int_t i, ny, nz; - - DUK_ASSERT(duk__bi_is_valid(y)); - DUK_ASSERT(duk__bi_is_valid(z)); - - if (z->n > y->n) { - duk__bigint *t; - t = y; - y = z; - z = t; - } - DUK_ASSERT(y->n >= z->n); - - ny = y->n; - nz = z->n; - tmp = 0U; - for (i = 0; i < ny; i++) { - DUK_ASSERT(i < DUK__BI_MAX_PARTS); - tmp += y->v[i]; - if (i < nz) { - tmp += z->v[i]; - } - x->v[i] = (duk_uint32_t) (tmp & 0xffffffffUL); - tmp = tmp >> 32; - } - if (tmp != 0U) { - DUK_ASSERT(i < DUK__BI_MAX_PARTS); - x->v[i++] = (duk_uint32_t) tmp; - } - x->n = i; - DUK_ASSERT(x->n <= DUK__BI_MAX_PARTS); - - /* no need to normalize */ - DUK_ASSERT(duk__bi_is_valid(x)); -} -#else /* DUK_USE_64BIT_OPS */ -DUK_LOCAL void duk__bi_add(duk__bigint *x, duk__bigint *y, duk__bigint *z) { - duk_uint32_t carry, tmp1, tmp2; - duk_small_int_t i, ny, nz; - - DUK_ASSERT(duk__bi_is_valid(y)); - DUK_ASSERT(duk__bi_is_valid(z)); - - if (z->n > y->n) { - duk__bigint *t; - t = y; - y = z; - z = t; - } - DUK_ASSERT(y->n >= z->n); - - ny = y->n; - nz = z->n; - carry = 0U; - for (i = 0; i < ny; i++) { - /* Carry is detected based on wrapping which relies on exact 32-bit - * types. - */ - DUK_ASSERT(i < DUK__BI_MAX_PARTS); - tmp1 = y->v[i]; - tmp2 = tmp1; - if (i < nz) { - tmp2 += z->v[i]; - } - - /* Careful with carry condition: - * - If carry not added: 0x12345678 + 0 + 0xffffffff = 0x12345677 (< 0x12345678) - * - If carry added: 0x12345678 + 1 + 0xffffffff = 0x12345678 (== 0x12345678) - */ - if (carry) { - tmp2++; - carry = (tmp2 <= tmp1 ? 1U : 0U); - } else { - carry = (tmp2 < tmp1 ? 1U : 0U); - } - - x->v[i] = tmp2; - } - if (carry) { - DUK_ASSERT(i < DUK__BI_MAX_PARTS); - DUK_ASSERT(carry == 1U); - x->v[i++] = carry; - } - x->n = i; - DUK_ASSERT(x->n <= DUK__BI_MAX_PARTS); - - /* no need to normalize */ - DUK_ASSERT(duk__bi_is_valid(x)); -} -#endif /* DUK_USE_64BIT_OPS */ - -/* x <- y + z */ -DUK_LOCAL void duk__bi_add_small(duk__bigint *x, duk__bigint *y, duk_uint32_t z) { - duk__bigint tmp; - - DUK_ASSERT(duk__bi_is_valid(y)); - - /* XXX: this could be optimized; there is only one call site now though */ - duk__bi_set_small(&tmp, z); - duk__bi_add(x, y, &tmp); - - DUK_ASSERT(duk__bi_is_valid(x)); -} - -#if 0 /* unused */ -/* x <- x + y, use t as temp */ -DUK_LOCAL void duk__bi_add_copy(duk__bigint *x, duk__bigint *y, duk__bigint *t) { - duk__bi_add(t, x, y); - duk__bi_copy(x, t); -} -#endif - -/* x <- y - z, require x >= y => z >= 0, i.e. y >= z */ -#if defined(DUK_USE_64BIT_OPS) -DUK_LOCAL void duk__bi_sub(duk__bigint *x, duk__bigint *y, duk__bigint *z) { - duk_small_int_t i, ny, nz; - duk_uint32_t ty, tz; - duk_int64_t tmp; - - DUK_ASSERT(duk__bi_is_valid(y)); - DUK_ASSERT(duk__bi_is_valid(z)); - DUK_ASSERT(duk__bi_compare(y, z) >= 0); - DUK_ASSERT(y->n >= z->n); - - ny = y->n; - nz = z->n; - tmp = 0; - for (i = 0; i < ny; i++) { - ty = y->v[i]; - if (i < nz) { - tz = z->v[i]; - } else { - tz = 0; - } - tmp = (duk_int64_t) ty - (duk_int64_t) tz + tmp; - x->v[i] = (duk_uint32_t) ((duk_uint64_t) tmp & 0xffffffffUL); - tmp = tmp >> 32; /* 0 or -1 */ - } - DUK_ASSERT(tmp == 0); - - x->n = i; - duk__bi_normalize(x); /* need to normalize, may even cancel to 0 */ - DUK_ASSERT(duk__bi_is_valid(x)); -} -#else -DUK_LOCAL void duk__bi_sub(duk__bigint *x, duk__bigint *y, duk__bigint *z) { - duk_small_int_t i, ny, nz; - duk_uint32_t tmp1, tmp2, borrow; - - DUK_ASSERT(duk__bi_is_valid(y)); - DUK_ASSERT(duk__bi_is_valid(z)); - DUK_ASSERT(duk__bi_compare(y, z) >= 0); - DUK_ASSERT(y->n >= z->n); - - ny = y->n; - nz = z->n; - borrow = 0U; - for (i = 0; i < ny; i++) { - /* Borrow is detected based on wrapping which relies on exact 32-bit - * types. - */ - tmp1 = y->v[i]; - tmp2 = tmp1; - if (i < nz) { - tmp2 -= z->v[i]; - } - - /* Careful with borrow condition: - * - If borrow not subtracted: 0x12345678 - 0 - 0xffffffff = 0x12345679 (> 0x12345678) - * - If borrow subtracted: 0x12345678 - 1 - 0xffffffff = 0x12345678 (== 0x12345678) - */ - if (borrow) { - tmp2--; - borrow = (tmp2 >= tmp1 ? 1U : 0U); - } else { - borrow = (tmp2 > tmp1 ? 1U : 0U); - } - - x->v[i] = tmp2; - } - DUK_ASSERT(borrow == 0U); - - x->n = i; - duk__bi_normalize(x); /* need to normalize, may even cancel to 0 */ - DUK_ASSERT(duk__bi_is_valid(x)); -} -#endif - -#if 0 /* unused */ -/* x <- y - z */ -DUK_LOCAL void duk__bi_sub_small(duk__bigint *x, duk__bigint *y, duk_uint32_t z) { - duk__bigint tmp; - - DUK_ASSERT(duk__bi_is_valid(y)); - - /* XXX: this could be optimized */ - duk__bi_set_small(&tmp, z); - duk__bi_sub(x, y, &tmp); - - DUK_ASSERT(duk__bi_is_valid(x)); -} -#endif - -/* x <- x - y, use t as temp */ -DUK_LOCAL void duk__bi_sub_copy(duk__bigint *x, duk__bigint *y, duk__bigint *t) { - duk__bi_sub(t, x, y); - duk__bi_copy(x, t); -} - -/* x <- y * z */ -DUK_LOCAL void duk__bi_mul(duk__bigint *x, duk__bigint *y, duk__bigint *z) { - duk_small_int_t i, j, nx, nz; - - DUK_ASSERT(duk__bi_is_valid(y)); - DUK_ASSERT(duk__bi_is_valid(z)); - - nx = y->n + z->n; /* max possible */ - DUK_ASSERT(nx <= DUK__BI_MAX_PARTS); - - if (nx == 0) { - /* Both inputs are zero; cases where only one is zero can go - * through main algorithm. - */ - x->n = 0; - return; - } - - duk_memzero((void *) x->v, (size_t) (sizeof(duk_uint32_t) * (size_t) nx)); - x->n = nx; - - nz = z->n; - for (i = 0; i < y->n; i++) { -#if defined(DUK_USE_64BIT_OPS) - duk_uint64_t tmp = 0U; - for (j = 0; j < nz; j++) { - tmp += (duk_uint64_t) y->v[i] * (duk_uint64_t) z->v[j] + x->v[i + j]; - x->v[i + j] = (duk_uint32_t) (tmp & 0xffffffffUL); - tmp = tmp >> 32; - } - if (tmp > 0) { - DUK_ASSERT(i + j < nx); - DUK_ASSERT(i + j < DUK__BI_MAX_PARTS); - DUK_ASSERT(x->v[i + j] == 0U); - x->v[i + j] = (duk_uint32_t) tmp; - } -#else - /* - * Multiply + add + carry for 32-bit components using only 16x16->32 - * multiplies and carry detection based on unsigned overflow. - * - * 1st mult, 32-bit: (A*2^16 + B) - * 2nd mult, 32-bit: (C*2^16 + D) - * 3rd add, 32-bit: E - * 4th add, 32-bit: F - * - * (AC*2^16 + B) * (C*2^16 + D) + E + F - * = AC*2^32 + AD*2^16 + BC*2^16 + BD + E + F - * = AC*2^32 + (AD + BC)*2^16 + (BD + E + F) - * = AC*2^32 + AD*2^16 + BC*2^16 + (BD + E + F) - */ - duk_uint32_t a, b, c, d, e, f; - duk_uint32_t r, s, t; - - a = y->v[i]; - b = a & 0xffffUL; - a = a >> 16; - - f = 0; - for (j = 0; j < nz; j++) { - c = z->v[j]; - d = c & 0xffffUL; - c = c >> 16; - e = x->v[i + j]; - - /* build result as: (r << 32) + s: start with (BD + E + F) */ - r = 0; - s = b * d; - - /* add E */ - t = s + e; - if (t < s) { - r++; - } /* carry */ - s = t; - - /* add F */ - t = s + f; - if (t < s) { - r++; - } /* carry */ - s = t; - - /* add BC*2^16 */ - t = b * c; - r += (t >> 16); - t = s + ((t & 0xffffUL) << 16); - if (t < s) { - r++; - } /* carry */ - s = t; - - /* add AD*2^16 */ - t = a * d; - r += (t >> 16); - t = s + ((t & 0xffffUL) << 16); - if (t < s) { - r++; - } /* carry */ - s = t; - - /* add AC*2^32 */ - t = a * c; - r += t; - - DUK_DDD(DUK_DDDPRINT("ab=%08lx cd=%08lx ef=%08lx -> rs=%08lx %08lx", - (unsigned long) y->v[i], - (unsigned long) z->v[j], - (unsigned long) x->v[i + j], - (unsigned long) r, - (unsigned long) s)); - - x->v[i + j] = s; - f = r; - } - if (f > 0U) { - DUK_ASSERT(i + j < nx); - DUK_ASSERT(i + j < DUK__BI_MAX_PARTS); - DUK_ASSERT(x->v[i + j] == 0U); - x->v[i + j] = (duk_uint32_t) f; - } -#endif /* DUK_USE_64BIT_OPS */ - } - - duk__bi_normalize(x); - DUK_ASSERT(duk__bi_is_valid(x)); -} - -/* x <- y * z */ -DUK_LOCAL void duk__bi_mul_small(duk__bigint *x, duk__bigint *y, duk_uint32_t z) { - duk__bigint tmp; - - DUK_ASSERT(duk__bi_is_valid(y)); - - /* XXX: this could be optimized */ - duk__bi_set_small(&tmp, z); - duk__bi_mul(x, y, &tmp); - - DUK_ASSERT(duk__bi_is_valid(x)); -} - -/* x <- x * y, use t as temp */ -DUK_LOCAL void duk__bi_mul_copy(duk__bigint *x, duk__bigint *y, duk__bigint *t) { - duk__bi_mul(t, x, y); - duk__bi_copy(x, t); -} - -/* x <- x * y, use t as temp */ -DUK_LOCAL void duk__bi_mul_small_copy(duk__bigint *x, duk_uint32_t y, duk__bigint *t) { - duk__bi_mul_small(t, x, y); - duk__bi_copy(x, t); -} - -DUK_LOCAL int duk__bi_is_even(duk__bigint *x) { - DUK_ASSERT(duk__bi_is_valid(x)); - return (x->n == 0) || ((x->v[0] & 0x01) == 0); -} - -DUK_LOCAL int duk__bi_is_zero(duk__bigint *x) { - DUK_ASSERT(duk__bi_is_valid(x)); - return (x->n == 0); /* this is the case for normalized numbers */ -} - -/* Bigint is 2^52. Used to detect normalized IEEE double mantissa values - * which are at the lowest edge (next floating point value downwards has - * a different exponent). The lowest mantissa has the form: - * - * 1000........000 (52 zeroes; only "hidden bit" is set) - */ -DUK_LOCAL duk_small_int_t duk__bi_is_2to52(duk__bigint *x) { - DUK_ASSERT(duk__bi_is_valid(x)); - return (duk_small_int_t) (x->n == 2) && (x->v[0] == 0U) && (x->v[1] == (1U << (52 - 32))); -} - -/* x <- (1< 0); - r = y % 32; - duk_memzero((void *) x->v, sizeof(duk_uint32_t) * (size_t) n); - x->n = n; - x->v[n - 1] = (((duk_uint32_t) 1) << r); -} - -/* x <- b^y; use t1 and t2 as temps */ -DUK_LOCAL void duk__bi_exp_small(duk__bigint *x, duk_small_int_t b, duk_small_int_t y, duk__bigint *t1, duk__bigint *t2) { - /* Fast path the binary case */ - - DUK_ASSERT(x != t1 && x != t2 && t1 != t2); /* distinct bignums, easy mistake to make */ - DUK_ASSERT(b >= 0); - DUK_ASSERT(y >= 0); - - if (b == 2) { - duk__bi_twoexp(x, y); - return; - } - - /* http://en.wikipedia.org/wiki/Exponentiation_by_squaring */ - - DUK_DDD(DUK_DDDPRINT("exp_small: b=%ld, y=%ld", (long) b, (long) y)); - - duk__bi_set_small(x, 1); - duk__bi_set_small(t1, (duk_uint32_t) b); - for (;;) { - /* Loop structure ensures that we don't compute t1^2 unnecessarily - * on the final round, as that might create a bignum exceeding the - * current DUK__BI_MAX_PARTS limit. - */ - if (y & 0x01) { - duk__bi_mul_copy(x, t1, t2); - } - y = y >> 1; - if (y == 0) { - break; - } - duk__bi_mul_copy(t1, t1, t2); - } - - DUK__BI_PRINT("exp_small result", x); -} - -/* - * A Dragon4 number-to-string variant, based on: - * - * Guy L. Steele Jr., Jon L. White: "How to Print Floating-Point Numbers - * Accurately" - * - * Robert G. Burger, R. Kent Dybvig: "Printing Floating-Point Numbers - * Quickly and Accurately" - * - * The current algorithm is based on Figure 1 of the Burger-Dybvig paper, - * i.e. the base implementation without logarithm estimation speedups - * (these would increase code footprint considerably). Fixed-format output - * does not follow the suggestions in the paper; instead, we generate an - * extra digit and round-with-carry. - * - * The same algorithm is used for number parsing (with b=10 and B=2) - * by generating one extra digit and doing rounding manually. - * - * See doc/number-conversion.rst for limitations. - */ - -/* Maximum number of digits generated. */ -#define DUK__MAX_OUTPUT_DIGITS 1040 /* (Number.MAX_VALUE).toString(2).length == 1024, + slack */ - -/* Maximum number of characters in formatted value. */ -#define DUK__MAX_FORMATTED_LENGTH 1040 /* (-Number.MAX_VALUE).toString(2).length == 1025, + slack */ - -/* Number and (minimum) size of bigints in the nc_ctx structure. */ -#define DUK__NUMCONV_CTX_NUM_BIGINTS 7 -#define DUK__NUMCONV_CTX_BIGINTS_SIZE (sizeof(duk__bigint) * DUK__NUMCONV_CTX_NUM_BIGINTS) - -typedef struct { - /* Currently about 7*152 = 1064 bytes. The space for these - * duk__bigints is used also as a temporary buffer for generating - * the final string. This is a bit awkard; a union would be - * more correct. - */ - duk__bigint f, r, s, mp, mm, t1, t2; - - duk_small_int_t is_s2n; /* if 1, doing a string-to-number; else doing a number-to-string */ - duk_small_int_t is_fixed; /* if 1, doing a fixed format output (not free format) */ - duk_small_int_t req_digits; /* requested number of output digits; 0 = free-format */ - duk_small_int_t abs_pos; /* digit position is absolute, not relative */ - duk_small_int_t e; /* exponent for 'f' */ - duk_small_int_t b; /* input radix */ - duk_small_int_t B; /* output radix */ - duk_small_int_t k; /* see algorithm */ - duk_small_int_t low_ok; /* see algorithm */ - duk_small_int_t high_ok; /* see algorithm */ - duk_small_int_t unequal_gaps; /* m+ != m- (very rarely) */ - - /* Buffer used for generated digits, values are in the range [0,B-1]. */ - duk_uint8_t digits[DUK__MAX_OUTPUT_DIGITS]; - duk_small_int_t count; /* digit count */ -} duk__numconv_stringify_ctx; - -/* Note: computes with 'idx' in assertions, so caller beware. - * 'idx' is preincremented, i.e. '1' on first call, because it - * is more convenient for the caller. - */ -#define DUK__DRAGON4_OUTPUT_PREINC(nc_ctx, preinc_idx, x) \ - do { \ - DUK_ASSERT((preinc_idx) -1 >= 0); \ - DUK_ASSERT((preinc_idx) -1 < DUK__MAX_OUTPUT_DIGITS); \ - ((nc_ctx)->digits[(preinc_idx) -1]) = (duk_uint8_t) (x); \ - } while (0) - -DUK_LOCAL duk_size_t duk__dragon4_format_uint32(duk_uint8_t *buf, duk_uint32_t x, duk_small_int_t radix) { - duk_uint8_t *p; - duk_size_t len; - duk_small_int_t dig; - duk_uint32_t t; - - DUK_ASSERT(buf != NULL); - DUK_ASSERT(radix >= 2 && radix <= 36); - - /* A 32-bit unsigned integer formats to at most 32 digits (the - * worst case happens with radix == 2). Output the digits backwards, - * and use a memmove() to get them in the right place. - */ - - p = buf + 32; - for (;;) { - t = x / (duk_uint32_t) radix; - dig = (duk_small_int_t) (x - t * (duk_uint32_t) radix); - x = t; - - DUK_ASSERT(dig >= 0 && dig < 36); - *(--p) = DUK__DIGITCHAR(dig); - - if (x == 0) { - break; - } - } - len = (duk_size_t) ((buf + 32) - p); - - duk_memmove((void *) buf, (const void *) p, (size_t) len); - - return len; -} - -DUK_LOCAL void duk__dragon4_prepare(duk__numconv_stringify_ctx *nc_ctx) { - duk_small_int_t lowest_mantissa; - -#if 1 - /* Assume IEEE round-to-even, so that shorter encoding can be used - * when round-to-even would produce correct result. By removing - * this check (and having low_ok == high_ok == 0) the results would - * still be accurate but in some cases longer than necessary. - */ - if (duk__bi_is_even(&nc_ctx->f)) { - DUK_DDD(DUK_DDDPRINT("f is even")); - nc_ctx->low_ok = 1; - nc_ctx->high_ok = 1; - } else { - DUK_DDD(DUK_DDDPRINT("f is odd")); - nc_ctx->low_ok = 0; - nc_ctx->high_ok = 0; - } -#else - /* Note: not honoring round-to-even should work but now generates incorrect - * results. For instance, 1e23 serializes to "a000...", i.e. the first digit - * equals the radix (10). Scaling stops one step too early in this case. - * Don't know why this is the case, but since this code path is unused, it - * doesn't matter. - */ - nc_ctx->low_ok = 0; - nc_ctx->high_ok = 0; -#endif - - /* For string-to-number, pretend we never have the lowest mantissa as there - * is no natural "precision" for inputs. Having lowest_mantissa == 0, we'll - * fall into the base cases for both e >= 0 and e < 0. - */ - if (nc_ctx->is_s2n) { - lowest_mantissa = 0; - } else { - lowest_mantissa = duk__bi_is_2to52(&nc_ctx->f); - } - - nc_ctx->unequal_gaps = 0; - if (nc_ctx->e >= 0) { - /* exponent non-negative (and thus not minimum exponent) */ - - if (lowest_mantissa) { - /* (>= e 0) AND (= f (expt b (- p 1))) - * - * be <- (expt b e) == b^e - * be1 <- (* be b) == (expt b (+ e 1)) == b^(e+1) - * r <- (* f be1 2) == 2 * f * b^(e+1) [if b==2 -> f * b^(e+2)] - * s <- (* b 2) [if b==2 -> 4] - * m+ <- be1 == b^(e+1) - * m- <- be == b^e - * k <- 0 - * B <- B - * low_ok <- round - * high_ok <- round - */ - - DUK_DDD(DUK_DDDPRINT("non-negative exponent (not smallest exponent); " - "lowest mantissa value for this exponent -> " - "unequal gaps")); - - duk__bi_exp_small(&nc_ctx->mm, nc_ctx->b, nc_ctx->e, &nc_ctx->t1, &nc_ctx->t2); /* mm <- b^e */ - duk__bi_mul_small(&nc_ctx->mp, &nc_ctx->mm, (duk_uint32_t) nc_ctx->b); /* mp <- b^(e+1) */ - duk__bi_mul_small(&nc_ctx->t1, &nc_ctx->f, 2); - duk__bi_mul(&nc_ctx->r, &nc_ctx->t1, &nc_ctx->mp); /* r <- (2 * f) * b^(e+1) */ - duk__bi_set_small(&nc_ctx->s, (duk_uint32_t) (nc_ctx->b * 2)); /* s <- 2 * b */ - nc_ctx->unequal_gaps = 1; - } else { - /* (>= e 0) AND (not (= f (expt b (- p 1)))) - * - * be <- (expt b e) == b^e - * r <- (* f be 2) == 2 * f * b^e [if b==2 -> f * b^(e+1)] - * s <- 2 - * m+ <- be == b^e - * m- <- be == b^e - * k <- 0 - * B <- B - * low_ok <- round - * high_ok <- round - */ - - DUK_DDD(DUK_DDDPRINT("non-negative exponent (not smallest exponent); " - "not lowest mantissa for this exponent -> " - "equal gaps")); - - duk__bi_exp_small(&nc_ctx->mm, nc_ctx->b, nc_ctx->e, &nc_ctx->t1, &nc_ctx->t2); /* mm <- b^e */ - duk__bi_copy(&nc_ctx->mp, &nc_ctx->mm); /* mp <- b^e */ - duk__bi_mul_small(&nc_ctx->t1, &nc_ctx->f, 2); - duk__bi_mul(&nc_ctx->r, &nc_ctx->t1, &nc_ctx->mp); /* r <- (2 * f) * b^e */ - duk__bi_set_small(&nc_ctx->s, 2); /* s <- 2 */ - } - } else { - /* When doing string-to-number, lowest_mantissa is always 0 so - * the exponent check, while incorrect, won't matter. - */ - if (nc_ctx->e > DUK__IEEE_DOUBLE_EXP_MIN /*not minimum exponent*/ && - lowest_mantissa /* lowest mantissa for this exponent*/) { - /* r <- (* f b 2) [if b==2 -> (* f 4)] - * s <- (* (expt b (- 1 e)) 2) == b^(1-e) * 2 [if b==2 -> b^(2-e)] - * m+ <- b == 2 - * m- <- 1 - * k <- 0 - * B <- B - * low_ok <- round - * high_ok <- round - */ - - DUK_DDD(DUK_DDDPRINT("negative exponent; not minimum exponent and " - "lowest mantissa for this exponent -> " - "unequal gaps")); - - duk__bi_mul_small(&nc_ctx->r, &nc_ctx->f, (duk_uint32_t) (nc_ctx->b * 2)); /* r <- (2 * b) * f */ - duk__bi_exp_small(&nc_ctx->t1, - nc_ctx->b, - 1 - nc_ctx->e, - &nc_ctx->s, - &nc_ctx->t2); /* NB: use 's' as temp on purpose */ - duk__bi_mul_small(&nc_ctx->s, &nc_ctx->t1, 2); /* s <- b^(1-e) * 2 */ - duk__bi_set_small(&nc_ctx->mp, 2); - duk__bi_set_small(&nc_ctx->mm, 1); - nc_ctx->unequal_gaps = 1; - } else { - /* r <- (* f 2) - * s <- (* (expt b (- e)) 2) == b^(-e) * 2 [if b==2 -> b^(1-e)] - * m+ <- 1 - * m- <- 1 - * k <- 0 - * B <- B - * low_ok <- round - * high_ok <- round - */ - - DUK_DDD(DUK_DDDPRINT("negative exponent; minimum exponent or not " - "lowest mantissa for this exponent -> " - "equal gaps")); - - duk__bi_mul_small(&nc_ctx->r, &nc_ctx->f, 2); /* r <- 2 * f */ - duk__bi_exp_small(&nc_ctx->t1, - nc_ctx->b, - -nc_ctx->e, - &nc_ctx->s, - &nc_ctx->t2); /* NB: use 's' as temp on purpose */ - duk__bi_mul_small(&nc_ctx->s, &nc_ctx->t1, 2); /* s <- b^(-e) * 2 */ - duk__bi_set_small(&nc_ctx->mp, 1); - duk__bi_set_small(&nc_ctx->mm, 1); - } - } -} - -DUK_LOCAL void duk__dragon4_scale(duk__numconv_stringify_ctx *nc_ctx) { - duk_small_int_t k = 0; - - /* This is essentially the 'scale' algorithm, with recursion removed. - * Note that 'k' is either correct immediately, or will move in one - * direction in the loop. There's no need to do the low/high checks - * on every round (like the Scheme algorithm does). - * - * The scheme algorithm finds 'k' and updates 's' simultaneously, - * while the logical algorithm finds 'k' with 's' having its initial - * value, after which 's' is updated separately (see the Burger-Dybvig - * paper, Section 3.1, steps 2 and 3). - * - * The case where m+ == m- (almost always) is optimized for, because - * it reduces the bigint operations considerably and almost always - * applies. The scale loop only needs to work with m+, so this works. - */ - - /* XXX: this algorithm could be optimized quite a lot by using e.g. - * a logarithm based estimator for 'k' and performing B^n multiplication - * using a lookup table or using some bit-representation based exp - * algorithm. Currently we just loop, with significant performance - * impact for very large and very small numbers. - */ - - DUK_DDD( - DUK_DDDPRINT("scale: B=%ld, low_ok=%ld, high_ok=%ld", (long) nc_ctx->B, (long) nc_ctx->low_ok, (long) nc_ctx->high_ok)); - DUK__BI_PRINT("r(init)", &nc_ctx->r); - DUK__BI_PRINT("s(init)", &nc_ctx->s); - DUK__BI_PRINT("mp(init)", &nc_ctx->mp); - DUK__BI_PRINT("mm(init)", &nc_ctx->mm); - - for (;;) { - DUK_DDD(DUK_DDDPRINT("scale loop (inc k), k=%ld", (long) k)); - DUK__BI_PRINT("r", &nc_ctx->r); - DUK__BI_PRINT("s", &nc_ctx->s); - DUK__BI_PRINT("m+", &nc_ctx->mp); - DUK__BI_PRINT("m-", &nc_ctx->mm); - - duk__bi_add(&nc_ctx->t1, &nc_ctx->r, &nc_ctx->mp); /* t1 = (+ r m+) */ - if (duk__bi_compare(&nc_ctx->t1, &nc_ctx->s) >= (nc_ctx->high_ok ? 0 : 1)) { - DUK_DDD(DUK_DDDPRINT("k is too low")); - /* r <- r - * s <- (* s B) - * m+ <- m+ - * m- <- m- - * k <- (+ k 1) - */ - - duk__bi_mul_small_copy(&nc_ctx->s, (duk_uint32_t) nc_ctx->B, &nc_ctx->t1); - k++; - } else { - break; - } - } - - /* k > 0 -> k was too low, and cannot be too high */ - if (k > 0) { - goto skip_dec_k; - } - - for (;;) { - DUK_DDD(DUK_DDDPRINT("scale loop (dec k), k=%ld", (long) k)); - DUK__BI_PRINT("r", &nc_ctx->r); - DUK__BI_PRINT("s", &nc_ctx->s); - DUK__BI_PRINT("m+", &nc_ctx->mp); - DUK__BI_PRINT("m-", &nc_ctx->mm); - - duk__bi_add(&nc_ctx->t1, &nc_ctx->r, &nc_ctx->mp); /* t1 = (+ r m+) */ - duk__bi_mul_small(&nc_ctx->t2, &nc_ctx->t1, (duk_uint32_t) nc_ctx->B); /* t2 = (* (+ r m+) B) */ - if (duk__bi_compare(&nc_ctx->t2, &nc_ctx->s) <= (nc_ctx->high_ok ? -1 : 0)) { - DUK_DDD(DUK_DDDPRINT("k is too high")); - /* r <- (* r B) - * s <- s - * m+ <- (* m+ B) - * m- <- (* m- B) - * k <- (- k 1) - */ - duk__bi_mul_small_copy(&nc_ctx->r, (duk_uint32_t) nc_ctx->B, &nc_ctx->t1); - duk__bi_mul_small_copy(&nc_ctx->mp, (duk_uint32_t) nc_ctx->B, &nc_ctx->t1); - if (nc_ctx->unequal_gaps) { - DUK_DDD(DUK_DDDPRINT("m+ != m- -> need to update m- too")); - duk__bi_mul_small_copy(&nc_ctx->mm, (duk_uint32_t) nc_ctx->B, &nc_ctx->t1); - } - k--; - } else { - break; - } - } - -skip_dec_k: - - if (!nc_ctx->unequal_gaps) { - DUK_DDD(DUK_DDDPRINT("equal gaps, copy m- from m+")); - duk__bi_copy(&nc_ctx->mm, &nc_ctx->mp); /* mm <- mp */ - } - nc_ctx->k = k; - - DUK_DDD(DUK_DDDPRINT("final k: %ld", (long) k)); - DUK__BI_PRINT("r(final)", &nc_ctx->r); - DUK__BI_PRINT("s(final)", &nc_ctx->s); - DUK__BI_PRINT("mp(final)", &nc_ctx->mp); - DUK__BI_PRINT("mm(final)", &nc_ctx->mm); -} - -DUK_LOCAL void duk__dragon4_generate(duk__numconv_stringify_ctx *nc_ctx) { - duk_small_int_t tc1, tc2; /* terminating conditions */ - duk_small_int_t d; /* current digit */ - duk_small_int_t count = 0; /* digit count */ - - /* - * Digit generation loop. - * - * Different termination conditions: - * - * 1. Free format output. Terminate when shortest accurate - * representation found. - * - * 2. Fixed format output, with specific number of digits. - * Ignore termination conditions, terminate when digits - * generated. Caller requests an extra digit and rounds. - * - * 3. Fixed format output, with a specific absolute cut-off - * position (e.g. 10 digits after decimal point). Note - * that we always generate at least one digit, even if - * the digit is below the cut-off point already. - */ - - for (;;) { - DUK_DDD(DUK_DDDPRINT("generate loop, count=%ld, k=%ld, B=%ld, low_ok=%ld, high_ok=%ld", - (long) count, - (long) nc_ctx->k, - (long) nc_ctx->B, - (long) nc_ctx->low_ok, - (long) nc_ctx->high_ok)); - DUK__BI_PRINT("r", &nc_ctx->r); - DUK__BI_PRINT("s", &nc_ctx->s); - DUK__BI_PRINT("m+", &nc_ctx->mp); - DUK__BI_PRINT("m-", &nc_ctx->mm); - - /* (quotient-remainder (* r B) s) using a dummy subtraction loop */ - duk__bi_mul_small(&nc_ctx->t1, &nc_ctx->r, (duk_uint32_t) nc_ctx->B); /* t1 <- (* r B) */ - d = 0; - for (;;) { - if (duk__bi_compare(&nc_ctx->t1, &nc_ctx->s) < 0) { - break; - } - duk__bi_sub_copy(&nc_ctx->t1, &nc_ctx->s, &nc_ctx->t2); /* t1 <- t1 - s */ - d++; - } - duk__bi_copy(&nc_ctx->r, &nc_ctx->t1); /* r <- (remainder (* r B) s) */ - /* d <- (quotient (* r B) s) (in range 0...B-1) */ - DUK_DDD(DUK_DDDPRINT("-> d(quot)=%ld", (long) d)); - DUK__BI_PRINT("r(rem)", &nc_ctx->r); - - duk__bi_mul_small_copy(&nc_ctx->mp, (duk_uint32_t) nc_ctx->B, &nc_ctx->t2); /* m+ <- (* m+ B) */ - duk__bi_mul_small_copy(&nc_ctx->mm, (duk_uint32_t) nc_ctx->B, &nc_ctx->t2); /* m- <- (* m- B) */ - DUK__BI_PRINT("mp(upd)", &nc_ctx->mp); - DUK__BI_PRINT("mm(upd)", &nc_ctx->mm); - - /* Terminating conditions. For fixed width output, we just ignore the - * terminating conditions (and pretend that tc1 == tc2 == false). The - * the current shortcut for fixed-format output is to generate a few - * extra digits and use rounding (with carry) to finish the output. - */ - - if (nc_ctx->is_fixed == 0) { - /* free-form */ - tc1 = (duk__bi_compare(&nc_ctx->r, &nc_ctx->mm) <= (nc_ctx->low_ok ? 0 : -1)); - - duk__bi_add(&nc_ctx->t1, &nc_ctx->r, &nc_ctx->mp); /* t1 <- (+ r m+) */ - tc2 = (duk__bi_compare(&nc_ctx->t1, &nc_ctx->s) >= (nc_ctx->high_ok ? 0 : 1)); - - DUK_DDD(DUK_DDDPRINT("tc1=%ld, tc2=%ld", (long) tc1, (long) tc2)); - } else { - /* fixed-format */ - tc1 = 0; - tc2 = 0; - } - - /* Count is incremented before DUK__DRAGON4_OUTPUT_PREINC() call - * on purpose, which is taken into account by the macro. - */ - count++; - - if (tc1) { - if (tc2) { - /* tc1 = true, tc2 = true */ - duk__bi_mul_small(&nc_ctx->t1, &nc_ctx->r, 2); - if (duk__bi_compare(&nc_ctx->t1, &nc_ctx->s) < 0) { /* (< (* r 2) s) */ - DUK_DDD(DUK_DDDPRINT("tc1=true, tc2=true, 2r > s: output d --> %ld (k=%ld)", - (long) d, - (long) nc_ctx->k)); - DUK__DRAGON4_OUTPUT_PREINC(nc_ctx, count, d); - } else { - DUK_DDD(DUK_DDDPRINT("tc1=true, tc2=true, 2r <= s: output d+1 --> %ld (k=%ld)", - (long) (d + 1), - (long) nc_ctx->k)); - DUK__DRAGON4_OUTPUT_PREINC(nc_ctx, count, d + 1); - } - break; - } else { - /* tc1 = true, tc2 = false */ - DUK_DDD(DUK_DDDPRINT("tc1=true, tc2=false: output d --> %ld (k=%ld)", (long) d, (long) nc_ctx->k)); - DUK__DRAGON4_OUTPUT_PREINC(nc_ctx, count, d); - break; - } - } else { - if (tc2) { - /* tc1 = false, tc2 = true */ - DUK_DDD(DUK_DDDPRINT("tc1=false, tc2=true: output d+1 --> %ld (k=%ld)", - (long) (d + 1), - (long) nc_ctx->k)); - DUK__DRAGON4_OUTPUT_PREINC(nc_ctx, count, d + 1); - break; - } else { - /* tc1 = false, tc2 = false */ - DUK_DDD(DUK_DDDPRINT("tc1=false, tc2=false: output d --> %ld (k=%ld)", (long) d, (long) nc_ctx->k)); - DUK__DRAGON4_OUTPUT_PREINC(nc_ctx, count, d); - - /* r <- r (updated above: r <- (remainder (* r B) s) - * s <- s - * m+ <- m+ (updated above: m+ <- (* m+ B) - * m- <- m- (updated above: m- <- (* m- B) - * B, low_ok, high_ok are fixed - */ - - /* fall through and continue for-loop */ - } - } - - /* fixed-format termination conditions */ - if (nc_ctx->is_fixed) { - if (nc_ctx->abs_pos) { - int pos = nc_ctx->k - count + 1; /* count is already incremented, take into account */ - DUK_DDD(DUK_DDDPRINT("fixed format, absolute: abs pos=%ld, k=%ld, count=%ld, req=%ld", - (long) pos, - (long) nc_ctx->k, - (long) count, - (long) nc_ctx->req_digits)); - if (pos <= nc_ctx->req_digits) { - DUK_DDD(DUK_DDDPRINT("digit position reached req_digits, end generate loop")); - break; - } - } else { - DUK_DDD(DUK_DDDPRINT("fixed format, relative: k=%ld, count=%ld, req=%ld", - (long) nc_ctx->k, - (long) count, - (long) nc_ctx->req_digits)); - if (count >= nc_ctx->req_digits) { - DUK_DDD(DUK_DDDPRINT("digit count reached req_digits, end generate loop")); - break; - } - } - } - } /* for */ - - nc_ctx->count = count; - - DUK_DDD(DUK_DDDPRINT("generate finished")); - -#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2) - { - duk_uint8_t buf[2048]; - duk_small_int_t i, t; - duk_memzero(buf, sizeof(buf)); - for (i = 0; i < nc_ctx->count; i++) { - t = nc_ctx->digits[i]; - if (t < 0 || t > 36) { - buf[i] = (duk_uint8_t) '?'; - } else { - buf[i] = (duk_uint8_t) DUK__DIGITCHAR(t); - } - } - DUK_DDD(DUK_DDDPRINT("-> generated digits; k=%ld, digits='%s'", (long) nc_ctx->k, (const char *) buf)); - } -#endif -} - -/* Round up digits to a given position. If position is out-of-bounds, - * does nothing. If carry propagates over the first digit, a '1' is - * prepended to digits and 'k' will be updated. Return value indicates - * whether carry propagated over the first digit. - * - * Note that nc_ctx->count is NOT updated based on the rounding position - * (it is updated only if carry overflows over the first digit and an - * extra digit is prepended). - */ -DUK_LOCAL duk_small_int_t duk__dragon4_fixed_format_round(duk__numconv_stringify_ctx *nc_ctx, duk_small_int_t round_idx) { - duk_small_int_t t; - duk_uint8_t *p; - duk_uint8_t roundup_limit; - duk_small_int_t ret = 0; - - /* - * round_idx points to the digit which is considered for rounding; the - * digit to its left is the final digit of the rounded value. If round_idx - * is zero, rounding will be performed; the result will either be an empty - * rounded value or if carry happens a '1' digit is generated. - */ - - if (round_idx >= nc_ctx->count) { - DUK_DDD(DUK_DDDPRINT("round_idx out of bounds (%ld >= %ld (count)) -> no rounding", - (long) round_idx, - (long) nc_ctx->count)); - return 0; - } else if (round_idx < 0) { - DUK_DDD(DUK_DDDPRINT("round_idx out of bounds (%ld < 0) -> no rounding", (long) round_idx)); - return 0; - } - - /* - * Round-up limit. - * - * For even values, divides evenly, e.g. 10 -> roundup_limit=5. - * - * For odd values, rounds up, e.g. 3 -> roundup_limit=2. - * If radix is 3, 0/3 -> down, 1/3 -> down, 2/3 -> up. - */ - roundup_limit = (duk_uint8_t) ((nc_ctx->B + 1) / 2); - - p = &nc_ctx->digits[round_idx]; - if (*p >= roundup_limit) { - DUK_DDD(DUK_DDDPRINT("fixed-format rounding carry required")); - /* carry */ - for (;;) { - *p = 0; - if (p == &nc_ctx->digits[0]) { - DUK_DDD(DUK_DDDPRINT("carry propagated to first digit -> special case handling")); - duk_memmove((void *) (&nc_ctx->digits[1]), - (const void *) (&nc_ctx->digits[0]), - (size_t) (sizeof(char) * (size_t) nc_ctx->count)); - nc_ctx->digits[0] = 1; /* don't increase 'count' */ - nc_ctx->k++; /* position of highest digit changed */ - nc_ctx->count++; /* number of digits changed */ - ret = 1; - break; - } - - DUK_DDD(DUK_DDDPRINT("fixed-format rounding carry: B=%ld, roundup_limit=%ld, p=%p, digits=%p", - (long) nc_ctx->B, - (long) roundup_limit, - (void *) p, - (void *) nc_ctx->digits)); - p--; - t = *p; - DUK_DDD(DUK_DDDPRINT("digit before carry: %ld", (long) t)); - if (++t < nc_ctx->B) { - DUK_DDD(DUK_DDDPRINT("rounding carry terminated")); - *p = (duk_uint8_t) t; - break; - } - - DUK_DDD(DUK_DDDPRINT("wraps, carry to next digit")); - } - } - - return ret; -} - -#define DUK__NO_EXP (65536) /* arbitrary marker, outside valid exp range */ - -DUK_LOCAL void duk__dragon4_convert_and_push(duk__numconv_stringify_ctx *nc_ctx, - duk_hthread *thr, - duk_small_int_t radix, - duk_small_int_t digits, - duk_small_uint_t flags, - duk_small_int_t neg) { - duk_small_int_t k; - duk_small_int_t pos, pos_end; - duk_small_int_t expt; - duk_small_int_t dig; - duk_uint8_t *q; - duk_uint8_t *buf; - - /* - * The string conversion here incorporates all the necessary ECMAScript - * semantics without attempting to be generic. nc_ctx->digits contains - * nc_ctx->count digits (>= 1), with the topmost digit's 'position' - * indicated by nc_ctx->k as follows: - * - * digits="123" count=3 k=0 --> 0.123 - * digits="123" count=3 k=1 --> 1.23 - * digits="123" count=3 k=5 --> 12300 - * digits="123" count=3 k=-1 --> 0.0123 - * - * Note that the identifier names used for format selection are different - * in Burger-Dybvig paper and ECMAScript specification (quite confusingly - * so, because e.g. 'k' has a totally different meaning in each). See - * documentation for discussion. - * - * ECMAScript doesn't specify any specific behavior for format selection - * (e.g. when to use exponent notation) for non-base-10 numbers. - * - * The bigint space in the context is reused for string output, as there - * is more than enough space for that (>1kB at the moment), and we avoid - * allocating even more stack. - */ - - DUK_ASSERT(DUK__NUMCONV_CTX_BIGINTS_SIZE >= DUK__MAX_FORMATTED_LENGTH); - DUK_ASSERT(nc_ctx->count >= 1); - - k = nc_ctx->k; - buf = (duk_uint8_t *) &nc_ctx->f; /* XXX: union would be more correct */ - q = buf; - - /* Exponent handling: if exponent format is used, record exponent value and - * fake k such that one leading digit is generated (e.g. digits=123 -> "1.23"). - * - * toFixed() prevents exponent use; otherwise apply a set of criteria to - * match the other API calls (toString(), toPrecision, etc). - */ - - expt = DUK__NO_EXP; - if (!nc_ctx->abs_pos /* toFixed() */) { - if ((flags & DUK_N2S_FLAG_FORCE_EXP) || /* exponential notation forced */ - ((flags & DUK_N2S_FLAG_NO_ZERO_PAD) && /* fixed precision and zero padding would be required */ - (k - digits >= 1)) || /* (e.g. k=3, digits=2 -> "12X") */ - ((k > 21 || k <= -6) && (radix == 10))) { /* toString() conditions */ - DUK_DDD(DUK_DDDPRINT("use exponential notation: k=%ld -> expt=%ld", (long) k, (long) (k - 1))); - expt = k - 1; /* e.g. 12.3 -> digits="123" k=2 -> 1.23e1 */ - k = 1; /* generate mantissa with a single leading whole number digit */ - } - } - - if (neg) { - *q++ = '-'; - } - - /* Start position (inclusive) and end position (exclusive) */ - pos = (k >= 1 ? k : 1); - if (nc_ctx->is_fixed) { - if (nc_ctx->abs_pos) { - /* toFixed() */ - pos_end = -digits; - } else { - pos_end = k - digits; - } - } else { - pos_end = k - nc_ctx->count; - } - if (pos_end > 0) { - pos_end = 0; - } - - DUK_DDD(DUK_DDDPRINT("expt=%ld, k=%ld, count=%ld, pos=%ld, pos_end=%ld, is_fixed=%ld, " - "digits=%ld, abs_pos=%ld", - (long) expt, - (long) k, - (long) nc_ctx->count, - (long) pos, - (long) pos_end, - (long) nc_ctx->is_fixed, - (long) digits, - (long) nc_ctx->abs_pos)); - - /* Digit generation */ - while (pos > pos_end) { - DUK_DDD(DUK_DDDPRINT("digit generation: pos=%ld, pos_end=%ld", (long) pos, (long) pos_end)); - if (pos == 0) { - *q++ = (duk_uint8_t) '.'; - } - if (pos > k) { - *q++ = (duk_uint8_t) '0'; - } else if (pos <= k - nc_ctx->count) { - *q++ = (duk_uint8_t) '0'; - } else { - dig = nc_ctx->digits[k - pos]; - DUK_ASSERT(dig >= 0 && dig < nc_ctx->B); - *q++ = (duk_uint8_t) DUK__DIGITCHAR(dig); - } - - pos--; - } - DUK_ASSERT(pos <= 1); - - /* Exponent */ - if (expt != DUK__NO_EXP) { - /* - * Exponent notation for non-base-10 numbers isn't specified in ECMAScript - * specification, as it never explicitly turns up: non-decimal numbers can - * only be formatted with Number.prototype.toString([radix]) and for that, - * behavior is not explicitly specified. - * - * Logical choices include formatting the exponent as decimal (e.g. binary - * 100000 as 1e+5) or in current radix (e.g. binary 100000 as 1e+101). - * The Dragon4 algorithm (in the original paper) prints the exponent value - * in the target radix B. However, for radix values 15 and above, the - * exponent separator 'e' is no longer easily parseable. Consider, for - * instance, the number "1.faecee+1c". - */ - - duk_size_t len; - char expt_sign; - - *q++ = 'e'; - if (expt >= 0) { - expt_sign = '+'; - } else { - expt_sign = '-'; - expt = -expt; - } - *q++ = (duk_uint8_t) expt_sign; - len = duk__dragon4_format_uint32(q, (duk_uint32_t) expt, radix); - q += len; - } - - duk_push_lstring(thr, (const char *) buf, (size_t) (q - buf)); -} - -/* - * Conversion helpers - */ - -DUK_LOCAL void duk__dragon4_double_to_ctx(duk__numconv_stringify_ctx *nc_ctx, duk_double_t x) { - duk_double_union u; - duk_uint32_t tmp; - duk_small_int_t expt; - - /* - * seeeeeee eeeeffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff - * A B C D E F G H - * - * s sign bit - * eee... exponent field - * fff... fraction - * - * ieee value = 1.ffff... * 2^(e - 1023) (normal) - * = 0.ffff... * 2^(-1022) (denormal) - * - * algorithm v = f * b^e - */ - - DUK_DBLUNION_SET_DOUBLE(&u, x); - - nc_ctx->f.n = 2; - - tmp = DUK_DBLUNION_GET_LOW32(&u); - nc_ctx->f.v[0] = tmp; - tmp = DUK_DBLUNION_GET_HIGH32(&u); - nc_ctx->f.v[1] = tmp & 0x000fffffUL; - expt = (duk_small_int_t) ((tmp >> 20) & 0x07ffUL); - - if (expt == 0) { - /* denormal */ - expt = DUK__IEEE_DOUBLE_EXP_MIN - 52; - duk__bi_normalize(&nc_ctx->f); - } else { - /* normal: implicit leading 1-bit */ - nc_ctx->f.v[1] |= 0x00100000UL; - expt = expt - DUK__IEEE_DOUBLE_EXP_BIAS - 52; - DUK_ASSERT(duk__bi_is_valid(&nc_ctx->f)); /* true, because v[1] has at least one bit set */ - } - - DUK_ASSERT(duk__bi_is_valid(&nc_ctx->f)); - - nc_ctx->e = expt; -} - -DUK_LOCAL void duk__dragon4_ctx_to_double(duk__numconv_stringify_ctx *nc_ctx, duk_double_t *x) { - duk_double_union u; - duk_small_int_t expt; - duk_small_int_t i; - duk_small_int_t bitstart; - duk_small_int_t bitround; - duk_small_int_t bitidx; - duk_small_int_t skip_round; - duk_uint32_t t, v; - - DUK_ASSERT(nc_ctx->count == 53 + 1); - - /* Sometimes this assert is not true right now; it will be true after - * rounding. See: test-bug-numconv-mantissa-assert.js. - */ - DUK_ASSERT_DISABLE(nc_ctx->digits[0] == 1); /* zero handled by caller */ - - /* Should not be required because the code below always sets both high - * and low parts, but at least gcc-4.4.5 fails to deduce this correctly - * (perhaps because the low part is set (seemingly) conditionally in a - * loop), so this is here to avoid the bogus warning. - */ - duk_memzero((void *) &u, sizeof(u)); - - /* - * Figure out how generated digits match up with the mantissa, - * and then perform rounding. If mantissa overflows, need to - * recompute the exponent (it is bumped and may overflow to - * infinity). - * - * For normal numbers the leading '1' is hidden and ignored, - * and the last bit is used for rounding: - * - * rounding pt - * <--------52------->| - * 1 x x x x ... x x x x|y ==> x x x x ... x x x x - * - * For denormals, the leading '1' is included in the number, - * and the rounding point is different: - * - * rounding pt - * <--52 or less--->| - * 1 x x x x ... x x|x x y ==> 0 0 ... 1 x x ... x x - * - * The largest denormals will have a mantissa beginning with - * a '1' (the explicit leading bit); smaller denormals will - * have leading zero bits. - * - * If the exponent would become too high, the result becomes - * Infinity. If the exponent is so small that the entire - * mantissa becomes zero, the result becomes zero. - * - * Note: the Dragon4 'k' is off-by-one with respect to the IEEE - * exponent. For instance, k==0 indicates that the leading '1' - * digit is at the first binary fraction position (0.1xxx...); - * the corresponding IEEE exponent would be -1. - */ - - skip_round = 0; - -recheck_exp: - - expt = nc_ctx->k - 1; /* IEEE exp without bias */ - if (expt > 1023) { - /* Infinity */ - bitstart = -255; /* needed for inf: causes mantissa to become zero, - * and rounding to be skipped. - */ - expt = 2047; - } else if (expt >= -1022) { - /* normal */ - bitstart = 1; /* skip leading digit */ - expt += DUK__IEEE_DOUBLE_EXP_BIAS; - DUK_ASSERT(expt >= 1 && expt <= 2046); - } else { - /* denormal or zero */ - bitstart = 1023 + expt; /* expt==-1023 -> bitstart=0 (leading 1); - * expt==-1024 -> bitstart=-1 (one left of leading 1), etc - */ - expt = 0; - } - bitround = bitstart + 52; - - DUK_DDD(DUK_DDDPRINT("ieee expt=%ld, bitstart=%ld, bitround=%ld", (long) expt, (long) bitstart, (long) bitround)); - - if (!skip_round) { - if (duk__dragon4_fixed_format_round(nc_ctx, bitround)) { - /* Corner case: see test-numconv-parse-mant-carry.js. We could - * just bump the exponent and update bitstart, but it's more robust - * to recompute (but avoid rounding twice). - */ - DUK_DDD(DUK_DDDPRINT("rounding caused exponent to be bumped, recheck exponent")); - skip_round = 1; - goto recheck_exp; - } - } - - /* - * Create mantissa - */ - - t = 0; - for (i = 0; i < 52; i++) { - bitidx = bitstart + 52 - 1 - i; - if (bitidx >= nc_ctx->count) { - v = 0; - } else if (bitidx < 0) { - v = 0; - } else { - v = nc_ctx->digits[bitidx]; - } - DUK_ASSERT(v == 0 || v == 1); - t += v << (i % 32); - if (i == 31) { - /* low 32 bits is complete */ - DUK_DBLUNION_SET_LOW32(&u, t); - t = 0; - } - } - /* t has high mantissa */ - - DUK_DDD(DUK_DDDPRINT("mantissa is complete: %08lx %08lx", (unsigned long) t, (unsigned long) DUK_DBLUNION_GET_LOW32(&u))); - - DUK_ASSERT(expt >= 0 && expt <= 0x7ffL); - t += ((duk_uint32_t) expt) << 20; -#if 0 /* caller handles sign change */ - if (negative) { - t |= 0x80000000U; - } -#endif - DUK_DBLUNION_SET_HIGH32(&u, t); - - DUK_DDD(DUK_DDDPRINT("number is complete: %08lx %08lx", - (unsigned long) DUK_DBLUNION_GET_HIGH32(&u), - (unsigned long) DUK_DBLUNION_GET_LOW32(&u))); - - *x = DUK_DBLUNION_GET_DOUBLE(&u); -} - -/* - * Exposed number-to-string API - * - * Input: [ number ] - * Output: [ string ] - */ - -DUK_LOCAL DUK_NOINLINE void duk__numconv_stringify_raw(duk_hthread *thr, - duk_small_int_t radix, - duk_small_int_t digits, - duk_small_uint_t flags) { - duk_double_t x; - duk_small_int_t c; - duk_small_int_t neg; - duk_uint32_t uval; - duk__numconv_stringify_ctx nc_ctx_alloc; /* large context; around 2kB now */ - duk__numconv_stringify_ctx *nc_ctx = &nc_ctx_alloc; - - x = (duk_double_t) duk_require_number(thr, -1); - duk_pop(thr); - - /* - * Handle special cases (NaN, infinity, zero). - */ - - c = (duk_small_int_t) DUK_FPCLASSIFY(x); - if (DUK_SIGNBIT((double) x)) { - x = -x; - neg = 1; - } else { - neg = 0; - } - - /* NaN sign bit is platform specific with unpacked, un-normalized NaNs */ - DUK_ASSERT(c == DUK_FP_NAN || DUK_SIGNBIT((double) x) == 0); - - if (c == DUK_FP_NAN) { - duk_push_hstring_stridx(thr, DUK_STRIDX_NAN); - return; - } else if (c == DUK_FP_INFINITE) { - if (neg) { - /* -Infinity */ - duk_push_hstring_stridx(thr, DUK_STRIDX_MINUS_INFINITY); - } else { - /* Infinity */ - duk_push_hstring_stridx(thr, DUK_STRIDX_INFINITY); - } - return; - } else if (c == DUK_FP_ZERO) { - /* We can't shortcut zero here if it goes through special formatting - * (such as forced exponential notation). - */ - ; - } - - /* - * Handle integers in 32-bit range (that is, [-(2**32-1),2**32-1]) - * specially, as they're very likely for embedded programs. This - * is now done for all radix values. We must be careful not to use - * the fast path when special formatting (e.g. forced exponential) - * is in force. - * - * XXX: could save space by supporting radix 10 only and using - * sprintf "%lu" for the fast path and for exponent formatting. - */ - - uval = duk_double_to_uint32_t(x); - if (duk_double_equals((double) uval, x) && /* integer number in range */ - flags == 0) { /* no special formatting */ - /* use bigint area as a temp */ - duk_uint8_t *buf = (duk_uint8_t *) (&nc_ctx->f); - duk_uint8_t *p = buf; - - DUK_ASSERT(DUK__NUMCONV_CTX_BIGINTS_SIZE >= 32 + 1); /* max size: radix=2 + sign */ - if (neg && uval != 0) { - /* no negative sign for zero */ - *p++ = (duk_uint8_t) '-'; - } - p += duk__dragon4_format_uint32(p, uval, radix); - duk_push_lstring(thr, (const char *) buf, (duk_size_t) (p - buf)); - return; - } - - /* - * Dragon4 setup. - * - * Convert double from IEEE representation for conversion; - * normal finite values have an implicit leading 1-bit. The - * slow path algorithm doesn't handle zero, so zero is special - * cased here but still creates a valid nc_ctx, and goes - * through normal formatting in case special formatting has - * been requested (e.g. forced exponential format: 0 -> "0e+0"). - */ - - /* Would be nice to bulk clear the allocation, but the context - * is 1-2 kilobytes and nothing should rely on it being zeroed. - */ -#if 0 - duk_memzero((void *) nc_ctx, sizeof(*nc_ctx)); /* slow init, do only for slow path cases */ -#endif - - nc_ctx->is_s2n = 0; - nc_ctx->b = 2; - nc_ctx->B = radix; - nc_ctx->abs_pos = 0; - if (flags & DUK_N2S_FLAG_FIXED_FORMAT) { - nc_ctx->is_fixed = 1; - if (flags & DUK_N2S_FLAG_FRACTION_DIGITS) { - /* absolute req_digits; e.g. digits = 1 -> last digit is 0, - * but add an extra digit for rounding. - */ - nc_ctx->abs_pos = 1; - nc_ctx->req_digits = (-digits + 1) - 1; - } else { - nc_ctx->req_digits = digits + 1; - } - } else { - nc_ctx->is_fixed = 0; - nc_ctx->req_digits = 0; - } - - if (c == DUK_FP_ZERO) { - /* Zero special case: fake requested number of zero digits; ensure - * no sign bit is printed. Relative and absolute fixed format - * require separate handling. - */ - duk_small_int_t count; - if (nc_ctx->is_fixed) { - if (nc_ctx->abs_pos) { - count = digits + 2; /* lead zero + 'digits' fractions + 1 for rounding */ - } else { - count = digits + 1; /* + 1 for rounding */ - } - } else { - count = 1; - } - DUK_DDD(DUK_DDDPRINT("count=%ld", (long) count)); - DUK_ASSERT(count >= 1); - duk_memzero((void *) nc_ctx->digits, (size_t) count); - nc_ctx->count = count; - nc_ctx->k = 1; /* 0.000... */ - neg = 0; - goto zero_skip; - } - - duk__dragon4_double_to_ctx(nc_ctx, x); /* -> sets 'f' and 'e' */ - DUK__BI_PRINT("f", &nc_ctx->f); - DUK_DDD(DUK_DDDPRINT("e=%ld", (long) nc_ctx->e)); - - /* - * Dragon4 slow path digit generation. - */ - - duk__dragon4_prepare(nc_ctx); /* setup many variables in nc_ctx */ - - DUK_DDD(DUK_DDDPRINT("after prepare:")); - DUK__BI_PRINT("r", &nc_ctx->r); - DUK__BI_PRINT("s", &nc_ctx->s); - DUK__BI_PRINT("mp", &nc_ctx->mp); - DUK__BI_PRINT("mm", &nc_ctx->mm); - - duk__dragon4_scale(nc_ctx); - - DUK_DDD(DUK_DDDPRINT("after scale; k=%ld", (long) nc_ctx->k)); - DUK__BI_PRINT("r", &nc_ctx->r); - DUK__BI_PRINT("s", &nc_ctx->s); - DUK__BI_PRINT("mp", &nc_ctx->mp); - DUK__BI_PRINT("mm", &nc_ctx->mm); - - duk__dragon4_generate(nc_ctx); - - /* - * Convert and push final string. - */ - -zero_skip: - - if (flags & DUK_N2S_FLAG_FIXED_FORMAT) { - /* Perform fixed-format rounding. */ - duk_small_int_t roundpos; - if (flags & DUK_N2S_FLAG_FRACTION_DIGITS) { - /* 'roundpos' is relative to nc_ctx->k and increases to the right - * (opposite of how 'k' changes). - */ - roundpos = -digits; /* absolute position for digit considered for rounding */ - roundpos = nc_ctx->k - roundpos; - } else { - roundpos = digits; - } - DUK_DDD(DUK_DDDPRINT("rounding: k=%ld, count=%ld, digits=%ld, roundpos=%ld", - (long) nc_ctx->k, - (long) nc_ctx->count, - (long) digits, - (long) roundpos)); - (void) duk__dragon4_fixed_format_round(nc_ctx, roundpos); - - /* Note: 'count' is currently not adjusted by rounding (i.e. the - * digits are not "chopped off". That shouldn't matter because - * the digit position (absolute or relative) is passed on to the - * convert-and-push function. - */ - } - - duk__dragon4_convert_and_push(nc_ctx, thr, radix, digits, flags, neg); -} - -DUK_INTERNAL void duk_numconv_stringify(duk_hthread *thr, duk_small_int_t radix, duk_small_int_t digits, duk_small_uint_t flags) { - duk_native_stack_check(thr); - duk__numconv_stringify_raw(thr, radix, digits, flags); -} - -/* - * Exposed string-to-number API - * - * Input: [ string ] - * Output: [ number ] - * - * If number parsing fails, a NaN is pushed as the result. If number parsing - * fails due to an internal error, an InternalError is thrown. - */ - -DUK_LOCAL DUK_NOINLINE void duk__numconv_parse_raw(duk_hthread *thr, duk_small_int_t radix, duk_small_uint_t flags) { - duk__numconv_stringify_ctx nc_ctx_alloc; /* large context; around 2kB now */ - duk__numconv_stringify_ctx *nc_ctx = &nc_ctx_alloc; - duk_double_t res; - duk_hstring *h_str; - duk_int_t expt; - duk_bool_t expt_neg; - duk_small_int_t expt_adj; - duk_small_int_t neg; - duk_small_int_t dig; - duk_small_int_t dig_whole; - duk_small_int_t dig_lzero; - duk_small_int_t dig_frac; - duk_small_int_t dig_expt; - duk_small_int_t dig_prec; - const duk__exp_limits *explim; - const duk_uint8_t *p; - duk_small_int_t ch; - - DUK_DDD(DUK_DDDPRINT("parse number: %!T, radix=%ld, flags=0x%08lx", - (duk_tval *) duk_get_tval(thr, -1), - (long) radix, - (unsigned long) flags)); - - DUK_ASSERT(radix >= 2 && radix <= 36); - DUK_ASSERT(radix - 2 < (duk_small_int_t) sizeof(duk__str2num_digits_for_radix)); - - /* - * Preliminaries: trim, sign, Infinity check - * - * We rely on the interned string having a NUL terminator, which will - * cause a parse failure wherever it is encountered. As a result, we - * don't need separate pointer checks. - * - * There is no special parsing for 'NaN' in the specification although - * 'Infinity' (with an optional sign) is allowed in some contexts. - * Some contexts allow plus/minus sign, while others only allow the - * minus sign (like JSON.parse()). - * - * Automatic hex number detection (leading '0x' or '0X') and octal - * number detection (leading '0' followed by at least one octal digit) - * is done here too. - * - * Symbols are not explicitly rejected here (that's up to the caller). - * If a symbol were passed here, it should ultimately safely fail - * parsing due to a syntax error. - */ - - if (flags & DUK_S2N_FLAG_TRIM_WHITE) { - /* Leading / trailing whitespace is sometimes accepted and - * sometimes not. After white space trimming, all valid input - * characters are pure ASCII. - */ - duk_trim(thr, -1); - } - h_str = duk_require_hstring(thr, -1); - DUK_ASSERT(h_str != NULL); - p = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_str); - - neg = 0; - ch = *p; - if (ch == (duk_small_int_t) '+') { - if ((flags & DUK_S2N_FLAG_ALLOW_PLUS) == 0) { - DUK_DDD(DUK_DDDPRINT("parse failed: leading plus sign not allowed")); - goto parse_fail; - } - p++; - } else if (ch == (duk_small_int_t) '-') { - if ((flags & DUK_S2N_FLAG_ALLOW_MINUS) == 0) { - DUK_DDD(DUK_DDDPRINT("parse failed: leading minus sign not allowed")); - goto parse_fail; - } - p++; - neg = 1; - } - - if ((flags & DUK_S2N_FLAG_ALLOW_INF) && DUK_STRNCMP((const char *) p, "Infinity", 8) == 0) { - /* Don't check for Infinity unless the context allows it. - * 'Infinity' is a valid integer literal in e.g. base-36: - * - * parseInt('Infinity', 36) - * 1461559270678 - */ - - if ((flags & DUK_S2N_FLAG_ALLOW_GARBAGE) == 0 && p[8] != DUK_ASC_NUL) { - DUK_DDD(DUK_DDDPRINT("parse failed: trailing garbage after matching 'Infinity' not allowed")); - goto parse_fail; - } else { - res = DUK_DOUBLE_INFINITY; - goto negcheck_and_ret; - } - } - ch = *p; - if (ch == (duk_small_int_t) '0') { - duk_small_int_t detect_radix = 0; - ch = DUK_LOWERCASE_CHAR_ASCII(p[1]); /* 'x' or 'X' -> 'x' */ - if ((flags & DUK_S2N_FLAG_ALLOW_AUTO_HEX_INT) && ch == DUK_ASC_LC_X) { - DUK_DDD(DUK_DDDPRINT("detected 0x/0X hex prefix, changing radix and preventing fractions and exponent")); - detect_radix = 16; -#if 0 - } else if ((flags & DUK_S2N_FLAG_ALLOW_AUTO_LEGACY_OCT_INT) && - (ch >= (duk_small_int_t) '0' && ch <= (duk_small_int_t) '9')) { - DUK_DDD(DUK_DDDPRINT("detected 0n oct prefix, changing radix and preventing fractions and exponent")); - detect_radix = 8; - - /* NOTE: if this legacy octal case is added back, it has - * different flags and 'p' advance so this needs to be - * reworked. - */ - flags |= DUK_S2N_FLAG_ALLOW_EMPTY_AS_ZERO; /* interpret e.g. '09' as '0', not NaN */ - p += 1; -#endif - } else if ((flags & DUK_S2N_FLAG_ALLOW_AUTO_OCT_INT) && ch == DUK_ASC_LC_O) { - DUK_DDD(DUK_DDDPRINT("detected 0o oct prefix, changing radix and preventing fractions and exponent")); - detect_radix = 8; - } else if ((flags & DUK_S2N_FLAG_ALLOW_AUTO_BIN_INT) && ch == DUK_ASC_LC_B) { - DUK_DDD(DUK_DDDPRINT("detected 0b bin prefix, changing radix and preventing fractions and exponent")); - detect_radix = 2; - } - if (detect_radix > 0) { - radix = detect_radix; - /* Clear empty as zero flag: interpret e.g. '0x' and '0xg' as a NaN (= parse error) */ - flags &= ~(DUK_S2N_FLAG_ALLOW_EXP | DUK_S2N_FLAG_ALLOW_EMPTY_FRAC | DUK_S2N_FLAG_ALLOW_FRAC | - DUK_S2N_FLAG_ALLOW_NAKED_FRAC | DUK_S2N_FLAG_ALLOW_EMPTY_AS_ZERO); - flags |= DUK_S2N_FLAG_ALLOW_LEADING_ZERO; /* allow e.g. '0x0009' and '0b00010001' */ - p += 2; - } - } - - /* - * Scan number and setup for Dragon4. - * - * The fast path case is detected during setup: an integer which - * can be converted without rounding, no net exponent. The fast - * path could be implemented as a separate scan, but may not really - * be worth it: the multiplications for building 'f' are not - * expensive when 'f' is small. - * - * The significand ('f') must contain enough bits of (apparent) - * accuracy, so that Dragon4 will generate enough binary output digits. - * For decimal numbers, this means generating a 20-digit significand, - * which should yield enough practical accuracy to parse IEEE doubles. - * In fact, the ECMAScript specification explicitly allows an - * implementation to treat digits beyond 20 as zeroes (and even - * to round the 20th digit upwards). For non-decimal numbers, the - * appropriate number of digits has been precomputed for comparable - * accuracy. - * - * Digit counts: - * - * [ dig_lzero ] - * | - * .+-..---[ dig_prec ]----. - * | || | - * 0000123.456789012345678901234567890e+123456 - * | | | | | | - * `--+--' `------[ dig_frac ]-------' `-+--' - * | | - * [ dig_whole ] [ dig_expt ] - * - * dig_frac and dig_expt are -1 if not present - * dig_lzero is only computed for whole number part - * - * Parsing state - * - * Parsing whole part dig_frac < 0 AND dig_expt < 0 - * Parsing fraction part dig_frac >= 0 AND dig_expt < 0 - * Parsing exponent part dig_expt >= 0 (dig_frac may be < 0 or >= 0) - * - * Note: in case we hit an implementation limit (like exponent range), - * we should throw an error, NOT return NaN or Infinity. Even with - * very large exponent (or significand) values the final result may be - * finite, so NaN/Infinity would be incorrect. - */ - - duk__bi_set_small(&nc_ctx->f, 0); - dig_prec = 0; - dig_lzero = 0; - dig_whole = 0; - dig_frac = -1; - dig_expt = -1; - expt = 0; - expt_adj = 0; /* essentially tracks digit position of lowest 'f' digit */ - expt_neg = 0; - for (;;) { - ch = *p++; - - DUK_DDD(DUK_DDDPRINT("parse digits: p=%p, ch='%c' (%ld), expt=%ld, expt_adj=%ld, " - "dig_whole=%ld, dig_frac=%ld, dig_expt=%ld, dig_lzero=%ld, dig_prec=%ld", - (const void *) p, - (int) ((ch >= 0x20 && ch <= 0x7e) ? ch : '?'), - (long) ch, - (long) expt, - (long) expt_adj, - (long) dig_whole, - (long) dig_frac, - (long) dig_expt, - (long) dig_lzero, - (long) dig_prec)); - DUK__BI_PRINT("f", &nc_ctx->f); - - /* Most common cases first. */ - if (ch >= (duk_small_int_t) '0' && ch <= (duk_small_int_t) '9') { - dig = (duk_small_int_t) ch - '0' + 0; - } else if (ch == (duk_small_int_t) '.') { - /* A leading digit is not required in some cases, e.g. accept ".123". - * In other cases (JSON.parse()) a leading digit is required. This - * is checked for after the loop. - */ - if (dig_frac >= 0 || dig_expt >= 0) { - if (flags & DUK_S2N_FLAG_ALLOW_GARBAGE) { - DUK_DDD(DUK_DDDPRINT("garbage termination (invalid period)")); - break; - } else { - DUK_DDD(DUK_DDDPRINT("parse failed: period not allowed")); - goto parse_fail; - } - } - - if ((flags & DUK_S2N_FLAG_ALLOW_FRAC) == 0) { - /* Some contexts don't allow fractions at all; this can't be a - * post-check because the state ('f' and expt) would be incorrect. - */ - if (flags & DUK_S2N_FLAG_ALLOW_GARBAGE) { - DUK_DDD(DUK_DDDPRINT("garbage termination (invalid first period)")); - break; - } else { - DUK_DDD(DUK_DDDPRINT("parse failed: fraction part not allowed")); - } - } - - DUK_DDD(DUK_DDDPRINT("start fraction part")); - dig_frac = 0; - continue; - } else if (ch == (duk_small_int_t) 0) { - DUK_DDD(DUK_DDDPRINT("NUL termination")); - break; - } else if ((flags & DUK_S2N_FLAG_ALLOW_EXP) && dig_expt < 0 && - (ch == (duk_small_int_t) 'e' || ch == (duk_small_int_t) 'E')) { - /* Note: we don't parse back exponent notation for anything else - * than radix 10, so this is not an ambiguous check (e.g. hex - * exponent values may have 'e' either as a significand digit - * or as an exponent separator). - * - * If the exponent separator occurs twice, 'e' will be interpreted - * as a digit (= 14) and will be rejected as an invalid decimal - * digit. - */ - - DUK_DDD(DUK_DDDPRINT("start exponent part")); - - /* Exponent without a sign or with a +/- sign is accepted - * by all call sites (even JSON.parse()). - */ - ch = *p; - if (ch == (duk_small_int_t) '-') { - expt_neg = 1; - p++; - } else if (ch == (duk_small_int_t) '+') { - p++; - } - dig_expt = 0; - continue; - } else if (ch >= (duk_small_int_t) 'a' && ch <= (duk_small_int_t) 'z') { - dig = (duk_small_int_t) (ch - (duk_small_int_t) 'a' + 0x0a); - } else if (ch >= (duk_small_int_t) 'A' && ch <= (duk_small_int_t) 'Z') { - dig = (duk_small_int_t) (ch - (duk_small_int_t) 'A' + 0x0a); - } else { - dig = 255; /* triggers garbage digit check below */ - } - DUK_ASSERT((dig >= 0 && dig <= 35) || dig == 255); - - if (dig >= radix) { - if (flags & DUK_S2N_FLAG_ALLOW_GARBAGE) { - DUK_DDD(DUK_DDDPRINT("garbage termination")); - break; - } else { - DUK_DDD(DUK_DDDPRINT("parse failed: trailing garbage or invalid digit")); - goto parse_fail; - } - } - - if (dig_expt < 0) { - /* whole or fraction digit */ - - if (dig_prec < duk__str2num_digits_for_radix[radix - 2]) { - /* significant from precision perspective */ - - duk_small_int_t f_zero = duk__bi_is_zero(&nc_ctx->f); - if (f_zero && dig == 0) { - /* Leading zero is not counted towards precision digits; not - * in the integer part, nor in the fraction part. - */ - if (dig_frac < 0) { - dig_lzero++; - } - } else { - /* XXX: join these ops (multiply-accumulate), but only if - * code footprint decreases. - */ - duk__bi_mul_small(&nc_ctx->t1, &nc_ctx->f, (duk_uint32_t) radix); - duk__bi_add_small(&nc_ctx->f, &nc_ctx->t1, (duk_uint32_t) dig); - dig_prec++; - } - } else { - /* Ignore digits beyond a radix-specific limit, but note them - * in expt_adj. - */ - expt_adj++; - } - - if (dig_frac >= 0) { - dig_frac++; - expt_adj--; - } else { - dig_whole++; - } - } else { - /* exponent digit */ - - DUK_ASSERT(radix == 10); - expt = expt * radix + dig; - if (expt > DUK_S2N_MAX_EXPONENT) { - /* Impose a reasonable exponent limit, so that exp - * doesn't need to get tracked using a bigint. - */ - DUK_DDD(DUK_DDDPRINT("parse failed: exponent too large")); - goto parse_explimit_error; - } - dig_expt++; - } - } - - /* Leading zero. */ - - if (dig_lzero > 0 && dig_whole > 1) { - if ((flags & DUK_S2N_FLAG_ALLOW_LEADING_ZERO) == 0) { - DUK_DDD(DUK_DDDPRINT("parse failed: leading zeroes not allowed in integer part")); - goto parse_fail; - } - } - - /* Validity checks for various fraction formats ("0.1", ".1", "1.", "."). */ - - if (dig_whole == 0) { - if (dig_frac == 0) { - /* "." is not accepted in any format */ - DUK_DDD(DUK_DDDPRINT("parse failed: plain period without leading or trailing digits")); - goto parse_fail; - } else if (dig_frac > 0) { - /* ".123" */ - if ((flags & DUK_S2N_FLAG_ALLOW_NAKED_FRAC) == 0) { - DUK_DDD(DUK_DDDPRINT("parse failed: fraction part not allowed without " - "leading integer digit(s)")); - goto parse_fail; - } - } else { - /* Empty ("") is allowed in some formats (e.g. Number(''), as zero, - * but it must not have a leading +/- sign (GH-2019). Note that - * for Number(), h_str is already trimmed so we can check for zero - * length and still get Number(' + ') == NaN. - */ - if ((flags & DUK_S2N_FLAG_ALLOW_EMPTY_AS_ZERO) == 0) { - DUK_DDD(DUK_DDDPRINT("parse failed: empty string not allowed (as zero)")); - goto parse_fail; - } else if (DUK_HSTRING_GET_BYTELEN(h_str) != 0) { - DUK_DDD(DUK_DDDPRINT("parse failed: no digits, but not empty (had a +/- sign)")); - goto parse_fail; - } - } - } else { - if (dig_frac == 0) { - /* "123." is allowed in some formats */ - if ((flags & DUK_S2N_FLAG_ALLOW_EMPTY_FRAC) == 0) { - DUK_DDD(DUK_DDDPRINT("parse failed: empty fractions")); - goto parse_fail; - } - } else if (dig_frac > 0) { - /* "123.456" */ - ; - } else { - /* "123" */ - ; - } - } - - /* Exponent without digits (e.g. "1e" or "1e+"). If trailing garbage is - * allowed, ignore exponent part as garbage (= parse as "1", i.e. exp 0). - */ - - if (dig_expt == 0) { - if ((flags & DUK_S2N_FLAG_ALLOW_GARBAGE) == 0) { - DUK_DDD(DUK_DDDPRINT("parse failed: empty exponent")); - goto parse_fail; - } - DUK_ASSERT(expt == 0); - } - - if (expt_neg) { - expt = -expt; - } - DUK_DDD( - DUK_DDDPRINT("expt=%ld, expt_adj=%ld, net exponent -> %ld", (long) expt, (long) expt_adj, (long) (expt + expt_adj))); - expt += expt_adj; - - /* Fast path check. */ - - if (nc_ctx->f.n <= 1 && /* 32-bit value */ - expt == 0 /* no net exponent */) { - /* Fast path is triggered for no exponent and also for balanced exponent - * and fraction parts, e.g. for "1.23e2" == "123". Remember to respect - * zero sign. - */ - - /* XXX: could accept numbers larger than 32 bits, e.g. up to 53 bits? */ - DUK_DDD(DUK_DDDPRINT("fast path number parse")); - if (nc_ctx->f.n == 1) { - res = (double) nc_ctx->f.v[0]; - } else { - res = 0.0; - } - goto negcheck_and_ret; - } - - /* Significand ('f') padding. */ - - while (dig_prec < duk__str2num_digits_for_radix[radix - 2]) { - /* Pad significand with "virtual" zero digits so that Dragon4 will - * have enough (apparent) precision to work with. - */ - DUK_DDD(DUK_DDDPRINT("dig_prec=%ld, pad significand with zero", (long) dig_prec)); - duk__bi_mul_small_copy(&nc_ctx->f, (duk_uint32_t) radix, &nc_ctx->t1); - DUK__BI_PRINT("f", &nc_ctx->f); - expt--; - dig_prec++; - } - - DUK_DDD(DUK_DDDPRINT("final exponent: %ld", (long) expt)); - - /* Detect zero special case. */ - - if (nc_ctx->f.n == 0) { - /* This may happen even after the fast path check, if exponent is - * not balanced (e.g. "0e1"). Remember to respect zero sign. - */ - DUK_DDD(DUK_DDDPRINT("significand is zero")); - res = 0.0; - goto negcheck_and_ret; - } - - /* Quick reject of too large or too small exponents. This check - * would be incorrect for zero (e.g. "0e1000" is zero, not Infinity) - * so zero check must be above. - */ - - explim = &duk__str2num_exp_limits[radix - 2]; - if (expt > explim->upper) { - DUK_DDD(DUK_DDDPRINT("exponent too large -> infinite")); - res = (duk_double_t) DUK_DOUBLE_INFINITY; - goto negcheck_and_ret; - } else if (expt < explim->lower) { - DUK_DDD(DUK_DDDPRINT("exponent too small -> zero")); - res = (duk_double_t) 0.0; - goto negcheck_and_ret; - } - - nc_ctx->is_s2n = 1; - nc_ctx->e = expt; - nc_ctx->b = radix; - nc_ctx->B = 2; - nc_ctx->is_fixed = 1; - nc_ctx->abs_pos = 0; - nc_ctx->req_digits = 53 + 1; - - DUK__BI_PRINT("f", &nc_ctx->f); - DUK_DDD(DUK_DDDPRINT("e=%ld", (long) nc_ctx->e)); - - /* - * Dragon4 slow path (binary) digit generation. - * An extra digit is generated for rounding. - */ - - duk__dragon4_prepare(nc_ctx); /* setup many variables in nc_ctx */ - - DUK_DDD(DUK_DDDPRINT("after prepare:")); - DUK__BI_PRINT("r", &nc_ctx->r); - DUK__BI_PRINT("s", &nc_ctx->s); - DUK__BI_PRINT("mp", &nc_ctx->mp); - DUK__BI_PRINT("mm", &nc_ctx->mm); - - duk__dragon4_scale(nc_ctx); - - DUK_DDD(DUK_DDDPRINT("after scale; k=%ld", (long) nc_ctx->k)); - DUK__BI_PRINT("r", &nc_ctx->r); - DUK__BI_PRINT("s", &nc_ctx->s); - DUK__BI_PRINT("mp", &nc_ctx->mp); - DUK__BI_PRINT("mm", &nc_ctx->mm); - - duk__dragon4_generate(nc_ctx); - - DUK_ASSERT(nc_ctx->count == 53 + 1); - - /* - * Convert binary digits into an IEEE double. Need to handle - * denormals and rounding correctly. - * - * Some call sites currently assume the result is always a - * non-fastint double. If this is changed, check all call - * sites. - */ - - duk__dragon4_ctx_to_double(nc_ctx, &res); - goto negcheck_and_ret; - -negcheck_and_ret: - if (neg) { - res = -res; - } - duk_pop(thr); - duk_push_number(thr, (double) res); - DUK_DDD(DUK_DDDPRINT("result: %!T", (duk_tval *) duk_get_tval(thr, -1))); - return; - -parse_fail: - DUK_DDD(DUK_DDDPRINT("parse failed")); - duk_pop(thr); - duk_push_nan(thr); - return; - -parse_explimit_error: - DUK_DDD(DUK_DDDPRINT("parse failed, internal error, can't return a value")); - DUK_ERROR_RANGE(thr, "exponent too large"); - DUK_WO_NORETURN(return;); -} - -DUK_INTERNAL void duk_numconv_parse(duk_hthread *thr, duk_small_int_t radix, duk_small_uint_t flags) { - duk_native_stack_check(thr); - duk__numconv_parse_raw(thr, radix, flags); -} - -/* automatic undefs */ -#undef DUK__BI_MAX_PARTS -#undef DUK__BI_PRINT -#undef DUK__DIGITCHAR -#undef DUK__DRAGON4_OUTPUT_PREINC -#undef DUK__IEEE_DOUBLE_EXP_BIAS -#undef DUK__IEEE_DOUBLE_EXP_MIN -#undef DUK__MAX_FORMATTED_LENGTH -#undef DUK__MAX_OUTPUT_DIGITS -#undef DUK__NO_EXP -#undef DUK__NUMCONV_CTX_BIGINTS_SIZE -#undef DUK__NUMCONV_CTX_NUM_BIGINTS -/* - * Regexp compilation. - * - * See doc/regexp.rst for a discussion of the compilation approach and - * current limitations. - * - * Regexp bytecode assumes jumps can be expressed with signed 32-bit - * integers. Consequently the bytecode size must not exceed 0x7fffffffL. - * The implementation casts duk_size_t (buffer size) to duk_(u)int32_t - * in many places. Although this could be changed, the bytecode format - * limit would still prevent regexps exceeding the signed 32-bit limit - * from working. - * - * XXX: The implementation does not prevent bytecode from exceeding the - * maximum supported size. This could be done by limiting the maximum - * input string size (assuming an upper bound can be computed for number - * of bytecode bytes emitted per input byte) or checking buffer maximum - * size when emitting bytecode (slower). - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_REGEXP_SUPPORT) - -/* - * Helper macros - */ - -#define DUK__RE_INITIAL_BUFSIZE 64 - -#define DUK__RE_BUFLEN(re_ctx) DUK_BW_GET_SIZE(re_ctx->thr, &re_ctx->bw) - -/* - * Disjunction struct: result of parsing a disjunction - */ - -typedef struct { - /* Number of characters that the atom matches (e.g. 3 for 'abc'), - * -1 if atom is complex and number of matched characters either - * varies or is not known. - */ - duk_int32_t charlen; - -#if 0 - /* These are not needed to implement quantifier capture handling, - * but might be needed at some point. - */ - - /* re_ctx->captures at start and end of atom parsing. - * Since 'captures' indicates highest capture number emitted - * so far in a DUK_REOP_SAVE, the captures numbers saved by - * the atom are: ]start_captures,end_captures]. - */ - duk_uint32_t start_captures; - duk_uint32_t end_captures; -#endif -} duk__re_disjunction_info; - -/* - * Encoding helpers - * - * Some of the typing is bytecode based, e.g. slice sizes are unsigned 32-bit - * even though the buffer operations will use duk_size_t. - */ - -/* XXX: the insert helpers should ensure that the bytecode result is not - * larger than expected (or at least assert for it). Many things in the - * bytecode, like skip offsets, won't work correctly if the bytecode is - * larger than say 2G. - */ - -DUK_LOCAL duk_uint32_t duk__encode_i32(duk_int32_t x) { - if (x < 0) { - return ((duk_uint32_t) (-x)) * 2 + 1; - } else { - return ((duk_uint32_t) x) * 2; - } -} - -/* XXX: return type should probably be duk_size_t, or explicit checks are needed for - * maximum size. - */ -DUK_LOCAL duk_uint32_t duk__insert_u32(duk_re_compiler_ctx *re_ctx, duk_uint32_t offset, duk_uint32_t x) { - duk_uint8_t buf[DUK_UNICODE_MAX_XUTF8_LENGTH]; - duk_small_int_t len; - - len = duk_unicode_encode_xutf8((duk_ucodepoint_t) x, buf); - DUK_ASSERT(len >= 0); - DUK_BW_INSERT_ENSURE_BYTES(re_ctx->thr, &re_ctx->bw, offset, buf, (duk_size_t) len); - return (duk_uint32_t) len; -} - -DUK_LOCAL void duk__append_u32(duk_re_compiler_ctx *re_ctx, duk_uint32_t x) { - DUK_BW_WRITE_ENSURE_XUTF8(re_ctx->thr, &re_ctx->bw, x); -} - -DUK_LOCAL void duk__append_7bit(duk_re_compiler_ctx *re_ctx, duk_uint32_t x) { -#if defined(DUK_USE_PREFER_SIZE) - duk__append_u32(re_ctx, x); -#else - DUK_ASSERT(x <= 0x7fU); - DUK_BW_WRITE_ENSURE_U8(re_ctx->thr, &re_ctx->bw, (duk_uint8_t) x); -#endif -} - -#if 0 -DUK_LOCAL void duk__append_2bytes(duk_re_compiler_ctx *re_ctx, duk_uint8_t x, duk_uint8_t y) { - DUK_BW_WRITE_ENSURE_U8_2(re_ctx->thr, &re_ctx->bw, x, y); -} -#endif - -DUK_LOCAL duk_uint32_t duk__insert_i32(duk_re_compiler_ctx *re_ctx, duk_uint32_t offset, duk_int32_t x) { - return duk__insert_u32(re_ctx, offset, duk__encode_i32(x)); -} - -DUK_LOCAL void duk__append_reop(duk_re_compiler_ctx *re_ctx, duk_uint32_t reop) { - DUK_ASSERT(reop <= 0x7fU); - (void) duk__append_7bit(re_ctx, reop); -} - -#if 0 /* unused */ -DUK_LOCAL void duk__append_i32(duk_re_compiler_ctx *re_ctx, duk_int32_t x) { - duk__append_u32(re_ctx, duk__encode_i32(x)); -} -#endif - -/* special helper for emitting u16 lists (used for character ranges for built-in char classes) */ -DUK_LOCAL void duk__append_u16_list(duk_re_compiler_ctx *re_ctx, const duk_uint16_t *values, duk_uint32_t count) { - /* Call sites don't need the result length so it's not accumulated. */ - while (count-- > 0) { - duk__append_u32(re_ctx, (duk_uint32_t) (*values++)); - } -} - -DUK_LOCAL void duk__insert_slice(duk_re_compiler_ctx *re_ctx, - duk_uint32_t offset, - duk_uint32_t data_offset, - duk_uint32_t data_length) { - DUK_BW_INSERT_ENSURE_SLICE(re_ctx->thr, &re_ctx->bw, offset, data_offset, data_length); -} - -DUK_LOCAL void duk__append_slice(duk_re_compiler_ctx *re_ctx, duk_uint32_t data_offset, duk_uint32_t data_length) { - DUK_BW_WRITE_ENSURE_SLICE(re_ctx->thr, &re_ctx->bw, data_offset, data_length); -} - -DUK_LOCAL void duk__remove_slice(duk_re_compiler_ctx *re_ctx, duk_uint32_t data_offset, duk_uint32_t data_length) { - DUK_BW_REMOVE_ENSURE_SLICE(re_ctx->thr, &re_ctx->bw, data_offset, data_length); -} - -/* - * Insert a jump offset at 'offset' to complete an instruction - * (the jump offset is always the last component of an instruction). - * The 'skip' argument must be computed relative to 'offset', - * -without- taking into account the skip field being inserted. - * - * ... A B C ins X Y Z ... (ins may be a JUMP, SPLIT1/SPLIT2, etc) - * => ... A B C ins SKIP X Y Z - * - * Computing the final (adjusted) skip value, which is relative to the - * first byte of the next instruction, is a bit tricky because of the - * variable length UTF-8 encoding. See doc/regexp.rst for discussion. - */ -DUK_LOCAL duk_uint32_t duk__insert_jump_offset(duk_re_compiler_ctx *re_ctx, duk_uint32_t offset, duk_int32_t skip) { -#if 0 - /* Iterative solution. */ - if (skip < 0) { - duk_small_int_t len; - /* two encoding attempts suffices */ - len = duk_unicode_get_xutf8_length((duk_codepoint_t) duk__encode_i32(skip)); - len = duk_unicode_get_xutf8_length((duk_codepoint_t) duk__encode_i32(skip - (duk_int32_t) len)); - DUK_ASSERT(duk_unicode_get_xutf8_length(duk__encode_i32(skip - (duk_int32_t) len)) == len); /* no change */ - skip -= (duk_int32_t) len; - } -#endif - -#if defined(DUK_USE_PREFER_SIZE) - /* Closed form solution, this produces smallest code. - * See re_neg_jump_offset (closed2). - */ - if (skip < 0) { - skip--; - if (skip < -0x3fL) { - skip--; - } - if (skip < -0x3ffL) { - skip--; - } - if (skip < -0x7fffL) { - skip--; - } - if (skip < -0xfffffL) { - skip--; - } - if (skip < -0x1ffffffL) { - skip--; - } - if (skip < -0x3fffffffL) { - skip--; - } - } -#else /* DUK_USE_PREFER_SIZE */ - /* Closed form solution, this produces fastest code. - * See re_neg_jump_offset (closed1). - */ - if (skip < 0) { - if (skip >= -0x3eL) { - skip -= 1; - } else if (skip >= -0x3fdL) { - skip -= 2; - } else if (skip >= -0x7ffcL) { - skip -= 3; - } else if (skip >= -0xffffbL) { - skip -= 4; - } else if (skip >= -0x1fffffaL) { - skip -= 5; - } else if (skip >= -0x3ffffff9L) { - skip -= 6; - } else { - skip -= 7; - } - } -#endif /* DUK_USE_PREFER_SIZE */ - - return duk__insert_i32(re_ctx, offset, skip); -} - -DUK_LOCAL duk_uint32_t duk__append_jump_offset(duk_re_compiler_ctx *re_ctx, duk_int32_t skip) { - return (duk_uint32_t) duk__insert_jump_offset(re_ctx, (duk_uint32_t) DUK__RE_BUFLEN(re_ctx), skip); -} - -/* - * duk_re_range_callback for generating character class ranges. - * - * When ignoreCase is false, the range is simply emitted as is. We don't, - * for instance, eliminate duplicates or overlapping ranges in a character - * class. - * - * When ignoreCase is true but the 'direct' flag is set, the caller knows - * that the range canonicalizes to itself for case insensitive matching, - * so the range is emitted as is. This is mainly useful for built-in ranges - * like \W. - * - * Otherwise, when ignoreCase is true, the range needs to be normalized - * through canonicalization. Unfortunately a canonicalized version of a - * continuous range is not necessarily continuous (e.g. [x-{] is continuous - * but [X-{] is not). As a result, a single input range may expand to a lot - * of output ranges. The current algorithm creates the canonicalized ranges - * footprint efficiently at the cost of compile time execution time; see - * doc/regexp.rst for discussion, and some more details below. - * - * Note that the ctx->nranges is a context-wide temporary value. This is OK - * because there cannot be multiple character classes being parsed - * simultaneously. - * - * More detail on canonicalization: - * - * Conceptually, a range is canonicalized by scanning the entire range, - * normalizing each codepoint by converting it to uppercase, and generating - * a set of result ranges. - * - * Ideally a minimal set of output ranges would be emitted by merging all - * possible ranges even if they're emitted out of sequence. Because the - * input string is also case normalized during matching, some codepoints - * never occur at runtime; these "don't care" codepoints can be included or - * excluded from ranges when merging/optimizing ranges. - * - * The current algorithm does not do optimal range merging. Rather, output - * codepoints are generated in sequence, and when the output codepoints are - * continuous (CP, CP+1, CP+2, ...), they are merged locally into as large a - * range as possible. A small canonicalization bitmap is used to reduce - * actual codepoint canonicalizations which are quite slow at present. The - * bitmap provides a "codepoint block is continuous with respect to - * canonicalization" for N-codepoint blocks. This allows blocks to be - * skipped quickly. - * - * There are a number of shortcomings and future work here: - * - * - Individual codepoint normalizations are slow because they involve - * walking bit-packed rules without a lookup index. - * - * - The conceptual algorithm needs to canonicalize every codepoint in the - * input range to figure out the output range(s). Even with the small - * canonicalization bitmap the algorithm runs quite slowly for worst case - * inputs. There are many data structure alternatives to improve this. - * - * - While the current algorithm generates maximal output ranges when the - * output codepoints are emitted linearly, output ranges are not sorted or - * merged otherwise. In the worst case a lot of ranges are emitted when - * most of the ranges could be merged. In this process one could take - * advantage of "don't care" codepoints, which are never matched against at - * runtime due to canonicalization of input codepoints before comparison, - * to merge otherwise discontinuous output ranges. - * - * - The runtime data structure is just a linear list of ranges to match - * against. This can be quite slow if there are a lot of output ranges. - * There are various ways to make matching against the ranges faster, - * e.g. sorting the ranges and using a binary search; skip lists; tree - * based representations; full or approximate codepoint bitmaps, etc. - * - * - Only BMP is supported, codepoints above BMP are assumed to canonicalize - * to themselves. For now this is one place where we don't want to - * support chars outside the BMP, because the exhaustive search would be - * massively larger. It would be possible to support non-BMP with a - * different algorithm, or perhaps doing case normalization only at match - * time. - */ - -DUK_LOCAL void duk__regexp_emit_range(duk_re_compiler_ctx *re_ctx, duk_codepoint_t r1, duk_codepoint_t r2) { - DUK_ASSERT(r2 >= r1); - duk__append_u32(re_ctx, (duk_uint32_t) r1); - duk__append_u32(re_ctx, (duk_uint32_t) r2); - re_ctx->nranges++; -} - -#if defined(DUK_USE_REGEXP_CANON_BITMAP) -/* Find next canonicalization discontinuity (conservative estimate) starting - * from 'start', not exceeding 'end'. If continuity is fine up to 'end' - * inclusive, returns end. Minimum possible return value is start. - */ -DUK_LOCAL duk_codepoint_t duk__re_canon_next_discontinuity(duk_codepoint_t start, duk_codepoint_t end) { - duk_uint_t start_blk; - duk_uint_t end_blk; - duk_uint_t blk; - duk_uint_t offset; - duk_uint8_t mask; - - /* Inclusive block range. */ - DUK_ASSERT(start >= 0); - DUK_ASSERT(end >= 0); - DUK_ASSERT(end >= start); - start_blk = (duk_uint_t) (start >> DUK_CANON_BITMAP_BLKSHIFT); - end_blk = (duk_uint_t) (end >> DUK_CANON_BITMAP_BLKSHIFT); - - for (blk = start_blk; blk <= end_blk; blk++) { - offset = blk >> 3; - mask = 1U << (blk & 0x07); - if (offset >= sizeof(duk_unicode_re_canon_bitmap)) { - /* Reached non-BMP range which is assumed continuous. */ - return end; - } - DUK_ASSERT(offset < sizeof(duk_unicode_re_canon_bitmap)); - if ((duk_unicode_re_canon_bitmap[offset] & mask) == 0) { - /* Block is discontinuous, continuity is guaranteed - * only up to end of previous block (+1 for exclusive - * return value => start of current block). Start - * block requires special handling. - */ - if (blk > start_blk) { - return (duk_codepoint_t) (blk << DUK_CANON_BITMAP_BLKSHIFT); - } else { - return start; - } - } - } - DUK_ASSERT(blk == end_blk + 1); /* Reached end block which is continuous. */ - return end; -} -#else /* DUK_USE_REGEXP_CANON_BITMAP */ -DUK_LOCAL duk_codepoint_t duk__re_canon_next_discontinuity(duk_codepoint_t start, duk_codepoint_t end) { - DUK_ASSERT(start >= 0); - DUK_ASSERT(end >= 0); - DUK_ASSERT(end >= start); - if (start >= 0x10000) { - /* Even without the bitmap, treat non-BMP as continuous. */ - return end; - } - return start; -} -#endif /* DUK_USE_REGEXP_CANON_BITMAP */ - -DUK_LOCAL void duk__regexp_generate_ranges(void *userdata, duk_codepoint_t r1, duk_codepoint_t r2, duk_bool_t direct) { - duk_re_compiler_ctx *re_ctx = (duk_re_compiler_ctx *) userdata; - duk_codepoint_t r_start; - duk_codepoint_t r_end; - duk_codepoint_t i; - duk_codepoint_t t; - duk_codepoint_t r_disc; - - DUK_DD(DUK_DDPRINT("duk__regexp_generate_ranges(): re_ctx=%p, range=[%ld,%ld] direct=%ld", - (void *) re_ctx, - (long) r1, - (long) r2, - (long) direct)); - - DUK_ASSERT(r2 >= r1); /* SyntaxError for out of order range. */ - - if (direct || (re_ctx->re_flags & DUK_RE_FLAG_IGNORE_CASE) == 0) { - DUK_DD(DUK_DDPRINT("direct or not case sensitive, emit range: [%ld,%ld]", (long) r1, (long) r2)); - duk__regexp_emit_range(re_ctx, r1, r2); - return; - } - - DUK_DD(DUK_DDPRINT("case sensitive, process range: [%ld,%ld]", (long) r1, (long) r2)); - - r_start = duk_unicode_re_canonicalize_char(re_ctx->thr, r1); - r_end = r_start; - - for (i = r1 + 1; i <= r2;) { - /* Input codepoint space processed up to i-1, and - * current range in r_{start,end} is up-to-date - * (inclusive) and may either break or continue. - */ - r_disc = duk__re_canon_next_discontinuity(i, r2); - DUK_ASSERT(r_disc >= i); - DUK_ASSERT(r_disc <= r2); - - r_end += r_disc - i; /* May be zero. */ - t = duk_unicode_re_canonicalize_char(re_ctx->thr, r_disc); - if (t == r_end + 1) { - /* Not actually a discontinuity, continue range - * to r_disc and recheck. - */ - r_end = t; - } else { - duk__regexp_emit_range(re_ctx, r_start, r_end); - r_start = t; - r_end = t; - } - i = r_disc + 1; /* Guarantees progress. */ - } - duk__regexp_emit_range(re_ctx, r_start, r_end); - -#if 0 /* Exhaustive search, very slow. */ - r_start = duk_unicode_re_canonicalize_char(re_ctx->thr, r1); - r_end = r_start; - for (i = r1 + 1; i <= r2; i++) { - t = duk_unicode_re_canonicalize_char(re_ctx->thr, i); - if (t == r_end + 1) { - r_end = t; - } else { - DUK_DD(DUK_DDPRINT("canonicalized, emit range: [%ld,%ld]", (long) r_start, (long) r_end)); - duk__append_u32(re_ctx, (duk_uint32_t) r_start); - duk__append_u32(re_ctx, (duk_uint32_t) r_end); - re_ctx->nranges++; - r_start = t; - r_end = t; - } - } - DUK_DD(DUK_DDPRINT("canonicalized, emit range: [%ld,%ld]", (long) r_start, (long) r_end)); - duk__append_u32(re_ctx, (duk_uint32_t) r_start); - duk__append_u32(re_ctx, (duk_uint32_t) r_end); - re_ctx->nranges++; -#endif -} - -/* - * Parse regexp Disjunction. Most of regexp compilation happens here. - * - * Handles Disjunction, Alternative, and Term productions directly without - * recursion. The only constructs requiring recursion are positive/negative - * lookaheads, capturing parentheses, and non-capturing parentheses. - * - * The function determines whether the entire disjunction is a 'simple atom' - * (see doc/regexp.rst discussion on 'simple quantifiers') and if so, - * returns the atom character length which is needed by the caller to keep - * track of its own atom character length. A disjunction with more than one - * alternative is never considered a simple atom (although in some cases - * that might be the case). - * - * Return value: simple atom character length or < 0 if not a simple atom. - * Appends the bytecode for the disjunction matcher to the end of the temp - * buffer. - * - * Regexp top level structure is: - * - * Disjunction = Term* - * | Term* | Disjunction - * - * Term = Assertion - * | Atom - * | Atom Quantifier - * - * An empty Term sequence is a valid disjunction alternative (e.g. /|||c||/). - * - * Notes: - * - * * Tracking of the 'simple-ness' of the current atom vs. the entire - * disjunction are separate matters. For instance, the disjunction - * may be complex, but individual atoms may be simple. Furthermore, - * simple quantifiers are used whenever possible, even if the - * disjunction as a whole is complex. - * - * * The estimate of whether an atom is simple is conservative now, - * and it would be possible to expand it. For instance, captures - * cause the disjunction to be marked complex, even though captures - * -can- be handled by simple quantifiers with some minor modifications. - * - * * Disjunction 'tainting' as 'complex' is handled at the end of the - * main for loop collectively for atoms. Assertions, quantifiers, - * and '|' tokens need to taint the result manually if necessary. - * Assertions cannot add to result char length, only atoms (and - * quantifiers) can; currently quantifiers will taint the result - * as complex though. - */ - -DUK_LOCAL const duk_uint16_t * const duk__re_range_lookup1[3] = { duk_unicode_re_ranges_digit, - duk_unicode_re_ranges_white, - duk_unicode_re_ranges_wordchar }; -DUK_LOCAL const duk_uint8_t duk__re_range_lookup2[3] = { sizeof(duk_unicode_re_ranges_digit) / (2 * sizeof(duk_uint16_t)), - sizeof(duk_unicode_re_ranges_white) / (2 * sizeof(duk_uint16_t)), - sizeof(duk_unicode_re_ranges_wordchar) / (2 * sizeof(duk_uint16_t)) }; - -DUK_LOCAL void duk__append_range_atom_matcher(duk_re_compiler_ctx *re_ctx, - duk_small_uint_t re_op, - const duk_uint16_t *ranges, - duk_small_uint_t count) { -#if 0 - DUK_ASSERT(re_op <= 0x7fUL); - DUK_ASSERT(count <= 0x7fUL); - duk__append_2bytes(re_ctx, (duk_uint8_t) re_op, (duk_uint8_t) count); -#endif - duk__append_reop(re_ctx, re_op); - duk__append_7bit(re_ctx, count); - duk__append_u16_list(re_ctx, ranges, count * 2); -} - -DUK_LOCAL void duk__parse_disjunction(duk_re_compiler_ctx *re_ctx, duk_bool_t expect_eof, duk__re_disjunction_info *out_atom_info) { - duk_int32_t atom_start_offset = -1; /* negative -> no atom matched on previous round */ - duk_int32_t atom_char_length = 0; /* negative -> complex atom */ - duk_uint32_t atom_start_captures = re_ctx->captures; /* value of re_ctx->captures at start of atom */ - duk_int32_t unpatched_disjunction_split = -1; - duk_int32_t unpatched_disjunction_jump = -1; - duk_uint32_t entry_offset = (duk_uint32_t) DUK__RE_BUFLEN(re_ctx); - duk_int32_t res_charlen = 0; /* -1 if disjunction is complex, char length if simple */ - duk__re_disjunction_info tmp_disj; - - DUK_ASSERT(out_atom_info != NULL); - - duk_native_stack_check(re_ctx->thr); - if (re_ctx->recursion_depth >= re_ctx->recursion_limit) { - DUK_ERROR_RANGE(re_ctx->thr, DUK_STR_REGEXP_COMPILER_RECURSION_LIMIT); - DUK_WO_NORETURN(return;); - } - re_ctx->recursion_depth++; - -#if 0 - out_atom_info->start_captures = re_ctx->captures; -#endif - - for (;;) { - /* atom_char_length, atom_start_offset, atom_start_offset reflect the - * atom matched on the previous loop. If a quantifier is encountered - * on this loop, these are needed to handle the quantifier correctly. - * new_atom_char_length etc are for the atom parsed on this round; - * they're written to atom_char_length etc at the end of the round. - */ - duk_int32_t new_atom_char_length; /* char length of the atom parsed in this loop */ - duk_int32_t new_atom_start_offset; /* bytecode start offset of the atom parsed in this loop - * (allows quantifiers to copy the atom bytecode) - */ - duk_uint32_t new_atom_start_captures; /* re_ctx->captures at the start of the atom parsed in this loop */ - - duk_lexer_parse_re_token(&re_ctx->lex, &re_ctx->curr_token); - - DUK_DD(DUK_DDPRINT( - "re token: %ld (num=%ld, char=%c)", - (long) re_ctx->curr_token.t, - (long) re_ctx->curr_token.num, - (re_ctx->curr_token.num >= 0x20 && re_ctx->curr_token.num <= 0x7e) ? (int) re_ctx->curr_token.num : (int) '?')); - - /* set by atom case clauses */ - new_atom_start_offset = -1; - new_atom_char_length = -1; - new_atom_start_captures = re_ctx->captures; - - switch (re_ctx->curr_token.t) { - case DUK_RETOK_DISJUNCTION: { - /* - * The handling here is a bit tricky. If a previous '|' has been processed, - * we have a pending split1 and a pending jump (for a previous match). These - * need to be back-patched carefully. See docs for a detailed example. - */ - - /* patch pending jump and split */ - if (unpatched_disjunction_jump >= 0) { - duk_uint32_t offset; - - DUK_ASSERT(unpatched_disjunction_split >= 0); - offset = (duk_uint32_t) unpatched_disjunction_jump; - offset += duk__insert_jump_offset(re_ctx, offset, (duk_int32_t) (DUK__RE_BUFLEN(re_ctx) - offset)); - /* offset is now target of the pending split (right after jump) */ - duk__insert_jump_offset(re_ctx, - (duk_uint32_t) unpatched_disjunction_split, - (duk_int32_t) offset - unpatched_disjunction_split); - } - - /* add a new pending split to the beginning of the entire disjunction */ - (void) duk__insert_u32(re_ctx, entry_offset, DUK_REOP_SPLIT1); /* prefer direct execution */ - unpatched_disjunction_split = (duk_int32_t) (entry_offset + 1); /* +1 for opcode */ - - /* add a new pending match jump for latest finished alternative */ - duk__append_reop(re_ctx, DUK_REOP_JUMP); - unpatched_disjunction_jump = (duk_int32_t) DUK__RE_BUFLEN(re_ctx); - - /* 'taint' result as complex */ - res_charlen = -1; - break; - } - case DUK_RETOK_QUANTIFIER: { - if (atom_start_offset < 0) { - DUK_ERROR_SYNTAX(re_ctx->thr, DUK_STR_INVALID_QUANTIFIER_NO_ATOM); - DUK_WO_NORETURN(return;); - } - if (re_ctx->curr_token.qmin > re_ctx->curr_token.qmax) { - DUK_ERROR_SYNTAX(re_ctx->thr, DUK_STR_INVALID_QUANTIFIER_VALUES); - DUK_WO_NORETURN(return;); - } - if (atom_char_length >= 0) { - /* - * Simple atom - * - * If atom_char_length is zero, we'll have unbounded execution time for e.g. - * /()*x/.exec('x'). We can't just skip the match because it might have some - * side effects (for instance, if we allowed captures in simple atoms, the - * capture needs to happen). The simple solution below is to force the - * quantifier to match at most once, since the additional matches have no effect. - * - * With a simple atom there can be no capture groups, so no captures need - * to be reset. - */ - duk_int32_t atom_code_length; - duk_uint32_t offset; - duk_uint32_t qmin, qmax; - - qmin = re_ctx->curr_token.qmin; - qmax = re_ctx->curr_token.qmax; - if (atom_char_length == 0) { - /* qmin and qmax will be 0 or 1 */ - if (qmin > 1) { - qmin = 1; - } - if (qmax > 1) { - qmax = 1; - } - } - - duk__append_reop(re_ctx, DUK_REOP_MATCH); /* complete 'sub atom' */ - atom_code_length = (duk_int32_t) (DUK__RE_BUFLEN(re_ctx) - (duk_size_t) atom_start_offset); - - offset = (duk_uint32_t) atom_start_offset; - if (re_ctx->curr_token.greedy) { - offset += duk__insert_u32(re_ctx, offset, DUK_REOP_SQGREEDY); - offset += duk__insert_u32(re_ctx, offset, qmin); - offset += duk__insert_u32(re_ctx, offset, qmax); - offset += duk__insert_u32(re_ctx, offset, (duk_uint32_t) atom_char_length); - offset += duk__insert_jump_offset(re_ctx, offset, atom_code_length); - } else { - offset += duk__insert_u32(re_ctx, offset, DUK_REOP_SQMINIMAL); - offset += duk__insert_u32(re_ctx, offset, qmin); - offset += duk__insert_u32(re_ctx, offset, qmax); - offset += duk__insert_jump_offset(re_ctx, offset, atom_code_length); - } - DUK_UNREF(offset); /* silence scan-build warning */ - } else { - /* - * Complex atom - * - * The original code is used as a template, and removed at the end - * (this differs from the handling of simple quantifiers). - * - * NOTE: there is no current solution for empty atoms in complex - * quantifiers. This would need some sort of a 'progress' instruction. - * - * XXX: impose limit on maximum result size, i.e. atom_code_len * atom_copies? - */ - duk_int32_t atom_code_length; - duk_uint32_t atom_copies; - duk_uint32_t tmp_qmin, tmp_qmax; - - /* pre-check how many atom copies we're willing to make (atom_copies not needed below) */ - atom_copies = (re_ctx->curr_token.qmax == DUK_RE_QUANTIFIER_INFINITE) ? re_ctx->curr_token.qmin : - re_ctx->curr_token.qmax; - if (atom_copies > DUK_RE_MAX_ATOM_COPIES) { - DUK_ERROR_RANGE(re_ctx->thr, DUK_STR_QUANTIFIER_TOO_MANY_COPIES); - DUK_WO_NORETURN(return;); - } - - /* wipe the capture range made by the atom (if any) */ - DUK_ASSERT(atom_start_captures <= re_ctx->captures); - if (atom_start_captures != re_ctx->captures) { - DUK_ASSERT(atom_start_captures < re_ctx->captures); - DUK_DDD(DUK_DDDPRINT("must wipe ]atom_start_captures,re_ctx->captures]: ]%ld,%ld]", - (long) atom_start_captures, - (long) re_ctx->captures)); - - /* insert (DUK_REOP_WIPERANGE, start, count) in reverse order so the order ends up right */ - duk__insert_u32(re_ctx, - (duk_uint32_t) atom_start_offset, - (re_ctx->captures - atom_start_captures) * 2U); - duk__insert_u32(re_ctx, (duk_uint32_t) atom_start_offset, (atom_start_captures + 1) * 2); - duk__insert_u32(re_ctx, (duk_uint32_t) atom_start_offset, DUK_REOP_WIPERANGE); - } else { - DUK_DDD( - DUK_DDDPRINT("no need to wipe captures: atom_start_captures == re_ctx->captures == %ld", - (long) atom_start_captures)); - } - - atom_code_length = (duk_int32_t) DUK__RE_BUFLEN(re_ctx) - atom_start_offset; - - /* insert the required matches (qmin) by copying the atom */ - tmp_qmin = re_ctx->curr_token.qmin; - tmp_qmax = re_ctx->curr_token.qmax; - while (tmp_qmin > 0) { - duk__append_slice(re_ctx, - (duk_uint32_t) atom_start_offset, - (duk_uint32_t) atom_code_length); - tmp_qmin--; - if (tmp_qmax != DUK_RE_QUANTIFIER_INFINITE) { - tmp_qmax--; - } - } - DUK_ASSERT(tmp_qmin == 0); - - /* insert code for matching the remainder - infinite or finite */ - if (tmp_qmax == DUK_RE_QUANTIFIER_INFINITE) { - /* reuse last emitted atom for remaining 'infinite' quantifier */ - - if (re_ctx->curr_token.qmin == 0) { - /* Special case: original qmin was zero so there is nothing - * to repeat. Emit an atom copy but jump over it here. - */ - duk__append_reop(re_ctx, DUK_REOP_JUMP); - duk__append_jump_offset(re_ctx, atom_code_length); - duk__append_slice(re_ctx, - (duk_uint32_t) atom_start_offset, - (duk_uint32_t) atom_code_length); - } - if (re_ctx->curr_token.greedy) { - duk__append_reop(re_ctx, DUK_REOP_SPLIT2); /* prefer jump */ - } else { - duk__append_reop(re_ctx, DUK_REOP_SPLIT1); /* prefer direct */ - } - duk__append_jump_offset(re_ctx, -atom_code_length - 1); /* -1 for opcode */ - } else { - /* - * The remaining matches are emitted as sequence of SPLITs and atom - * copies; the SPLITs skip the remaining copies and match the sequel. - * This sequence needs to be emitted starting from the last copy - * because the SPLITs are variable length due to the variable length - * skip offset. This causes a lot of memory copying now. - * - * Example structure (greedy, match maximum # atoms): - * - * SPLIT1 LSEQ - * (atom) - * SPLIT1 LSEQ ; <- the byte length of this instruction is needed - * (atom) ; to encode the above SPLIT1 correctly - * ... - * LSEQ: - */ - duk_uint32_t offset = (duk_uint32_t) DUK__RE_BUFLEN(re_ctx); - while (tmp_qmax > 0) { - duk__insert_slice(re_ctx, - offset, - (duk_uint32_t) atom_start_offset, - (duk_uint32_t) atom_code_length); - if (re_ctx->curr_token.greedy) { - duk__insert_u32(re_ctx, offset, DUK_REOP_SPLIT1); /* prefer direct */ - } else { - duk__insert_u32(re_ctx, offset, DUK_REOP_SPLIT2); /* prefer jump */ - } - duk__insert_jump_offset(re_ctx, - offset + 1, /* +1 for opcode */ - (duk_int32_t) (DUK__RE_BUFLEN(re_ctx) - (offset + 1))); - tmp_qmax--; - } - } - - /* remove the original 'template' atom */ - duk__remove_slice(re_ctx, (duk_uint32_t) atom_start_offset, (duk_uint32_t) atom_code_length); - } - - /* 'taint' result as complex */ - res_charlen = -1; - break; - } - case DUK_RETOK_ASSERT_START: { - duk__append_reop(re_ctx, DUK_REOP_ASSERT_START); - break; - } - case DUK_RETOK_ASSERT_END: { - duk__append_reop(re_ctx, DUK_REOP_ASSERT_END); - break; - } - case DUK_RETOK_ASSERT_WORD_BOUNDARY: { - duk__append_reop(re_ctx, DUK_REOP_ASSERT_WORD_BOUNDARY); - break; - } - case DUK_RETOK_ASSERT_NOT_WORD_BOUNDARY: { - duk__append_reop(re_ctx, DUK_REOP_ASSERT_NOT_WORD_BOUNDARY); - break; - } - case DUK_RETOK_ASSERT_START_POS_LOOKAHEAD: - case DUK_RETOK_ASSERT_START_NEG_LOOKAHEAD: { - duk_uint32_t offset; - duk_uint32_t opcode = - (re_ctx->curr_token.t == DUK_RETOK_ASSERT_START_POS_LOOKAHEAD) ? DUK_REOP_LOOKPOS : DUK_REOP_LOOKNEG; - - offset = (duk_uint32_t) DUK__RE_BUFLEN(re_ctx); - duk__parse_disjunction(re_ctx, 0, &tmp_disj); - duk__append_reop(re_ctx, DUK_REOP_MATCH); - - (void) duk__insert_u32(re_ctx, offset, opcode); - (void) duk__insert_jump_offset(re_ctx, - offset + 1, /* +1 for opcode */ - (duk_int32_t) (DUK__RE_BUFLEN(re_ctx) - (offset + 1))); - - /* 'taint' result as complex -- this is conservative, - * as lookaheads do not backtrack. - */ - res_charlen = -1; - break; - } - case DUK_RETOK_ATOM_PERIOD: { - new_atom_char_length = 1; - new_atom_start_offset = (duk_int32_t) DUK__RE_BUFLEN(re_ctx); - duk__append_reop(re_ctx, DUK_REOP_PERIOD); - break; - } - case DUK_RETOK_ATOM_CHAR: { - /* Note: successive characters could be joined into string matches - * but this is not trivial (consider e.g. '/xyz+/); see docs for - * more discussion. - * - * No support for \u{H+} yet. While only BMP Unicode escapes are - * supported for RegExps at present, 'ch' may still be a non-BMP - * codepoint if it is decoded straight from source text UTF-8. - * There's no non-BMP support yet so this is handled simply by - * matching the non-BMP character (which is custom behavior). - */ - duk_uint32_t ch; - - new_atom_char_length = 1; - new_atom_start_offset = (duk_int32_t) DUK__RE_BUFLEN(re_ctx); - duk__append_reop(re_ctx, DUK_REOP_CHAR); - ch = re_ctx->curr_token.num; - if (re_ctx->re_flags & DUK_RE_FLAG_IGNORE_CASE) { - ch = (duk_uint32_t) duk_unicode_re_canonicalize_char(re_ctx->thr, (duk_codepoint_t) ch); - } - duk__append_u32(re_ctx, ch); - break; - } - case DUK_RETOK_ATOM_DIGIT: - case DUK_RETOK_ATOM_NOT_DIGIT: - case DUK_RETOK_ATOM_WHITE: - case DUK_RETOK_ATOM_NOT_WHITE: - case DUK_RETOK_ATOM_WORD_CHAR: - case DUK_RETOK_ATOM_NOT_WORD_CHAR: { - duk_small_uint_t re_op; - duk_small_uint_t idx; - - new_atom_char_length = 1; - new_atom_start_offset = (duk_int32_t) DUK__RE_BUFLEN(re_ctx); - - DUK_ASSERT((DUK_RETOK_ATOM_DIGIT & 0x01) != 0); - DUK_ASSERT((DUK_RETOK_ATOM_WHITE & 0x01) != 0); - DUK_ASSERT((DUK_RETOK_ATOM_WORD_CHAR & 0x01) != 0); - DUK_ASSERT((DUK_RETOK_ATOM_NOT_DIGIT & 0x01) == 0); - DUK_ASSERT((DUK_RETOK_ATOM_NOT_WHITE & 0x01) == 0); - DUK_ASSERT((DUK_RETOK_ATOM_NOT_WORD_CHAR & 0x01) == 0); - re_op = (re_ctx->curr_token.t & 0x01) ? DUK_REOP_RANGES : DUK_REOP_INVRANGES; - - DUK_ASSERT(DUK_RETOK_ATOM_WHITE == DUK_RETOK_ATOM_DIGIT + 2); - DUK_ASSERT(DUK_RETOK_ATOM_WORD_CHAR == DUK_RETOK_ATOM_DIGIT + 4); - idx = (duk_small_uint_t) ((re_ctx->curr_token.t - DUK_RETOK_ATOM_DIGIT) >> 1U); - DUK_ASSERT(idx <= 2U); /* Assume continuous token numbers; also checks negative underflow. */ - - duk__append_range_atom_matcher(re_ctx, re_op, duk__re_range_lookup1[idx], duk__re_range_lookup2[idx]); - break; - } - case DUK_RETOK_ATOM_BACKREFERENCE: { - duk_uint32_t backref = (duk_uint32_t) re_ctx->curr_token.num; - if (backref > re_ctx->highest_backref) { - re_ctx->highest_backref = backref; - } - new_atom_char_length = -1; /* mark as complex */ - new_atom_start_offset = (duk_int32_t) DUK__RE_BUFLEN(re_ctx); - duk__append_reop(re_ctx, DUK_REOP_BACKREFERENCE); - duk__append_u32(re_ctx, backref); - break; - } - case DUK_RETOK_ATOM_START_CAPTURE_GROUP: { - duk_uint32_t cap; - - new_atom_char_length = -1; /* mark as complex (capture handling) */ - new_atom_start_offset = (duk_int32_t) DUK__RE_BUFLEN(re_ctx); - cap = ++re_ctx->captures; - duk__append_reop(re_ctx, DUK_REOP_SAVE); - duk__append_u32(re_ctx, cap * 2); - duk__parse_disjunction(re_ctx, - 0, - &tmp_disj); /* retval (sub-atom char length) unused, tainted as complex above */ - duk__append_reop(re_ctx, DUK_REOP_SAVE); - duk__append_u32(re_ctx, cap * 2 + 1); - break; - } - case DUK_RETOK_ATOM_START_NONCAPTURE_GROUP: { - new_atom_start_offset = (duk_int32_t) DUK__RE_BUFLEN(re_ctx); - duk__parse_disjunction(re_ctx, 0, &tmp_disj); - new_atom_char_length = tmp_disj.charlen; - break; - } - case DUK_RETOK_ATOM_START_CHARCLASS: - case DUK_RETOK_ATOM_START_CHARCLASS_INVERTED: { - /* - * Range parsing is done with a special lexer function which calls - * us for every range parsed. This is different from how rest of - * the parsing works, but avoids a heavy, arbitrary size intermediate - * value type to hold the ranges. - * - * Another complication is the handling of character ranges when - * case insensitive matching is used (see docs for discussion). - * The range handler callback given to the lexer takes care of this - * as well. - * - * Note that duplicate ranges are not eliminated when parsing character - * classes, so that canonicalization of - * - * [0-9a-fA-Fx-{] - * - * creates the result (note the duplicate ranges): - * - * [0-9A-FA-FX-Z{-{] - * - * where [x-{] is split as a result of canonicalization. The duplicate - * ranges are not a semantics issue: they work correctly. - */ - - duk_uint32_t offset; - - DUK_DD(DUK_DDPRINT("character class")); - - /* insert ranges instruction, range count patched in later */ - new_atom_char_length = 1; - new_atom_start_offset = (duk_int32_t) DUK__RE_BUFLEN(re_ctx); - duk__append_reop(re_ctx, - (re_ctx->curr_token.t == DUK_RETOK_ATOM_START_CHARCLASS) ? DUK_REOP_RANGES : - DUK_REOP_INVRANGES); - offset = (duk_uint32_t) DUK__RE_BUFLEN(re_ctx); /* patch in range count later */ - - /* parse ranges until character class ends */ - re_ctx->nranges = 0; /* note: ctx-wide temporary */ - duk_lexer_parse_re_ranges(&re_ctx->lex, duk__regexp_generate_ranges, (void *) re_ctx); - - /* insert range count */ - duk__insert_u32(re_ctx, offset, re_ctx->nranges); - break; - } - case DUK_RETOK_ATOM_END_GROUP: { - if (expect_eof) { - DUK_ERROR_SYNTAX(re_ctx->thr, DUK_STR_UNEXPECTED_CLOSING_PAREN); - DUK_WO_NORETURN(return;); - } - goto done; - } - case DUK_RETOK_EOF: { - if (!expect_eof) { - DUK_ERROR_SYNTAX(re_ctx->thr, DUK_STR_UNEXPECTED_END_OF_PATTERN); - DUK_WO_NORETURN(return;); - } - goto done; - } - default: { - DUK_ERROR_SYNTAX(re_ctx->thr, DUK_STR_UNEXPECTED_REGEXP_TOKEN); - DUK_WO_NORETURN(return;); - } - } - - /* a complex (new) atom taints the result */ - if (new_atom_start_offset >= 0) { - if (new_atom_char_length < 0) { - res_charlen = -1; - } else if (res_charlen >= 0) { - /* only advance if not tainted */ - res_charlen += new_atom_char_length; - } - } - - /* record previous atom info in case next token is a quantifier */ - atom_start_offset = new_atom_start_offset; - atom_char_length = new_atom_char_length; - atom_start_captures = new_atom_start_captures; - } - -done: - - /* finish up pending jump and split for last alternative */ - if (unpatched_disjunction_jump >= 0) { - duk_uint32_t offset; - - DUK_ASSERT(unpatched_disjunction_split >= 0); - offset = (duk_uint32_t) unpatched_disjunction_jump; - offset += duk__insert_jump_offset(re_ctx, offset, (duk_int32_t) (DUK__RE_BUFLEN(re_ctx) - offset)); - /* offset is now target of the pending split (right after jump) */ - duk__insert_jump_offset(re_ctx, - (duk_uint32_t) unpatched_disjunction_split, - (duk_int32_t) offset - unpatched_disjunction_split); - } - -#if 0 - out_atom_info->end_captures = re_ctx->captures; -#endif - out_atom_info->charlen = res_charlen; - DUK_DDD(DUK_DDDPRINT("parse disjunction finished: charlen=%ld", (long) out_atom_info->charlen)); - - re_ctx->recursion_depth--; -} - -/* - * Flags parsing (see E5 Section 15.10.4.1). - */ - -DUK_LOCAL duk_uint32_t duk__parse_regexp_flags(duk_hthread *thr, duk_hstring *h) { - const duk_uint8_t *p; - const duk_uint8_t *p_end; - duk_uint32_t flags = 0; - - p = DUK_HSTRING_GET_DATA(h); - p_end = p + DUK_HSTRING_GET_BYTELEN(h); - - /* Note: can be safely scanned as bytes (undecoded) */ - - while (p < p_end) { - duk_uint8_t c = *p++; - switch (c) { - case (duk_uint8_t) 'g': { - if (flags & DUK_RE_FLAG_GLOBAL) { - goto flags_error; - } - flags |= DUK_RE_FLAG_GLOBAL; - break; - } - case (duk_uint8_t) 'i': { - if (flags & DUK_RE_FLAG_IGNORE_CASE) { - goto flags_error; - } - flags |= DUK_RE_FLAG_IGNORE_CASE; - break; - } - case (duk_uint8_t) 'm': { - if (flags & DUK_RE_FLAG_MULTILINE) { - goto flags_error; - } - flags |= DUK_RE_FLAG_MULTILINE; - break; - } - default: { - goto flags_error; - } - } - } - - return flags; - -flags_error: - DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_REGEXP_FLAGS); - DUK_WO_NORETURN(return 0U;); -} - -/* - * Create escaped RegExp source (E5 Section 15.10.3). - * - * The current approach is to special case the empty RegExp - * ('' -> '(?:)') and otherwise replace unescaped '/' characters - * with '\/' regardless of where they occur in the regexp. - * - * Note that normalization does not seem to be necessary for - * RegExp literals (e.g. '/foo/') because to be acceptable as - * a RegExp literal, the text between forward slashes must - * already match the escaping requirements (e.g. must not contain - * unescaped forward slashes or be empty). Escaping IS needed - * for expressions like 'new Regexp("...", "")' however. - * Currently, we re-escape in either case. - * - * Also note that we process the source here in UTF-8 encoded - * form. This is correct, because any non-ASCII characters are - * passed through without change. - */ - -DUK_LOCAL void duk__create_escaped_source(duk_hthread *thr, int idx_pattern) { - duk_hstring *h; - const duk_uint8_t *p; - duk_bufwriter_ctx bw_alloc; - duk_bufwriter_ctx *bw; - duk_uint8_t *q; - duk_size_t i, n; - duk_uint_fast8_t c_prev, c; - - h = duk_known_hstring(thr, idx_pattern); - p = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h); - n = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h); - - if (n == 0) { - duk_push_literal(thr, "(?:)"); - return; - } - - bw = &bw_alloc; - DUK_BW_INIT_PUSHBUF(thr, bw, n); - q = DUK_BW_GET_PTR(thr, bw); - - c_prev = (duk_uint_fast8_t) 0; - - for (i = 0; i < n; i++) { - c = p[i]; - - q = DUK_BW_ENSURE_RAW(thr, bw, 2, q); - - if (c == (duk_uint_fast8_t) '/' && c_prev != (duk_uint_fast8_t) '\\') { - /* Unescaped '/' ANYWHERE in the regexp (in disjunction, - * inside a character class, ...) => same escape works. - */ - *q++ = DUK_ASC_BACKSLASH; - } - *q++ = (duk_uint8_t) c; - - c_prev = c; - } - - DUK_BW_SETPTR_AND_COMPACT(thr, bw, q); - (void) duk_buffer_to_string(thr, -1); /* Safe if input is safe. */ - - /* [ ... escaped_source ] */ -} - -/* - * Exposed regexp compilation primitive. - * - * Sets up a regexp compilation context, and calls duk__parse_disjunction() to do the - * actual parsing. Handles generation of the compiled regexp header and the - * "boilerplate" capture of the matching substring (save 0 and 1). Also does some - * global level regexp checks after recursive compilation has finished. - * - * An escaped version of the regexp source, suitable for use as a RegExp instance - * 'source' property (see E5 Section 15.10.3), is also left on the stack. - * - * Input stack: [ pattern flags ] - * Output stack: [ bytecode escaped_source ] (both as strings) - */ - -DUK_INTERNAL void duk_regexp_compile(duk_hthread *thr) { - duk_re_compiler_ctx re_ctx; - duk_lexer_point lex_point; - duk_hstring *h_pattern; - duk_hstring *h_flags; - duk__re_disjunction_info ign_disj; - - DUK_ASSERT(thr != NULL); - - /* - * Args validation - */ - - /* TypeError if fails */ - h_pattern = duk_require_hstring_notsymbol(thr, -2); - h_flags = duk_require_hstring_notsymbol(thr, -1); - - /* - * Create normalized 'source' property (E5 Section 15.10.3). - */ - - /* [ ... pattern flags ] */ - - duk__create_escaped_source(thr, -2); - - /* [ ... pattern flags escaped_source ] */ - - /* - * Init compilation context - */ - - /* [ ... pattern flags escaped_source buffer ] */ - - duk_memzero(&re_ctx, sizeof(re_ctx)); - DUK_LEXER_INITCTX(&re_ctx.lex); /* duplicate zeroing, expect for (possible) NULL inits */ - re_ctx.thr = thr; - re_ctx.lex.thr = thr; - re_ctx.lex.input = DUK_HSTRING_GET_DATA(h_pattern); - re_ctx.lex.input_length = DUK_HSTRING_GET_BYTELEN(h_pattern); - re_ctx.lex.token_limit = DUK_RE_COMPILE_TOKEN_LIMIT; - re_ctx.recursion_limit = DUK_USE_REGEXP_COMPILER_RECLIMIT; - re_ctx.re_flags = duk__parse_regexp_flags(thr, h_flags); - - DUK_BW_INIT_PUSHBUF(thr, &re_ctx.bw, DUK__RE_INITIAL_BUFSIZE); - - DUK_DD(DUK_DDPRINT("regexp compiler ctx initialized, flags=0x%08lx, recursion_limit=%ld", - (unsigned long) re_ctx.re_flags, - (long) re_ctx.recursion_limit)); - - /* - * Init lexer - */ - - lex_point.offset = 0; /* expensive init, just want to fill window */ - lex_point.line = 1; - DUK_LEXER_SETPOINT(&re_ctx.lex, &lex_point); - - /* - * Compilation - */ - - DUK_DD(DUK_DDPRINT("starting regexp compilation")); - - duk__append_reop(&re_ctx, DUK_REOP_SAVE); - duk__append_7bit(&re_ctx, 0); - duk__parse_disjunction(&re_ctx, 1 /*expect_eof*/, &ign_disj); - duk__append_reop(&re_ctx, DUK_REOP_SAVE); - duk__append_7bit(&re_ctx, 1); - duk__append_reop(&re_ctx, DUK_REOP_MATCH); - - /* - * Check for invalid backreferences; note that it is NOT an error - * to back-reference a capture group which has not yet been introduced - * in the pattern (as in /\1(foo)/); in fact, the backreference will - * always match! It IS an error to back-reference a capture group - * which will never be introduced in the pattern. Thus, we can check - * for such references only after parsing is complete. - */ - - if (re_ctx.highest_backref > re_ctx.captures) { - DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_BACKREFS); - DUK_WO_NORETURN(return;); - } - - /* - * Emit compiled regexp header: flags, ncaptures - * (insertion order inverted on purpose) - */ - - duk__insert_u32(&re_ctx, 0, (re_ctx.captures + 1) * 2); - duk__insert_u32(&re_ctx, 0, re_ctx.re_flags); - - /* [ ... pattern flags escaped_source buffer ] */ - - DUK_BW_COMPACT(thr, &re_ctx.bw); - (void) duk_buffer_to_string(thr, -1); /* Safe because flags is at most 7 bit. */ - - /* [ ... pattern flags escaped_source bytecode ] */ - - /* - * Finalize stack - */ - - duk_remove(thr, -4); /* -> [ ... flags escaped_source bytecode ] */ - duk_remove(thr, -3); /* -> [ ... escaped_source bytecode ] */ - - DUK_DD(DUK_DDPRINT("regexp compilation successful, bytecode: %!T, escaped source: %!T", - (duk_tval *) duk_get_tval(thr, -1), - (duk_tval *) duk_get_tval(thr, -2))); -} - -/* - * Create a RegExp instance (E5 Section 15.10.7). - * - * Note: the output stack left by duk_regexp_compile() is directly compatible - * with the input here. - * - * Input stack: [ escaped_source bytecode ] (both as strings) - * Output stack: [ RegExp ] - */ - -DUK_INTERNAL void duk_regexp_create_instance(duk_hthread *thr) { - duk_hobject *h; - - /* [ ... escaped_source bytecode ] */ - - duk_push_object(thr); - h = duk_known_hobject(thr, -1); - duk_insert(thr, -3); - - /* [ ... regexp_object escaped_source bytecode ] */ - - DUK_HOBJECT_SET_CLASS_NUMBER(h, DUK_HOBJECT_CLASS_REGEXP); - DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h, thr->builtins[DUK_BIDX_REGEXP_PROTOTYPE]); - - duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_INT_BYTECODE, DUK_PROPDESC_FLAGS_NONE); - - /* [ ... regexp_object escaped_source ] */ - - /* In ES2015 .source, and the .global, .multiline, etc flags are - * inherited getters. Store the escaped source as an internal - * property for the getter. - */ - - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_SOURCE, DUK_PROPDESC_FLAGS_NONE); - - /* [ ... regexp_object ] */ - - duk_push_int(thr, 0); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LAST_INDEX, DUK_PROPDESC_FLAGS_W); - - /* [ ... regexp_object ] */ -} - -#else /* DUK_USE_REGEXP_SUPPORT */ - -/* regexp support disabled */ - -#endif /* DUK_USE_REGEXP_SUPPORT */ - -/* automatic undefs */ -#undef DUK__RE_BUFLEN -#undef DUK__RE_INITIAL_BUFSIZE -/* - * Regexp executor. - * - * Safety: the ECMAScript executor should prevent user from reading and - * replacing regexp bytecode. Even so, the executor must validate all - * memory accesses etc. When an invalid access is detected (e.g. a 'save' - * opcode to invalid, unallocated index) it should fail with an internal - * error but not cause a segmentation fault. - * - * Notes: - * - * - Backtrack counts are limited to unsigned 32 bits but should - * technically be duk_size_t for strings longer than 4G chars. - * This also requires a regexp bytecode change. - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_REGEXP_SUPPORT) - -/* - * Helpers for UTF-8 handling - * - * For bytecode readers the duk_uint32_t and duk_int32_t types are correct - * because they're used for more than just codepoints. - */ - -DUK_LOCAL duk_uint32_t duk__bc_get_u32(duk_re_matcher_ctx *re_ctx, const duk_uint8_t **pc) { - return (duk_uint32_t) duk_unicode_decode_xutf8_checked(re_ctx->thr, pc, re_ctx->bytecode, re_ctx->bytecode_end); -} - -DUK_LOCAL duk_int32_t duk__bc_get_i32(duk_re_matcher_ctx *re_ctx, const duk_uint8_t **pc) { - duk_uint32_t t; - - /* signed integer encoding needed to work with UTF-8 */ - t = (duk_uint32_t) duk_unicode_decode_xutf8_checked(re_ctx->thr, pc, re_ctx->bytecode, re_ctx->bytecode_end); - if (t & 1) { - return -((duk_int32_t) (t >> 1)); - } else { - return (duk_int32_t) (t >> 1); - } -} - -DUK_LOCAL const duk_uint8_t *duk__utf8_backtrack(duk_hthread *thr, - const duk_uint8_t **ptr, - const duk_uint8_t *ptr_start, - const duk_uint8_t *ptr_end, - duk_uint_fast32_t count) { - const duk_uint8_t *p; - - /* Note: allow backtracking from p == ptr_end */ - p = *ptr; - if (p < ptr_start || p > ptr_end) { - goto fail; - } - - while (count > 0) { - for (;;) { - p--; - if (p < ptr_start) { - goto fail; - } - if ((*p & 0xc0) != 0x80) { - /* utf-8 continuation bytes have the form 10xx xxxx */ - break; - } - } - count--; - } - *ptr = p; - return p; - -fail: - DUK_ERROR_INTERNAL(thr); - DUK_WO_NORETURN(return NULL;); -} - -DUK_LOCAL const duk_uint8_t *duk__utf8_advance(duk_hthread *thr, - const duk_uint8_t **ptr, - const duk_uint8_t *ptr_start, - const duk_uint8_t *ptr_end, - duk_uint_fast32_t count) { - const duk_uint8_t *p; - - p = *ptr; - if (p < ptr_start || p >= ptr_end) { - goto fail; - } - - while (count > 0) { - for (;;) { - p++; - - /* Note: if encoding ends by hitting end of input, we don't check that - * the encoding is valid, we just assume it is. - */ - if (p >= ptr_end || ((*p & 0xc0) != 0x80)) { - /* utf-8 continuation bytes have the form 10xx xxxx */ - break; - } - } - count--; - } - - *ptr = p; - return p; - -fail: - DUK_ERROR_INTERNAL(thr); - DUK_WO_NORETURN(return NULL;); -} - -/* - * Helpers for dealing with the input string - */ - -/* Get a (possibly canonicalized) input character from current sp. The input - * itself is never modified, and captures always record non-canonicalized - * characters even in case-insensitive matching. Return <0 if out of input. - */ -DUK_LOCAL duk_codepoint_t duk__inp_get_cp(duk_re_matcher_ctx *re_ctx, const duk_uint8_t **sp) { - duk_codepoint_t res; - - if (*sp >= re_ctx->input_end) { - return -1; - } - res = (duk_codepoint_t) duk_unicode_decode_xutf8_checked(re_ctx->thr, sp, re_ctx->input, re_ctx->input_end); - if (re_ctx->re_flags & DUK_RE_FLAG_IGNORE_CASE) { - res = duk_unicode_re_canonicalize_char(re_ctx->thr, res); - } - return res; -} - -DUK_LOCAL const duk_uint8_t *duk__inp_backtrack(duk_re_matcher_ctx *re_ctx, const duk_uint8_t **sp, duk_uint_fast32_t count) { - return duk__utf8_backtrack(re_ctx->thr, sp, re_ctx->input, re_ctx->input_end, count); -} - -/* Backtrack utf-8 input and return a (possibly canonicalized) input character. */ -DUK_LOCAL duk_codepoint_t duk__inp_get_prev_cp(duk_re_matcher_ctx *re_ctx, const duk_uint8_t *sp) { - /* note: caller 'sp' is intentionally not updated here */ - (void) duk__inp_backtrack(re_ctx, &sp, (duk_uint_fast32_t) 1); - return duk__inp_get_cp(re_ctx, &sp); -} - -/* - * Regexp recursive matching function. - * - * Returns 'sp' on successful match (points to character after last matched one), - * NULL otherwise. - * - * The C recursion depth limit check is only performed in this function, this - * suffices because the function is present in all true recursion required by - * regexp execution. - */ - -DUK_LOCAL const duk_uint8_t *duk__match_regexp(duk_re_matcher_ctx *re_ctx, const duk_uint8_t *pc, const duk_uint8_t *sp) { - duk_native_stack_check(re_ctx->thr); - if (re_ctx->recursion_depth >= re_ctx->recursion_limit) { - DUK_ERROR_RANGE(re_ctx->thr, DUK_STR_REGEXP_EXECUTOR_RECURSION_LIMIT); - DUK_WO_NORETURN(return NULL;); - } - re_ctx->recursion_depth++; - - for (;;) { - duk_small_int_t op; - - if (re_ctx->steps_count >= re_ctx->steps_limit) { - DUK_ERROR_RANGE(re_ctx->thr, DUK_STR_REGEXP_EXECUTOR_STEP_LIMIT); - DUK_WO_NORETURN(return NULL;); - } - re_ctx->steps_count++; - - /* Opcodes are at most 7 bits now so they encode to one byte. If this - * were not the case or 'pc' is invalid here (due to a bug etc) we'll - * still fail safely through the switch default case. - */ - DUK_ASSERT(pc[0] <= 0x7fU); -#if 0 - op = (duk_small_int_t) duk__bc_get_u32(re_ctx, &pc); -#endif - op = *pc++; - - DUK_DDD(DUK_DDDPRINT("match: rec=%ld, steps=%ld, pc (after op)=%ld, sp=%ld, op=%ld", - (long) re_ctx->recursion_depth, - (long) re_ctx->steps_count, - (long) (pc - re_ctx->bytecode), - (long) (sp - re_ctx->input), - (long) op)); - - switch (op) { - case DUK_REOP_MATCH: { - goto match; - } - case DUK_REOP_CHAR: { - /* - * Byte-based matching would be possible for case-sensitive - * matching but not for case-insensitive matching. So, we - * match by decoding the input and bytecode character normally. - * - * Bytecode characters are assumed to be already canonicalized. - * Input characters are canonicalized automatically by - * duk__inp_get_cp() if necessary. - * - * There is no opcode for matching multiple characters. The - * regexp compiler has trouble joining strings efficiently - * during compilation. See doc/regexp.rst for more discussion. - */ - duk_codepoint_t c1, c2; - - c1 = (duk_codepoint_t) duk__bc_get_u32(re_ctx, &pc); - DUK_ASSERT(!(re_ctx->re_flags & DUK_RE_FLAG_IGNORE_CASE) || - c1 == duk_unicode_re_canonicalize_char(re_ctx->thr, c1)); /* canonicalized by compiler */ - c2 = duk__inp_get_cp(re_ctx, &sp); - /* No need to check for c2 < 0 (end of input): because c1 >= 0, it - * will fail the match below automatically and cause goto fail. - */ -#if 0 - if (c2 < 0) { - goto fail; - } -#endif - DUK_ASSERT(c1 >= 0); - - DUK_DDD(DUK_DDDPRINT("char match, c1=%ld, c2=%ld", (long) c1, (long) c2)); - if (c1 != c2) { - goto fail; - } - break; - } - case DUK_REOP_PERIOD: { - duk_codepoint_t c; - - c = duk__inp_get_cp(re_ctx, &sp); - if (c < 0 || duk_unicode_is_line_terminator(c)) { - /* E5 Sections 15.10.2.8, 7.3 */ - goto fail; - } - break; - } - case DUK_REOP_RANGES: - case DUK_REOP_INVRANGES: { - duk_uint32_t n; - duk_codepoint_t c; - duk_small_int_t match; - - n = duk__bc_get_u32(re_ctx, &pc); - c = duk__inp_get_cp(re_ctx, &sp); - if (c < 0) { - goto fail; - } - - match = 0; - while (n) { - duk_codepoint_t r1, r2; - r1 = (duk_codepoint_t) duk__bc_get_u32(re_ctx, &pc); - r2 = (duk_codepoint_t) duk__bc_get_u32(re_ctx, &pc); - DUK_DDD(DUK_DDDPRINT("matching ranges/invranges, n=%ld, r1=%ld, r2=%ld, c=%ld", - (long) n, - (long) r1, - (long) r2, - (long) c)); - if (c >= r1 && c <= r2) { - /* Note: don't bail out early, we must read all the ranges from - * bytecode. Another option is to skip them efficiently after - * breaking out of here. Prefer smallest code. - */ - match = 1; - } - n--; - } - - if (op == DUK_REOP_RANGES) { - if (!match) { - goto fail; - } - } else { - DUK_ASSERT(op == DUK_REOP_INVRANGES); - if (match) { - goto fail; - } - } - break; - } - case DUK_REOP_ASSERT_START: { - duk_codepoint_t c; - - if (sp <= re_ctx->input) { - break; - } - if (!(re_ctx->re_flags & DUK_RE_FLAG_MULTILINE)) { - goto fail; - } - c = duk__inp_get_prev_cp(re_ctx, sp); - if (duk_unicode_is_line_terminator(c)) { - /* E5 Sections 15.10.2.8, 7.3 */ - break; - } - goto fail; - } - case DUK_REOP_ASSERT_END: { - duk_codepoint_t c; - const duk_uint8_t *tmp_sp; - - tmp_sp = sp; - c = duk__inp_get_cp(re_ctx, &tmp_sp); - if (c < 0) { - break; - } - if (!(re_ctx->re_flags & DUK_RE_FLAG_MULTILINE)) { - goto fail; - } - if (duk_unicode_is_line_terminator(c)) { - /* E5 Sections 15.10.2.8, 7.3 */ - break; - } - goto fail; - } - case DUK_REOP_ASSERT_WORD_BOUNDARY: - case DUK_REOP_ASSERT_NOT_WORD_BOUNDARY: { - /* - * E5 Section 15.10.2.6. The previous and current character - * should -not- be canonicalized as they are now. However, - * canonicalization does not affect the result of IsWordChar() - * (which depends on Unicode characters never canonicalizing - * into ASCII characters) so this does not matter. - */ - duk_small_int_t w1, w2; - - if (sp <= re_ctx->input) { - w1 = 0; /* not a wordchar */ - } else { - duk_codepoint_t c; - c = duk__inp_get_prev_cp(re_ctx, sp); - w1 = duk_unicode_re_is_wordchar(c); - } - if (sp >= re_ctx->input_end) { - w2 = 0; /* not a wordchar */ - } else { - const duk_uint8_t *tmp_sp = sp; /* dummy so sp won't get updated */ - duk_codepoint_t c; - c = duk__inp_get_cp(re_ctx, &tmp_sp); - w2 = duk_unicode_re_is_wordchar(c); - } - - if (op == DUK_REOP_ASSERT_WORD_BOUNDARY) { - if (w1 == w2) { - goto fail; - } - } else { - DUK_ASSERT(op == DUK_REOP_ASSERT_NOT_WORD_BOUNDARY); - if (w1 != w2) { - goto fail; - } - } - break; - } - case DUK_REOP_JUMP: { - duk_int32_t skip; - - skip = duk__bc_get_i32(re_ctx, &pc); - pc += skip; - break; - } - case DUK_REOP_SPLIT1: { - /* split1: prefer direct execution (no jump) */ - const duk_uint8_t *sub_sp; - duk_int32_t skip; - - skip = duk__bc_get_i32(re_ctx, &pc); - sub_sp = duk__match_regexp(re_ctx, pc, sp); - if (sub_sp) { - sp = sub_sp; - goto match; - } - pc += skip; - break; - } - case DUK_REOP_SPLIT2: { - /* split2: prefer jump execution (not direct) */ - const duk_uint8_t *sub_sp; - duk_int32_t skip; - - skip = duk__bc_get_i32(re_ctx, &pc); - sub_sp = duk__match_regexp(re_ctx, pc + skip, sp); - if (sub_sp) { - sp = sub_sp; - goto match; - } - break; - } - case DUK_REOP_SQMINIMAL: { - duk_uint32_t q, qmin, qmax; - duk_int32_t skip; - const duk_uint8_t *sub_sp; - - qmin = duk__bc_get_u32(re_ctx, &pc); - qmax = duk__bc_get_u32(re_ctx, &pc); - skip = duk__bc_get_i32(re_ctx, &pc); - DUK_DDD(DUK_DDDPRINT("minimal quantifier, qmin=%lu, qmax=%lu, skip=%ld", - (unsigned long) qmin, - (unsigned long) qmax, - (long) skip)); - - q = 0; - while (q <= qmax) { - if (q >= qmin) { - sub_sp = duk__match_regexp(re_ctx, pc + skip, sp); - if (sub_sp) { - sp = sub_sp; - goto match; - } - } - sub_sp = duk__match_regexp(re_ctx, pc, sp); - if (!sub_sp) { - break; - } - sp = sub_sp; - q++; - } - goto fail; - } - case DUK_REOP_SQGREEDY: { - duk_uint32_t q, qmin, qmax, atomlen; - duk_int32_t skip; - const duk_uint8_t *sub_sp; - - qmin = duk__bc_get_u32(re_ctx, &pc); - qmax = duk__bc_get_u32(re_ctx, &pc); - atomlen = duk__bc_get_u32(re_ctx, &pc); - skip = duk__bc_get_i32(re_ctx, &pc); - DUK_DDD(DUK_DDDPRINT("greedy quantifier, qmin=%lu, qmax=%lu, atomlen=%lu, skip=%ld", - (unsigned long) qmin, - (unsigned long) qmax, - (unsigned long) atomlen, - (long) skip)); - - q = 0; - while (q < qmax) { - sub_sp = duk__match_regexp(re_ctx, pc, sp); - if (!sub_sp) { - break; - } - sp = sub_sp; - q++; - } - while (q >= qmin) { - sub_sp = duk__match_regexp(re_ctx, pc + skip, sp); - if (sub_sp) { - sp = sub_sp; - goto match; - } - if (q == qmin) { - break; - } - - /* Note: if atom were to contain e.g. captures, we would need to - * re-match the atom to get correct captures. Simply quantifiers - * do not allow captures in their atom now, so this is not an issue. - */ - - DUK_DDD(DUK_DDDPRINT("greedy quantifier, backtrack %ld characters (atomlen)", (long) atomlen)); - sp = duk__inp_backtrack(re_ctx, &sp, (duk_uint_fast32_t) atomlen); - q--; - } - goto fail; - } - case DUK_REOP_SAVE: { - duk_uint32_t idx; - const duk_uint8_t *old; - const duk_uint8_t *sub_sp; - - idx = duk__bc_get_u32(re_ctx, &pc); - if (idx >= re_ctx->nsaved) { - /* idx is unsigned, < 0 check is not necessary */ - DUK_D(DUK_DPRINT("internal error, regexp save index insane: idx=%ld", (long) idx)); - goto internal_error; - } - old = re_ctx->saved[idx]; - re_ctx->saved[idx] = sp; - sub_sp = duk__match_regexp(re_ctx, pc, sp); - if (sub_sp) { - sp = sub_sp; - goto match; - } - re_ctx->saved[idx] = old; - goto fail; - } - case DUK_REOP_WIPERANGE: { - /* Wipe capture range and save old values for backtracking. - * - * XXX: this typically happens with a relatively small idx_count. - * It might be useful to handle cases where the count is small - * (say <= 8) by saving the values in stack instead. This would - * reduce memory churn and improve performance, at the cost of a - * slightly higher code footprint. - */ - duk_uint32_t idx_start, idx_count; -#if defined(DUK_USE_EXPLICIT_NULL_INIT) - duk_uint32_t idx_end, idx; -#endif - duk_uint8_t **range_save; - const duk_uint8_t *sub_sp; - - idx_start = duk__bc_get_u32(re_ctx, &pc); - idx_count = duk__bc_get_u32(re_ctx, &pc); - DUK_DDD(DUK_DDDPRINT("wipe saved range: start=%ld, count=%ld -> [%ld,%ld] (captures [%ld,%ld])", - (long) idx_start, - (long) idx_count, - (long) idx_start, - (long) (idx_start + idx_count - 1), - (long) (idx_start / 2), - (long) ((idx_start + idx_count - 1) / 2))); - if (idx_start + idx_count > re_ctx->nsaved || idx_count == 0) { - /* idx is unsigned, < 0 check is not necessary */ - DUK_D(DUK_DPRINT("internal error, regexp wipe indices insane: idx_start=%ld, idx_count=%ld", - (long) idx_start, - (long) idx_count)); - goto internal_error; - } - DUK_ASSERT(idx_count > 0); - - duk_require_stack(re_ctx->thr, 1); - range_save = (duk_uint8_t **) duk_push_fixed_buffer_nozero(re_ctx->thr, sizeof(duk_uint8_t *) * idx_count); - DUK_ASSERT(range_save != NULL); - duk_memcpy(range_save, re_ctx->saved + idx_start, sizeof(duk_uint8_t *) * idx_count); -#if defined(DUK_USE_EXPLICIT_NULL_INIT) - idx_end = idx_start + idx_count; - for (idx = idx_start; idx < idx_end; idx++) { - re_ctx->saved[idx] = NULL; - } -#else - duk_memzero((void *) (re_ctx->saved + idx_start), sizeof(duk_uint8_t *) * idx_count); -#endif - - sub_sp = duk__match_regexp(re_ctx, pc, sp); - if (sub_sp) { - /* match: keep wiped/resaved values */ - DUK_DDD(DUK_DDDPRINT("match: keep wiped/resaved values [%ld,%ld] (captures [%ld,%ld])", - (long) idx_start, - (long) (idx_start + idx_count - 1), - (long) (idx_start / 2), - (long) ((idx_start + idx_count - 1) / 2))); - duk_pop_unsafe(re_ctx->thr); - sp = sub_sp; - goto match; - } - - /* fail: restore saves */ - DUK_DDD(DUK_DDDPRINT("fail: restore wiped/resaved values [%ld,%ld] (captures [%ld,%ld])", - (long) idx_start, - (long) (idx_start + idx_count - 1), - (long) (idx_start / 2), - (long) ((idx_start + idx_count - 1) / 2))); - duk_memcpy((void *) (re_ctx->saved + idx_start), - (const void *) range_save, - sizeof(duk_uint8_t *) * idx_count); - duk_pop_unsafe(re_ctx->thr); - goto fail; - } - case DUK_REOP_LOOKPOS: - case DUK_REOP_LOOKNEG: { - /* - * Needs a save of multiple saved[] entries depending on what range - * may be overwritten. Because the regexp parser does no such analysis, - * we currently save the entire saved array here. Lookaheads are thus - * a bit expensive. Note that the saved array is not needed for just - * the lookahead sub-match, but for the matching of the entire sequel. - * - * The temporary save buffer is pushed on to the valstack to handle - * errors correctly. Each lookahead causes a C recursion and pushes - * more stuff on the value stack. If the C recursion limit is less - * than the value stack slack, there is no need to check the stack. - * We do so regardless, just in case. - */ - - duk_int32_t skip; - duk_uint8_t **full_save; - const duk_uint8_t *sub_sp; - - DUK_ASSERT(re_ctx->nsaved > 0); - - duk_require_stack(re_ctx->thr, 1); - full_save = - (duk_uint8_t **) duk_push_fixed_buffer_nozero(re_ctx->thr, sizeof(duk_uint8_t *) * re_ctx->nsaved); - DUK_ASSERT(full_save != NULL); - duk_memcpy(full_save, re_ctx->saved, sizeof(duk_uint8_t *) * re_ctx->nsaved); - - skip = duk__bc_get_i32(re_ctx, &pc); - sub_sp = duk__match_regexp(re_ctx, pc, sp); - if (op == DUK_REOP_LOOKPOS) { - if (!sub_sp) { - goto lookahead_fail; - } - } else { - if (sub_sp) { - goto lookahead_fail; - } - } - sub_sp = duk__match_regexp(re_ctx, pc + skip, sp); - if (sub_sp) { - /* match: keep saves */ - duk_pop_unsafe(re_ctx->thr); - sp = sub_sp; - goto match; - } - - /* fall through */ - - lookahead_fail: - /* fail: restore saves */ - duk_memcpy((void *) re_ctx->saved, (const void *) full_save, sizeof(duk_uint8_t *) * re_ctx->nsaved); - duk_pop_unsafe(re_ctx->thr); - goto fail; - } - case DUK_REOP_BACKREFERENCE: { - /* - * Byte matching for back-references would be OK in case- - * sensitive matching. In case-insensitive matching we need - * to canonicalize characters, so back-reference matching needs - * to be done with codepoints instead. So, we just decode - * everything normally here, too. - * - * Note: back-reference index which is 0 or higher than - * NCapturingParens (= number of capturing parens in the - * -entire- regexp) is a compile time error. However, a - * backreference referring to a valid capture which has - * not matched anything always succeeds! See E5 Section - * 15.10.2.9, step 5, sub-step 3. - */ - duk_uint32_t idx; - const duk_uint8_t *p; - - idx = duk__bc_get_u32(re_ctx, &pc); - idx = idx << 1; /* backref n -> saved indices [n*2, n*2+1] */ - if (idx < 2 || idx + 1 >= re_ctx->nsaved) { - /* regexp compiler should catch these */ - DUK_D(DUK_DPRINT("internal error, backreference index insane")); - goto internal_error; - } - if (!re_ctx->saved[idx] || !re_ctx->saved[idx + 1]) { - /* capture is 'undefined', always matches! */ - DUK_DDD(DUK_DDDPRINT("backreference: saved[%ld,%ld] not complete, always match", - (long) idx, - (long) (idx + 1))); - break; - } - DUK_DDD(DUK_DDDPRINT("backreference: match saved[%ld,%ld]", (long) idx, (long) (idx + 1))); - - p = re_ctx->saved[idx]; - while (p < re_ctx->saved[idx + 1]) { - duk_codepoint_t c1, c2; - - /* Note: not necessary to check p against re_ctx->input_end: - * the memory access is checked by duk__inp_get_cp(), while - * valid compiled regexps cannot write a saved[] entry - * which points to outside the string. - */ - c1 = duk__inp_get_cp(re_ctx, &p); - DUK_ASSERT(c1 >= 0); - c2 = duk__inp_get_cp(re_ctx, &sp); - /* No need for an explicit c2 < 0 check: because c1 >= 0, - * the comparison will always fail if c2 < 0. - */ -#if 0 - if (c2 < 0) { - goto fail; - } -#endif - if (c1 != c2) { - goto fail; - } - } - break; - } - default: { - DUK_D(DUK_DPRINT("internal error, regexp opcode error: %ld", (long) op)); - goto internal_error; - } - } - } - -match: - re_ctx->recursion_depth--; - return sp; - -fail: - re_ctx->recursion_depth--; - return NULL; - -internal_error: - DUK_ERROR_INTERNAL(re_ctx->thr); - DUK_WO_NORETURN(return NULL;); -} - -/* - * Exposed matcher function which provides the semantics of RegExp.prototype.exec(). - * - * RegExp.prototype.test() has the same semantics as exec() but does not return the - * result object (which contains the matching string and capture groups). Currently - * there is no separate test() helper, so a temporary result object is created and - * discarded if test() is needed. This is intentional, to save code space. - * - * Input stack: [ ... re_obj input ] - * Output stack: [ ... result ] - */ - -DUK_LOCAL void duk__regexp_match_helper(duk_hthread *thr, duk_small_int_t force_global) { - duk_re_matcher_ctx re_ctx; - duk_hobject *h_regexp; - duk_hstring *h_bytecode; - duk_hstring *h_input; - duk_uint8_t *p_buf; - const duk_uint8_t *pc; - const duk_uint8_t *sp; - duk_small_int_t match = 0; - duk_small_int_t global; - duk_uint_fast32_t i; - double d; - duk_uint32_t char_offset; - - DUK_ASSERT(thr != NULL); - - DUK_DD(DUK_DDPRINT("regexp match: regexp=%!T, input=%!T", - (duk_tval *) duk_get_tval(thr, -2), - (duk_tval *) duk_get_tval(thr, -1))); - - /* - * Regexp instance check, bytecode check, input coercion. - * - * See E5 Section 15.10.6. - */ - - /* TypeError if wrong; class check, see E5 Section 15.10.6 */ - h_regexp = duk_require_hobject_with_class(thr, -2, DUK_HOBJECT_CLASS_REGEXP); - DUK_ASSERT(h_regexp != NULL); - DUK_ASSERT(DUK_HOBJECT_GET_CLASS_NUMBER(h_regexp) == DUK_HOBJECT_CLASS_REGEXP); - DUK_UNREF(h_regexp); - - h_input = duk_to_hstring(thr, -1); - DUK_ASSERT(h_input != NULL); - - duk_xget_owndataprop_stridx_short(thr, -2, DUK_STRIDX_INT_BYTECODE); /* [ ... re_obj input ] -> [ ... re_obj input bc ] */ - h_bytecode = - duk_require_hstring(thr, -1); /* no regexp instance should exist without a non-configurable bytecode property */ - DUK_ASSERT(h_bytecode != NULL); - - /* - * Basic context initialization. - * - * Some init values are read from the bytecode header - * whose format is (UTF-8 codepoints): - * - * uint flags - * uint nsaved (even, 2n+2 where n = num captures) - */ - - /* [ ... re_obj input bc ] */ - - duk_memzero(&re_ctx, sizeof(re_ctx)); - - re_ctx.thr = thr; - re_ctx.input = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_input); - re_ctx.input_end = re_ctx.input + DUK_HSTRING_GET_BYTELEN(h_input); - re_ctx.bytecode = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_bytecode); - re_ctx.bytecode_end = re_ctx.bytecode + DUK_HSTRING_GET_BYTELEN(h_bytecode); - re_ctx.saved = NULL; - re_ctx.recursion_limit = DUK_USE_REGEXP_EXECUTOR_RECLIMIT; - re_ctx.steps_limit = DUK_RE_EXECUTE_STEPS_LIMIT; - - /* read header */ - pc = re_ctx.bytecode; - re_ctx.re_flags = duk__bc_get_u32(&re_ctx, &pc); - re_ctx.nsaved = duk__bc_get_u32(&re_ctx, &pc); - re_ctx.bytecode = pc; - - DUK_ASSERT(DUK_RE_FLAG_GLOBAL < 0x10000UL); /* must fit into duk_small_int_t */ - global = (duk_small_int_t) (force_global | (duk_small_int_t) (re_ctx.re_flags & DUK_RE_FLAG_GLOBAL)); - - DUK_ASSERT(re_ctx.nsaved >= 2); - DUK_ASSERT((re_ctx.nsaved % 2) == 0); - - p_buf = (duk_uint8_t *) duk_push_fixed_buffer(thr, sizeof(duk_uint8_t *) * re_ctx.nsaved); /* rely on zeroing */ - DUK_UNREF(p_buf); - re_ctx.saved = (const duk_uint8_t **) duk_get_buffer(thr, -1, NULL); - DUK_ASSERT(re_ctx.saved != NULL); - - /* [ ... re_obj input bc saved_buf ] */ - -#if defined(DUK_USE_EXPLICIT_NULL_INIT) - for (i = 0; i < re_ctx.nsaved; i++) { - re_ctx.saved[i] = (duk_uint8_t *) NULL; - } -#elif defined(DUK_USE_ZERO_BUFFER_DATA) - /* buffer is automatically zeroed */ -#else - duk_memzero((void *) p_buf, sizeof(duk_uint8_t *) * re_ctx.nsaved); -#endif - - DUK_DDD(DUK_DDDPRINT("regexp ctx initialized, flags=0x%08lx, nsaved=%ld, recursion_limit=%ld, steps_limit=%ld", - (unsigned long) re_ctx.re_flags, - (long) re_ctx.nsaved, - (long) re_ctx.recursion_limit, - (long) re_ctx.steps_limit)); - - /* - * Get starting character offset for match, and initialize 'sp' based on it. - * - * Note: lastIndex is non-configurable so it must be present (we check the - * internal class of the object above, so we know it is). User code can set - * its value to an arbitrary (garbage) value though; E5 requires that lastIndex - * be coerced to a number before using. The code below works even if the - * property is missing: the value will then be coerced to zero. - * - * Note: lastIndex may be outside Uint32 range even after ToInteger() coercion. - * For instance, ToInteger(+Infinity) = +Infinity. We track the match offset - * as an integer, but pre-check it to be inside the 32-bit range before the loop. - * If not, the check in E5 Section 15.10.6.2, step 9.a applies. - */ - - /* XXX: lastIndex handling produces a lot of asm */ - - /* [ ... re_obj input bc saved_buf ] */ - - duk_get_prop_stridx_short(thr, -4, DUK_STRIDX_LAST_INDEX); /* -> [ ... re_obj input bc saved_buf lastIndex ] */ - (void) duk_to_int(thr, -1); /* ToInteger(lastIndex) */ - d = duk_get_number(thr, -1); /* integer, but may be +/- Infinite, +/- zero (not NaN, though) */ - duk_pop_nodecref_unsafe(thr); - - if (global) { - if (d < 0.0 || d > (double) DUK_HSTRING_GET_CHARLEN(h_input)) { - /* match fail */ - char_offset = 0; /* not really necessary */ - DUK_ASSERT(match == 0); - goto match_over; - } - char_offset = (duk_uint32_t) d; - } else { - /* lastIndex must be ignored for non-global regexps, but get the - * value for (theoretical) side effects. No side effects can - * really occur, because lastIndex is a normal property and is - * always non-configurable for RegExp instances. - */ - char_offset = (duk_uint32_t) 0; - } - - DUK_ASSERT(char_offset <= DUK_HSTRING_GET_CHARLEN(h_input)); - sp = re_ctx.input + duk_heap_strcache_offset_char2byte(thr, h_input, char_offset); - - /* - * Match loop. - * - * Try matching at different offsets until match found or input exhausted. - */ - - /* [ ... re_obj input bc saved_buf ] */ - - DUK_ASSERT(match == 0); - - for (;;) { - /* char offset in [0, h_input->clen] (both ends inclusive), checked before entry */ - DUK_ASSERT_DISABLE(char_offset >= 0); - DUK_ASSERT(char_offset <= DUK_HSTRING_GET_CHARLEN(h_input)); - - /* Note: re_ctx.steps is intentionally not reset, it applies to the entire unanchored match */ - DUK_ASSERT(re_ctx.recursion_depth == 0); - - DUK_DDD(DUK_DDDPRINT("attempt match at char offset %ld; %p [%p,%p]", - (long) char_offset, - (const void *) sp, - (const void *) re_ctx.input, - (const void *) re_ctx.input_end)); - - /* - * Note: - * - * - duk__match_regexp() is required not to longjmp() in ordinary "non-match" - * conditions; a longjmp() will terminate the entire matching process. - * - * - Clearing saved[] is not necessary because backtracking does it - * - * - Backtracking also rewinds re_ctx.recursion back to zero, unless an - * internal/limit error occurs (which causes a longjmp()) - * - * - If we supported anchored matches, we would break out here - * unconditionally; however, ECMAScript regexps don't have anchored - * matches. It might make sense to implement a fast bail-out if - * the regexp begins with '^' and sp is not 0: currently we'll just - * run through the entire input string, trivially failing the match - * at every non-zero offset. - */ - - if (duk__match_regexp(&re_ctx, re_ctx.bytecode, sp) != NULL) { - DUK_DDD(DUK_DDDPRINT("match at offset %ld", (long) char_offset)); - match = 1; - break; - } - - /* advance by one character (code point) and one char_offset */ - char_offset++; - if (char_offset > DUK_HSTRING_GET_CHARLEN(h_input)) { - /* - * Note: - * - * - Intentionally attempt (empty) match at char_offset == k_input->clen - * - * - Negative char_offsets have been eliminated and char_offset is duk_uint32_t - * -> no need or use for a negative check - */ - - DUK_DDD(DUK_DDDPRINT("no match after trying all sp offsets")); - break; - } - - /* avoid calling at end of input, will DUK_ERROR (above check suffices to avoid this) */ - (void) duk__utf8_advance(thr, &sp, re_ctx.input, re_ctx.input_end, (duk_uint_fast32_t) 1); - } - -match_over: - - /* - * Matching complete, create result array or return a 'null'. Update lastIndex - * if necessary. See E5 Section 15.10.6.2. - * - * Because lastIndex is a character (not byte) offset, we need the character - * length of the match which we conveniently get as a side effect of interning - * the matching substring (0th index of result array). - * - * saved[0] start pointer (~ byte offset) of current match - * saved[1] end pointer (~ byte offset) of current match (exclusive) - * char_offset start character offset of current match (-> .index of result) - * char_end_offset end character offset (computed below) - */ - - /* [ ... re_obj input bc saved_buf ] */ - - if (match) { -#if defined(DUK_USE_ASSERTIONS) - duk_hobject *h_res; -#endif - duk_uint32_t char_end_offset = 0; - - DUK_DDD(DUK_DDDPRINT("regexp matches at char_offset %ld", (long) char_offset)); - - DUK_ASSERT(re_ctx.nsaved >= 2); /* must have start and end */ - DUK_ASSERT((re_ctx.nsaved % 2) == 0); /* and even number */ - - /* XXX: Array size is known before and (2 * re_ctx.nsaved) but not taken - * advantage of now. The array is not compacted either, as regexp match - * objects are usually short lived. - */ - - duk_push_array(thr); - -#if defined(DUK_USE_ASSERTIONS) - h_res = duk_require_hobject(thr, -1); - DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(h_res)); - DUK_ASSERT(DUK_HOBJECT_HAS_EXOTIC_ARRAY(h_res)); - DUK_ASSERT(DUK_HOBJECT_GET_CLASS_NUMBER(h_res) == DUK_HOBJECT_CLASS_ARRAY); -#endif - - /* [ ... re_obj input bc saved_buf res_obj ] */ - - duk_push_u32(thr, char_offset); - duk_xdef_prop_stridx_short_wec(thr, -2, DUK_STRIDX_INDEX); - - duk_dup_m4(thr); - duk_xdef_prop_stridx_short_wec(thr, -2, DUK_STRIDX_INPUT); - - for (i = 0; i < re_ctx.nsaved; i += 2) { - /* Captures which are undefined have NULL pointers and are returned - * as 'undefined'. The same is done when saved[] pointers are insane - * (this should, of course, never happen in practice). - */ - duk_push_uarridx(thr, (duk_uarridx_t) (i / 2)); - - if (re_ctx.saved[i] && re_ctx.saved[i + 1] && re_ctx.saved[i + 1] >= re_ctx.saved[i]) { - duk_push_lstring(thr, - (const char *) re_ctx.saved[i], - (duk_size_t) (re_ctx.saved[i + 1] - re_ctx.saved[i])); - if (i == 0) { - /* Assumes that saved[0] and saved[1] are always - * set by regexp bytecode (if not, char_end_offset - * will be zero). Also assumes clen reflects the - * correct char length. - */ - char_end_offset = char_offset + (duk_uint32_t) duk_get_length(thr, -1); /* add charlen */ - } - } else { - duk_push_undefined(thr); - } - - /* [ ... re_obj input bc saved_buf res_obj idx val ] */ - duk_def_prop(thr, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_WEC); - } - - /* [ ... re_obj input bc saved_buf res_obj ] */ - - /* NB: 'length' property is automatically updated by the array setup loop */ - - if (global) { - /* global regexp: lastIndex updated on match */ - duk_push_u32(thr, char_end_offset); - duk_put_prop_stridx_short(thr, -6, DUK_STRIDX_LAST_INDEX); - } else { - /* non-global regexp: lastIndex never updated on match */ - ; - } - } else { - /* - * No match, E5 Section 15.10.6.2, step 9.a.i - 9.a.ii apply, regardless - * of 'global' flag of the RegExp. In particular, if lastIndex is invalid - * initially, it is reset to zero. - */ - - DUK_DDD(DUK_DDDPRINT("regexp does not match")); - - duk_push_null(thr); - - /* [ ... re_obj input bc saved_buf res_obj ] */ - - duk_push_int(thr, 0); - duk_put_prop_stridx_short(thr, -6, DUK_STRIDX_LAST_INDEX); - } - - /* [ ... re_obj input bc saved_buf res_obj ] */ - - duk_insert(thr, -5); - - /* [ ... res_obj re_obj input bc saved_buf ] */ - - duk_pop_n_unsafe(thr, 4); - - /* [ ... res_obj ] */ - - /* XXX: these last tricks are unnecessary if the function is made - * a genuine native function. - */ -} - -DUK_INTERNAL void duk_regexp_match(duk_hthread *thr) { - duk__regexp_match_helper(thr, 0 /*force_global*/); -} - -/* This variant is needed by String.prototype.split(); it needs to perform - * global-style matching on a cloned RegExp which is potentially non-global. - */ -DUK_INTERNAL void duk_regexp_match_force_global(duk_hthread *thr) { - duk__regexp_match_helper(thr, 1 /*force_global*/); -} - -#else /* DUK_USE_REGEXP_SUPPORT */ - -/* regexp support disabled */ - -#endif /* DUK_USE_REGEXP_SUPPORT */ -/* - * Self tests to ensure execution environment is sane. Intended to catch - * compiler/platform problems which cannot be detected at compile time. - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_SELF_TESTS) - -/* - * Unions and structs for self tests - */ - -typedef union { - double d; - duk_uint8_t x[8]; -} duk__test_double_union; - -/* Self test failed. Expects a local variable 'error_count' to exist. */ -#define DUK__FAILED(msg) \ - do { \ - DUK_D(DUK_DPRINT("self test failed: " #msg " at " DUK_FILE_MACRO ":" DUK_MACRO_STRINGIFY(DUK_LINE_MACRO))); \ - error_count++; \ - } while (0) - -#define DUK__DBLUNION_CMP_TRUE(a, b) \ - do { \ - if (duk_memcmp((const void *) (a), (const void *) (b), sizeof(duk__test_double_union)) != 0) { \ - DUK__FAILED("double union compares false (expected true)"); \ - } \ - } while (0) - -#define DUK__DBLUNION_CMP_FALSE(a, b) \ - do { \ - if (duk_memcmp((const void *) (a), (const void *) (b), sizeof(duk__test_double_union)) == 0) { \ - DUK__FAILED("double union compares true (expected false)"); \ - } \ - } while (0) - -typedef union { - duk_uint32_t i; - duk_uint8_t x[8]; -} duk__test_u32_union; - -#if defined(DUK_USE_INTEGER_LE) -#define DUK__U32_INIT(u, a, b, c, d) \ - do { \ - (u)->x[0] = (d); \ - (u)->x[1] = (c); \ - (u)->x[2] = (b); \ - (u)->x[3] = (a); \ - } while (0) -#elif defined(DUK_USE_INTEGER_ME) -#error integer mixed endian not supported now -#elif defined(DUK_USE_INTEGER_BE) -#define DUK__U32_INIT(u, a, b, c, d) \ - do { \ - (u)->x[0] = (a); \ - (u)->x[1] = (b); \ - (u)->x[2] = (c); \ - (u)->x[3] = (d); \ - } while (0) -#else -#error unknown integer endianness -#endif - -#if defined(DUK_USE_DOUBLE_LE) -#define DUK__DOUBLE_INIT(u, a, b, c, d, e, f, g, h) \ - do { \ - (u)->x[0] = (h); \ - (u)->x[1] = (g); \ - (u)->x[2] = (f); \ - (u)->x[3] = (e); \ - (u)->x[4] = (d); \ - (u)->x[5] = (c); \ - (u)->x[6] = (b); \ - (u)->x[7] = (a); \ - } while (0) -#define DUK__DOUBLE_COMPARE(u, a, b, c, d, e, f, g, h) \ - ((u)->x[0] == (h) && (u)->x[1] == (g) && (u)->x[2] == (f) && (u)->x[3] == (e) && (u)->x[4] == (d) && (u)->x[5] == (c) && \ - (u)->x[6] == (b) && (u)->x[7] == (a)) -#elif defined(DUK_USE_DOUBLE_ME) -#define DUK__DOUBLE_INIT(u, a, b, c, d, e, f, g, h) \ - do { \ - (u)->x[0] = (d); \ - (u)->x[1] = (c); \ - (u)->x[2] = (b); \ - (u)->x[3] = (a); \ - (u)->x[4] = (h); \ - (u)->x[5] = (g); \ - (u)->x[6] = (f); \ - (u)->x[7] = (e); \ - } while (0) -#define DUK__DOUBLE_COMPARE(u, a, b, c, d, e, f, g, h) \ - ((u)->x[0] == (d) && (u)->x[1] == (c) && (u)->x[2] == (b) && (u)->x[3] == (a) && (u)->x[4] == (h) && (u)->x[5] == (g) && \ - (u)->x[6] == (f) && (u)->x[7] == (e)) -#elif defined(DUK_USE_DOUBLE_BE) -#define DUK__DOUBLE_INIT(u, a, b, c, d, e, f, g, h) \ - do { \ - (u)->x[0] = (a); \ - (u)->x[1] = (b); \ - (u)->x[2] = (c); \ - (u)->x[3] = (d); \ - (u)->x[4] = (e); \ - (u)->x[5] = (f); \ - (u)->x[6] = (g); \ - (u)->x[7] = (h); \ - } while (0) -#define DUK__DOUBLE_COMPARE(u, a, b, c, d, e, f, g, h) \ - ((u)->x[0] == (a) && (u)->x[1] == (b) && (u)->x[2] == (c) && (u)->x[3] == (d) && (u)->x[4] == (e) && (u)->x[5] == (f) && \ - (u)->x[6] == (g) && (u)->x[7] == (h)) -#else -#error unknown double endianness -#endif - -/* - * Various sanity checks for typing - */ - -DUK_LOCAL duk_uint_t duk__selftest_types(void) { - duk_uint_t error_count = 0; - - if (!(sizeof(duk_int8_t) == 1 && sizeof(duk_uint8_t) == 1 && sizeof(duk_int16_t) == 2 && sizeof(duk_uint16_t) == 2 && - sizeof(duk_int32_t) == 4 && sizeof(duk_uint32_t) == 4)) { - DUK__FAILED("duk_(u)int{8,16,32}_t size"); - } -#if defined(DUK_USE_64BIT_OPS) - if (!(sizeof(duk_int64_t) == 8 && sizeof(duk_uint64_t) == 8)) { - DUK__FAILED("duk_(u)int64_t size"); - } -#endif - - if (!(sizeof(duk_size_t) >= sizeof(duk_uint_t))) { - /* Some internal code now assumes that all duk_uint_t values - * can be expressed with a duk_size_t. - */ - DUK__FAILED("duk_size_t is smaller than duk_uint_t"); - } - if (!(sizeof(duk_int_t) >= 4)) { - DUK__FAILED("duk_int_t is not 32 bits"); - } - - return error_count; -} - -/* - * Packed tval sanity - */ - -DUK_LOCAL duk_uint_t duk__selftest_packed_tval(void) { - duk_uint_t error_count = 0; - -#if defined(DUK_USE_PACKED_TVAL) - if (sizeof(void *) > 4) { - DUK__FAILED("packed duk_tval in use but sizeof(void *) > 4"); - } -#endif - - return error_count; -} - -/* - * Two's complement arithmetic. - */ - -DUK_LOCAL duk_uint_t duk__selftest_twos_complement(void) { - duk_uint_t error_count = 0; - volatile int test; - test = -1; - - /* Note that byte order doesn't affect this test: all bytes in - * 'test' will be 0xFF for two's complement. - */ - if (((volatile duk_uint8_t *) &test)[0] != (duk_uint8_t) 0xff) { - DUK__FAILED("two's complement arithmetic"); - } - - return error_count; -} - -/* - * Byte order. Important to self check, because on some exotic platforms - * there is no actual detection but rather assumption based on platform - * defines. - */ - -DUK_LOCAL duk_uint_t duk__selftest_byte_order(void) { - duk_uint_t error_count = 0; - duk__test_u32_union u1; - duk__test_double_union u2; - - /* - * >>> struct.pack('>d', 102030405060).encode('hex') - * '4237c17c6dc40000' - */ - - DUK__U32_INIT(&u1, 0xde, 0xad, 0xbe, 0xef); - DUK__DOUBLE_INIT(&u2, 0x42, 0x37, 0xc1, 0x7c, 0x6d, 0xc4, 0x00, 0x00); - - if (u1.i != (duk_uint32_t) 0xdeadbeefUL) { - DUK__FAILED("duk_uint32_t byte order"); - } - - if (!duk_double_equals(u2.d, 102030405060.0)) { - DUK__FAILED("double byte order"); - } - - return error_count; -} - -/* - * DUK_BSWAP macros - */ - -DUK_LOCAL duk_uint_t duk__selftest_bswap_macros(void) { - duk_uint_t error_count = 0; - volatile duk_uint32_t x32_input, x32_output; - duk_uint32_t x32; - volatile duk_uint16_t x16_input, x16_output; - duk_uint16_t x16; - duk_double_union du; - duk_double_t du_diff; -#if defined(DUK_BSWAP64) - volatile duk_uint64_t x64_input, x64_output; - duk_uint64_t x64; -#endif - - /* Cover both compile time and runtime bswap operations, as these - * may have different bugs. - */ - - x16_input = 0xbeefUL; - x16 = x16_input; - x16 = DUK_BSWAP16(x16); - x16_output = x16; - if (x16_output != (duk_uint16_t) 0xefbeUL) { - DUK__FAILED("DUK_BSWAP16"); - } - - x16 = 0xbeefUL; - x16 = DUK_BSWAP16(x16); - if (x16 != (duk_uint16_t) 0xefbeUL) { - DUK__FAILED("DUK_BSWAP16"); - } - - x32_input = 0xdeadbeefUL; - x32 = x32_input; - x32 = DUK_BSWAP32(x32); - x32_output = x32; - if (x32_output != (duk_uint32_t) 0xefbeaddeUL) { - DUK__FAILED("DUK_BSWAP32"); - } - - x32 = 0xdeadbeefUL; - x32 = DUK_BSWAP32(x32); - if (x32 != (duk_uint32_t) 0xefbeaddeUL) { - DUK__FAILED("DUK_BSWAP32"); - } - -#if defined(DUK_BSWAP64) - x64_input = DUK_U64_CONSTANT(0x8899aabbccddeeff); - x64 = x64_input; - x64 = DUK_BSWAP64(x64); - x64_output = x64; - if (x64_output != (duk_uint64_t) DUK_U64_CONSTANT(0xffeeddccbbaa9988)) { - DUK__FAILED("DUK_BSWAP64"); - } - - x64 = DUK_U64_CONSTANT(0x8899aabbccddeeff); - x64 = DUK_BSWAP64(x64); - if (x64 != (duk_uint64_t) DUK_U64_CONSTANT(0xffeeddccbbaa9988)) { - DUK__FAILED("DUK_BSWAP64"); - } -#endif - - /* >>> struct.unpack('>d', '4000112233445566'.decode('hex')) - * (2.008366013071895,) - */ - - du.uc[0] = 0x40; - du.uc[1] = 0x00; - du.uc[2] = 0x11; - du.uc[3] = 0x22; - du.uc[4] = 0x33; - du.uc[5] = 0x44; - du.uc[6] = 0x55; - du.uc[7] = 0x66; - DUK_DBLUNION_DOUBLE_NTOH(&du); - du_diff = du.d - 2.008366013071895; -#if 0 - DUK_D(DUK_DPRINT("du_diff: %lg\n", (double) du_diff)); -#endif - if (du_diff > 1e-15) { - /* Allow very small lenience because some compilers won't parse - * exact IEEE double constants (happened in matrix testing with - * Linux gcc-4.8 -m32 at least). - */ -#if 0 - DUK_D(DUK_DPRINT("Result of DUK_DBLUNION_DOUBLE_NTOH: %02x %02x %02x %02x %02x %02x %02x %02x\n", - (unsigned int) du.uc[0], (unsigned int) du.uc[1], - (unsigned int) du.uc[2], (unsigned int) du.uc[3], - (unsigned int) du.uc[4], (unsigned int) du.uc[5], - (unsigned int) du.uc[6], (unsigned int) du.uc[7])); -#endif - DUK__FAILED("DUK_DBLUNION_DOUBLE_NTOH"); - } - - return error_count; -} - -/* - * Basic double / byte union memory layout. - */ - -DUK_LOCAL duk_uint_t duk__selftest_double_union_size(void) { - duk_uint_t error_count = 0; - - if (sizeof(duk__test_double_union) != 8) { - DUK__FAILED("invalid union size"); - } - - return error_count; -} - -/* - * Union aliasing, see misc/clang_aliasing.c. - */ - -DUK_LOCAL duk_uint_t duk__selftest_double_aliasing(void) { - /* This testcase fails when Emscripten-generated code runs on Firefox. - * It's not an issue because the failure should only affect packed - * duk_tval representation, which is not used with Emscripten. - */ -#if defined(DUK_USE_PACKED_TVAL) - duk_uint_t error_count = 0; - duk__test_double_union a, b; - - /* Test signaling NaN and alias assignment in all endianness combinations. - */ - - /* little endian */ - a.x[0] = 0x11; - a.x[1] = 0x22; - a.x[2] = 0x33; - a.x[3] = 0x44; - a.x[4] = 0x00; - a.x[5] = 0x00; - a.x[6] = 0xf1; - a.x[7] = 0xff; - b = a; - DUK__DBLUNION_CMP_TRUE(&a, &b); - - /* big endian */ - a.x[0] = 0xff; - a.x[1] = 0xf1; - a.x[2] = 0x00; - a.x[3] = 0x00; - a.x[4] = 0x44; - a.x[5] = 0x33; - a.x[6] = 0x22; - a.x[7] = 0x11; - b = a; - DUK__DBLUNION_CMP_TRUE(&a, &b); - - /* mixed endian */ - a.x[0] = 0x00; - a.x[1] = 0x00; - a.x[2] = 0xf1; - a.x[3] = 0xff; - a.x[4] = 0x11; - a.x[5] = 0x22; - a.x[6] = 0x33; - a.x[7] = 0x44; - b = a; - DUK__DBLUNION_CMP_TRUE(&a, &b); - - return error_count; -#else - DUK_D(DUK_DPRINT("skip double aliasing self test when duk_tval is not packed")); - return 0; -#endif -} - -/* - * Zero sign, see misc/tcc_zerosign2.c. - */ - -DUK_LOCAL duk_uint_t duk__selftest_double_zero_sign(void) { - duk_uint_t error_count = 0; - duk__test_double_union a, b; - - a.d = 0.0; - b.d = -a.d; - DUK__DBLUNION_CMP_FALSE(&a, &b); - - return error_count; -} - -/* - * Rounding mode: Duktape assumes round-to-nearest, check that this is true. - * If we had C99 fenv.h we could check that fegetround() == FE_TONEAREST, - * but we don't want to rely on that header; and even if we did, it's good - * to ensure the rounding actually works. - */ - -DUK_LOCAL duk_uint_t duk__selftest_double_rounding(void) { - duk_uint_t error_count = 0; - duk__test_double_union a, b, c; - -#if 0 - /* Include and test manually; these trigger failures: */ - fesetround(FE_UPWARD); - fesetround(FE_DOWNWARD); - fesetround(FE_TOWARDZERO); - - /* This is the default and passes. */ - fesetround(FE_TONEAREST); -#endif - - /* Rounding tests check that none of the other modes (round to - * +Inf, round to -Inf, round to zero) can be active: - * http://www.gnu.org/software/libc/manual/html_node/Rounding.html - */ - - /* 1.0 + 2^(-53): result is midway between 1.0 and 1.0 + ulp. - * Round to nearest: 1.0 - * Round to +Inf: 1.0 + ulp - * Round to -Inf: 1.0 - * Round to zero: 1.0 - * => Correct result eliminates round to +Inf. - */ - DUK__DOUBLE_INIT(&a, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); - DUK__DOUBLE_INIT(&b, 0x3c, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); - duk_memset((void *) &c, 0, sizeof(c)); - c.d = a.d + b.d; - if (!DUK__DOUBLE_COMPARE(&c, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)) { - DUK_D(DUK_DPRINT("broken result (native endiannesss): %02x %02x %02x %02x %02x %02x %02x %02x", - (unsigned int) c.x[0], - (unsigned int) c.x[1], - (unsigned int) c.x[2], - (unsigned int) c.x[3], - (unsigned int) c.x[4], - (unsigned int) c.x[5], - (unsigned int) c.x[6], - (unsigned int) c.x[7])); - DUK__FAILED("invalid result from 1.0 + 0.5ulp"); - } - - /* (1.0 + ulp) + 2^(-53): result is midway between 1.0 + ulp and 1.0 + 2*ulp. - * Round to nearest: 1.0 + 2*ulp (round to even mantissa) - * Round to +Inf: 1.0 + 2*ulp - * Round to -Inf: 1.0 + ulp - * Round to zero: 1.0 + ulp - * => Correct result eliminates round to -Inf and round to zero. - */ - DUK__DOUBLE_INIT(&a, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01); - DUK__DOUBLE_INIT(&b, 0x3c, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); - duk_memset((void *) &c, 0, sizeof(c)); - c.d = a.d + b.d; - if (!DUK__DOUBLE_COMPARE(&c, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02)) { - DUK_D(DUK_DPRINT("broken result (native endiannesss): %02x %02x %02x %02x %02x %02x %02x %02x", - (unsigned int) c.x[0], - (unsigned int) c.x[1], - (unsigned int) c.x[2], - (unsigned int) c.x[3], - (unsigned int) c.x[4], - (unsigned int) c.x[5], - (unsigned int) c.x[6], - (unsigned int) c.x[7])); - DUK__FAILED("invalid result from (1.0 + ulp) + 0.5ulp"); - } - - /* Could do negative number testing too, but the tests above should - * differentiate between IEEE 754 rounding modes. - */ - return error_count; -} - -/* - * fmod(): often a portability issue in embedded or bare platform targets. - * Check for at least minimally correct behavior. Unlike some other math - * functions (like cos()) Duktape relies on fmod() internally too. - */ - -DUK_LOCAL duk_uint_t duk__selftest_fmod(void) { - duk_uint_t error_count = 0; - duk__test_double_union u1, u2; - volatile duk_double_t t1, t2, t3; - - /* fmod() with integer argument and exponent 2^32 is used by e.g. - * ToUint32() and some Duktape internals. - */ - u1.d = DUK_FMOD(10.0, 4294967296.0); - u2.d = 10.0; - DUK__DBLUNION_CMP_TRUE(&u1, &u2); - - u1.d = DUK_FMOD(4294967306.0, 4294967296.0); - u2.d = 10.0; - DUK__DBLUNION_CMP_TRUE(&u1, &u2); - - u1.d = DUK_FMOD(73014444042.0, 4294967296.0); - u2.d = 10.0; - DUK__DBLUNION_CMP_TRUE(&u1, &u2); - - /* 52-bit integer split into two parts: - * >>> 0x1fedcba9876543 - * 8987183256397123 - * >>> float(0x1fedcba9876543) / float(2**53) - * 0.9977777777777778 - */ - u1.d = DUK_FMOD(8987183256397123.0, 4294967296.0); - u2.d = (duk_double_t) 0xa9876543UL; - DUK__DBLUNION_CMP_TRUE(&u1, &u2); - t1 = 8987183256397123.0; - t2 = 4294967296.0; - t3 = t1 / t2; - u1.d = DUK_FLOOR(t3); - u2.d = (duk_double_t) 0x1fedcbUL; - DUK__DBLUNION_CMP_TRUE(&u1, &u2); - - /* C99 behavior is for fmod() result sign to mathc argument sign. */ - u1.d = DUK_FMOD(-10.0, 4294967296.0); - u2.d = -10.0; - DUK__DBLUNION_CMP_TRUE(&u1, &u2); - - u1.d = DUK_FMOD(-4294967306.0, 4294967296.0); - u2.d = -10.0; - DUK__DBLUNION_CMP_TRUE(&u1, &u2); - - u1.d = DUK_FMOD(-73014444042.0, 4294967296.0); - u2.d = -10.0; - DUK__DBLUNION_CMP_TRUE(&u1, &u2); - - return error_count; -} - -/* - * Struct size/alignment if platform requires it - * - * There are some compiler specific struct padding pragmas etc in use, this - * selftest ensures they're correctly detected and used. - */ - -DUK_LOCAL duk_uint_t duk__selftest_struct_align(void) { - duk_uint_t error_count = 0; - -#if (DUK_USE_ALIGN_BY == 4) - if ((sizeof(duk_hbuffer_fixed) % 4) != 0) { - DUK__FAILED("sizeof(duk_hbuffer_fixed) not aligned to 4"); - } -#elif (DUK_USE_ALIGN_BY == 8) - if ((sizeof(duk_hbuffer_fixed) % 8) != 0) { - DUK__FAILED("sizeof(duk_hbuffer_fixed) not aligned to 8"); - } -#elif (DUK_USE_ALIGN_BY == 1) - /* no check */ -#else -#error invalid DUK_USE_ALIGN_BY -#endif - return error_count; -} - -/* - * 64-bit arithmetic - * - * There are some platforms/compilers where 64-bit types are available - * but don't work correctly. Test for known cases. - */ - -DUK_LOCAL duk_uint_t duk__selftest_64bit_arithmetic(void) { - duk_uint_t error_count = 0; -#if defined(DUK_USE_64BIT_OPS) - volatile duk_int64_t i; - volatile duk_double_t d; - - /* Catch a double-to-int64 cast issue encountered in practice. */ - d = 2147483648.0; - i = (duk_int64_t) d; - if (i != DUK_I64_CONSTANT(0x80000000)) { - DUK__FAILED("casting 2147483648.0 to duk_int64_t failed"); - } -#else - /* nop */ -#endif - return error_count; -} - -/* - * Casting - */ - -DUK_LOCAL duk_uint_t duk__selftest_cast_double_to_small_uint(void) { - /* - * https://github.com/svaarala/duktape/issues/127#issuecomment-77863473 - */ - - duk_uint_t error_count = 0; - - duk_double_t d1, d2; - duk_small_uint_t u; - - duk_double_t d1v, d2v; - duk_small_uint_t uv; - - /* Test without volatiles */ - - d1 = 1.0; - u = (duk_small_uint_t) d1; - d2 = (duk_double_t) u; - - if (!(duk_double_equals(d1, 1.0) && u == 1 && duk_double_equals(d2, 1.0) && duk_double_equals(d1, d2))) { - DUK__FAILED("double to duk_small_uint_t cast failed"); - } - - /* Same test with volatiles */ - - d1v = 1.0; - uv = (duk_small_uint_t) d1v; - d2v = (duk_double_t) uv; - - if (!(duk_double_equals(d1v, 1.0) && uv == 1 && duk_double_equals(d2v, 1.0) && duk_double_equals(d1v, d2v))) { - DUK__FAILED("double to duk_small_uint_t cast failed"); - } - - return error_count; -} - -DUK_LOCAL duk_uint_t duk__selftest_cast_double_to_uint32(void) { - /* - * This test fails on an exotic ARM target; double-to-uint - * cast is incorrectly clamped to -signed- int highest value. - * - * https://github.com/svaarala/duktape/issues/336 - */ - - duk_uint_t error_count = 0; - duk_double_t dv; - duk_uint32_t uv; - - dv = 3735928559.0; /* 0xdeadbeef in decimal */ - uv = (duk_uint32_t) dv; - - if (uv != 0xdeadbeefUL) { - DUK__FAILED("double to duk_uint32_t cast failed"); - } - - return error_count; -} - -/* - * Minimal test of user supplied allocation functions - * - * - Basic alloc + realloc + free cycle - * - * - Realloc to significantly larger size to (hopefully) trigger a - * relocation and check that relocation copying works - */ - -DUK_LOCAL duk_uint_t duk__selftest_alloc_funcs(duk_alloc_function alloc_func, - duk_realloc_function realloc_func, - duk_free_function free_func, - void *udata) { - duk_uint_t error_count = 0; - void *ptr; - void *new_ptr; - duk_small_int_t i, j; - unsigned char x; - - if (alloc_func == NULL || realloc_func == NULL || free_func == NULL) { - return 0; - } - - for (i = 1; i <= 256; i++) { - ptr = alloc_func(udata, (duk_size_t) i); - if (ptr == NULL) { - DUK_D(DUK_DPRINT("alloc failed, ignore")); - continue; /* alloc failed, ignore */ - } - for (j = 0; j < i; j++) { - ((unsigned char *) ptr)[j] = (unsigned char) (0x80 + j); - } - new_ptr = realloc_func(udata, ptr, 1024); - if (new_ptr == NULL) { - DUK_D(DUK_DPRINT("realloc failed, ignore")); - free_func(udata, ptr); - continue; /* realloc failed, ignore */ - } - ptr = new_ptr; - for (j = 0; j < i; j++) { - x = ((unsigned char *) ptr)[j]; - if (x != (unsigned char) (0x80 + j)) { - DUK_D(DUK_DPRINT("byte at index %ld doesn't match after realloc: %02lx", - (long) j, - (unsigned long) x)); - DUK__FAILED("byte compare after realloc"); - break; - } - } - free_func(udata, ptr); - } - - return error_count; -} - -/* - * Self test main - */ - -DUK_INTERNAL duk_uint_t duk_selftest_run_tests(duk_alloc_function alloc_func, - duk_realloc_function realloc_func, - duk_free_function free_func, - void *udata) { - duk_uint_t error_count = 0; - - DUK_D(DUK_DPRINT("self test starting")); - - error_count += duk__selftest_types(); - error_count += duk__selftest_packed_tval(); - error_count += duk__selftest_twos_complement(); - error_count += duk__selftest_byte_order(); - error_count += duk__selftest_bswap_macros(); - error_count += duk__selftest_double_union_size(); - error_count += duk__selftest_double_aliasing(); - error_count += duk__selftest_double_zero_sign(); - error_count += duk__selftest_double_rounding(); - error_count += duk__selftest_fmod(); - error_count += duk__selftest_struct_align(); - error_count += duk__selftest_64bit_arithmetic(); - error_count += duk__selftest_cast_double_to_small_uint(); - error_count += duk__selftest_cast_double_to_uint32(); - error_count += duk__selftest_alloc_funcs(alloc_func, realloc_func, free_func, udata); - - DUK_D(DUK_DPRINT("self test complete, total error count: %ld", (long) error_count)); - - return error_count; -} - -#endif /* DUK_USE_SELF_TESTS */ - -/* automatic undefs */ -#undef DUK__DBLUNION_CMP_FALSE -#undef DUK__DBLUNION_CMP_TRUE -#undef DUK__DOUBLE_COMPARE -#undef DUK__DOUBLE_INIT -#undef DUK__FAILED -#undef DUK__U32_INIT -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_FASTINT) - -/* - * Manually optimized double-to-fastint downgrade check. - * - * This check has a large impact on performance, especially for fastint - * slow paths, so must be changed carefully. The code should probably be - * optimized for the case where the result does not fit into a fastint, - * to minimize the penalty for "slow path code" dealing with fractions etc. - * - * At least on one tested soft float ARM platform double-to-int64 coercion - * is very slow (and sometimes produces incorrect results, see self tests). - * This algorithm combines a fastint compatibility check and extracting the - * integer value from an IEEE double for setting the tagged fastint. For - * other platforms a more naive approach might be better. - * - * See doc/fastint.rst for details. - */ - -DUK_INTERNAL DUK_ALWAYS_INLINE void duk_tval_set_number_chkfast_fast(duk_tval *tv, duk_double_t x) { - duk_double_union du; - duk_int64_t i; - duk_small_int_t expt; - duk_small_int_t shift; - - /* XXX: optimize for packed duk_tval directly? */ - - du.d = x; - i = (duk_int64_t) DUK_DBLUNION_GET_INT64(&du); - expt = (duk_small_int_t) ((i >> 52) & 0x07ff); - shift = expt - 1023; - - if (shift >= 0 && shift <= 46) { /* exponents 1023 to 1069 */ - duk_int64_t t; - - if (((DUK_I64_CONSTANT(0x000fffffffffffff) >> shift) & i) == 0) { - t = i | DUK_I64_CONSTANT(0x0010000000000000); /* implicit leading one */ - t = t & DUK_I64_CONSTANT(0x001fffffffffffff); - t = t >> (52 - shift); - if (i < 0) { - t = -t; - } - DUK_TVAL_SET_FASTINT(tv, t); - return; - } - } else if (shift == -1023) { /* exponent 0 */ - if (i >= 0 && (i & DUK_I64_CONSTANT(0x000fffffffffffff)) == 0) { - /* Note: reject negative zero. */ - DUK_TVAL_SET_FASTINT(tv, (duk_int64_t) 0); - return; - } - } else if (shift == 47) { /* exponent 1070 */ - if (i < 0 && (i & DUK_I64_CONSTANT(0x000fffffffffffff)) == 0) { - DUK_TVAL_SET_FASTINT(tv, (duk_int64_t) DUK_FASTINT_MIN); - return; - } - } - - DUK_TVAL_SET_DOUBLE(tv, x); - return; -} - -DUK_INTERNAL DUK_NOINLINE void duk_tval_set_number_chkfast_slow(duk_tval *tv, duk_double_t x) { - duk_tval_set_number_chkfast_fast(tv, x); -} - -/* - * Manually optimized number-to-double conversion - */ - -#if defined(DUK_USE_FASTINT) && defined(DUK_USE_PACKED_TVAL) -DUK_INTERNAL DUK_ALWAYS_INLINE duk_double_t duk_tval_get_number_packed(duk_tval *tv) { - duk_double_union du; - duk_uint64_t t; - - t = (duk_uint64_t) DUK_DBLUNION_GET_UINT64(tv); - if ((t >> 48) != DUK_TAG_FASTINT) { - return tv->d; - } else if (t & DUK_U64_CONSTANT(0x0000800000000000)) { - t = (duk_uint64_t) (-((duk_int64_t) t)); /* avoid unary minus on unsigned */ - t = t & DUK_U64_CONSTANT(0x0000ffffffffffff); /* negative */ - t |= DUK_U64_CONSTANT(0xc330000000000000); - DUK_DBLUNION_SET_UINT64(&du, t); - return du.d + 4503599627370496.0; /* 1 << 52 */ - } else if (t != 0) { - t &= DUK_U64_CONSTANT(0x0000ffffffffffff); /* positive */ - t |= DUK_U64_CONSTANT(0x4330000000000000); - DUK_DBLUNION_SET_UINT64(&du, t); - return du.d - 4503599627370496.0; /* 1 << 52 */ - } else { - return 0.0; /* zero */ - } -} -#endif /* DUK_USE_FASTINT && DUK_USE_PACKED_TVAL */ - -#if 0 /* unused */ -#if defined(DUK_USE_FASTINT) && !defined(DUK_USE_PACKED_TVAL) -DUK_INTERNAL DUK_ALWAYS_INLINE duk_double_t duk_tval_get_number_unpacked(duk_tval *tv) { - duk_double_union du; - duk_uint64_t t; - - DUK_ASSERT(tv->t == DUK_TAG_NUMBER || tv->t == DUK_TAG_FASTINT); - - if (tv->t == DUK_TAG_FASTINT) { - if (tv->v.fi >= 0) { - t = DUK_U64_CONSTANT(0x4330000000000000) | (duk_uint64_t) tv->v.fi; - DUK_DBLUNION_SET_UINT64(&du, t); - return du.d - 4503599627370496.0; /* 1 << 52 */ - } else { - t = DUK_U64_CONSTANT(0xc330000000000000) | (duk_uint64_t) (-tv->v.fi); - DUK_DBLUNION_SET_UINT64(&du, t); - return du.d + 4503599627370496.0; /* 1 << 52 */ - } - } else { - return tv->v.d; - } -} -#endif /* DUK_USE_FASTINT && DUK_USE_PACKED_TVAL */ -#endif /* 0 */ - -#if defined(DUK_USE_FASTINT) && !defined(DUK_USE_PACKED_TVAL) -DUK_INTERNAL DUK_ALWAYS_INLINE duk_double_t duk_tval_get_number_unpacked_fastint(duk_tval *tv) { - duk_double_union du; - duk_uint64_t t; - - DUK_ASSERT(tv->t == DUK_TAG_FASTINT); - - if (tv->v.fi >= 0) { - t = DUK_U64_CONSTANT(0x4330000000000000) | (duk_uint64_t) tv->v.fi; - DUK_DBLUNION_SET_UINT64(&du, t); - return du.d - 4503599627370496.0; /* 1 << 52 */ - } else { - t = DUK_U64_CONSTANT(0xc330000000000000) | (duk_uint64_t) (-tv->v.fi); - DUK_DBLUNION_SET_UINT64(&du, t); - return du.d + 4503599627370496.0; /* 1 << 52 */ - } -} -#endif /* DUK_USE_FASTINT && DUK_USE_PACKED_TVAL */ - -#endif /* DUK_USE_FASTINT */ - -/* - * Assertion helpers. - */ - -#if defined(DUK_USE_ASSERTIONS) -DUK_INTERNAL void duk_tval_assert_valid(duk_tval *tv) { - DUK_ASSERT(tv != NULL); -} -#endif -/* - * Unicode support tables automatically generated during build. - */ - -/* #include duk_internal.h -> already included */ - -/* - * Unicode tables containing ranges of Unicode characters in a - * packed format. These tables are used to match non-ASCII - * characters of complex productions by resorting to a linear - * range-by-range comparison. This is very slow, but is expected - * to be very rare in practical ECMAScript source code, and thus - * compactness is most important. - * - * The tables are matched using uni_range_match() and the format - * is described in tools/extract_chars.py. - */ - -#if defined(DUK_USE_SOURCE_NONBMP) -/* IdentifierStart production with ASCII excluded */ -/* duk_unicode_ids_noa[] */ -/* - * Automatically generated by extract_chars.py, do not edit! - */ - -const duk_uint8_t duk_unicode_ids_noa[1116] = { -249,176,176,80,111,7,47,15,47,254,11,197,191,0,72,2,15,115,66,19,50,7,2,34, -2,240,66,244,50,247,185,249,98,241,99,7,241,159,57,240,181,63,31,241,191, -21,18,245,50,15,1,24,27,35,15,2,2,240,239,15,244,156,15,10,241,26,21,6,240, -101,10,4,15,9,240,152,175,39,240,82,127,56,242,100,15,4,8,159,1,240,5,115, -19,240,98,98,4,52,15,2,14,18,47,0,27,9,85,19,240,98,98,18,18,31,17,50,15,5, -47,2,130,34,240,98,98,18,68,15,4,15,1,31,9,12,115,19,240,98,98,18,68,15,16, -18,47,1,15,3,2,84,34,52,18,2,20,20,36,191,8,15,38,114,34,240,114,240,4,15, -12,38,31,16,5,114,34,240,114,146,68,15,18,2,31,1,31,4,114,34,241,147,15,2, -6,41,47,10,86,240,36,240,130,130,3,111,44,242,2,29,111,44,18,2,66,240,130, -2,146,26,3,66,15,7,63,18,15,49,114,241,79,13,79,101,241,191,6,15,2,85,52,4, -24,37,205,15,3,241,98,6,3,241,178,255,224,63,35,54,32,35,63,25,35,63,17,35, -54,32,35,62,47,41,35,63,51,241,127,0,240,47,70,53,79,254,21,227,240,18,240, -166,243,180,168,194,63,0,240,47,0,240,47,0,194,47,1,242,79,21,5,15,53,244, -152,67,241,34,6,243,107,240,255,35,240,227,76,241,197,240,175,40,240,122, -242,95,68,15,79,241,255,3,111,41,240,238,27,241,207,12,241,79,27,43,241,67, -136,241,179,47,27,50,82,20,6,251,15,50,255,224,8,53,63,22,53,55,32,32,32, -47,15,63,37,38,32,66,38,67,53,92,98,38,246,96,224,240,44,245,112,80,57,32, -68,112,32,32,35,42,51,100,80,240,63,25,255,233,107,241,242,241,242,247,87, -52,29,241,98,6,3,242,136,15,2,240,122,98,98,98,98,98,98,98,111,66,15,254, -12,146,240,184,132,52,95,70,114,47,74,35,111,27,47,78,240,63,11,242,127,0, -255,224,244,255,240,0,138,143,60,255,240,4,14,47,2,255,227,127,243,95,30, -63,253,79,0,177,240,111,31,240,47,15,63,64,241,152,63,87,63,37,52,242,42, -34,35,47,7,240,255,36,240,15,34,243,5,64,33,207,12,191,7,240,191,13,143,31, -240,224,240,36,41,180,47,25,240,146,39,240,111,7,64,79,34,32,65,52,48,32, -240,162,58,130,213,53,53,166,38,47,27,43,159,99,240,255,255,0,26,150,223,7, -95,33,255,240,0,255,143,254,6,3,245,175,24,109,70,2,146,194,66,2,18,18,245, -207,19,255,224,93,240,79,48,63,38,241,171,246,100,47,119,241,111,10,127,10, -207,73,69,53,53,50,241,91,47,10,47,3,33,46,61,241,79,107,243,127,37,255, -223,13,79,33,242,31,16,239,14,111,22,191,14,63,20,87,36,241,207,142,240,79, -20,95,20,95,24,159,36,248,239,254,2,154,240,107,127,138,83,2,241,194,20,3, -240,123,240,122,240,255,51,240,50,27,240,107,240,175,56,242,135,31,50,15,1, -50,34,240,223,28,240,212,240,223,21,114,240,207,13,242,107,240,107,240,62, -240,47,96,243,159,41,242,62,242,62,241,79,254,13,15,13,176,159,6,248,207,7, -223,37,243,223,29,241,47,9,240,207,20,240,240,207,19,64,223,32,240,3,240, -112,32,241,95,2,47,9,244,102,32,35,46,41,143,31,241,135,49,63,6,38,33,36, -64,240,64,212,249,15,37,240,67,240,96,241,47,32,240,97,32,250,175,31,241, -179,241,111,32,240,96,242,223,27,224,243,159,11,253,127,28,246,111,48,241, -16,249,39,63,23,240,32,32,240,224,191,24,128,240,112,207,30,240,80,241,79, -41,255,152,47,21,240,48,242,63,14,246,38,33,47,22,240,112,240,181,33,47,16, -240,0,255,224,59,240,63,254,0,31,254,40,207,88,245,255,3,251,79,254,155,15, -254,50,31,254,236,95,254,19,159,255,0,16,173,255,225,43,143,15,246,63,14, -240,79,32,240,35,241,31,5,111,3,255,225,164,243,15,114,243,182,15,52,207, -50,18,15,14,255,240,0,110,169,255,225,229,255,240,1,64,31,254,1,31,35,47,3, -57,255,224,126,255,231,248,245,182,196,136,159,255,0,6,90,244,82,243,114, -19,3,19,50,178,2,98,243,18,51,114,98,240,194,50,66,4,98,255,224,70,63,9,47, -9,47,15,47,9,47,15,47,9,47,15,47,9,47,15,47,9,39,255,232,40,241,219,111,2, -15,254,6,95,28,255,228,8,251,95,45,243,72,15,254,58,131,47,11,33,32,48,41, -35,32,32,112,80,32,32,34,33,32,48,32,32,32,32,33,32,51,38,35,35,32,41,47,1, -98,36,47,1,255,240,0,3,143,255,0,149,201,241,191,254,242,124,252,227,255, -240,0,87,79,0,255,240,0,194,63,254,177,63,254,17,0, -}; -#else -/* IdentifierStart production with ASCII and non-BMP excluded */ -/* duk_unicode_ids_noabmp[] */ -/* - * Automatically generated by extract_chars.py, do not edit! - */ - -const duk_uint8_t duk_unicode_ids_noabmp[625] = { -249,176,176,80,111,7,47,15,47,254,11,197,191,0,72,2,15,115,66,19,50,7,2,34, -2,240,66,244,50,247,185,249,98,241,99,7,241,159,57,240,181,63,31,241,191, -21,18,245,50,15,1,24,27,35,15,2,2,240,239,15,244,156,15,10,241,26,21,6,240, -101,10,4,15,9,240,152,175,39,240,82,127,56,242,100,15,4,8,159,1,240,5,115, -19,240,98,98,4,52,15,2,14,18,47,0,27,9,85,19,240,98,98,18,18,31,17,50,15,5, -47,2,130,34,240,98,98,18,68,15,4,15,1,31,9,12,115,19,240,98,98,18,68,15,16, -18,47,1,15,3,2,84,34,52,18,2,20,20,36,191,8,15,38,114,34,240,114,240,4,15, -12,38,31,16,5,114,34,240,114,146,68,15,18,2,31,1,31,4,114,34,241,147,15,2, -6,41,47,10,86,240,36,240,130,130,3,111,44,242,2,29,111,44,18,2,66,240,130, -2,146,26,3,66,15,7,63,18,15,49,114,241,79,13,79,101,241,191,6,15,2,85,52,4, -24,37,205,15,3,241,98,6,3,241,178,255,224,63,35,54,32,35,63,25,35,63,17,35, -54,32,35,62,47,41,35,63,51,241,127,0,240,47,70,53,79,254,21,227,240,18,240, -166,243,180,168,194,63,0,240,47,0,240,47,0,194,47,1,242,79,21,5,15,53,244, -152,67,241,34,6,243,107,240,255,35,240,227,76,241,197,240,175,40,240,122, -242,95,68,15,79,241,255,3,111,41,240,238,27,241,207,12,241,79,27,43,241,67, -136,241,179,47,27,50,82,20,6,251,15,50,255,224,8,53,63,22,53,55,32,32,32, -47,15,63,37,38,32,66,38,67,53,92,98,38,246,96,224,240,44,245,112,80,57,32, -68,112,32,32,35,42,51,100,80,240,63,25,255,233,107,241,242,241,242,247,87, -52,29,241,98,6,3,242,136,15,2,240,122,98,98,98,98,98,98,98,111,66,15,254, -12,146,240,184,132,52,95,70,114,47,74,35,111,27,47,78,240,63,11,242,127,0, -255,224,244,255,240,0,138,143,60,255,240,4,14,47,2,255,227,127,243,95,30, -63,253,79,0,177,240,111,31,240,47,15,63,64,241,152,63,87,63,37,52,242,42, -34,35,47,7,240,255,36,240,15,34,243,5,64,33,207,12,191,7,240,191,13,143,31, -240,224,240,36,41,180,47,25,240,146,39,240,111,7,64,79,34,32,65,52,48,32, -240,162,58,130,213,53,53,166,38,47,27,43,159,99,240,255,255,0,26,150,223,7, -95,33,255,240,0,255,143,254,6,3,245,175,24,109,70,2,146,194,66,2,18,18,245, -207,19,255,224,93,240,79,48,63,38,241,171,246,100,47,119,241,111,10,127,10, -207,73,69,53,53,50,0, -}; -#endif - -#if defined(DUK_USE_SOURCE_NONBMP) -/* IdentifierStart production with Letter and ASCII excluded */ -/* duk_unicode_ids_m_let_noa[] */ -/* - * Automatically generated by extract_chars.py, do not edit! - */ - -const duk_uint8_t duk_unicode_ids_m_let_noa[42] = { -255,240,0,94,18,255,233,99,241,51,63,254,215,32,240,184,240,2,255,240,6,89, -249,255,240,4,148,79,37,255,224,192,9,15,120,79,255,0,15,30,245,240, -}; -#else -/* IdentifierStart production with Letter, ASCII, and non-BMP excluded */ -/* duk_unicode_ids_m_let_noabmp[] */ -/* - * Automatically generated by extract_chars.py, do not edit! - */ - -const duk_uint8_t duk_unicode_ids_m_let_noabmp[24] = { -255,240,0,94,18,255,233,99,241,51,63,254,215,32,240,184,240,2,255,240,6,89, -249,0, -}; -#endif - -#if defined(DUK_USE_SOURCE_NONBMP) -/* IdentifierPart production with IdentifierStart and ASCII excluded */ -/* duk_unicode_idp_m_ids_noa[] */ -/* - * Automatically generated by extract_chars.py, do not edit! - */ - -const duk_uint8_t duk_unicode_idp_m_ids_noa[576] = { -255,225,243,246,15,254,0,116,255,191,29,32,33,33,32,243,170,242,47,15,112, -245,118,53,49,35,57,240,144,241,15,11,244,218,240,25,241,56,160,240,163,40, -34,36,241,210,246,158,47,17,242,130,47,2,38,177,57,240,50,242,160,38,49,50, -160,177,57,240,0,50,242,160,36,81,50,64,240,107,64,194,242,160,39,34,34, -240,97,57,181,34,242,160,38,49,50,145,177,57,240,64,242,212,66,35,160,240, -9,240,36,242,182,34,35,129,193,57,240,50,242,160,38,34,35,129,193,57,240, -35,242,145,38,34,35,160,177,57,240,65,243,128,85,32,39,121,49,242,240,54, -215,41,244,144,56,197,57,243,1,121,192,32,32,81,242,63,4,33,106,47,20,160, -245,111,4,41,211,82,34,54,67,235,46,255,225,179,47,254,42,98,240,242,240, -241,241,1,243,47,16,160,57,241,50,57,245,209,241,64,246,139,91,185,247,41, -242,244,242,185,47,13,58,121,240,141,243,68,242,31,1,201,240,56,210,241,12, -57,241,237,242,47,4,153,121,246,130,47,5,80,112,50,251,143,42,36,255,225,0, -31,35,31,5,15,109,197,4,191,254,175,34,247,240,245,47,16,255,225,30,95,91, -31,255,0,100,121,159,55,5,159,18,31,66,31,254,0,64,64,80,240,148,244,161, -242,79,2,185,127,2,234,240,231,240,188,241,227,242,29,240,25,192,185,242, -29,208,145,57,241,50,242,64,34,49,97,32,241,180,97,253,231,33,57,255,240,3, -225,128,255,225,213,240,15,2,240,4,31,10,47,178,159,23,15,254,27,16,253,64, -248,116,255,224,25,159,254,68,178,33,99,241,162,80,249,113,255,225,49,57, -159,254,16,10,250,18,242,126,241,25,240,19,241,250,242,121,114,241,109,41, -97,241,224,210,242,45,147,73,244,75,112,249,43,105,115,242,145,38,49,50, -160,177,54,68,251,47,2,169,80,244,63,4,217,252,118,56,240,209,244,79,1,240, -25,244,60,153,244,94,89,254,78,249,121,253,150,54,64,240,233,241,166,35, -144,170,242,15,0,255,224,137,114,127,2,159,42,240,98,223,108,84,2,18,98,9, -159,34,66,18,73,159,254,3,211,255,240,3,165,217,247,132,242,214,240,185, -255,226,233,2,242,120,63,255,0,59,254,31,255,0,3,186,68,89,115,111,16,63, -134,47,254,71,223,34,255,224,244,242,117,242,41,15,0,15,8,66,239,254,68,70, -47,1,54,33,36,255,118,169,255,224,150,223,254,76,166,245,246,105,255,240, -192,105,175,224,0, -}; -#else -/* IdentifierPart production with IdentifierStart, ASCII, and non-BMP excluded */ -/* duk_unicode_idp_m_ids_noabmp[] */ -/* - * Automatically generated by extract_chars.py, do not edit! - */ - -const duk_uint8_t duk_unicode_idp_m_ids_noabmp[358] = { -255,225,243,246,15,254,0,116,255,191,29,32,33,33,32,243,170,242,47,15,112, -245,118,53,49,35,57,240,144,241,15,11,244,218,240,25,241,56,160,240,163,40, -34,36,241,210,246,158,47,17,242,130,47,2,38,177,57,240,50,242,160,38,49,50, -160,177,57,240,0,50,242,160,36,81,50,64,240,107,64,194,242,160,39,34,34, -240,97,57,181,34,242,160,38,49,50,145,177,57,240,64,242,212,66,35,160,240, -9,240,36,242,182,34,35,129,193,57,240,50,242,160,38,34,35,129,193,57,240, -35,242,145,38,34,35,160,177,57,240,65,243,128,85,32,39,121,49,242,240,54, -215,41,244,144,56,197,57,243,1,121,192,32,32,81,242,63,4,33,106,47,20,160, -245,111,4,41,211,82,34,54,67,235,46,255,225,179,47,254,42,98,240,242,240, -241,241,1,243,47,16,160,57,241,50,57,245,209,241,64,246,139,91,185,247,41, -242,244,242,185,47,13,58,121,240,141,243,68,242,31,1,201,240,56,210,241,12, -57,241,237,242,47,4,153,121,246,130,47,5,80,112,50,251,143,42,36,255,225,0, -31,35,31,5,15,109,197,4,191,254,175,34,247,240,245,47,16,255,225,30,95,91, -31,255,0,100,121,159,55,5,159,18,31,66,31,254,0,64,64,80,240,148,244,161, -242,79,2,185,127,2,234,240,231,240,188,241,227,242,29,240,25,192,185,242, -29,208,145,57,241,50,242,64,34,49,97,32,241,180,97,253,231,33,57,255,240,3, -225,128,255,225,213,240,15,2,240,4,31,10,47,178,159,23,0, -}; -#endif - -/* - * Case conversion tables generated using tools/extract_caseconv.py. - */ - -/* duk_unicode_caseconv_uc[] */ -/* duk_unicode_caseconv_lc[] */ - -/* - * Automatically generated by extract_caseconv.py, do not edit! - */ - -const duk_uint8_t duk_unicode_caseconv_uc[1411] = { -152,3,128,3,0,184,7,192,6,192,112,35,242,199,224,64,74,192,49,32,128,162, -128,108,65,1,189,129,254,131,3,173,3,136,6,7,98,7,34,68,15,12,14,140,72,30, -104,28,112,32,67,0,65,4,0,138,0,128,4,1,88,65,76,83,8,104,14,72,43,16,253, -28,189,6,39,240,39,224,24,114,12,16,132,16,248,0,248,64,129,241,1,241,128, -195,228,3,229,2,7,204,7,206,4,15,160,15,164,6,31,96,31,104,16,62,224,63, -116,8,125,200,127,32,32,251,176,254,208,33,247,129,255,128,67,239,67,253, -64,135,223,7,254,129,15,216,15,220,2,31,208,31,216,4,63,192,63,208,8,133, -192,133,128,129,38,129,37,177,162,195,2,192,5,229,160,2,20,9,170,220,4,232, -40,127,160,255,144,154,136,4,4,4,0,192,9,152,9,144,48,19,160,19,145,0,41, -96,41,69,192,94,128,94,65,128,193,128,193,2,1,161,1,160,6,3,104,3,102,8,7, -56,7,52,64,14,248,14,240,144,31,144,31,130,128,68,96,68,66,64,145,192,145, -130,129,184,129,184,2,3,217,3,216,24,8,194,8,192,68,18,44,18,40,216,38,16, -38,8,112,77,16,77,6,3,192,35,192,18,199,168,71,168,24,15,168,143,172,132, -44,104,44,103,6,89,2,89,0,200,179,176,179,172,21,50,13,50,1,122,104,26,104, -1,212,228,116,228,65,233,204,233,204,143,211,189,83,188,130,167,127,167, -126,11,79,35,79,32,10,158,94,158,88,85,61,173,61,160,97,192,107,64,107,1,0, -226,128,226,3,1,198,1,196,6,3,228,3,226,8,10,0,6,152,16,31,192,31,184,34, -199,50,199,32,65,128,196,0,195,130,1,185,1,184,4,4,205,79,84,8,0,192,143,0, -142,193,1,52,128,203,2,45,39,16,199,5,253,0,11,80,57,192,15,240,23,128,19, -16,4,144,23,240,5,48,24,0,36,48,25,32,25,16,25,80,31,96,25,144,25,128,25, -160,35,208,25,224,34,0,26,128,26,112,27,240,31,112,29,208,24,224,31,48,31, -16,37,2,198,240,37,18,198,208,37,34,199,0,37,48,24,16,37,64,24,96,37,144, -24,240,37,176,25,0,37,202,122,176,38,0,25,48,38,26,122,192,38,48,25,64,38, -90,120,208,38,128,25,112,38,178,198,32,38,202,122,208,39,18,198,224,39,32, -25,208,39,80,25,240,39,210,198,64,40,42,124,80,40,122,123,16,40,128,26,224, -40,144,36,64,40,192,36,80,41,32,27,112,41,218,123,32,41,234,123,0,52,80,57, -144,55,112,55,96,58,192,56,96,60,32,58,48,60,192,56,192,61,0,57,32,61,16, -57,128,61,80,58,96,61,96,58,0,61,112,60,240,63,0,57,160,63,16,58,16,63,32, -63,144,63,48,55,240,63,80,57,80,76,240,76,1,200,0,65,33,200,16,65,65,200, -32,65,225,200,80,66,33,200,96,66,161,200,112,70,33,200,138,100,161,215,154, -119,209,215,210,198,49,216,234,124,97,233,177,230,1,251,224,57,145,254,81, -254,194,20,226,19,34,24,66,24,50,198,18,198,2,198,80,35,162,198,96,35,226, -207,50,207,42,120,202,120,186,121,74,124,74,124,58,124,42,181,58,123,60, -192,27,240,2,152,2,152,10,76,5,120,0,156,3,225,0,37,1,134,1,200,96,115,32, -97,0,96,32,118,24,29,40,24,64,24,8,44,60,10,106,10,164,61,45,0,36,1,152, -143,75,192,10,128,97,3,211,16,2,184,24,80,244,204,0,178,6,20,61,53,0,32, -129,95,15,168,64,116,160,98,99,234,88,29,40,24,152,24,0,250,166,7,74,6,38, -6,2,62,173,129,210,129,137,129,161,15,192,67,225,0,115,35,240,48,248,72,28, -200,252,20,62,20,7,50,63,7,15,133,129,204,143,194,67,225,128,115,35,240, -176,248,104,28,200,252,52,62,28,7,50,63,15,15,135,129,204,143,196,67,225,0, -115,35,241,48,248,72,28,200,252,84,62,20,7,50,63,23,15,133,129,204,143,198, -67,225,128,115,35,241,176,248,104,28,200,252,116,62,28,7,50,63,31,15,135, -129,204,143,200,67,229,0,115,35,242,48,249,72,28,200,252,148,62,84,7,50,63, -39,15,149,129,204,143,202,67,229,128,115,35,242,176,249,104,28,200,252,180, -62,92,7,50,63,47,15,151,129,204,143,204,67,229,0,115,35,243,48,249,72,28, -200,252,212,62,84,7,50,63,55,15,149,129,204,143,206,67,229,128,115,35,243, -176,249,104,28,200,252,244,62,92,7,50,63,63,15,151,129,204,143,208,67,237, -0,115,35,244,48,251,72,28,200,253,20,62,212,7,50,63,71,15,181,129,204,143, -210,67,237,128,115,35,244,176,251,104,28,200,253,52,62,220,7,50,63,79,15, -183,129,204,143,212,67,237,0,115,35,245,48,251,72,28,200,253,84,62,212,7, -50,63,87,15,181,129,204,143,214,67,237,128,115,35,245,176,251,104,28,200, -253,116,62,220,7,50,63,95,15,183,129,204,143,217,67,247,64,115,35,246,112, -28,136,28,200,253,164,7,12,7,50,63,109,1,200,129,161,15,219,224,114,32,104, -64,115,35,247,144,28,136,28,200,254,20,63,148,7,50,63,135,1,203,129,204, -143,226,64,113,32,115,35,248,208,28,184,26,16,254,62,7,46,6,132,7,50,63, -153,1,203,129,204,143,233,96,115,32,97,0,96,3,250,120,28,200,24,64,24,8, -254,180,7,50,6,132,63,175,129,204,129,132,1,161,15,241,96,116,160,97,0,96, -3,252,120,29,40,24,64,24,8,255,36,7,66,6,38,63,205,1,210,129,161,15,243, -224,116,160,97,0,104,67,254,80,255,208,28,200,255,156,7,82,7,50,63,233,1, -199,129,204,143,251,64,117,32,104,67,254,248,29,72,26,16,28,200,255,228,7, -82,7,51,246,1,0,35,0,35,125,128,192,8,192,9,63,96,80,2,48,2,103,216,30,0, -140,0,140,0,147,246,9,128,35,0,35,0,38,125,130,192,10,96,10,159,96,208,2, -152,2,167,216,156,10,136,10,141,246,41,2,162,2,154,253,138,192,168,128,167, -127,98,208,42,112,42,55,216,188,10,136,10,122, -}; -const duk_uint8_t duk_unicode_caseconv_lc[706] = { -160,3,0,3,128,184,6,192,7,192,112,24,144,37,96,64,54,32,81,64,128,226,0, -235,65,129,199,1,230,130,3,145,3,177,34,7,70,7,134,36,15,244,13,236,24,32, -0,34,129,0,65,0,67,4,0,166,32,172,41,132,40,11,64,19,9,208,85,184,80,19, -240,19,248,12,57,32,33,160,172,114,244,67,244,24,248,64,248,0,129,241,129, -241,0,195,229,3,228,2,7,206,7,204,4,15,164,15,160,6,31,104,31,96,16,63,16, -63,0,32,126,96,126,64,64,253,64,253,0,129,251,129,251,0,67,247,67,238,0, -135,242,7,220,130,15,236,15,232,2,31,218,31,118,4,63,208,63,192,8,127,168, -125,232,16,255,192,251,192,33,255,161,247,192,68,44,4,46,4,9,45,137,52,13, -22,0,22,24,47,44,126,2,63,5,254,67,254,130,106,48,16,0,16,19,0,38,64,38,96, -192,78,64,78,132,0,165,0,165,151,1,121,1,122,6,3,4,3,6,8,6,128,6,132,24,13, -152,13,160,32,28,176,28,193,32,59,192,59,226,64,124,128,124,193,0,252,0, -252,148,2,34,2,35,18,4,140,4,142,20,13,192,13,196,16,30,192,30,200,192,70, -0,70,18,32,145,64,145,102,193,48,65,48,131,130,104,2,104,176,30,0,30,1,150, -61,64,61,66,192,125,100,125,68,33,99,57,99,64,50,200,2,200,22,69,157,101, -157,128,169,144,41,144,75,211,64,83,64,142,167,34,167,35,15,78,101,78,102, -126,157,230,157,232,21,59,245,59,248,90,121,10,121,16,84,242,212,242,226, -169,237,41,237,67,12,3,76,5,0,8,6,176,6,180,16,14,32,14,48,48,28,80,28,96, -64,126,224,127,0,139,28,139,28,193,6,3,14,3,16,8,6,224,6,228,21,61,80,19, -48,32,3,1,150,2,105,4,4,118,4,120,8,67,28,180,156,23,240,192,94,0,63,192, -96,64,148,192,97,128,149,0,99,128,119,64,99,192,150,64,100,0,150,192,100, -64,100,128,100,192,152,0,101,0,152,192,101,192,154,0,102,0,102,64,103,64, -156,128,103,192,157,64,105,192,106,0,107,128,162,0,109,192,164,128,124,64, -124,192,125,128,101,64,125,192,111,192,136,0,103,128,142,139,25,64,143,64, -102,128,143,139,25,128,144,192,96,0,145,0,162,64,145,64,163,0,221,128,221, -192,223,192,252,192,225,128,235,0,227,0,243,0,243,192,245,192,253,0,238,0, -254,64,252,129,48,1,51,199,167,128,55,199,239,7,236,199,243,7,240,199,251, -7,249,71,255,7,252,200,73,128,242,72,74,128,26,200,74,192,57,72,76,136,83, -136,96,200,97,11,24,11,24,75,24,128,154,203,24,199,95,75,25,0,159,75,27,64, -148,75,27,128,156,75,27,192,148,11,28,0,148,139,60,139,60,233,223,71,94, -105,226,233,227,41,227,64,153,105,234,192,151,41,235,0,152,105,235,64,155, -41,236,0,167,169,236,64,161,233,236,128,167,105,236,234,212,233,240,169, -240,233,241,41,229,41,241,64,160,169,241,135,99,128,128,152,64,13,32,96, -224, -}; - -#if defined(DUK_USE_REGEXP_CANON_WORKAROUND) -/* - * Automatically generated by extract_caseconv.py, do not edit! - */ - -const duk_uint16_t duk_unicode_re_canon_lookup[65536] = { -0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27, -28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52, -53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77, -78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,65,66,67,68,69,70, -71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,123,124,125, -126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, -144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161, -162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179, -180,924,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197, -198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215, -216,217,218,219,220,221,222,223,192,193,194,195,196,197,198,199,200,201, -202,203,204,205,206,207,208,209,210,211,212,213,214,247,216,217,218,219, -220,221,222,376,256,256,258,258,260,260,262,262,264,264,266,266,268,268, -270,270,272,272,274,274,276,276,278,278,280,280,282,282,284,284,286,286, -288,288,290,290,292,292,294,294,296,296,298,298,300,300,302,302,304,305, -306,306,308,308,310,310,312,313,313,315,315,317,317,319,319,321,321,323, -323,325,325,327,327,329,330,330,332,332,334,334,336,336,338,338,340,340, -342,342,344,344,346,346,348,348,350,350,352,352,354,354,356,356,358,358, -360,360,362,362,364,364,366,366,368,368,370,370,372,372,374,374,376,377, -377,379,379,381,381,383,579,385,386,386,388,388,390,391,391,393,394,395, -395,397,398,399,400,401,401,403,404,502,406,407,408,408,573,411,412,413, -544,415,416,416,418,418,420,420,422,423,423,425,426,427,428,428,430,431, -431,433,434,435,435,437,437,439,440,440,442,443,444,444,446,503,448,449, -450,451,452,452,452,455,455,455,458,458,458,461,461,463,463,465,465,467, -467,469,469,471,471,473,473,475,475,398,478,478,480,480,482,482,484,484, -486,486,488,488,490,490,492,492,494,494,496,497,497,497,500,500,502,503, -504,504,506,506,508,508,510,510,512,512,514,514,516,516,518,518,520,520, -522,522,524,524,526,526,528,528,530,530,532,532,534,534,536,536,538,538, -540,540,542,542,544,545,546,546,548,548,550,550,552,552,554,554,556,556, -558,558,560,560,562,562,564,565,566,567,568,569,570,571,571,573,574,11390, -11391,577,577,579,580,581,582,582,584,584,586,586,588,588,590,590,11375, -11373,11376,385,390,597,393,394,600,399,602,400,42923L,605,606,607,403, -42924L,610,404,612,42893L,42922L,615,407,406,42926L,11362,42925L,621,622, -412,624,11374,413,627,628,415,630,631,632,633,634,635,636,11364,638,639, -422,641,42949L,425,644,645,646,42929L,430,580,433,434,581,653,654,655,656, -657,439,659,660,661,662,663,664,665,666,667,668,42930L,42928L,671,672,673, -674,675,676,677,678,679,680,681,682,683,684,685,686,687,688,689,690,691, -692,693,694,695,696,697,698,699,700,701,702,703,704,705,706,707,708,709, -710,711,712,713,714,715,716,717,718,719,720,721,722,723,724,725,726,727, -728,729,730,731,732,733,734,735,736,737,738,739,740,741,742,743,744,745, -746,747,748,749,750,751,752,753,754,755,756,757,758,759,760,761,762,763, -764,765,766,767,768,769,770,771,772,773,774,775,776,777,778,779,780,781, -782,783,784,785,786,787,788,789,790,791,792,793,794,795,796,797,798,799, -800,801,802,803,804,805,806,807,808,809,810,811,812,813,814,815,816,817, -818,819,820,821,822,823,824,825,826,827,828,829,830,831,832,833,834,835, -836,921,838,839,840,841,842,843,844,845,846,847,848,849,850,851,852,853, -854,855,856,857,858,859,860,861,862,863,864,865,866,867,868,869,870,871, -872,873,874,875,876,877,878,879,880,880,882,882,884,885,886,886,888,889, -890,1021,1022,1023,894,895,896,897,898,899,900,901,902,903,904,905,906,907, -908,909,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925, -926,927,928,929,930,931,932,933,934,935,936,937,938,939,902,904,905,906, -944,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929, -931,931,932,933,934,935,936,937,938,939,908,910,911,975,914,920,978,979, -980,934,928,975,984,984,986,986,988,988,990,990,992,992,994,994,996,996, -998,998,1000,1000,1002,1002,1004,1004,1006,1006,922,929,1017,895,1012,917, -1014,1015,1015,1017,1018,1018,1020,1021,1022,1023,1024,1025,1026,1027,1028, -1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041,1042,1043, -1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058, -1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1040,1041, -1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056, -1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071, -1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038, -1039,1120,1120,1122,1122,1124,1124,1126,1126,1128,1128,1130,1130,1132,1132, -1134,1134,1136,1136,1138,1138,1140,1140,1142,1142,1144,1144,1146,1146,1148, -1148,1150,1150,1152,1152,1154,1155,1156,1157,1158,1159,1160,1161,1162,1162, -1164,1164,1166,1166,1168,1168,1170,1170,1172,1172,1174,1174,1176,1176,1178, -1178,1180,1180,1182,1182,1184,1184,1186,1186,1188,1188,1190,1190,1192,1192, -1194,1194,1196,1196,1198,1198,1200,1200,1202,1202,1204,1204,1206,1206,1208, -1208,1210,1210,1212,1212,1214,1214,1216,1217,1217,1219,1219,1221,1221,1223, -1223,1225,1225,1227,1227,1229,1229,1216,1232,1232,1234,1234,1236,1236,1238, -1238,1240,1240,1242,1242,1244,1244,1246,1246,1248,1248,1250,1250,1252,1252, -1254,1254,1256,1256,1258,1258,1260,1260,1262,1262,1264,1264,1266,1266,1268, -1268,1270,1270,1272,1272,1274,1274,1276,1276,1278,1278,1280,1280,1282,1282, -1284,1284,1286,1286,1288,1288,1290,1290,1292,1292,1294,1294,1296,1296,1298, -1298,1300,1300,1302,1302,1304,1304,1306,1306,1308,1308,1310,1310,1312,1312, -1314,1314,1316,1316,1318,1318,1320,1320,1322,1322,1324,1324,1326,1326,1328, -1329,1330,1331,1332,1333,1334,1335,1336,1337,1338,1339,1340,1341,1342,1343, -1344,1345,1346,1347,1348,1349,1350,1351,1352,1353,1354,1355,1356,1357,1358, -1359,1360,1361,1362,1363,1364,1365,1366,1367,1368,1369,1370,1371,1372,1373, -1374,1375,1376,1329,1330,1331,1332,1333,1334,1335,1336,1337,1338,1339,1340, -1341,1342,1343,1344,1345,1346,1347,1348,1349,1350,1351,1352,1353,1354,1355, -1356,1357,1358,1359,1360,1361,1362,1363,1364,1365,1366,1415,1416,1417,1418, -1419,1420,1421,1422,1423,1424,1425,1426,1427,1428,1429,1430,1431,1432,1433, -1434,1435,1436,1437,1438,1439,1440,1441,1442,1443,1444,1445,1446,1447,1448, -1449,1450,1451,1452,1453,1454,1455,1456,1457,1458,1459,1460,1461,1462,1463, -1464,1465,1466,1467,1468,1469,1470,1471,1472,1473,1474,1475,1476,1477,1478, -1479,1480,1481,1482,1483,1484,1485,1486,1487,1488,1489,1490,1491,1492,1493, -1494,1495,1496,1497,1498,1499,1500,1501,1502,1503,1504,1505,1506,1507,1508, -1509,1510,1511,1512,1513,1514,1515,1516,1517,1518,1519,1520,1521,1522,1523, -1524,1525,1526,1527,1528,1529,1530,1531,1532,1533,1534,1535,1536,1537,1538, -1539,1540,1541,1542,1543,1544,1545,1546,1547,1548,1549,1550,1551,1552,1553, -1554,1555,1556,1557,1558,1559,1560,1561,1562,1563,1564,1565,1566,1567,1568, -1569,1570,1571,1572,1573,1574,1575,1576,1577,1578,1579,1580,1581,1582,1583, -1584,1585,1586,1587,1588,1589,1590,1591,1592,1593,1594,1595,1596,1597,1598, -1599,1600,1601,1602,1603,1604,1605,1606,1607,1608,1609,1610,1611,1612,1613, -1614,1615,1616,1617,1618,1619,1620,1621,1622,1623,1624,1625,1626,1627,1628, -1629,1630,1631,1632,1633,1634,1635,1636,1637,1638,1639,1640,1641,1642,1643, -1644,1645,1646,1647,1648,1649,1650,1651,1652,1653,1654,1655,1656,1657,1658, -1659,1660,1661,1662,1663,1664,1665,1666,1667,1668,1669,1670,1671,1672,1673, -1674,1675,1676,1677,1678,1679,1680,1681,1682,1683,1684,1685,1686,1687,1688, -1689,1690,1691,1692,1693,1694,1695,1696,1697,1698,1699,1700,1701,1702,1703, -1704,1705,1706,1707,1708,1709,1710,1711,1712,1713,1714,1715,1716,1717,1718, -1719,1720,1721,1722,1723,1724,1725,1726,1727,1728,1729,1730,1731,1732,1733, -1734,1735,1736,1737,1738,1739,1740,1741,1742,1743,1744,1745,1746,1747,1748, -1749,1750,1751,1752,1753,1754,1755,1756,1757,1758,1759,1760,1761,1762,1763, -1764,1765,1766,1767,1768,1769,1770,1771,1772,1773,1774,1775,1776,1777,1778, -1779,1780,1781,1782,1783,1784,1785,1786,1787,1788,1789,1790,1791,1792,1793, -1794,1795,1796,1797,1798,1799,1800,1801,1802,1803,1804,1805,1806,1807,1808, -1809,1810,1811,1812,1813,1814,1815,1816,1817,1818,1819,1820,1821,1822,1823, -1824,1825,1826,1827,1828,1829,1830,1831,1832,1833,1834,1835,1836,1837,1838, -1839,1840,1841,1842,1843,1844,1845,1846,1847,1848,1849,1850,1851,1852,1853, -1854,1855,1856,1857,1858,1859,1860,1861,1862,1863,1864,1865,1866,1867,1868, -1869,1870,1871,1872,1873,1874,1875,1876,1877,1878,1879,1880,1881,1882,1883, -1884,1885,1886,1887,1888,1889,1890,1891,1892,1893,1894,1895,1896,1897,1898, -1899,1900,1901,1902,1903,1904,1905,1906,1907,1908,1909,1910,1911,1912,1913, -1914,1915,1916,1917,1918,1919,1920,1921,1922,1923,1924,1925,1926,1927,1928, -1929,1930,1931,1932,1933,1934,1935,1936,1937,1938,1939,1940,1941,1942,1943, -1944,1945,1946,1947,1948,1949,1950,1951,1952,1953,1954,1955,1956,1957,1958, -1959,1960,1961,1962,1963,1964,1965,1966,1967,1968,1969,1970,1971,1972,1973, -1974,1975,1976,1977,1978,1979,1980,1981,1982,1983,1984,1985,1986,1987,1988, -1989,1990,1991,1992,1993,1994,1995,1996,1997,1998,1999,2000,2001,2002,2003, -2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018, -2019,2020,2021,2022,2023,2024,2025,2026,2027,2028,2029,2030,2031,2032,2033, -2034,2035,2036,2037,2038,2039,2040,2041,2042,2043,2044,2045,2046,2047,2048, -2049,2050,2051,2052,2053,2054,2055,2056,2057,2058,2059,2060,2061,2062,2063, -2064,2065,2066,2067,2068,2069,2070,2071,2072,2073,2074,2075,2076,2077,2078, -2079,2080,2081,2082,2083,2084,2085,2086,2087,2088,2089,2090,2091,2092,2093, -2094,2095,2096,2097,2098,2099,2100,2101,2102,2103,2104,2105,2106,2107,2108, -2109,2110,2111,2112,2113,2114,2115,2116,2117,2118,2119,2120,2121,2122,2123, -2124,2125,2126,2127,2128,2129,2130,2131,2132,2133,2134,2135,2136,2137,2138, -2139,2140,2141,2142,2143,2144,2145,2146,2147,2148,2149,2150,2151,2152,2153, -2154,2155,2156,2157,2158,2159,2160,2161,2162,2163,2164,2165,2166,2167,2168, -2169,2170,2171,2172,2173,2174,2175,2176,2177,2178,2179,2180,2181,2182,2183, -2184,2185,2186,2187,2188,2189,2190,2191,2192,2193,2194,2195,2196,2197,2198, -2199,2200,2201,2202,2203,2204,2205,2206,2207,2208,2209,2210,2211,2212,2213, -2214,2215,2216,2217,2218,2219,2220,2221,2222,2223,2224,2225,2226,2227,2228, -2229,2230,2231,2232,2233,2234,2235,2236,2237,2238,2239,2240,2241,2242,2243, -2244,2245,2246,2247,2248,2249,2250,2251,2252,2253,2254,2255,2256,2257,2258, -2259,2260,2261,2262,2263,2264,2265,2266,2267,2268,2269,2270,2271,2272,2273, -2274,2275,2276,2277,2278,2279,2280,2281,2282,2283,2284,2285,2286,2287,2288, -2289,2290,2291,2292,2293,2294,2295,2296,2297,2298,2299,2300,2301,2302,2303, -2304,2305,2306,2307,2308,2309,2310,2311,2312,2313,2314,2315,2316,2317,2318, -2319,2320,2321,2322,2323,2324,2325,2326,2327,2328,2329,2330,2331,2332,2333, -2334,2335,2336,2337,2338,2339,2340,2341,2342,2343,2344,2345,2346,2347,2348, -2349,2350,2351,2352,2353,2354,2355,2356,2357,2358,2359,2360,2361,2362,2363, -2364,2365,2366,2367,2368,2369,2370,2371,2372,2373,2374,2375,2376,2377,2378, -2379,2380,2381,2382,2383,2384,2385,2386,2387,2388,2389,2390,2391,2392,2393, -2394,2395,2396,2397,2398,2399,2400,2401,2402,2403,2404,2405,2406,2407,2408, -2409,2410,2411,2412,2413,2414,2415,2416,2417,2418,2419,2420,2421,2422,2423, -2424,2425,2426,2427,2428,2429,2430,2431,2432,2433,2434,2435,2436,2437,2438, -2439,2440,2441,2442,2443,2444,2445,2446,2447,2448,2449,2450,2451,2452,2453, -2454,2455,2456,2457,2458,2459,2460,2461,2462,2463,2464,2465,2466,2467,2468, -2469,2470,2471,2472,2473,2474,2475,2476,2477,2478,2479,2480,2481,2482,2483, -2484,2485,2486,2487,2488,2489,2490,2491,2492,2493,2494,2495,2496,2497,2498, -2499,2500,2501,2502,2503,2504,2505,2506,2507,2508,2509,2510,2511,2512,2513, -2514,2515,2516,2517,2518,2519,2520,2521,2522,2523,2524,2525,2526,2527,2528, -2529,2530,2531,2532,2533,2534,2535,2536,2537,2538,2539,2540,2541,2542,2543, -2544,2545,2546,2547,2548,2549,2550,2551,2552,2553,2554,2555,2556,2557,2558, -2559,2560,2561,2562,2563,2564,2565,2566,2567,2568,2569,2570,2571,2572,2573, -2574,2575,2576,2577,2578,2579,2580,2581,2582,2583,2584,2585,2586,2587,2588, -2589,2590,2591,2592,2593,2594,2595,2596,2597,2598,2599,2600,2601,2602,2603, -2604,2605,2606,2607,2608,2609,2610,2611,2612,2613,2614,2615,2616,2617,2618, -2619,2620,2621,2622,2623,2624,2625,2626,2627,2628,2629,2630,2631,2632,2633, -2634,2635,2636,2637,2638,2639,2640,2641,2642,2643,2644,2645,2646,2647,2648, -2649,2650,2651,2652,2653,2654,2655,2656,2657,2658,2659,2660,2661,2662,2663, -2664,2665,2666,2667,2668,2669,2670,2671,2672,2673,2674,2675,2676,2677,2678, -2679,2680,2681,2682,2683,2684,2685,2686,2687,2688,2689,2690,2691,2692,2693, -2694,2695,2696,2697,2698,2699,2700,2701,2702,2703,2704,2705,2706,2707,2708, -2709,2710,2711,2712,2713,2714,2715,2716,2717,2718,2719,2720,2721,2722,2723, -2724,2725,2726,2727,2728,2729,2730,2731,2732,2733,2734,2735,2736,2737,2738, -2739,2740,2741,2742,2743,2744,2745,2746,2747,2748,2749,2750,2751,2752,2753, -2754,2755,2756,2757,2758,2759,2760,2761,2762,2763,2764,2765,2766,2767,2768, -2769,2770,2771,2772,2773,2774,2775,2776,2777,2778,2779,2780,2781,2782,2783, -2784,2785,2786,2787,2788,2789,2790,2791,2792,2793,2794,2795,2796,2797,2798, -2799,2800,2801,2802,2803,2804,2805,2806,2807,2808,2809,2810,2811,2812,2813, -2814,2815,2816,2817,2818,2819,2820,2821,2822,2823,2824,2825,2826,2827,2828, -2829,2830,2831,2832,2833,2834,2835,2836,2837,2838,2839,2840,2841,2842,2843, -2844,2845,2846,2847,2848,2849,2850,2851,2852,2853,2854,2855,2856,2857,2858, -2859,2860,2861,2862,2863,2864,2865,2866,2867,2868,2869,2870,2871,2872,2873, -2874,2875,2876,2877,2878,2879,2880,2881,2882,2883,2884,2885,2886,2887,2888, -2889,2890,2891,2892,2893,2894,2895,2896,2897,2898,2899,2900,2901,2902,2903, -2904,2905,2906,2907,2908,2909,2910,2911,2912,2913,2914,2915,2916,2917,2918, -2919,2920,2921,2922,2923,2924,2925,2926,2927,2928,2929,2930,2931,2932,2933, -2934,2935,2936,2937,2938,2939,2940,2941,2942,2943,2944,2945,2946,2947,2948, -2949,2950,2951,2952,2953,2954,2955,2956,2957,2958,2959,2960,2961,2962,2963, -2964,2965,2966,2967,2968,2969,2970,2971,2972,2973,2974,2975,2976,2977,2978, -2979,2980,2981,2982,2983,2984,2985,2986,2987,2988,2989,2990,2991,2992,2993, -2994,2995,2996,2997,2998,2999,3000,3001,3002,3003,3004,3005,3006,3007,3008, -3009,3010,3011,3012,3013,3014,3015,3016,3017,3018,3019,3020,3021,3022,3023, -3024,3025,3026,3027,3028,3029,3030,3031,3032,3033,3034,3035,3036,3037,3038, -3039,3040,3041,3042,3043,3044,3045,3046,3047,3048,3049,3050,3051,3052,3053, -3054,3055,3056,3057,3058,3059,3060,3061,3062,3063,3064,3065,3066,3067,3068, -3069,3070,3071,3072,3073,3074,3075,3076,3077,3078,3079,3080,3081,3082,3083, -3084,3085,3086,3087,3088,3089,3090,3091,3092,3093,3094,3095,3096,3097,3098, -3099,3100,3101,3102,3103,3104,3105,3106,3107,3108,3109,3110,3111,3112,3113, -3114,3115,3116,3117,3118,3119,3120,3121,3122,3123,3124,3125,3126,3127,3128, -3129,3130,3131,3132,3133,3134,3135,3136,3137,3138,3139,3140,3141,3142,3143, -3144,3145,3146,3147,3148,3149,3150,3151,3152,3153,3154,3155,3156,3157,3158, -3159,3160,3161,3162,3163,3164,3165,3166,3167,3168,3169,3170,3171,3172,3173, -3174,3175,3176,3177,3178,3179,3180,3181,3182,3183,3184,3185,3186,3187,3188, -3189,3190,3191,3192,3193,3194,3195,3196,3197,3198,3199,3200,3201,3202,3203, -3204,3205,3206,3207,3208,3209,3210,3211,3212,3213,3214,3215,3216,3217,3218, -3219,3220,3221,3222,3223,3224,3225,3226,3227,3228,3229,3230,3231,3232,3233, -3234,3235,3236,3237,3238,3239,3240,3241,3242,3243,3244,3245,3246,3247,3248, -3249,3250,3251,3252,3253,3254,3255,3256,3257,3258,3259,3260,3261,3262,3263, -3264,3265,3266,3267,3268,3269,3270,3271,3272,3273,3274,3275,3276,3277,3278, -3279,3280,3281,3282,3283,3284,3285,3286,3287,3288,3289,3290,3291,3292,3293, -3294,3295,3296,3297,3298,3299,3300,3301,3302,3303,3304,3305,3306,3307,3308, -3309,3310,3311,3312,3313,3314,3315,3316,3317,3318,3319,3320,3321,3322,3323, -3324,3325,3326,3327,3328,3329,3330,3331,3332,3333,3334,3335,3336,3337,3338, -3339,3340,3341,3342,3343,3344,3345,3346,3347,3348,3349,3350,3351,3352,3353, -3354,3355,3356,3357,3358,3359,3360,3361,3362,3363,3364,3365,3366,3367,3368, -3369,3370,3371,3372,3373,3374,3375,3376,3377,3378,3379,3380,3381,3382,3383, -3384,3385,3386,3387,3388,3389,3390,3391,3392,3393,3394,3395,3396,3397,3398, -3399,3400,3401,3402,3403,3404,3405,3406,3407,3408,3409,3410,3411,3412,3413, -3414,3415,3416,3417,3418,3419,3420,3421,3422,3423,3424,3425,3426,3427,3428, -3429,3430,3431,3432,3433,3434,3435,3436,3437,3438,3439,3440,3441,3442,3443, -3444,3445,3446,3447,3448,3449,3450,3451,3452,3453,3454,3455,3456,3457,3458, -3459,3460,3461,3462,3463,3464,3465,3466,3467,3468,3469,3470,3471,3472,3473, -3474,3475,3476,3477,3478,3479,3480,3481,3482,3483,3484,3485,3486,3487,3488, -3489,3490,3491,3492,3493,3494,3495,3496,3497,3498,3499,3500,3501,3502,3503, -3504,3505,3506,3507,3508,3509,3510,3511,3512,3513,3514,3515,3516,3517,3518, -3519,3520,3521,3522,3523,3524,3525,3526,3527,3528,3529,3530,3531,3532,3533, -3534,3535,3536,3537,3538,3539,3540,3541,3542,3543,3544,3545,3546,3547,3548, -3549,3550,3551,3552,3553,3554,3555,3556,3557,3558,3559,3560,3561,3562,3563, -3564,3565,3566,3567,3568,3569,3570,3571,3572,3573,3574,3575,3576,3577,3578, -3579,3580,3581,3582,3583,3584,3585,3586,3587,3588,3589,3590,3591,3592,3593, -3594,3595,3596,3597,3598,3599,3600,3601,3602,3603,3604,3605,3606,3607,3608, -3609,3610,3611,3612,3613,3614,3615,3616,3617,3618,3619,3620,3621,3622,3623, -3624,3625,3626,3627,3628,3629,3630,3631,3632,3633,3634,3635,3636,3637,3638, -3639,3640,3641,3642,3643,3644,3645,3646,3647,3648,3649,3650,3651,3652,3653, -3654,3655,3656,3657,3658,3659,3660,3661,3662,3663,3664,3665,3666,3667,3668, -3669,3670,3671,3672,3673,3674,3675,3676,3677,3678,3679,3680,3681,3682,3683, -3684,3685,3686,3687,3688,3689,3690,3691,3692,3693,3694,3695,3696,3697,3698, -3699,3700,3701,3702,3703,3704,3705,3706,3707,3708,3709,3710,3711,3712,3713, -3714,3715,3716,3717,3718,3719,3720,3721,3722,3723,3724,3725,3726,3727,3728, -3729,3730,3731,3732,3733,3734,3735,3736,3737,3738,3739,3740,3741,3742,3743, -3744,3745,3746,3747,3748,3749,3750,3751,3752,3753,3754,3755,3756,3757,3758, -3759,3760,3761,3762,3763,3764,3765,3766,3767,3768,3769,3770,3771,3772,3773, -3774,3775,3776,3777,3778,3779,3780,3781,3782,3783,3784,3785,3786,3787,3788, -3789,3790,3791,3792,3793,3794,3795,3796,3797,3798,3799,3800,3801,3802,3803, -3804,3805,3806,3807,3808,3809,3810,3811,3812,3813,3814,3815,3816,3817,3818, -3819,3820,3821,3822,3823,3824,3825,3826,3827,3828,3829,3830,3831,3832,3833, -3834,3835,3836,3837,3838,3839,3840,3841,3842,3843,3844,3845,3846,3847,3848, -3849,3850,3851,3852,3853,3854,3855,3856,3857,3858,3859,3860,3861,3862,3863, -3864,3865,3866,3867,3868,3869,3870,3871,3872,3873,3874,3875,3876,3877,3878, -3879,3880,3881,3882,3883,3884,3885,3886,3887,3888,3889,3890,3891,3892,3893, -3894,3895,3896,3897,3898,3899,3900,3901,3902,3903,3904,3905,3906,3907,3908, -3909,3910,3911,3912,3913,3914,3915,3916,3917,3918,3919,3920,3921,3922,3923, -3924,3925,3926,3927,3928,3929,3930,3931,3932,3933,3934,3935,3936,3937,3938, -3939,3940,3941,3942,3943,3944,3945,3946,3947,3948,3949,3950,3951,3952,3953, -3954,3955,3956,3957,3958,3959,3960,3961,3962,3963,3964,3965,3966,3967,3968, -3969,3970,3971,3972,3973,3974,3975,3976,3977,3978,3979,3980,3981,3982,3983, -3984,3985,3986,3987,3988,3989,3990,3991,3992,3993,3994,3995,3996,3997,3998, -3999,4000,4001,4002,4003,4004,4005,4006,4007,4008,4009,4010,4011,4012,4013, -4014,4015,4016,4017,4018,4019,4020,4021,4022,4023,4024,4025,4026,4027,4028, -4029,4030,4031,4032,4033,4034,4035,4036,4037,4038,4039,4040,4041,4042,4043, -4044,4045,4046,4047,4048,4049,4050,4051,4052,4053,4054,4055,4056,4057,4058, -4059,4060,4061,4062,4063,4064,4065,4066,4067,4068,4069,4070,4071,4072,4073, -4074,4075,4076,4077,4078,4079,4080,4081,4082,4083,4084,4085,4086,4087,4088, -4089,4090,4091,4092,4093,4094,4095,4096,4097,4098,4099,4100,4101,4102,4103, -4104,4105,4106,4107,4108,4109,4110,4111,4112,4113,4114,4115,4116,4117,4118, -4119,4120,4121,4122,4123,4124,4125,4126,4127,4128,4129,4130,4131,4132,4133, -4134,4135,4136,4137,4138,4139,4140,4141,4142,4143,4144,4145,4146,4147,4148, -4149,4150,4151,4152,4153,4154,4155,4156,4157,4158,4159,4160,4161,4162,4163, -4164,4165,4166,4167,4168,4169,4170,4171,4172,4173,4174,4175,4176,4177,4178, -4179,4180,4181,4182,4183,4184,4185,4186,4187,4188,4189,4190,4191,4192,4193, -4194,4195,4196,4197,4198,4199,4200,4201,4202,4203,4204,4205,4206,4207,4208, -4209,4210,4211,4212,4213,4214,4215,4216,4217,4218,4219,4220,4221,4222,4223, -4224,4225,4226,4227,4228,4229,4230,4231,4232,4233,4234,4235,4236,4237,4238, -4239,4240,4241,4242,4243,4244,4245,4246,4247,4248,4249,4250,4251,4252,4253, -4254,4255,4256,4257,4258,4259,4260,4261,4262,4263,4264,4265,4266,4267,4268, -4269,4270,4271,4272,4273,4274,4275,4276,4277,4278,4279,4280,4281,4282,4283, -4284,4285,4286,4287,4288,4289,4290,4291,4292,4293,4294,4295,4296,4297,4298, -4299,4300,4301,4302,4303,7312,7313,7314,7315,7316,7317,7318,7319,7320,7321, -7322,7323,7324,7325,7326,7327,7328,7329,7330,7331,7332,7333,7334,7335,7336, -7337,7338,7339,7340,7341,7342,7343,7344,7345,7346,7347,7348,7349,7350,7351, -7352,7353,7354,4347,4348,7357,7358,7359,4352,4353,4354,4355,4356,4357,4358, -4359,4360,4361,4362,4363,4364,4365,4366,4367,4368,4369,4370,4371,4372,4373, -4374,4375,4376,4377,4378,4379,4380,4381,4382,4383,4384,4385,4386,4387,4388, -4389,4390,4391,4392,4393,4394,4395,4396,4397,4398,4399,4400,4401,4402,4403, -4404,4405,4406,4407,4408,4409,4410,4411,4412,4413,4414,4415,4416,4417,4418, -4419,4420,4421,4422,4423,4424,4425,4426,4427,4428,4429,4430,4431,4432,4433, -4434,4435,4436,4437,4438,4439,4440,4441,4442,4443,4444,4445,4446,4447,4448, -4449,4450,4451,4452,4453,4454,4455,4456,4457,4458,4459,4460,4461,4462,4463, -4464,4465,4466,4467,4468,4469,4470,4471,4472,4473,4474,4475,4476,4477,4478, -4479,4480,4481,4482,4483,4484,4485,4486,4487,4488,4489,4490,4491,4492,4493, -4494,4495,4496,4497,4498,4499,4500,4501,4502,4503,4504,4505,4506,4507,4508, -4509,4510,4511,4512,4513,4514,4515,4516,4517,4518,4519,4520,4521,4522,4523, -4524,4525,4526,4527,4528,4529,4530,4531,4532,4533,4534,4535,4536,4537,4538, -4539,4540,4541,4542,4543,4544,4545,4546,4547,4548,4549,4550,4551,4552,4553, -4554,4555,4556,4557,4558,4559,4560,4561,4562,4563,4564,4565,4566,4567,4568, -4569,4570,4571,4572,4573,4574,4575,4576,4577,4578,4579,4580,4581,4582,4583, -4584,4585,4586,4587,4588,4589,4590,4591,4592,4593,4594,4595,4596,4597,4598, -4599,4600,4601,4602,4603,4604,4605,4606,4607,4608,4609,4610,4611,4612,4613, -4614,4615,4616,4617,4618,4619,4620,4621,4622,4623,4624,4625,4626,4627,4628, -4629,4630,4631,4632,4633,4634,4635,4636,4637,4638,4639,4640,4641,4642,4643, -4644,4645,4646,4647,4648,4649,4650,4651,4652,4653,4654,4655,4656,4657,4658, -4659,4660,4661,4662,4663,4664,4665,4666,4667,4668,4669,4670,4671,4672,4673, -4674,4675,4676,4677,4678,4679,4680,4681,4682,4683,4684,4685,4686,4687,4688, -4689,4690,4691,4692,4693,4694,4695,4696,4697,4698,4699,4700,4701,4702,4703, -4704,4705,4706,4707,4708,4709,4710,4711,4712,4713,4714,4715,4716,4717,4718, -4719,4720,4721,4722,4723,4724,4725,4726,4727,4728,4729,4730,4731,4732,4733, -4734,4735,4736,4737,4738,4739,4740,4741,4742,4743,4744,4745,4746,4747,4748, -4749,4750,4751,4752,4753,4754,4755,4756,4757,4758,4759,4760,4761,4762,4763, -4764,4765,4766,4767,4768,4769,4770,4771,4772,4773,4774,4775,4776,4777,4778, -4779,4780,4781,4782,4783,4784,4785,4786,4787,4788,4789,4790,4791,4792,4793, -4794,4795,4796,4797,4798,4799,4800,4801,4802,4803,4804,4805,4806,4807,4808, -4809,4810,4811,4812,4813,4814,4815,4816,4817,4818,4819,4820,4821,4822,4823, -4824,4825,4826,4827,4828,4829,4830,4831,4832,4833,4834,4835,4836,4837,4838, -4839,4840,4841,4842,4843,4844,4845,4846,4847,4848,4849,4850,4851,4852,4853, -4854,4855,4856,4857,4858,4859,4860,4861,4862,4863,4864,4865,4866,4867,4868, -4869,4870,4871,4872,4873,4874,4875,4876,4877,4878,4879,4880,4881,4882,4883, -4884,4885,4886,4887,4888,4889,4890,4891,4892,4893,4894,4895,4896,4897,4898, -4899,4900,4901,4902,4903,4904,4905,4906,4907,4908,4909,4910,4911,4912,4913, -4914,4915,4916,4917,4918,4919,4920,4921,4922,4923,4924,4925,4926,4927,4928, -4929,4930,4931,4932,4933,4934,4935,4936,4937,4938,4939,4940,4941,4942,4943, -4944,4945,4946,4947,4948,4949,4950,4951,4952,4953,4954,4955,4956,4957,4958, -4959,4960,4961,4962,4963,4964,4965,4966,4967,4968,4969,4970,4971,4972,4973, -4974,4975,4976,4977,4978,4979,4980,4981,4982,4983,4984,4985,4986,4987,4988, -4989,4990,4991,4992,4993,4994,4995,4996,4997,4998,4999,5000,5001,5002,5003, -5004,5005,5006,5007,5008,5009,5010,5011,5012,5013,5014,5015,5016,5017,5018, -5019,5020,5021,5022,5023,5024,5025,5026,5027,5028,5029,5030,5031,5032,5033, -5034,5035,5036,5037,5038,5039,5040,5041,5042,5043,5044,5045,5046,5047,5048, -5049,5050,5051,5052,5053,5054,5055,5056,5057,5058,5059,5060,5061,5062,5063, -5064,5065,5066,5067,5068,5069,5070,5071,5072,5073,5074,5075,5076,5077,5078, -5079,5080,5081,5082,5083,5084,5085,5086,5087,5088,5089,5090,5091,5092,5093, -5094,5095,5096,5097,5098,5099,5100,5101,5102,5103,5104,5105,5106,5107,5108, -5109,5110,5111,5104,5105,5106,5107,5108,5109,5118,5119,5120,5121,5122,5123, -5124,5125,5126,5127,5128,5129,5130,5131,5132,5133,5134,5135,5136,5137,5138, -5139,5140,5141,5142,5143,5144,5145,5146,5147,5148,5149,5150,5151,5152,5153, -5154,5155,5156,5157,5158,5159,5160,5161,5162,5163,5164,5165,5166,5167,5168, -5169,5170,5171,5172,5173,5174,5175,5176,5177,5178,5179,5180,5181,5182,5183, -5184,5185,5186,5187,5188,5189,5190,5191,5192,5193,5194,5195,5196,5197,5198, -5199,5200,5201,5202,5203,5204,5205,5206,5207,5208,5209,5210,5211,5212,5213, -5214,5215,5216,5217,5218,5219,5220,5221,5222,5223,5224,5225,5226,5227,5228, -5229,5230,5231,5232,5233,5234,5235,5236,5237,5238,5239,5240,5241,5242,5243, -5244,5245,5246,5247,5248,5249,5250,5251,5252,5253,5254,5255,5256,5257,5258, -5259,5260,5261,5262,5263,5264,5265,5266,5267,5268,5269,5270,5271,5272,5273, -5274,5275,5276,5277,5278,5279,5280,5281,5282,5283,5284,5285,5286,5287,5288, -5289,5290,5291,5292,5293,5294,5295,5296,5297,5298,5299,5300,5301,5302,5303, -5304,5305,5306,5307,5308,5309,5310,5311,5312,5313,5314,5315,5316,5317,5318, -5319,5320,5321,5322,5323,5324,5325,5326,5327,5328,5329,5330,5331,5332,5333, -5334,5335,5336,5337,5338,5339,5340,5341,5342,5343,5344,5345,5346,5347,5348, -5349,5350,5351,5352,5353,5354,5355,5356,5357,5358,5359,5360,5361,5362,5363, -5364,5365,5366,5367,5368,5369,5370,5371,5372,5373,5374,5375,5376,5377,5378, -5379,5380,5381,5382,5383,5384,5385,5386,5387,5388,5389,5390,5391,5392,5393, -5394,5395,5396,5397,5398,5399,5400,5401,5402,5403,5404,5405,5406,5407,5408, -5409,5410,5411,5412,5413,5414,5415,5416,5417,5418,5419,5420,5421,5422,5423, -5424,5425,5426,5427,5428,5429,5430,5431,5432,5433,5434,5435,5436,5437,5438, -5439,5440,5441,5442,5443,5444,5445,5446,5447,5448,5449,5450,5451,5452,5453, -5454,5455,5456,5457,5458,5459,5460,5461,5462,5463,5464,5465,5466,5467,5468, -5469,5470,5471,5472,5473,5474,5475,5476,5477,5478,5479,5480,5481,5482,5483, -5484,5485,5486,5487,5488,5489,5490,5491,5492,5493,5494,5495,5496,5497,5498, -5499,5500,5501,5502,5503,5504,5505,5506,5507,5508,5509,5510,5511,5512,5513, -5514,5515,5516,5517,5518,5519,5520,5521,5522,5523,5524,5525,5526,5527,5528, -5529,5530,5531,5532,5533,5534,5535,5536,5537,5538,5539,5540,5541,5542,5543, -5544,5545,5546,5547,5548,5549,5550,5551,5552,5553,5554,5555,5556,5557,5558, -5559,5560,5561,5562,5563,5564,5565,5566,5567,5568,5569,5570,5571,5572,5573, -5574,5575,5576,5577,5578,5579,5580,5581,5582,5583,5584,5585,5586,5587,5588, -5589,5590,5591,5592,5593,5594,5595,5596,5597,5598,5599,5600,5601,5602,5603, -5604,5605,5606,5607,5608,5609,5610,5611,5612,5613,5614,5615,5616,5617,5618, -5619,5620,5621,5622,5623,5624,5625,5626,5627,5628,5629,5630,5631,5632,5633, -5634,5635,5636,5637,5638,5639,5640,5641,5642,5643,5644,5645,5646,5647,5648, -5649,5650,5651,5652,5653,5654,5655,5656,5657,5658,5659,5660,5661,5662,5663, -5664,5665,5666,5667,5668,5669,5670,5671,5672,5673,5674,5675,5676,5677,5678, -5679,5680,5681,5682,5683,5684,5685,5686,5687,5688,5689,5690,5691,5692,5693, -5694,5695,5696,5697,5698,5699,5700,5701,5702,5703,5704,5705,5706,5707,5708, -5709,5710,5711,5712,5713,5714,5715,5716,5717,5718,5719,5720,5721,5722,5723, -5724,5725,5726,5727,5728,5729,5730,5731,5732,5733,5734,5735,5736,5737,5738, -5739,5740,5741,5742,5743,5744,5745,5746,5747,5748,5749,5750,5751,5752,5753, -5754,5755,5756,5757,5758,5759,5760,5761,5762,5763,5764,5765,5766,5767,5768, -5769,5770,5771,5772,5773,5774,5775,5776,5777,5778,5779,5780,5781,5782,5783, -5784,5785,5786,5787,5788,5789,5790,5791,5792,5793,5794,5795,5796,5797,5798, -5799,5800,5801,5802,5803,5804,5805,5806,5807,5808,5809,5810,5811,5812,5813, -5814,5815,5816,5817,5818,5819,5820,5821,5822,5823,5824,5825,5826,5827,5828, -5829,5830,5831,5832,5833,5834,5835,5836,5837,5838,5839,5840,5841,5842,5843, -5844,5845,5846,5847,5848,5849,5850,5851,5852,5853,5854,5855,5856,5857,5858, -5859,5860,5861,5862,5863,5864,5865,5866,5867,5868,5869,5870,5871,5872,5873, -5874,5875,5876,5877,5878,5879,5880,5881,5882,5883,5884,5885,5886,5887,5888, -5889,5890,5891,5892,5893,5894,5895,5896,5897,5898,5899,5900,5901,5902,5903, -5904,5905,5906,5907,5908,5909,5910,5911,5912,5913,5914,5915,5916,5917,5918, -5919,5920,5921,5922,5923,5924,5925,5926,5927,5928,5929,5930,5931,5932,5933, -5934,5935,5936,5937,5938,5939,5940,5941,5942,5943,5944,5945,5946,5947,5948, -5949,5950,5951,5952,5953,5954,5955,5956,5957,5958,5959,5960,5961,5962,5963, -5964,5965,5966,5967,5968,5969,5970,5971,5972,5973,5974,5975,5976,5977,5978, -5979,5980,5981,5982,5983,5984,5985,5986,5987,5988,5989,5990,5991,5992,5993, -5994,5995,5996,5997,5998,5999,6000,6001,6002,6003,6004,6005,6006,6007,6008, -6009,6010,6011,6012,6013,6014,6015,6016,6017,6018,6019,6020,6021,6022,6023, -6024,6025,6026,6027,6028,6029,6030,6031,6032,6033,6034,6035,6036,6037,6038, -6039,6040,6041,6042,6043,6044,6045,6046,6047,6048,6049,6050,6051,6052,6053, -6054,6055,6056,6057,6058,6059,6060,6061,6062,6063,6064,6065,6066,6067,6068, -6069,6070,6071,6072,6073,6074,6075,6076,6077,6078,6079,6080,6081,6082,6083, -6084,6085,6086,6087,6088,6089,6090,6091,6092,6093,6094,6095,6096,6097,6098, -6099,6100,6101,6102,6103,6104,6105,6106,6107,6108,6109,6110,6111,6112,6113, -6114,6115,6116,6117,6118,6119,6120,6121,6122,6123,6124,6125,6126,6127,6128, -6129,6130,6131,6132,6133,6134,6135,6136,6137,6138,6139,6140,6141,6142,6143, -6144,6145,6146,6147,6148,6149,6150,6151,6152,6153,6154,6155,6156,6157,6158, -6159,6160,6161,6162,6163,6164,6165,6166,6167,6168,6169,6170,6171,6172,6173, -6174,6175,6176,6177,6178,6179,6180,6181,6182,6183,6184,6185,6186,6187,6188, -6189,6190,6191,6192,6193,6194,6195,6196,6197,6198,6199,6200,6201,6202,6203, -6204,6205,6206,6207,6208,6209,6210,6211,6212,6213,6214,6215,6216,6217,6218, -6219,6220,6221,6222,6223,6224,6225,6226,6227,6228,6229,6230,6231,6232,6233, -6234,6235,6236,6237,6238,6239,6240,6241,6242,6243,6244,6245,6246,6247,6248, -6249,6250,6251,6252,6253,6254,6255,6256,6257,6258,6259,6260,6261,6262,6263, -6264,6265,6266,6267,6268,6269,6270,6271,6272,6273,6274,6275,6276,6277,6278, -6279,6280,6281,6282,6283,6284,6285,6286,6287,6288,6289,6290,6291,6292,6293, -6294,6295,6296,6297,6298,6299,6300,6301,6302,6303,6304,6305,6306,6307,6308, -6309,6310,6311,6312,6313,6314,6315,6316,6317,6318,6319,6320,6321,6322,6323, -6324,6325,6326,6327,6328,6329,6330,6331,6332,6333,6334,6335,6336,6337,6338, -6339,6340,6341,6342,6343,6344,6345,6346,6347,6348,6349,6350,6351,6352,6353, -6354,6355,6356,6357,6358,6359,6360,6361,6362,6363,6364,6365,6366,6367,6368, -6369,6370,6371,6372,6373,6374,6375,6376,6377,6378,6379,6380,6381,6382,6383, -6384,6385,6386,6387,6388,6389,6390,6391,6392,6393,6394,6395,6396,6397,6398, -6399,6400,6401,6402,6403,6404,6405,6406,6407,6408,6409,6410,6411,6412,6413, -6414,6415,6416,6417,6418,6419,6420,6421,6422,6423,6424,6425,6426,6427,6428, -6429,6430,6431,6432,6433,6434,6435,6436,6437,6438,6439,6440,6441,6442,6443, -6444,6445,6446,6447,6448,6449,6450,6451,6452,6453,6454,6455,6456,6457,6458, -6459,6460,6461,6462,6463,6464,6465,6466,6467,6468,6469,6470,6471,6472,6473, -6474,6475,6476,6477,6478,6479,6480,6481,6482,6483,6484,6485,6486,6487,6488, -6489,6490,6491,6492,6493,6494,6495,6496,6497,6498,6499,6500,6501,6502,6503, -6504,6505,6506,6507,6508,6509,6510,6511,6512,6513,6514,6515,6516,6517,6518, -6519,6520,6521,6522,6523,6524,6525,6526,6527,6528,6529,6530,6531,6532,6533, -6534,6535,6536,6537,6538,6539,6540,6541,6542,6543,6544,6545,6546,6547,6548, -6549,6550,6551,6552,6553,6554,6555,6556,6557,6558,6559,6560,6561,6562,6563, -6564,6565,6566,6567,6568,6569,6570,6571,6572,6573,6574,6575,6576,6577,6578, -6579,6580,6581,6582,6583,6584,6585,6586,6587,6588,6589,6590,6591,6592,6593, -6594,6595,6596,6597,6598,6599,6600,6601,6602,6603,6604,6605,6606,6607,6608, -6609,6610,6611,6612,6613,6614,6615,6616,6617,6618,6619,6620,6621,6622,6623, -6624,6625,6626,6627,6628,6629,6630,6631,6632,6633,6634,6635,6636,6637,6638, -6639,6640,6641,6642,6643,6644,6645,6646,6647,6648,6649,6650,6651,6652,6653, -6654,6655,6656,6657,6658,6659,6660,6661,6662,6663,6664,6665,6666,6667,6668, -6669,6670,6671,6672,6673,6674,6675,6676,6677,6678,6679,6680,6681,6682,6683, -6684,6685,6686,6687,6688,6689,6690,6691,6692,6693,6694,6695,6696,6697,6698, -6699,6700,6701,6702,6703,6704,6705,6706,6707,6708,6709,6710,6711,6712,6713, -6714,6715,6716,6717,6718,6719,6720,6721,6722,6723,6724,6725,6726,6727,6728, -6729,6730,6731,6732,6733,6734,6735,6736,6737,6738,6739,6740,6741,6742,6743, -6744,6745,6746,6747,6748,6749,6750,6751,6752,6753,6754,6755,6756,6757,6758, -6759,6760,6761,6762,6763,6764,6765,6766,6767,6768,6769,6770,6771,6772,6773, -6774,6775,6776,6777,6778,6779,6780,6781,6782,6783,6784,6785,6786,6787,6788, -6789,6790,6791,6792,6793,6794,6795,6796,6797,6798,6799,6800,6801,6802,6803, -6804,6805,6806,6807,6808,6809,6810,6811,6812,6813,6814,6815,6816,6817,6818, -6819,6820,6821,6822,6823,6824,6825,6826,6827,6828,6829,6830,6831,6832,6833, -6834,6835,6836,6837,6838,6839,6840,6841,6842,6843,6844,6845,6846,6847,6848, -6849,6850,6851,6852,6853,6854,6855,6856,6857,6858,6859,6860,6861,6862,6863, -6864,6865,6866,6867,6868,6869,6870,6871,6872,6873,6874,6875,6876,6877,6878, -6879,6880,6881,6882,6883,6884,6885,6886,6887,6888,6889,6890,6891,6892,6893, -6894,6895,6896,6897,6898,6899,6900,6901,6902,6903,6904,6905,6906,6907,6908, -6909,6910,6911,6912,6913,6914,6915,6916,6917,6918,6919,6920,6921,6922,6923, -6924,6925,6926,6927,6928,6929,6930,6931,6932,6933,6934,6935,6936,6937,6938, -6939,6940,6941,6942,6943,6944,6945,6946,6947,6948,6949,6950,6951,6952,6953, -6954,6955,6956,6957,6958,6959,6960,6961,6962,6963,6964,6965,6966,6967,6968, -6969,6970,6971,6972,6973,6974,6975,6976,6977,6978,6979,6980,6981,6982,6983, -6984,6985,6986,6987,6988,6989,6990,6991,6992,6993,6994,6995,6996,6997,6998, -6999,7000,7001,7002,7003,7004,7005,7006,7007,7008,7009,7010,7011,7012,7013, -7014,7015,7016,7017,7018,7019,7020,7021,7022,7023,7024,7025,7026,7027,7028, -7029,7030,7031,7032,7033,7034,7035,7036,7037,7038,7039,7040,7041,7042,7043, -7044,7045,7046,7047,7048,7049,7050,7051,7052,7053,7054,7055,7056,7057,7058, -7059,7060,7061,7062,7063,7064,7065,7066,7067,7068,7069,7070,7071,7072,7073, -7074,7075,7076,7077,7078,7079,7080,7081,7082,7083,7084,7085,7086,7087,7088, -7089,7090,7091,7092,7093,7094,7095,7096,7097,7098,7099,7100,7101,7102,7103, -7104,7105,7106,7107,7108,7109,7110,7111,7112,7113,7114,7115,7116,7117,7118, -7119,7120,7121,7122,7123,7124,7125,7126,7127,7128,7129,7130,7131,7132,7133, -7134,7135,7136,7137,7138,7139,7140,7141,7142,7143,7144,7145,7146,7147,7148, -7149,7150,7151,7152,7153,7154,7155,7156,7157,7158,7159,7160,7161,7162,7163, -7164,7165,7166,7167,7168,7169,7170,7171,7172,7173,7174,7175,7176,7177,7178, -7179,7180,7181,7182,7183,7184,7185,7186,7187,7188,7189,7190,7191,7192,7193, -7194,7195,7196,7197,7198,7199,7200,7201,7202,7203,7204,7205,7206,7207,7208, -7209,7210,7211,7212,7213,7214,7215,7216,7217,7218,7219,7220,7221,7222,7223, -7224,7225,7226,7227,7228,7229,7230,7231,7232,7233,7234,7235,7236,7237,7238, -7239,7240,7241,7242,7243,7244,7245,7246,7247,7248,7249,7250,7251,7252,7253, -7254,7255,7256,7257,7258,7259,7260,7261,7262,7263,7264,7265,7266,7267,7268, -7269,7270,7271,7272,7273,7274,7275,7276,7277,7278,7279,7280,7281,7282,7283, -7284,7285,7286,7287,7288,7289,7290,7291,7292,7293,7294,7295,1042,1044,1054, -1057,1058,1058,1066,1122,42570L,7305,7306,7307,7308,7309,7310,7311,7312, -7313,7314,7315,7316,7317,7318,7319,7320,7321,7322,7323,7324,7325,7326,7327, -7328,7329,7330,7331,7332,7333,7334,7335,7336,7337,7338,7339,7340,7341,7342, -7343,7344,7345,7346,7347,7348,7349,7350,7351,7352,7353,7354,7355,7356,7357, -7358,7359,7360,7361,7362,7363,7364,7365,7366,7367,7368,7369,7370,7371,7372, -7373,7374,7375,7376,7377,7378,7379,7380,7381,7382,7383,7384,7385,7386,7387, -7388,7389,7390,7391,7392,7393,7394,7395,7396,7397,7398,7399,7400,7401,7402, -7403,7404,7405,7406,7407,7408,7409,7410,7411,7412,7413,7414,7415,7416,7417, -7418,7419,7420,7421,7422,7423,7424,7425,7426,7427,7428,7429,7430,7431,7432, -7433,7434,7435,7436,7437,7438,7439,7440,7441,7442,7443,7444,7445,7446,7447, -7448,7449,7450,7451,7452,7453,7454,7455,7456,7457,7458,7459,7460,7461,7462, -7463,7464,7465,7466,7467,7468,7469,7470,7471,7472,7473,7474,7475,7476,7477, -7478,7479,7480,7481,7482,7483,7484,7485,7486,7487,7488,7489,7490,7491,7492, -7493,7494,7495,7496,7497,7498,7499,7500,7501,7502,7503,7504,7505,7506,7507, -7508,7509,7510,7511,7512,7513,7514,7515,7516,7517,7518,7519,7520,7521,7522, -7523,7524,7525,7526,7527,7528,7529,7530,7531,7532,7533,7534,7535,7536,7537, -7538,7539,7540,7541,7542,7543,7544,42877L,7546,7547,7548,11363,7550,7551, -7552,7553,7554,7555,7556,7557,7558,7559,7560,7561,7562,7563,7564,7565, -42950L,7567,7568,7569,7570,7571,7572,7573,7574,7575,7576,7577,7578,7579, -7580,7581,7582,7583,7584,7585,7586,7587,7588,7589,7590,7591,7592,7593,7594, -7595,7596,7597,7598,7599,7600,7601,7602,7603,7604,7605,7606,7607,7608,7609, -7610,7611,7612,7613,7614,7615,7616,7617,7618,7619,7620,7621,7622,7623,7624, -7625,7626,7627,7628,7629,7630,7631,7632,7633,7634,7635,7636,7637,7638,7639, -7640,7641,7642,7643,7644,7645,7646,7647,7648,7649,7650,7651,7652,7653,7654, -7655,7656,7657,7658,7659,7660,7661,7662,7663,7664,7665,7666,7667,7668,7669, -7670,7671,7672,7673,7674,7675,7676,7677,7678,7679,7680,7680,7682,7682,7684, -7684,7686,7686,7688,7688,7690,7690,7692,7692,7694,7694,7696,7696,7698,7698, -7700,7700,7702,7702,7704,7704,7706,7706,7708,7708,7710,7710,7712,7712,7714, -7714,7716,7716,7718,7718,7720,7720,7722,7722,7724,7724,7726,7726,7728,7728, -7730,7730,7732,7732,7734,7734,7736,7736,7738,7738,7740,7740,7742,7742,7744, -7744,7746,7746,7748,7748,7750,7750,7752,7752,7754,7754,7756,7756,7758,7758, -7760,7760,7762,7762,7764,7764,7766,7766,7768,7768,7770,7770,7772,7772,7774, -7774,7776,7776,7778,7778,7780,7780,7782,7782,7784,7784,7786,7786,7788,7788, -7790,7790,7792,7792,7794,7794,7796,7796,7798,7798,7800,7800,7802,7802,7804, -7804,7806,7806,7808,7808,7810,7810,7812,7812,7814,7814,7816,7816,7818,7818, -7820,7820,7822,7822,7824,7824,7826,7826,7828,7828,7830,7831,7832,7833,7834, -7776,7836,7837,7838,7839,7840,7840,7842,7842,7844,7844,7846,7846,7848,7848, -7850,7850,7852,7852,7854,7854,7856,7856,7858,7858,7860,7860,7862,7862,7864, -7864,7866,7866,7868,7868,7870,7870,7872,7872,7874,7874,7876,7876,7878,7878, -7880,7880,7882,7882,7884,7884,7886,7886,7888,7888,7890,7890,7892,7892,7894, -7894,7896,7896,7898,7898,7900,7900,7902,7902,7904,7904,7906,7906,7908,7908, -7910,7910,7912,7912,7914,7914,7916,7916,7918,7918,7920,7920,7922,7922,7924, -7924,7926,7926,7928,7928,7930,7930,7932,7932,7934,7934,7944,7945,7946,7947, -7948,7949,7950,7951,7944,7945,7946,7947,7948,7949,7950,7951,7960,7961,7962, -7963,7964,7965,7958,7959,7960,7961,7962,7963,7964,7965,7966,7967,7976,7977, -7978,7979,7980,7981,7982,7983,7976,7977,7978,7979,7980,7981,7982,7983,7992, -7993,7994,7995,7996,7997,7998,7999,7992,7993,7994,7995,7996,7997,7998,7999, -8008,8009,8010,8011,8012,8013,8006,8007,8008,8009,8010,8011,8012,8013,8014, -8015,8016,8025,8018,8027,8020,8029,8022,8031,8024,8025,8026,8027,8028,8029, -8030,8031,8040,8041,8042,8043,8044,8045,8046,8047,8040,8041,8042,8043,8044, -8045,8046,8047,8122,8123,8136,8137,8138,8139,8154,8155,8184,8185,8170,8171, -8186,8187,8062,8063,8064,8065,8066,8067,8068,8069,8070,8071,8072,8073,8074, -8075,8076,8077,8078,8079,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089, -8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8102,8103,8104, -8105,8106,8107,8108,8109,8110,8111,8120,8121,8114,8115,8116,8117,8118,8119, -8120,8121,8122,8123,8124,8125,921,8127,8128,8129,8130,8131,8132,8133,8134, -8135,8136,8137,8138,8139,8140,8141,8142,8143,8152,8153,8146,8147,8148,8149, -8150,8151,8152,8153,8154,8155,8156,8157,8158,8159,8168,8169,8162,8163,8164, -8172,8166,8167,8168,8169,8170,8171,8172,8173,8174,8175,8176,8177,8178,8179, -8180,8181,8182,8183,8184,8185,8186,8187,8188,8189,8190,8191,8192,8193,8194, -8195,8196,8197,8198,8199,8200,8201,8202,8203,8204,8205,8206,8207,8208,8209, -8210,8211,8212,8213,8214,8215,8216,8217,8218,8219,8220,8221,8222,8223,8224, -8225,8226,8227,8228,8229,8230,8231,8232,8233,8234,8235,8236,8237,8238,8239, -8240,8241,8242,8243,8244,8245,8246,8247,8248,8249,8250,8251,8252,8253,8254, -8255,8256,8257,8258,8259,8260,8261,8262,8263,8264,8265,8266,8267,8268,8269, -8270,8271,8272,8273,8274,8275,8276,8277,8278,8279,8280,8281,8282,8283,8284, -8285,8286,8287,8288,8289,8290,8291,8292,8293,8294,8295,8296,8297,8298,8299, -8300,8301,8302,8303,8304,8305,8306,8307,8308,8309,8310,8311,8312,8313,8314, -8315,8316,8317,8318,8319,8320,8321,8322,8323,8324,8325,8326,8327,8328,8329, -8330,8331,8332,8333,8334,8335,8336,8337,8338,8339,8340,8341,8342,8343,8344, -8345,8346,8347,8348,8349,8350,8351,8352,8353,8354,8355,8356,8357,8358,8359, -8360,8361,8362,8363,8364,8365,8366,8367,8368,8369,8370,8371,8372,8373,8374, -8375,8376,8377,8378,8379,8380,8381,8382,8383,8384,8385,8386,8387,8388,8389, -8390,8391,8392,8393,8394,8395,8396,8397,8398,8399,8400,8401,8402,8403,8404, -8405,8406,8407,8408,8409,8410,8411,8412,8413,8414,8415,8416,8417,8418,8419, -8420,8421,8422,8423,8424,8425,8426,8427,8428,8429,8430,8431,8432,8433,8434, -8435,8436,8437,8438,8439,8440,8441,8442,8443,8444,8445,8446,8447,8448,8449, -8450,8451,8452,8453,8454,8455,8456,8457,8458,8459,8460,8461,8462,8463,8464, -8465,8466,8467,8468,8469,8470,8471,8472,8473,8474,8475,8476,8477,8478,8479, -8480,8481,8482,8483,8484,8485,8486,8487,8488,8489,8490,8491,8492,8493,8494, -8495,8496,8497,8498,8499,8500,8501,8502,8503,8504,8505,8506,8507,8508,8509, -8510,8511,8512,8513,8514,8515,8516,8517,8518,8519,8520,8521,8522,8523,8524, -8525,8498,8527,8528,8529,8530,8531,8532,8533,8534,8535,8536,8537,8538,8539, -8540,8541,8542,8543,8544,8545,8546,8547,8548,8549,8550,8551,8552,8553,8554, -8555,8556,8557,8558,8559,8544,8545,8546,8547,8548,8549,8550,8551,8552,8553, -8554,8555,8556,8557,8558,8559,8576,8577,8578,8579,8579,8581,8582,8583,8584, -8585,8586,8587,8588,8589,8590,8591,8592,8593,8594,8595,8596,8597,8598,8599, -8600,8601,8602,8603,8604,8605,8606,8607,8608,8609,8610,8611,8612,8613,8614, -8615,8616,8617,8618,8619,8620,8621,8622,8623,8624,8625,8626,8627,8628,8629, -8630,8631,8632,8633,8634,8635,8636,8637,8638,8639,8640,8641,8642,8643,8644, -8645,8646,8647,8648,8649,8650,8651,8652,8653,8654,8655,8656,8657,8658,8659, -8660,8661,8662,8663,8664,8665,8666,8667,8668,8669,8670,8671,8672,8673,8674, -8675,8676,8677,8678,8679,8680,8681,8682,8683,8684,8685,8686,8687,8688,8689, -8690,8691,8692,8693,8694,8695,8696,8697,8698,8699,8700,8701,8702,8703,8704, -8705,8706,8707,8708,8709,8710,8711,8712,8713,8714,8715,8716,8717,8718,8719, -8720,8721,8722,8723,8724,8725,8726,8727,8728,8729,8730,8731,8732,8733,8734, -8735,8736,8737,8738,8739,8740,8741,8742,8743,8744,8745,8746,8747,8748,8749, -8750,8751,8752,8753,8754,8755,8756,8757,8758,8759,8760,8761,8762,8763,8764, -8765,8766,8767,8768,8769,8770,8771,8772,8773,8774,8775,8776,8777,8778,8779, -8780,8781,8782,8783,8784,8785,8786,8787,8788,8789,8790,8791,8792,8793,8794, -8795,8796,8797,8798,8799,8800,8801,8802,8803,8804,8805,8806,8807,8808,8809, -8810,8811,8812,8813,8814,8815,8816,8817,8818,8819,8820,8821,8822,8823,8824, -8825,8826,8827,8828,8829,8830,8831,8832,8833,8834,8835,8836,8837,8838,8839, -8840,8841,8842,8843,8844,8845,8846,8847,8848,8849,8850,8851,8852,8853,8854, -8855,8856,8857,8858,8859,8860,8861,8862,8863,8864,8865,8866,8867,8868,8869, -8870,8871,8872,8873,8874,8875,8876,8877,8878,8879,8880,8881,8882,8883,8884, -8885,8886,8887,8888,8889,8890,8891,8892,8893,8894,8895,8896,8897,8898,8899, -8900,8901,8902,8903,8904,8905,8906,8907,8908,8909,8910,8911,8912,8913,8914, -8915,8916,8917,8918,8919,8920,8921,8922,8923,8924,8925,8926,8927,8928,8929, -8930,8931,8932,8933,8934,8935,8936,8937,8938,8939,8940,8941,8942,8943,8944, -8945,8946,8947,8948,8949,8950,8951,8952,8953,8954,8955,8956,8957,8958,8959, -8960,8961,8962,8963,8964,8965,8966,8967,8968,8969,8970,8971,8972,8973,8974, -8975,8976,8977,8978,8979,8980,8981,8982,8983,8984,8985,8986,8987,8988,8989, -8990,8991,8992,8993,8994,8995,8996,8997,8998,8999,9000,9001,9002,9003,9004, -9005,9006,9007,9008,9009,9010,9011,9012,9013,9014,9015,9016,9017,9018,9019, -9020,9021,9022,9023,9024,9025,9026,9027,9028,9029,9030,9031,9032,9033,9034, -9035,9036,9037,9038,9039,9040,9041,9042,9043,9044,9045,9046,9047,9048,9049, -9050,9051,9052,9053,9054,9055,9056,9057,9058,9059,9060,9061,9062,9063,9064, -9065,9066,9067,9068,9069,9070,9071,9072,9073,9074,9075,9076,9077,9078,9079, -9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094, -9095,9096,9097,9098,9099,9100,9101,9102,9103,9104,9105,9106,9107,9108,9109, -9110,9111,9112,9113,9114,9115,9116,9117,9118,9119,9120,9121,9122,9123,9124, -9125,9126,9127,9128,9129,9130,9131,9132,9133,9134,9135,9136,9137,9138,9139, -9140,9141,9142,9143,9144,9145,9146,9147,9148,9149,9150,9151,9152,9153,9154, -9155,9156,9157,9158,9159,9160,9161,9162,9163,9164,9165,9166,9167,9168,9169, -9170,9171,9172,9173,9174,9175,9176,9177,9178,9179,9180,9181,9182,9183,9184, -9185,9186,9187,9188,9189,9190,9191,9192,9193,9194,9195,9196,9197,9198,9199, -9200,9201,9202,9203,9204,9205,9206,9207,9208,9209,9210,9211,9212,9213,9214, -9215,9216,9217,9218,9219,9220,9221,9222,9223,9224,9225,9226,9227,9228,9229, -9230,9231,9232,9233,9234,9235,9236,9237,9238,9239,9240,9241,9242,9243,9244, -9245,9246,9247,9248,9249,9250,9251,9252,9253,9254,9255,9256,9257,9258,9259, -9260,9261,9262,9263,9264,9265,9266,9267,9268,9269,9270,9271,9272,9273,9274, -9275,9276,9277,9278,9279,9280,9281,9282,9283,9284,9285,9286,9287,9288,9289, -9290,9291,9292,9293,9294,9295,9296,9297,9298,9299,9300,9301,9302,9303,9304, -9305,9306,9307,9308,9309,9310,9311,9312,9313,9314,9315,9316,9317,9318,9319, -9320,9321,9322,9323,9324,9325,9326,9327,9328,9329,9330,9331,9332,9333,9334, -9335,9336,9337,9338,9339,9340,9341,9342,9343,9344,9345,9346,9347,9348,9349, -9350,9351,9352,9353,9354,9355,9356,9357,9358,9359,9360,9361,9362,9363,9364, -9365,9366,9367,9368,9369,9370,9371,9372,9373,9374,9375,9376,9377,9378,9379, -9380,9381,9382,9383,9384,9385,9386,9387,9388,9389,9390,9391,9392,9393,9394, -9395,9396,9397,9398,9399,9400,9401,9402,9403,9404,9405,9406,9407,9408,9409, -9410,9411,9412,9413,9414,9415,9416,9417,9418,9419,9420,9421,9422,9423,9398, -9399,9400,9401,9402,9403,9404,9405,9406,9407,9408,9409,9410,9411,9412,9413, -9414,9415,9416,9417,9418,9419,9420,9421,9422,9423,9450,9451,9452,9453,9454, -9455,9456,9457,9458,9459,9460,9461,9462,9463,9464,9465,9466,9467,9468,9469, -9470,9471,9472,9473,9474,9475,9476,9477,9478,9479,9480,9481,9482,9483,9484, -9485,9486,9487,9488,9489,9490,9491,9492,9493,9494,9495,9496,9497,9498,9499, -9500,9501,9502,9503,9504,9505,9506,9507,9508,9509,9510,9511,9512,9513,9514, -9515,9516,9517,9518,9519,9520,9521,9522,9523,9524,9525,9526,9527,9528,9529, -9530,9531,9532,9533,9534,9535,9536,9537,9538,9539,9540,9541,9542,9543,9544, -9545,9546,9547,9548,9549,9550,9551,9552,9553,9554,9555,9556,9557,9558,9559, -9560,9561,9562,9563,9564,9565,9566,9567,9568,9569,9570,9571,9572,9573,9574, -9575,9576,9577,9578,9579,9580,9581,9582,9583,9584,9585,9586,9587,9588,9589, -9590,9591,9592,9593,9594,9595,9596,9597,9598,9599,9600,9601,9602,9603,9604, -9605,9606,9607,9608,9609,9610,9611,9612,9613,9614,9615,9616,9617,9618,9619, -9620,9621,9622,9623,9624,9625,9626,9627,9628,9629,9630,9631,9632,9633,9634, -9635,9636,9637,9638,9639,9640,9641,9642,9643,9644,9645,9646,9647,9648,9649, -9650,9651,9652,9653,9654,9655,9656,9657,9658,9659,9660,9661,9662,9663,9664, -9665,9666,9667,9668,9669,9670,9671,9672,9673,9674,9675,9676,9677,9678,9679, -9680,9681,9682,9683,9684,9685,9686,9687,9688,9689,9690,9691,9692,9693,9694, -9695,9696,9697,9698,9699,9700,9701,9702,9703,9704,9705,9706,9707,9708,9709, -9710,9711,9712,9713,9714,9715,9716,9717,9718,9719,9720,9721,9722,9723,9724, -9725,9726,9727,9728,9729,9730,9731,9732,9733,9734,9735,9736,9737,9738,9739, -9740,9741,9742,9743,9744,9745,9746,9747,9748,9749,9750,9751,9752,9753,9754, -9755,9756,9757,9758,9759,9760,9761,9762,9763,9764,9765,9766,9767,9768,9769, -9770,9771,9772,9773,9774,9775,9776,9777,9778,9779,9780,9781,9782,9783,9784, -9785,9786,9787,9788,9789,9790,9791,9792,9793,9794,9795,9796,9797,9798,9799, -9800,9801,9802,9803,9804,9805,9806,9807,9808,9809,9810,9811,9812,9813,9814, -9815,9816,9817,9818,9819,9820,9821,9822,9823,9824,9825,9826,9827,9828,9829, -9830,9831,9832,9833,9834,9835,9836,9837,9838,9839,9840,9841,9842,9843,9844, -9845,9846,9847,9848,9849,9850,9851,9852,9853,9854,9855,9856,9857,9858,9859, -9860,9861,9862,9863,9864,9865,9866,9867,9868,9869,9870,9871,9872,9873,9874, -9875,9876,9877,9878,9879,9880,9881,9882,9883,9884,9885,9886,9887,9888,9889, -9890,9891,9892,9893,9894,9895,9896,9897,9898,9899,9900,9901,9902,9903,9904, -9905,9906,9907,9908,9909,9910,9911,9912,9913,9914,9915,9916,9917,9918,9919, -9920,9921,9922,9923,9924,9925,9926,9927,9928,9929,9930,9931,9932,9933,9934, -9935,9936,9937,9938,9939,9940,9941,9942,9943,9944,9945,9946,9947,9948,9949, -9950,9951,9952,9953,9954,9955,9956,9957,9958,9959,9960,9961,9962,9963,9964, -9965,9966,9967,9968,9969,9970,9971,9972,9973,9974,9975,9976,9977,9978,9979, -9980,9981,9982,9983,9984,9985,9986,9987,9988,9989,9990,9991,9992,9993,9994, -9995,9996,9997,9998,9999,10000,10001,10002,10003,10004,10005,10006,10007, -10008,10009,10010,10011,10012,10013,10014,10015,10016,10017,10018,10019, -10020,10021,10022,10023,10024,10025,10026,10027,10028,10029,10030,10031, -10032,10033,10034,10035,10036,10037,10038,10039,10040,10041,10042,10043, -10044,10045,10046,10047,10048,10049,10050,10051,10052,10053,10054,10055, -10056,10057,10058,10059,10060,10061,10062,10063,10064,10065,10066,10067, -10068,10069,10070,10071,10072,10073,10074,10075,10076,10077,10078,10079, -10080,10081,10082,10083,10084,10085,10086,10087,10088,10089,10090,10091, -10092,10093,10094,10095,10096,10097,10098,10099,10100,10101,10102,10103, -10104,10105,10106,10107,10108,10109,10110,10111,10112,10113,10114,10115, -10116,10117,10118,10119,10120,10121,10122,10123,10124,10125,10126,10127, -10128,10129,10130,10131,10132,10133,10134,10135,10136,10137,10138,10139, -10140,10141,10142,10143,10144,10145,10146,10147,10148,10149,10150,10151, -10152,10153,10154,10155,10156,10157,10158,10159,10160,10161,10162,10163, -10164,10165,10166,10167,10168,10169,10170,10171,10172,10173,10174,10175, -10176,10177,10178,10179,10180,10181,10182,10183,10184,10185,10186,10187, -10188,10189,10190,10191,10192,10193,10194,10195,10196,10197,10198,10199, -10200,10201,10202,10203,10204,10205,10206,10207,10208,10209,10210,10211, -10212,10213,10214,10215,10216,10217,10218,10219,10220,10221,10222,10223, -10224,10225,10226,10227,10228,10229,10230,10231,10232,10233,10234,10235, -10236,10237,10238,10239,10240,10241,10242,10243,10244,10245,10246,10247, -10248,10249,10250,10251,10252,10253,10254,10255,10256,10257,10258,10259, -10260,10261,10262,10263,10264,10265,10266,10267,10268,10269,10270,10271, -10272,10273,10274,10275,10276,10277,10278,10279,10280,10281,10282,10283, -10284,10285,10286,10287,10288,10289,10290,10291,10292,10293,10294,10295, -10296,10297,10298,10299,10300,10301,10302,10303,10304,10305,10306,10307, -10308,10309,10310,10311,10312,10313,10314,10315,10316,10317,10318,10319, -10320,10321,10322,10323,10324,10325,10326,10327,10328,10329,10330,10331, -10332,10333,10334,10335,10336,10337,10338,10339,10340,10341,10342,10343, -10344,10345,10346,10347,10348,10349,10350,10351,10352,10353,10354,10355, -10356,10357,10358,10359,10360,10361,10362,10363,10364,10365,10366,10367, -10368,10369,10370,10371,10372,10373,10374,10375,10376,10377,10378,10379, -10380,10381,10382,10383,10384,10385,10386,10387,10388,10389,10390,10391, -10392,10393,10394,10395,10396,10397,10398,10399,10400,10401,10402,10403, -10404,10405,10406,10407,10408,10409,10410,10411,10412,10413,10414,10415, -10416,10417,10418,10419,10420,10421,10422,10423,10424,10425,10426,10427, -10428,10429,10430,10431,10432,10433,10434,10435,10436,10437,10438,10439, -10440,10441,10442,10443,10444,10445,10446,10447,10448,10449,10450,10451, -10452,10453,10454,10455,10456,10457,10458,10459,10460,10461,10462,10463, -10464,10465,10466,10467,10468,10469,10470,10471,10472,10473,10474,10475, -10476,10477,10478,10479,10480,10481,10482,10483,10484,10485,10486,10487, -10488,10489,10490,10491,10492,10493,10494,10495,10496,10497,10498,10499, -10500,10501,10502,10503,10504,10505,10506,10507,10508,10509,10510,10511, -10512,10513,10514,10515,10516,10517,10518,10519,10520,10521,10522,10523, -10524,10525,10526,10527,10528,10529,10530,10531,10532,10533,10534,10535, -10536,10537,10538,10539,10540,10541,10542,10543,10544,10545,10546,10547, -10548,10549,10550,10551,10552,10553,10554,10555,10556,10557,10558,10559, -10560,10561,10562,10563,10564,10565,10566,10567,10568,10569,10570,10571, -10572,10573,10574,10575,10576,10577,10578,10579,10580,10581,10582,10583, -10584,10585,10586,10587,10588,10589,10590,10591,10592,10593,10594,10595, -10596,10597,10598,10599,10600,10601,10602,10603,10604,10605,10606,10607, -10608,10609,10610,10611,10612,10613,10614,10615,10616,10617,10618,10619, -10620,10621,10622,10623,10624,10625,10626,10627,10628,10629,10630,10631, -10632,10633,10634,10635,10636,10637,10638,10639,10640,10641,10642,10643, -10644,10645,10646,10647,10648,10649,10650,10651,10652,10653,10654,10655, -10656,10657,10658,10659,10660,10661,10662,10663,10664,10665,10666,10667, -10668,10669,10670,10671,10672,10673,10674,10675,10676,10677,10678,10679, -10680,10681,10682,10683,10684,10685,10686,10687,10688,10689,10690,10691, -10692,10693,10694,10695,10696,10697,10698,10699,10700,10701,10702,10703, -10704,10705,10706,10707,10708,10709,10710,10711,10712,10713,10714,10715, -10716,10717,10718,10719,10720,10721,10722,10723,10724,10725,10726,10727, -10728,10729,10730,10731,10732,10733,10734,10735,10736,10737,10738,10739, -10740,10741,10742,10743,10744,10745,10746,10747,10748,10749,10750,10751, -10752,10753,10754,10755,10756,10757,10758,10759,10760,10761,10762,10763, -10764,10765,10766,10767,10768,10769,10770,10771,10772,10773,10774,10775, -10776,10777,10778,10779,10780,10781,10782,10783,10784,10785,10786,10787, -10788,10789,10790,10791,10792,10793,10794,10795,10796,10797,10798,10799, -10800,10801,10802,10803,10804,10805,10806,10807,10808,10809,10810,10811, -10812,10813,10814,10815,10816,10817,10818,10819,10820,10821,10822,10823, -10824,10825,10826,10827,10828,10829,10830,10831,10832,10833,10834,10835, -10836,10837,10838,10839,10840,10841,10842,10843,10844,10845,10846,10847, -10848,10849,10850,10851,10852,10853,10854,10855,10856,10857,10858,10859, -10860,10861,10862,10863,10864,10865,10866,10867,10868,10869,10870,10871, -10872,10873,10874,10875,10876,10877,10878,10879,10880,10881,10882,10883, -10884,10885,10886,10887,10888,10889,10890,10891,10892,10893,10894,10895, -10896,10897,10898,10899,10900,10901,10902,10903,10904,10905,10906,10907, -10908,10909,10910,10911,10912,10913,10914,10915,10916,10917,10918,10919, -10920,10921,10922,10923,10924,10925,10926,10927,10928,10929,10930,10931, -10932,10933,10934,10935,10936,10937,10938,10939,10940,10941,10942,10943, -10944,10945,10946,10947,10948,10949,10950,10951,10952,10953,10954,10955, -10956,10957,10958,10959,10960,10961,10962,10963,10964,10965,10966,10967, -10968,10969,10970,10971,10972,10973,10974,10975,10976,10977,10978,10979, -10980,10981,10982,10983,10984,10985,10986,10987,10988,10989,10990,10991, -10992,10993,10994,10995,10996,10997,10998,10999,11000,11001,11002,11003, -11004,11005,11006,11007,11008,11009,11010,11011,11012,11013,11014,11015, -11016,11017,11018,11019,11020,11021,11022,11023,11024,11025,11026,11027, -11028,11029,11030,11031,11032,11033,11034,11035,11036,11037,11038,11039, -11040,11041,11042,11043,11044,11045,11046,11047,11048,11049,11050,11051, -11052,11053,11054,11055,11056,11057,11058,11059,11060,11061,11062,11063, -11064,11065,11066,11067,11068,11069,11070,11071,11072,11073,11074,11075, -11076,11077,11078,11079,11080,11081,11082,11083,11084,11085,11086,11087, -11088,11089,11090,11091,11092,11093,11094,11095,11096,11097,11098,11099, -11100,11101,11102,11103,11104,11105,11106,11107,11108,11109,11110,11111, -11112,11113,11114,11115,11116,11117,11118,11119,11120,11121,11122,11123, -11124,11125,11126,11127,11128,11129,11130,11131,11132,11133,11134,11135, -11136,11137,11138,11139,11140,11141,11142,11143,11144,11145,11146,11147, -11148,11149,11150,11151,11152,11153,11154,11155,11156,11157,11158,11159, -11160,11161,11162,11163,11164,11165,11166,11167,11168,11169,11170,11171, -11172,11173,11174,11175,11176,11177,11178,11179,11180,11181,11182,11183, -11184,11185,11186,11187,11188,11189,11190,11191,11192,11193,11194,11195, -11196,11197,11198,11199,11200,11201,11202,11203,11204,11205,11206,11207, -11208,11209,11210,11211,11212,11213,11214,11215,11216,11217,11218,11219, -11220,11221,11222,11223,11224,11225,11226,11227,11228,11229,11230,11231, -11232,11233,11234,11235,11236,11237,11238,11239,11240,11241,11242,11243, -11244,11245,11246,11247,11248,11249,11250,11251,11252,11253,11254,11255, -11256,11257,11258,11259,11260,11261,11262,11263,11264,11265,11266,11267, -11268,11269,11270,11271,11272,11273,11274,11275,11276,11277,11278,11279, -11280,11281,11282,11283,11284,11285,11286,11287,11288,11289,11290,11291, -11292,11293,11294,11295,11296,11297,11298,11299,11300,11301,11302,11303, -11304,11305,11306,11307,11308,11309,11310,11311,11264,11265,11266,11267, -11268,11269,11270,11271,11272,11273,11274,11275,11276,11277,11278,11279, -11280,11281,11282,11283,11284,11285,11286,11287,11288,11289,11290,11291, -11292,11293,11294,11295,11296,11297,11298,11299,11300,11301,11302,11303, -11304,11305,11306,11307,11308,11309,11310,11359,11360,11360,11362,11363, -11364,570,574,11367,11367,11369,11369,11371,11371,11373,11374,11375,11376, -11377,11378,11378,11380,11381,11381,11383,11384,11385,11386,11387,11388, -11389,11390,11391,11392,11392,11394,11394,11396,11396,11398,11398,11400, -11400,11402,11402,11404,11404,11406,11406,11408,11408,11410,11410,11412, -11412,11414,11414,11416,11416,11418,11418,11420,11420,11422,11422,11424, -11424,11426,11426,11428,11428,11430,11430,11432,11432,11434,11434,11436, -11436,11438,11438,11440,11440,11442,11442,11444,11444,11446,11446,11448, -11448,11450,11450,11452,11452,11454,11454,11456,11456,11458,11458,11460, -11460,11462,11462,11464,11464,11466,11466,11468,11468,11470,11470,11472, -11472,11474,11474,11476,11476,11478,11478,11480,11480,11482,11482,11484, -11484,11486,11486,11488,11488,11490,11490,11492,11493,11494,11495,11496, -11497,11498,11499,11499,11501,11501,11503,11504,11505,11506,11506,11508, -11509,11510,11511,11512,11513,11514,11515,11516,11517,11518,11519,4256, -4257,4258,4259,4260,4261,4262,4263,4264,4265,4266,4267,4268,4269,4270,4271, -4272,4273,4274,4275,4276,4277,4278,4279,4280,4281,4282,4283,4284,4285,4286, -4287,4288,4289,4290,4291,4292,4293,11558,4295,11560,11561,11562,11563, -11564,4301,11566,11567,11568,11569,11570,11571,11572,11573,11574,11575, -11576,11577,11578,11579,11580,11581,11582,11583,11584,11585,11586,11587, -11588,11589,11590,11591,11592,11593,11594,11595,11596,11597,11598,11599, -11600,11601,11602,11603,11604,11605,11606,11607,11608,11609,11610,11611, -11612,11613,11614,11615,11616,11617,11618,11619,11620,11621,11622,11623, -11624,11625,11626,11627,11628,11629,11630,11631,11632,11633,11634,11635, -11636,11637,11638,11639,11640,11641,11642,11643,11644,11645,11646,11647, -11648,11649,11650,11651,11652,11653,11654,11655,11656,11657,11658,11659, -11660,11661,11662,11663,11664,11665,11666,11667,11668,11669,11670,11671, -11672,11673,11674,11675,11676,11677,11678,11679,11680,11681,11682,11683, -11684,11685,11686,11687,11688,11689,11690,11691,11692,11693,11694,11695, -11696,11697,11698,11699,11700,11701,11702,11703,11704,11705,11706,11707, -11708,11709,11710,11711,11712,11713,11714,11715,11716,11717,11718,11719, -11720,11721,11722,11723,11724,11725,11726,11727,11728,11729,11730,11731, -11732,11733,11734,11735,11736,11737,11738,11739,11740,11741,11742,11743, -11744,11745,11746,11747,11748,11749,11750,11751,11752,11753,11754,11755, -11756,11757,11758,11759,11760,11761,11762,11763,11764,11765,11766,11767, -11768,11769,11770,11771,11772,11773,11774,11775,11776,11777,11778,11779, -11780,11781,11782,11783,11784,11785,11786,11787,11788,11789,11790,11791, -11792,11793,11794,11795,11796,11797,11798,11799,11800,11801,11802,11803, -11804,11805,11806,11807,11808,11809,11810,11811,11812,11813,11814,11815, -11816,11817,11818,11819,11820,11821,11822,11823,11824,11825,11826,11827, -11828,11829,11830,11831,11832,11833,11834,11835,11836,11837,11838,11839, -11840,11841,11842,11843,11844,11845,11846,11847,11848,11849,11850,11851, -11852,11853,11854,11855,11856,11857,11858,11859,11860,11861,11862,11863, -11864,11865,11866,11867,11868,11869,11870,11871,11872,11873,11874,11875, -11876,11877,11878,11879,11880,11881,11882,11883,11884,11885,11886,11887, -11888,11889,11890,11891,11892,11893,11894,11895,11896,11897,11898,11899, -11900,11901,11902,11903,11904,11905,11906,11907,11908,11909,11910,11911, -11912,11913,11914,11915,11916,11917,11918,11919,11920,11921,11922,11923, -11924,11925,11926,11927,11928,11929,11930,11931,11932,11933,11934,11935, -11936,11937,11938,11939,11940,11941,11942,11943,11944,11945,11946,11947, -11948,11949,11950,11951,11952,11953,11954,11955,11956,11957,11958,11959, -11960,11961,11962,11963,11964,11965,11966,11967,11968,11969,11970,11971, -11972,11973,11974,11975,11976,11977,11978,11979,11980,11981,11982,11983, -11984,11985,11986,11987,11988,11989,11990,11991,11992,11993,11994,11995, -11996,11997,11998,11999,12000,12001,12002,12003,12004,12005,12006,12007, -12008,12009,12010,12011,12012,12013,12014,12015,12016,12017,12018,12019, -12020,12021,12022,12023,12024,12025,12026,12027,12028,12029,12030,12031, -12032,12033,12034,12035,12036,12037,12038,12039,12040,12041,12042,12043, -12044,12045,12046,12047,12048,12049,12050,12051,12052,12053,12054,12055, -12056,12057,12058,12059,12060,12061,12062,12063,12064,12065,12066,12067, -12068,12069,12070,12071,12072,12073,12074,12075,12076,12077,12078,12079, -12080,12081,12082,12083,12084,12085,12086,12087,12088,12089,12090,12091, -12092,12093,12094,12095,12096,12097,12098,12099,12100,12101,12102,12103, -12104,12105,12106,12107,12108,12109,12110,12111,12112,12113,12114,12115, -12116,12117,12118,12119,12120,12121,12122,12123,12124,12125,12126,12127, -12128,12129,12130,12131,12132,12133,12134,12135,12136,12137,12138,12139, -12140,12141,12142,12143,12144,12145,12146,12147,12148,12149,12150,12151, -12152,12153,12154,12155,12156,12157,12158,12159,12160,12161,12162,12163, -12164,12165,12166,12167,12168,12169,12170,12171,12172,12173,12174,12175, -12176,12177,12178,12179,12180,12181,12182,12183,12184,12185,12186,12187, -12188,12189,12190,12191,12192,12193,12194,12195,12196,12197,12198,12199, -12200,12201,12202,12203,12204,12205,12206,12207,12208,12209,12210,12211, -12212,12213,12214,12215,12216,12217,12218,12219,12220,12221,12222,12223, -12224,12225,12226,12227,12228,12229,12230,12231,12232,12233,12234,12235, -12236,12237,12238,12239,12240,12241,12242,12243,12244,12245,12246,12247, -12248,12249,12250,12251,12252,12253,12254,12255,12256,12257,12258,12259, -12260,12261,12262,12263,12264,12265,12266,12267,12268,12269,12270,12271, -12272,12273,12274,12275,12276,12277,12278,12279,12280,12281,12282,12283, -12284,12285,12286,12287,12288,12289,12290,12291,12292,12293,12294,12295, -12296,12297,12298,12299,12300,12301,12302,12303,12304,12305,12306,12307, -12308,12309,12310,12311,12312,12313,12314,12315,12316,12317,12318,12319, -12320,12321,12322,12323,12324,12325,12326,12327,12328,12329,12330,12331, -12332,12333,12334,12335,12336,12337,12338,12339,12340,12341,12342,12343, -12344,12345,12346,12347,12348,12349,12350,12351,12352,12353,12354,12355, -12356,12357,12358,12359,12360,12361,12362,12363,12364,12365,12366,12367, -12368,12369,12370,12371,12372,12373,12374,12375,12376,12377,12378,12379, -12380,12381,12382,12383,12384,12385,12386,12387,12388,12389,12390,12391, -12392,12393,12394,12395,12396,12397,12398,12399,12400,12401,12402,12403, -12404,12405,12406,12407,12408,12409,12410,12411,12412,12413,12414,12415, -12416,12417,12418,12419,12420,12421,12422,12423,12424,12425,12426,12427, -12428,12429,12430,12431,12432,12433,12434,12435,12436,12437,12438,12439, -12440,12441,12442,12443,12444,12445,12446,12447,12448,12449,12450,12451, -12452,12453,12454,12455,12456,12457,12458,12459,12460,12461,12462,12463, -12464,12465,12466,12467,12468,12469,12470,12471,12472,12473,12474,12475, -12476,12477,12478,12479,12480,12481,12482,12483,12484,12485,12486,12487, -12488,12489,12490,12491,12492,12493,12494,12495,12496,12497,12498,12499, -12500,12501,12502,12503,12504,12505,12506,12507,12508,12509,12510,12511, -12512,12513,12514,12515,12516,12517,12518,12519,12520,12521,12522,12523, -12524,12525,12526,12527,12528,12529,12530,12531,12532,12533,12534,12535, -12536,12537,12538,12539,12540,12541,12542,12543,12544,12545,12546,12547, -12548,12549,12550,12551,12552,12553,12554,12555,12556,12557,12558,12559, -12560,12561,12562,12563,12564,12565,12566,12567,12568,12569,12570,12571, -12572,12573,12574,12575,12576,12577,12578,12579,12580,12581,12582,12583, -12584,12585,12586,12587,12588,12589,12590,12591,12592,12593,12594,12595, -12596,12597,12598,12599,12600,12601,12602,12603,12604,12605,12606,12607, -12608,12609,12610,12611,12612,12613,12614,12615,12616,12617,12618,12619, -12620,12621,12622,12623,12624,12625,12626,12627,12628,12629,12630,12631, -12632,12633,12634,12635,12636,12637,12638,12639,12640,12641,12642,12643, -12644,12645,12646,12647,12648,12649,12650,12651,12652,12653,12654,12655, -12656,12657,12658,12659,12660,12661,12662,12663,12664,12665,12666,12667, -12668,12669,12670,12671,12672,12673,12674,12675,12676,12677,12678,12679, -12680,12681,12682,12683,12684,12685,12686,12687,12688,12689,12690,12691, -12692,12693,12694,12695,12696,12697,12698,12699,12700,12701,12702,12703, -12704,12705,12706,12707,12708,12709,12710,12711,12712,12713,12714,12715, -12716,12717,12718,12719,12720,12721,12722,12723,12724,12725,12726,12727, -12728,12729,12730,12731,12732,12733,12734,12735,12736,12737,12738,12739, -12740,12741,12742,12743,12744,12745,12746,12747,12748,12749,12750,12751, -12752,12753,12754,12755,12756,12757,12758,12759,12760,12761,12762,12763, -12764,12765,12766,12767,12768,12769,12770,12771,12772,12773,12774,12775, -12776,12777,12778,12779,12780,12781,12782,12783,12784,12785,12786,12787, -12788,12789,12790,12791,12792,12793,12794,12795,12796,12797,12798,12799, -12800,12801,12802,12803,12804,12805,12806,12807,12808,12809,12810,12811, -12812,12813,12814,12815,12816,12817,12818,12819,12820,12821,12822,12823, -12824,12825,12826,12827,12828,12829,12830,12831,12832,12833,12834,12835, -12836,12837,12838,12839,12840,12841,12842,12843,12844,12845,12846,12847, -12848,12849,12850,12851,12852,12853,12854,12855,12856,12857,12858,12859, -12860,12861,12862,12863,12864,12865,12866,12867,12868,12869,12870,12871, -12872,12873,12874,12875,12876,12877,12878,12879,12880,12881,12882,12883, -12884,12885,12886,12887,12888,12889,12890,12891,12892,12893,12894,12895, -12896,12897,12898,12899,12900,12901,12902,12903,12904,12905,12906,12907, -12908,12909,12910,12911,12912,12913,12914,12915,12916,12917,12918,12919, -12920,12921,12922,12923,12924,12925,12926,12927,12928,12929,12930,12931, -12932,12933,12934,12935,12936,12937,12938,12939,12940,12941,12942,12943, -12944,12945,12946,12947,12948,12949,12950,12951,12952,12953,12954,12955, -12956,12957,12958,12959,12960,12961,12962,12963,12964,12965,12966,12967, -12968,12969,12970,12971,12972,12973,12974,12975,12976,12977,12978,12979, -12980,12981,12982,12983,12984,12985,12986,12987,12988,12989,12990,12991, -12992,12993,12994,12995,12996,12997,12998,12999,13000,13001,13002,13003, -13004,13005,13006,13007,13008,13009,13010,13011,13012,13013,13014,13015, -13016,13017,13018,13019,13020,13021,13022,13023,13024,13025,13026,13027, -13028,13029,13030,13031,13032,13033,13034,13035,13036,13037,13038,13039, -13040,13041,13042,13043,13044,13045,13046,13047,13048,13049,13050,13051, -13052,13053,13054,13055,13056,13057,13058,13059,13060,13061,13062,13063, -13064,13065,13066,13067,13068,13069,13070,13071,13072,13073,13074,13075, -13076,13077,13078,13079,13080,13081,13082,13083,13084,13085,13086,13087, -13088,13089,13090,13091,13092,13093,13094,13095,13096,13097,13098,13099, -13100,13101,13102,13103,13104,13105,13106,13107,13108,13109,13110,13111, -13112,13113,13114,13115,13116,13117,13118,13119,13120,13121,13122,13123, -13124,13125,13126,13127,13128,13129,13130,13131,13132,13133,13134,13135, -13136,13137,13138,13139,13140,13141,13142,13143,13144,13145,13146,13147, -13148,13149,13150,13151,13152,13153,13154,13155,13156,13157,13158,13159, -13160,13161,13162,13163,13164,13165,13166,13167,13168,13169,13170,13171, -13172,13173,13174,13175,13176,13177,13178,13179,13180,13181,13182,13183, -13184,13185,13186,13187,13188,13189,13190,13191,13192,13193,13194,13195, -13196,13197,13198,13199,13200,13201,13202,13203,13204,13205,13206,13207, -13208,13209,13210,13211,13212,13213,13214,13215,13216,13217,13218,13219, -13220,13221,13222,13223,13224,13225,13226,13227,13228,13229,13230,13231, -13232,13233,13234,13235,13236,13237,13238,13239,13240,13241,13242,13243, -13244,13245,13246,13247,13248,13249,13250,13251,13252,13253,13254,13255, -13256,13257,13258,13259,13260,13261,13262,13263,13264,13265,13266,13267, -13268,13269,13270,13271,13272,13273,13274,13275,13276,13277,13278,13279, -13280,13281,13282,13283,13284,13285,13286,13287,13288,13289,13290,13291, -13292,13293,13294,13295,13296,13297,13298,13299,13300,13301,13302,13303, -13304,13305,13306,13307,13308,13309,13310,13311,13312,13313,13314,13315, -13316,13317,13318,13319,13320,13321,13322,13323,13324,13325,13326,13327, -13328,13329,13330,13331,13332,13333,13334,13335,13336,13337,13338,13339, -13340,13341,13342,13343,13344,13345,13346,13347,13348,13349,13350,13351, -13352,13353,13354,13355,13356,13357,13358,13359,13360,13361,13362,13363, -13364,13365,13366,13367,13368,13369,13370,13371,13372,13373,13374,13375, -13376,13377,13378,13379,13380,13381,13382,13383,13384,13385,13386,13387, -13388,13389,13390,13391,13392,13393,13394,13395,13396,13397,13398,13399, -13400,13401,13402,13403,13404,13405,13406,13407,13408,13409,13410,13411, -13412,13413,13414,13415,13416,13417,13418,13419,13420,13421,13422,13423, -13424,13425,13426,13427,13428,13429,13430,13431,13432,13433,13434,13435, -13436,13437,13438,13439,13440,13441,13442,13443,13444,13445,13446,13447, -13448,13449,13450,13451,13452,13453,13454,13455,13456,13457,13458,13459, -13460,13461,13462,13463,13464,13465,13466,13467,13468,13469,13470,13471, -13472,13473,13474,13475,13476,13477,13478,13479,13480,13481,13482,13483, -13484,13485,13486,13487,13488,13489,13490,13491,13492,13493,13494,13495, -13496,13497,13498,13499,13500,13501,13502,13503,13504,13505,13506,13507, -13508,13509,13510,13511,13512,13513,13514,13515,13516,13517,13518,13519, -13520,13521,13522,13523,13524,13525,13526,13527,13528,13529,13530,13531, -13532,13533,13534,13535,13536,13537,13538,13539,13540,13541,13542,13543, -13544,13545,13546,13547,13548,13549,13550,13551,13552,13553,13554,13555, -13556,13557,13558,13559,13560,13561,13562,13563,13564,13565,13566,13567, -13568,13569,13570,13571,13572,13573,13574,13575,13576,13577,13578,13579, -13580,13581,13582,13583,13584,13585,13586,13587,13588,13589,13590,13591, -13592,13593,13594,13595,13596,13597,13598,13599,13600,13601,13602,13603, -13604,13605,13606,13607,13608,13609,13610,13611,13612,13613,13614,13615, -13616,13617,13618,13619,13620,13621,13622,13623,13624,13625,13626,13627, -13628,13629,13630,13631,13632,13633,13634,13635,13636,13637,13638,13639, -13640,13641,13642,13643,13644,13645,13646,13647,13648,13649,13650,13651, -13652,13653,13654,13655,13656,13657,13658,13659,13660,13661,13662,13663, -13664,13665,13666,13667,13668,13669,13670,13671,13672,13673,13674,13675, -13676,13677,13678,13679,13680,13681,13682,13683,13684,13685,13686,13687, -13688,13689,13690,13691,13692,13693,13694,13695,13696,13697,13698,13699, -13700,13701,13702,13703,13704,13705,13706,13707,13708,13709,13710,13711, -13712,13713,13714,13715,13716,13717,13718,13719,13720,13721,13722,13723, -13724,13725,13726,13727,13728,13729,13730,13731,13732,13733,13734,13735, -13736,13737,13738,13739,13740,13741,13742,13743,13744,13745,13746,13747, -13748,13749,13750,13751,13752,13753,13754,13755,13756,13757,13758,13759, -13760,13761,13762,13763,13764,13765,13766,13767,13768,13769,13770,13771, -13772,13773,13774,13775,13776,13777,13778,13779,13780,13781,13782,13783, -13784,13785,13786,13787,13788,13789,13790,13791,13792,13793,13794,13795, -13796,13797,13798,13799,13800,13801,13802,13803,13804,13805,13806,13807, -13808,13809,13810,13811,13812,13813,13814,13815,13816,13817,13818,13819, -13820,13821,13822,13823,13824,13825,13826,13827,13828,13829,13830,13831, -13832,13833,13834,13835,13836,13837,13838,13839,13840,13841,13842,13843, -13844,13845,13846,13847,13848,13849,13850,13851,13852,13853,13854,13855, -13856,13857,13858,13859,13860,13861,13862,13863,13864,13865,13866,13867, -13868,13869,13870,13871,13872,13873,13874,13875,13876,13877,13878,13879, -13880,13881,13882,13883,13884,13885,13886,13887,13888,13889,13890,13891, -13892,13893,13894,13895,13896,13897,13898,13899,13900,13901,13902,13903, -13904,13905,13906,13907,13908,13909,13910,13911,13912,13913,13914,13915, -13916,13917,13918,13919,13920,13921,13922,13923,13924,13925,13926,13927, -13928,13929,13930,13931,13932,13933,13934,13935,13936,13937,13938,13939, -13940,13941,13942,13943,13944,13945,13946,13947,13948,13949,13950,13951, -13952,13953,13954,13955,13956,13957,13958,13959,13960,13961,13962,13963, -13964,13965,13966,13967,13968,13969,13970,13971,13972,13973,13974,13975, -13976,13977,13978,13979,13980,13981,13982,13983,13984,13985,13986,13987, -13988,13989,13990,13991,13992,13993,13994,13995,13996,13997,13998,13999, -14000,14001,14002,14003,14004,14005,14006,14007,14008,14009,14010,14011, -14012,14013,14014,14015,14016,14017,14018,14019,14020,14021,14022,14023, -14024,14025,14026,14027,14028,14029,14030,14031,14032,14033,14034,14035, -14036,14037,14038,14039,14040,14041,14042,14043,14044,14045,14046,14047, -14048,14049,14050,14051,14052,14053,14054,14055,14056,14057,14058,14059, -14060,14061,14062,14063,14064,14065,14066,14067,14068,14069,14070,14071, -14072,14073,14074,14075,14076,14077,14078,14079,14080,14081,14082,14083, -14084,14085,14086,14087,14088,14089,14090,14091,14092,14093,14094,14095, -14096,14097,14098,14099,14100,14101,14102,14103,14104,14105,14106,14107, -14108,14109,14110,14111,14112,14113,14114,14115,14116,14117,14118,14119, -14120,14121,14122,14123,14124,14125,14126,14127,14128,14129,14130,14131, -14132,14133,14134,14135,14136,14137,14138,14139,14140,14141,14142,14143, -14144,14145,14146,14147,14148,14149,14150,14151,14152,14153,14154,14155, -14156,14157,14158,14159,14160,14161,14162,14163,14164,14165,14166,14167, -14168,14169,14170,14171,14172,14173,14174,14175,14176,14177,14178,14179, -14180,14181,14182,14183,14184,14185,14186,14187,14188,14189,14190,14191, -14192,14193,14194,14195,14196,14197,14198,14199,14200,14201,14202,14203, -14204,14205,14206,14207,14208,14209,14210,14211,14212,14213,14214,14215, -14216,14217,14218,14219,14220,14221,14222,14223,14224,14225,14226,14227, -14228,14229,14230,14231,14232,14233,14234,14235,14236,14237,14238,14239, -14240,14241,14242,14243,14244,14245,14246,14247,14248,14249,14250,14251, -14252,14253,14254,14255,14256,14257,14258,14259,14260,14261,14262,14263, -14264,14265,14266,14267,14268,14269,14270,14271,14272,14273,14274,14275, -14276,14277,14278,14279,14280,14281,14282,14283,14284,14285,14286,14287, -14288,14289,14290,14291,14292,14293,14294,14295,14296,14297,14298,14299, -14300,14301,14302,14303,14304,14305,14306,14307,14308,14309,14310,14311, -14312,14313,14314,14315,14316,14317,14318,14319,14320,14321,14322,14323, -14324,14325,14326,14327,14328,14329,14330,14331,14332,14333,14334,14335, -14336,14337,14338,14339,14340,14341,14342,14343,14344,14345,14346,14347, -14348,14349,14350,14351,14352,14353,14354,14355,14356,14357,14358,14359, -14360,14361,14362,14363,14364,14365,14366,14367,14368,14369,14370,14371, -14372,14373,14374,14375,14376,14377,14378,14379,14380,14381,14382,14383, -14384,14385,14386,14387,14388,14389,14390,14391,14392,14393,14394,14395, -14396,14397,14398,14399,14400,14401,14402,14403,14404,14405,14406,14407, -14408,14409,14410,14411,14412,14413,14414,14415,14416,14417,14418,14419, -14420,14421,14422,14423,14424,14425,14426,14427,14428,14429,14430,14431, -14432,14433,14434,14435,14436,14437,14438,14439,14440,14441,14442,14443, -14444,14445,14446,14447,14448,14449,14450,14451,14452,14453,14454,14455, -14456,14457,14458,14459,14460,14461,14462,14463,14464,14465,14466,14467, -14468,14469,14470,14471,14472,14473,14474,14475,14476,14477,14478,14479, -14480,14481,14482,14483,14484,14485,14486,14487,14488,14489,14490,14491, -14492,14493,14494,14495,14496,14497,14498,14499,14500,14501,14502,14503, -14504,14505,14506,14507,14508,14509,14510,14511,14512,14513,14514,14515, -14516,14517,14518,14519,14520,14521,14522,14523,14524,14525,14526,14527, -14528,14529,14530,14531,14532,14533,14534,14535,14536,14537,14538,14539, -14540,14541,14542,14543,14544,14545,14546,14547,14548,14549,14550,14551, -14552,14553,14554,14555,14556,14557,14558,14559,14560,14561,14562,14563, -14564,14565,14566,14567,14568,14569,14570,14571,14572,14573,14574,14575, -14576,14577,14578,14579,14580,14581,14582,14583,14584,14585,14586,14587, -14588,14589,14590,14591,14592,14593,14594,14595,14596,14597,14598,14599, -14600,14601,14602,14603,14604,14605,14606,14607,14608,14609,14610,14611, -14612,14613,14614,14615,14616,14617,14618,14619,14620,14621,14622,14623, -14624,14625,14626,14627,14628,14629,14630,14631,14632,14633,14634,14635, -14636,14637,14638,14639,14640,14641,14642,14643,14644,14645,14646,14647, -14648,14649,14650,14651,14652,14653,14654,14655,14656,14657,14658,14659, -14660,14661,14662,14663,14664,14665,14666,14667,14668,14669,14670,14671, -14672,14673,14674,14675,14676,14677,14678,14679,14680,14681,14682,14683, -14684,14685,14686,14687,14688,14689,14690,14691,14692,14693,14694,14695, -14696,14697,14698,14699,14700,14701,14702,14703,14704,14705,14706,14707, -14708,14709,14710,14711,14712,14713,14714,14715,14716,14717,14718,14719, -14720,14721,14722,14723,14724,14725,14726,14727,14728,14729,14730,14731, -14732,14733,14734,14735,14736,14737,14738,14739,14740,14741,14742,14743, -14744,14745,14746,14747,14748,14749,14750,14751,14752,14753,14754,14755, -14756,14757,14758,14759,14760,14761,14762,14763,14764,14765,14766,14767, -14768,14769,14770,14771,14772,14773,14774,14775,14776,14777,14778,14779, -14780,14781,14782,14783,14784,14785,14786,14787,14788,14789,14790,14791, -14792,14793,14794,14795,14796,14797,14798,14799,14800,14801,14802,14803, -14804,14805,14806,14807,14808,14809,14810,14811,14812,14813,14814,14815, -14816,14817,14818,14819,14820,14821,14822,14823,14824,14825,14826,14827, -14828,14829,14830,14831,14832,14833,14834,14835,14836,14837,14838,14839, -14840,14841,14842,14843,14844,14845,14846,14847,14848,14849,14850,14851, -14852,14853,14854,14855,14856,14857,14858,14859,14860,14861,14862,14863, -14864,14865,14866,14867,14868,14869,14870,14871,14872,14873,14874,14875, -14876,14877,14878,14879,14880,14881,14882,14883,14884,14885,14886,14887, -14888,14889,14890,14891,14892,14893,14894,14895,14896,14897,14898,14899, -14900,14901,14902,14903,14904,14905,14906,14907,14908,14909,14910,14911, -14912,14913,14914,14915,14916,14917,14918,14919,14920,14921,14922,14923, -14924,14925,14926,14927,14928,14929,14930,14931,14932,14933,14934,14935, -14936,14937,14938,14939,14940,14941,14942,14943,14944,14945,14946,14947, -14948,14949,14950,14951,14952,14953,14954,14955,14956,14957,14958,14959, -14960,14961,14962,14963,14964,14965,14966,14967,14968,14969,14970,14971, -14972,14973,14974,14975,14976,14977,14978,14979,14980,14981,14982,14983, -14984,14985,14986,14987,14988,14989,14990,14991,14992,14993,14994,14995, -14996,14997,14998,14999,15000,15001,15002,15003,15004,15005,15006,15007, -15008,15009,15010,15011,15012,15013,15014,15015,15016,15017,15018,15019, -15020,15021,15022,15023,15024,15025,15026,15027,15028,15029,15030,15031, -15032,15033,15034,15035,15036,15037,15038,15039,15040,15041,15042,15043, -15044,15045,15046,15047,15048,15049,15050,15051,15052,15053,15054,15055, -15056,15057,15058,15059,15060,15061,15062,15063,15064,15065,15066,15067, -15068,15069,15070,15071,15072,15073,15074,15075,15076,15077,15078,15079, -15080,15081,15082,15083,15084,15085,15086,15087,15088,15089,15090,15091, -15092,15093,15094,15095,15096,15097,15098,15099,15100,15101,15102,15103, -15104,15105,15106,15107,15108,15109,15110,15111,15112,15113,15114,15115, -15116,15117,15118,15119,15120,15121,15122,15123,15124,15125,15126,15127, -15128,15129,15130,15131,15132,15133,15134,15135,15136,15137,15138,15139, -15140,15141,15142,15143,15144,15145,15146,15147,15148,15149,15150,15151, -15152,15153,15154,15155,15156,15157,15158,15159,15160,15161,15162,15163, -15164,15165,15166,15167,15168,15169,15170,15171,15172,15173,15174,15175, -15176,15177,15178,15179,15180,15181,15182,15183,15184,15185,15186,15187, -15188,15189,15190,15191,15192,15193,15194,15195,15196,15197,15198,15199, -15200,15201,15202,15203,15204,15205,15206,15207,15208,15209,15210,15211, -15212,15213,15214,15215,15216,15217,15218,15219,15220,15221,15222,15223, -15224,15225,15226,15227,15228,15229,15230,15231,15232,15233,15234,15235, -15236,15237,15238,15239,15240,15241,15242,15243,15244,15245,15246,15247, -15248,15249,15250,15251,15252,15253,15254,15255,15256,15257,15258,15259, -15260,15261,15262,15263,15264,15265,15266,15267,15268,15269,15270,15271, -15272,15273,15274,15275,15276,15277,15278,15279,15280,15281,15282,15283, -15284,15285,15286,15287,15288,15289,15290,15291,15292,15293,15294,15295, -15296,15297,15298,15299,15300,15301,15302,15303,15304,15305,15306,15307, -15308,15309,15310,15311,15312,15313,15314,15315,15316,15317,15318,15319, -15320,15321,15322,15323,15324,15325,15326,15327,15328,15329,15330,15331, -15332,15333,15334,15335,15336,15337,15338,15339,15340,15341,15342,15343, -15344,15345,15346,15347,15348,15349,15350,15351,15352,15353,15354,15355, -15356,15357,15358,15359,15360,15361,15362,15363,15364,15365,15366,15367, -15368,15369,15370,15371,15372,15373,15374,15375,15376,15377,15378,15379, -15380,15381,15382,15383,15384,15385,15386,15387,15388,15389,15390,15391, -15392,15393,15394,15395,15396,15397,15398,15399,15400,15401,15402,15403, -15404,15405,15406,15407,15408,15409,15410,15411,15412,15413,15414,15415, -15416,15417,15418,15419,15420,15421,15422,15423,15424,15425,15426,15427, -15428,15429,15430,15431,15432,15433,15434,15435,15436,15437,15438,15439, -15440,15441,15442,15443,15444,15445,15446,15447,15448,15449,15450,15451, -15452,15453,15454,15455,15456,15457,15458,15459,15460,15461,15462,15463, -15464,15465,15466,15467,15468,15469,15470,15471,15472,15473,15474,15475, -15476,15477,15478,15479,15480,15481,15482,15483,15484,15485,15486,15487, -15488,15489,15490,15491,15492,15493,15494,15495,15496,15497,15498,15499, -15500,15501,15502,15503,15504,15505,15506,15507,15508,15509,15510,15511, -15512,15513,15514,15515,15516,15517,15518,15519,15520,15521,15522,15523, -15524,15525,15526,15527,15528,15529,15530,15531,15532,15533,15534,15535, -15536,15537,15538,15539,15540,15541,15542,15543,15544,15545,15546,15547, -15548,15549,15550,15551,15552,15553,15554,15555,15556,15557,15558,15559, -15560,15561,15562,15563,15564,15565,15566,15567,15568,15569,15570,15571, -15572,15573,15574,15575,15576,15577,15578,15579,15580,15581,15582,15583, -15584,15585,15586,15587,15588,15589,15590,15591,15592,15593,15594,15595, -15596,15597,15598,15599,15600,15601,15602,15603,15604,15605,15606,15607, -15608,15609,15610,15611,15612,15613,15614,15615,15616,15617,15618,15619, -15620,15621,15622,15623,15624,15625,15626,15627,15628,15629,15630,15631, -15632,15633,15634,15635,15636,15637,15638,15639,15640,15641,15642,15643, -15644,15645,15646,15647,15648,15649,15650,15651,15652,15653,15654,15655, -15656,15657,15658,15659,15660,15661,15662,15663,15664,15665,15666,15667, -15668,15669,15670,15671,15672,15673,15674,15675,15676,15677,15678,15679, -15680,15681,15682,15683,15684,15685,15686,15687,15688,15689,15690,15691, -15692,15693,15694,15695,15696,15697,15698,15699,15700,15701,15702,15703, -15704,15705,15706,15707,15708,15709,15710,15711,15712,15713,15714,15715, -15716,15717,15718,15719,15720,15721,15722,15723,15724,15725,15726,15727, -15728,15729,15730,15731,15732,15733,15734,15735,15736,15737,15738,15739, -15740,15741,15742,15743,15744,15745,15746,15747,15748,15749,15750,15751, -15752,15753,15754,15755,15756,15757,15758,15759,15760,15761,15762,15763, -15764,15765,15766,15767,15768,15769,15770,15771,15772,15773,15774,15775, -15776,15777,15778,15779,15780,15781,15782,15783,15784,15785,15786,15787, -15788,15789,15790,15791,15792,15793,15794,15795,15796,15797,15798,15799, -15800,15801,15802,15803,15804,15805,15806,15807,15808,15809,15810,15811, -15812,15813,15814,15815,15816,15817,15818,15819,15820,15821,15822,15823, -15824,15825,15826,15827,15828,15829,15830,15831,15832,15833,15834,15835, -15836,15837,15838,15839,15840,15841,15842,15843,15844,15845,15846,15847, -15848,15849,15850,15851,15852,15853,15854,15855,15856,15857,15858,15859, -15860,15861,15862,15863,15864,15865,15866,15867,15868,15869,15870,15871, -15872,15873,15874,15875,15876,15877,15878,15879,15880,15881,15882,15883, -15884,15885,15886,15887,15888,15889,15890,15891,15892,15893,15894,15895, -15896,15897,15898,15899,15900,15901,15902,15903,15904,15905,15906,15907, -15908,15909,15910,15911,15912,15913,15914,15915,15916,15917,15918,15919, -15920,15921,15922,15923,15924,15925,15926,15927,15928,15929,15930,15931, -15932,15933,15934,15935,15936,15937,15938,15939,15940,15941,15942,15943, -15944,15945,15946,15947,15948,15949,15950,15951,15952,15953,15954,15955, -15956,15957,15958,15959,15960,15961,15962,15963,15964,15965,15966,15967, -15968,15969,15970,15971,15972,15973,15974,15975,15976,15977,15978,15979, -15980,15981,15982,15983,15984,15985,15986,15987,15988,15989,15990,15991, -15992,15993,15994,15995,15996,15997,15998,15999,16000,16001,16002,16003, -16004,16005,16006,16007,16008,16009,16010,16011,16012,16013,16014,16015, -16016,16017,16018,16019,16020,16021,16022,16023,16024,16025,16026,16027, -16028,16029,16030,16031,16032,16033,16034,16035,16036,16037,16038,16039, -16040,16041,16042,16043,16044,16045,16046,16047,16048,16049,16050,16051, -16052,16053,16054,16055,16056,16057,16058,16059,16060,16061,16062,16063, -16064,16065,16066,16067,16068,16069,16070,16071,16072,16073,16074,16075, -16076,16077,16078,16079,16080,16081,16082,16083,16084,16085,16086,16087, -16088,16089,16090,16091,16092,16093,16094,16095,16096,16097,16098,16099, -16100,16101,16102,16103,16104,16105,16106,16107,16108,16109,16110,16111, -16112,16113,16114,16115,16116,16117,16118,16119,16120,16121,16122,16123, -16124,16125,16126,16127,16128,16129,16130,16131,16132,16133,16134,16135, -16136,16137,16138,16139,16140,16141,16142,16143,16144,16145,16146,16147, -16148,16149,16150,16151,16152,16153,16154,16155,16156,16157,16158,16159, -16160,16161,16162,16163,16164,16165,16166,16167,16168,16169,16170,16171, -16172,16173,16174,16175,16176,16177,16178,16179,16180,16181,16182,16183, -16184,16185,16186,16187,16188,16189,16190,16191,16192,16193,16194,16195, -16196,16197,16198,16199,16200,16201,16202,16203,16204,16205,16206,16207, -16208,16209,16210,16211,16212,16213,16214,16215,16216,16217,16218,16219, -16220,16221,16222,16223,16224,16225,16226,16227,16228,16229,16230,16231, -16232,16233,16234,16235,16236,16237,16238,16239,16240,16241,16242,16243, -16244,16245,16246,16247,16248,16249,16250,16251,16252,16253,16254,16255, -16256,16257,16258,16259,16260,16261,16262,16263,16264,16265,16266,16267, -16268,16269,16270,16271,16272,16273,16274,16275,16276,16277,16278,16279, -16280,16281,16282,16283,16284,16285,16286,16287,16288,16289,16290,16291, -16292,16293,16294,16295,16296,16297,16298,16299,16300,16301,16302,16303, -16304,16305,16306,16307,16308,16309,16310,16311,16312,16313,16314,16315, -16316,16317,16318,16319,16320,16321,16322,16323,16324,16325,16326,16327, -16328,16329,16330,16331,16332,16333,16334,16335,16336,16337,16338,16339, -16340,16341,16342,16343,16344,16345,16346,16347,16348,16349,16350,16351, -16352,16353,16354,16355,16356,16357,16358,16359,16360,16361,16362,16363, -16364,16365,16366,16367,16368,16369,16370,16371,16372,16373,16374,16375, -16376,16377,16378,16379,16380,16381,16382,16383,16384,16385,16386,16387, -16388,16389,16390,16391,16392,16393,16394,16395,16396,16397,16398,16399, -16400,16401,16402,16403,16404,16405,16406,16407,16408,16409,16410,16411, -16412,16413,16414,16415,16416,16417,16418,16419,16420,16421,16422,16423, -16424,16425,16426,16427,16428,16429,16430,16431,16432,16433,16434,16435, -16436,16437,16438,16439,16440,16441,16442,16443,16444,16445,16446,16447, -16448,16449,16450,16451,16452,16453,16454,16455,16456,16457,16458,16459, -16460,16461,16462,16463,16464,16465,16466,16467,16468,16469,16470,16471, -16472,16473,16474,16475,16476,16477,16478,16479,16480,16481,16482,16483, -16484,16485,16486,16487,16488,16489,16490,16491,16492,16493,16494,16495, -16496,16497,16498,16499,16500,16501,16502,16503,16504,16505,16506,16507, -16508,16509,16510,16511,16512,16513,16514,16515,16516,16517,16518,16519, -16520,16521,16522,16523,16524,16525,16526,16527,16528,16529,16530,16531, -16532,16533,16534,16535,16536,16537,16538,16539,16540,16541,16542,16543, -16544,16545,16546,16547,16548,16549,16550,16551,16552,16553,16554,16555, -16556,16557,16558,16559,16560,16561,16562,16563,16564,16565,16566,16567, -16568,16569,16570,16571,16572,16573,16574,16575,16576,16577,16578,16579, -16580,16581,16582,16583,16584,16585,16586,16587,16588,16589,16590,16591, -16592,16593,16594,16595,16596,16597,16598,16599,16600,16601,16602,16603, -16604,16605,16606,16607,16608,16609,16610,16611,16612,16613,16614,16615, -16616,16617,16618,16619,16620,16621,16622,16623,16624,16625,16626,16627, -16628,16629,16630,16631,16632,16633,16634,16635,16636,16637,16638,16639, -16640,16641,16642,16643,16644,16645,16646,16647,16648,16649,16650,16651, -16652,16653,16654,16655,16656,16657,16658,16659,16660,16661,16662,16663, -16664,16665,16666,16667,16668,16669,16670,16671,16672,16673,16674,16675, -16676,16677,16678,16679,16680,16681,16682,16683,16684,16685,16686,16687, -16688,16689,16690,16691,16692,16693,16694,16695,16696,16697,16698,16699, -16700,16701,16702,16703,16704,16705,16706,16707,16708,16709,16710,16711, -16712,16713,16714,16715,16716,16717,16718,16719,16720,16721,16722,16723, -16724,16725,16726,16727,16728,16729,16730,16731,16732,16733,16734,16735, -16736,16737,16738,16739,16740,16741,16742,16743,16744,16745,16746,16747, -16748,16749,16750,16751,16752,16753,16754,16755,16756,16757,16758,16759, -16760,16761,16762,16763,16764,16765,16766,16767,16768,16769,16770,16771, -16772,16773,16774,16775,16776,16777,16778,16779,16780,16781,16782,16783, -16784,16785,16786,16787,16788,16789,16790,16791,16792,16793,16794,16795, -16796,16797,16798,16799,16800,16801,16802,16803,16804,16805,16806,16807, -16808,16809,16810,16811,16812,16813,16814,16815,16816,16817,16818,16819, -16820,16821,16822,16823,16824,16825,16826,16827,16828,16829,16830,16831, -16832,16833,16834,16835,16836,16837,16838,16839,16840,16841,16842,16843, -16844,16845,16846,16847,16848,16849,16850,16851,16852,16853,16854,16855, -16856,16857,16858,16859,16860,16861,16862,16863,16864,16865,16866,16867, -16868,16869,16870,16871,16872,16873,16874,16875,16876,16877,16878,16879, -16880,16881,16882,16883,16884,16885,16886,16887,16888,16889,16890,16891, -16892,16893,16894,16895,16896,16897,16898,16899,16900,16901,16902,16903, -16904,16905,16906,16907,16908,16909,16910,16911,16912,16913,16914,16915, -16916,16917,16918,16919,16920,16921,16922,16923,16924,16925,16926,16927, -16928,16929,16930,16931,16932,16933,16934,16935,16936,16937,16938,16939, -16940,16941,16942,16943,16944,16945,16946,16947,16948,16949,16950,16951, -16952,16953,16954,16955,16956,16957,16958,16959,16960,16961,16962,16963, -16964,16965,16966,16967,16968,16969,16970,16971,16972,16973,16974,16975, -16976,16977,16978,16979,16980,16981,16982,16983,16984,16985,16986,16987, -16988,16989,16990,16991,16992,16993,16994,16995,16996,16997,16998,16999, -17000,17001,17002,17003,17004,17005,17006,17007,17008,17009,17010,17011, -17012,17013,17014,17015,17016,17017,17018,17019,17020,17021,17022,17023, -17024,17025,17026,17027,17028,17029,17030,17031,17032,17033,17034,17035, -17036,17037,17038,17039,17040,17041,17042,17043,17044,17045,17046,17047, -17048,17049,17050,17051,17052,17053,17054,17055,17056,17057,17058,17059, -17060,17061,17062,17063,17064,17065,17066,17067,17068,17069,17070,17071, -17072,17073,17074,17075,17076,17077,17078,17079,17080,17081,17082,17083, -17084,17085,17086,17087,17088,17089,17090,17091,17092,17093,17094,17095, -17096,17097,17098,17099,17100,17101,17102,17103,17104,17105,17106,17107, -17108,17109,17110,17111,17112,17113,17114,17115,17116,17117,17118,17119, -17120,17121,17122,17123,17124,17125,17126,17127,17128,17129,17130,17131, -17132,17133,17134,17135,17136,17137,17138,17139,17140,17141,17142,17143, -17144,17145,17146,17147,17148,17149,17150,17151,17152,17153,17154,17155, -17156,17157,17158,17159,17160,17161,17162,17163,17164,17165,17166,17167, -17168,17169,17170,17171,17172,17173,17174,17175,17176,17177,17178,17179, -17180,17181,17182,17183,17184,17185,17186,17187,17188,17189,17190,17191, -17192,17193,17194,17195,17196,17197,17198,17199,17200,17201,17202,17203, -17204,17205,17206,17207,17208,17209,17210,17211,17212,17213,17214,17215, -17216,17217,17218,17219,17220,17221,17222,17223,17224,17225,17226,17227, -17228,17229,17230,17231,17232,17233,17234,17235,17236,17237,17238,17239, -17240,17241,17242,17243,17244,17245,17246,17247,17248,17249,17250,17251, -17252,17253,17254,17255,17256,17257,17258,17259,17260,17261,17262,17263, -17264,17265,17266,17267,17268,17269,17270,17271,17272,17273,17274,17275, -17276,17277,17278,17279,17280,17281,17282,17283,17284,17285,17286,17287, -17288,17289,17290,17291,17292,17293,17294,17295,17296,17297,17298,17299, -17300,17301,17302,17303,17304,17305,17306,17307,17308,17309,17310,17311, -17312,17313,17314,17315,17316,17317,17318,17319,17320,17321,17322,17323, -17324,17325,17326,17327,17328,17329,17330,17331,17332,17333,17334,17335, -17336,17337,17338,17339,17340,17341,17342,17343,17344,17345,17346,17347, -17348,17349,17350,17351,17352,17353,17354,17355,17356,17357,17358,17359, -17360,17361,17362,17363,17364,17365,17366,17367,17368,17369,17370,17371, -17372,17373,17374,17375,17376,17377,17378,17379,17380,17381,17382,17383, -17384,17385,17386,17387,17388,17389,17390,17391,17392,17393,17394,17395, -17396,17397,17398,17399,17400,17401,17402,17403,17404,17405,17406,17407, -17408,17409,17410,17411,17412,17413,17414,17415,17416,17417,17418,17419, -17420,17421,17422,17423,17424,17425,17426,17427,17428,17429,17430,17431, -17432,17433,17434,17435,17436,17437,17438,17439,17440,17441,17442,17443, -17444,17445,17446,17447,17448,17449,17450,17451,17452,17453,17454,17455, -17456,17457,17458,17459,17460,17461,17462,17463,17464,17465,17466,17467, -17468,17469,17470,17471,17472,17473,17474,17475,17476,17477,17478,17479, -17480,17481,17482,17483,17484,17485,17486,17487,17488,17489,17490,17491, -17492,17493,17494,17495,17496,17497,17498,17499,17500,17501,17502,17503, -17504,17505,17506,17507,17508,17509,17510,17511,17512,17513,17514,17515, -17516,17517,17518,17519,17520,17521,17522,17523,17524,17525,17526,17527, -17528,17529,17530,17531,17532,17533,17534,17535,17536,17537,17538,17539, -17540,17541,17542,17543,17544,17545,17546,17547,17548,17549,17550,17551, -17552,17553,17554,17555,17556,17557,17558,17559,17560,17561,17562,17563, -17564,17565,17566,17567,17568,17569,17570,17571,17572,17573,17574,17575, -17576,17577,17578,17579,17580,17581,17582,17583,17584,17585,17586,17587, -17588,17589,17590,17591,17592,17593,17594,17595,17596,17597,17598,17599, -17600,17601,17602,17603,17604,17605,17606,17607,17608,17609,17610,17611, -17612,17613,17614,17615,17616,17617,17618,17619,17620,17621,17622,17623, -17624,17625,17626,17627,17628,17629,17630,17631,17632,17633,17634,17635, -17636,17637,17638,17639,17640,17641,17642,17643,17644,17645,17646,17647, -17648,17649,17650,17651,17652,17653,17654,17655,17656,17657,17658,17659, -17660,17661,17662,17663,17664,17665,17666,17667,17668,17669,17670,17671, -17672,17673,17674,17675,17676,17677,17678,17679,17680,17681,17682,17683, -17684,17685,17686,17687,17688,17689,17690,17691,17692,17693,17694,17695, -17696,17697,17698,17699,17700,17701,17702,17703,17704,17705,17706,17707, -17708,17709,17710,17711,17712,17713,17714,17715,17716,17717,17718,17719, -17720,17721,17722,17723,17724,17725,17726,17727,17728,17729,17730,17731, -17732,17733,17734,17735,17736,17737,17738,17739,17740,17741,17742,17743, -17744,17745,17746,17747,17748,17749,17750,17751,17752,17753,17754,17755, -17756,17757,17758,17759,17760,17761,17762,17763,17764,17765,17766,17767, -17768,17769,17770,17771,17772,17773,17774,17775,17776,17777,17778,17779, -17780,17781,17782,17783,17784,17785,17786,17787,17788,17789,17790,17791, -17792,17793,17794,17795,17796,17797,17798,17799,17800,17801,17802,17803, -17804,17805,17806,17807,17808,17809,17810,17811,17812,17813,17814,17815, -17816,17817,17818,17819,17820,17821,17822,17823,17824,17825,17826,17827, -17828,17829,17830,17831,17832,17833,17834,17835,17836,17837,17838,17839, -17840,17841,17842,17843,17844,17845,17846,17847,17848,17849,17850,17851, -17852,17853,17854,17855,17856,17857,17858,17859,17860,17861,17862,17863, -17864,17865,17866,17867,17868,17869,17870,17871,17872,17873,17874,17875, -17876,17877,17878,17879,17880,17881,17882,17883,17884,17885,17886,17887, -17888,17889,17890,17891,17892,17893,17894,17895,17896,17897,17898,17899, -17900,17901,17902,17903,17904,17905,17906,17907,17908,17909,17910,17911, -17912,17913,17914,17915,17916,17917,17918,17919,17920,17921,17922,17923, -17924,17925,17926,17927,17928,17929,17930,17931,17932,17933,17934,17935, -17936,17937,17938,17939,17940,17941,17942,17943,17944,17945,17946,17947, -17948,17949,17950,17951,17952,17953,17954,17955,17956,17957,17958,17959, -17960,17961,17962,17963,17964,17965,17966,17967,17968,17969,17970,17971, -17972,17973,17974,17975,17976,17977,17978,17979,17980,17981,17982,17983, -17984,17985,17986,17987,17988,17989,17990,17991,17992,17993,17994,17995, -17996,17997,17998,17999,18000,18001,18002,18003,18004,18005,18006,18007, -18008,18009,18010,18011,18012,18013,18014,18015,18016,18017,18018,18019, -18020,18021,18022,18023,18024,18025,18026,18027,18028,18029,18030,18031, -18032,18033,18034,18035,18036,18037,18038,18039,18040,18041,18042,18043, -18044,18045,18046,18047,18048,18049,18050,18051,18052,18053,18054,18055, -18056,18057,18058,18059,18060,18061,18062,18063,18064,18065,18066,18067, -18068,18069,18070,18071,18072,18073,18074,18075,18076,18077,18078,18079, -18080,18081,18082,18083,18084,18085,18086,18087,18088,18089,18090,18091, -18092,18093,18094,18095,18096,18097,18098,18099,18100,18101,18102,18103, -18104,18105,18106,18107,18108,18109,18110,18111,18112,18113,18114,18115, -18116,18117,18118,18119,18120,18121,18122,18123,18124,18125,18126,18127, -18128,18129,18130,18131,18132,18133,18134,18135,18136,18137,18138,18139, -18140,18141,18142,18143,18144,18145,18146,18147,18148,18149,18150,18151, -18152,18153,18154,18155,18156,18157,18158,18159,18160,18161,18162,18163, -18164,18165,18166,18167,18168,18169,18170,18171,18172,18173,18174,18175, -18176,18177,18178,18179,18180,18181,18182,18183,18184,18185,18186,18187, -18188,18189,18190,18191,18192,18193,18194,18195,18196,18197,18198,18199, -18200,18201,18202,18203,18204,18205,18206,18207,18208,18209,18210,18211, -18212,18213,18214,18215,18216,18217,18218,18219,18220,18221,18222,18223, -18224,18225,18226,18227,18228,18229,18230,18231,18232,18233,18234,18235, -18236,18237,18238,18239,18240,18241,18242,18243,18244,18245,18246,18247, -18248,18249,18250,18251,18252,18253,18254,18255,18256,18257,18258,18259, -18260,18261,18262,18263,18264,18265,18266,18267,18268,18269,18270,18271, -18272,18273,18274,18275,18276,18277,18278,18279,18280,18281,18282,18283, -18284,18285,18286,18287,18288,18289,18290,18291,18292,18293,18294,18295, -18296,18297,18298,18299,18300,18301,18302,18303,18304,18305,18306,18307, -18308,18309,18310,18311,18312,18313,18314,18315,18316,18317,18318,18319, -18320,18321,18322,18323,18324,18325,18326,18327,18328,18329,18330,18331, -18332,18333,18334,18335,18336,18337,18338,18339,18340,18341,18342,18343, -18344,18345,18346,18347,18348,18349,18350,18351,18352,18353,18354,18355, -18356,18357,18358,18359,18360,18361,18362,18363,18364,18365,18366,18367, -18368,18369,18370,18371,18372,18373,18374,18375,18376,18377,18378,18379, -18380,18381,18382,18383,18384,18385,18386,18387,18388,18389,18390,18391, -18392,18393,18394,18395,18396,18397,18398,18399,18400,18401,18402,18403, -18404,18405,18406,18407,18408,18409,18410,18411,18412,18413,18414,18415, -18416,18417,18418,18419,18420,18421,18422,18423,18424,18425,18426,18427, -18428,18429,18430,18431,18432,18433,18434,18435,18436,18437,18438,18439, -18440,18441,18442,18443,18444,18445,18446,18447,18448,18449,18450,18451, -18452,18453,18454,18455,18456,18457,18458,18459,18460,18461,18462,18463, -18464,18465,18466,18467,18468,18469,18470,18471,18472,18473,18474,18475, -18476,18477,18478,18479,18480,18481,18482,18483,18484,18485,18486,18487, -18488,18489,18490,18491,18492,18493,18494,18495,18496,18497,18498,18499, -18500,18501,18502,18503,18504,18505,18506,18507,18508,18509,18510,18511, -18512,18513,18514,18515,18516,18517,18518,18519,18520,18521,18522,18523, -18524,18525,18526,18527,18528,18529,18530,18531,18532,18533,18534,18535, -18536,18537,18538,18539,18540,18541,18542,18543,18544,18545,18546,18547, -18548,18549,18550,18551,18552,18553,18554,18555,18556,18557,18558,18559, -18560,18561,18562,18563,18564,18565,18566,18567,18568,18569,18570,18571, -18572,18573,18574,18575,18576,18577,18578,18579,18580,18581,18582,18583, -18584,18585,18586,18587,18588,18589,18590,18591,18592,18593,18594,18595, -18596,18597,18598,18599,18600,18601,18602,18603,18604,18605,18606,18607, -18608,18609,18610,18611,18612,18613,18614,18615,18616,18617,18618,18619, -18620,18621,18622,18623,18624,18625,18626,18627,18628,18629,18630,18631, -18632,18633,18634,18635,18636,18637,18638,18639,18640,18641,18642,18643, -18644,18645,18646,18647,18648,18649,18650,18651,18652,18653,18654,18655, -18656,18657,18658,18659,18660,18661,18662,18663,18664,18665,18666,18667, -18668,18669,18670,18671,18672,18673,18674,18675,18676,18677,18678,18679, -18680,18681,18682,18683,18684,18685,18686,18687,18688,18689,18690,18691, -18692,18693,18694,18695,18696,18697,18698,18699,18700,18701,18702,18703, -18704,18705,18706,18707,18708,18709,18710,18711,18712,18713,18714,18715, -18716,18717,18718,18719,18720,18721,18722,18723,18724,18725,18726,18727, -18728,18729,18730,18731,18732,18733,18734,18735,18736,18737,18738,18739, -18740,18741,18742,18743,18744,18745,18746,18747,18748,18749,18750,18751, -18752,18753,18754,18755,18756,18757,18758,18759,18760,18761,18762,18763, -18764,18765,18766,18767,18768,18769,18770,18771,18772,18773,18774,18775, -18776,18777,18778,18779,18780,18781,18782,18783,18784,18785,18786,18787, -18788,18789,18790,18791,18792,18793,18794,18795,18796,18797,18798,18799, -18800,18801,18802,18803,18804,18805,18806,18807,18808,18809,18810,18811, -18812,18813,18814,18815,18816,18817,18818,18819,18820,18821,18822,18823, -18824,18825,18826,18827,18828,18829,18830,18831,18832,18833,18834,18835, -18836,18837,18838,18839,18840,18841,18842,18843,18844,18845,18846,18847, -18848,18849,18850,18851,18852,18853,18854,18855,18856,18857,18858,18859, -18860,18861,18862,18863,18864,18865,18866,18867,18868,18869,18870,18871, -18872,18873,18874,18875,18876,18877,18878,18879,18880,18881,18882,18883, -18884,18885,18886,18887,18888,18889,18890,18891,18892,18893,18894,18895, -18896,18897,18898,18899,18900,18901,18902,18903,18904,18905,18906,18907, -18908,18909,18910,18911,18912,18913,18914,18915,18916,18917,18918,18919, -18920,18921,18922,18923,18924,18925,18926,18927,18928,18929,18930,18931, -18932,18933,18934,18935,18936,18937,18938,18939,18940,18941,18942,18943, -18944,18945,18946,18947,18948,18949,18950,18951,18952,18953,18954,18955, -18956,18957,18958,18959,18960,18961,18962,18963,18964,18965,18966,18967, -18968,18969,18970,18971,18972,18973,18974,18975,18976,18977,18978,18979, -18980,18981,18982,18983,18984,18985,18986,18987,18988,18989,18990,18991, -18992,18993,18994,18995,18996,18997,18998,18999,19000,19001,19002,19003, -19004,19005,19006,19007,19008,19009,19010,19011,19012,19013,19014,19015, -19016,19017,19018,19019,19020,19021,19022,19023,19024,19025,19026,19027, -19028,19029,19030,19031,19032,19033,19034,19035,19036,19037,19038,19039, -19040,19041,19042,19043,19044,19045,19046,19047,19048,19049,19050,19051, -19052,19053,19054,19055,19056,19057,19058,19059,19060,19061,19062,19063, -19064,19065,19066,19067,19068,19069,19070,19071,19072,19073,19074,19075, -19076,19077,19078,19079,19080,19081,19082,19083,19084,19085,19086,19087, -19088,19089,19090,19091,19092,19093,19094,19095,19096,19097,19098,19099, -19100,19101,19102,19103,19104,19105,19106,19107,19108,19109,19110,19111, -19112,19113,19114,19115,19116,19117,19118,19119,19120,19121,19122,19123, -19124,19125,19126,19127,19128,19129,19130,19131,19132,19133,19134,19135, -19136,19137,19138,19139,19140,19141,19142,19143,19144,19145,19146,19147, -19148,19149,19150,19151,19152,19153,19154,19155,19156,19157,19158,19159, -19160,19161,19162,19163,19164,19165,19166,19167,19168,19169,19170,19171, -19172,19173,19174,19175,19176,19177,19178,19179,19180,19181,19182,19183, -19184,19185,19186,19187,19188,19189,19190,19191,19192,19193,19194,19195, -19196,19197,19198,19199,19200,19201,19202,19203,19204,19205,19206,19207, -19208,19209,19210,19211,19212,19213,19214,19215,19216,19217,19218,19219, -19220,19221,19222,19223,19224,19225,19226,19227,19228,19229,19230,19231, -19232,19233,19234,19235,19236,19237,19238,19239,19240,19241,19242,19243, -19244,19245,19246,19247,19248,19249,19250,19251,19252,19253,19254,19255, -19256,19257,19258,19259,19260,19261,19262,19263,19264,19265,19266,19267, -19268,19269,19270,19271,19272,19273,19274,19275,19276,19277,19278,19279, -19280,19281,19282,19283,19284,19285,19286,19287,19288,19289,19290,19291, -19292,19293,19294,19295,19296,19297,19298,19299,19300,19301,19302,19303, -19304,19305,19306,19307,19308,19309,19310,19311,19312,19313,19314,19315, -19316,19317,19318,19319,19320,19321,19322,19323,19324,19325,19326,19327, -19328,19329,19330,19331,19332,19333,19334,19335,19336,19337,19338,19339, -19340,19341,19342,19343,19344,19345,19346,19347,19348,19349,19350,19351, -19352,19353,19354,19355,19356,19357,19358,19359,19360,19361,19362,19363, -19364,19365,19366,19367,19368,19369,19370,19371,19372,19373,19374,19375, -19376,19377,19378,19379,19380,19381,19382,19383,19384,19385,19386,19387, -19388,19389,19390,19391,19392,19393,19394,19395,19396,19397,19398,19399, -19400,19401,19402,19403,19404,19405,19406,19407,19408,19409,19410,19411, -19412,19413,19414,19415,19416,19417,19418,19419,19420,19421,19422,19423, -19424,19425,19426,19427,19428,19429,19430,19431,19432,19433,19434,19435, -19436,19437,19438,19439,19440,19441,19442,19443,19444,19445,19446,19447, -19448,19449,19450,19451,19452,19453,19454,19455,19456,19457,19458,19459, -19460,19461,19462,19463,19464,19465,19466,19467,19468,19469,19470,19471, -19472,19473,19474,19475,19476,19477,19478,19479,19480,19481,19482,19483, -19484,19485,19486,19487,19488,19489,19490,19491,19492,19493,19494,19495, -19496,19497,19498,19499,19500,19501,19502,19503,19504,19505,19506,19507, -19508,19509,19510,19511,19512,19513,19514,19515,19516,19517,19518,19519, -19520,19521,19522,19523,19524,19525,19526,19527,19528,19529,19530,19531, -19532,19533,19534,19535,19536,19537,19538,19539,19540,19541,19542,19543, -19544,19545,19546,19547,19548,19549,19550,19551,19552,19553,19554,19555, -19556,19557,19558,19559,19560,19561,19562,19563,19564,19565,19566,19567, -19568,19569,19570,19571,19572,19573,19574,19575,19576,19577,19578,19579, -19580,19581,19582,19583,19584,19585,19586,19587,19588,19589,19590,19591, -19592,19593,19594,19595,19596,19597,19598,19599,19600,19601,19602,19603, -19604,19605,19606,19607,19608,19609,19610,19611,19612,19613,19614,19615, -19616,19617,19618,19619,19620,19621,19622,19623,19624,19625,19626,19627, -19628,19629,19630,19631,19632,19633,19634,19635,19636,19637,19638,19639, -19640,19641,19642,19643,19644,19645,19646,19647,19648,19649,19650,19651, -19652,19653,19654,19655,19656,19657,19658,19659,19660,19661,19662,19663, -19664,19665,19666,19667,19668,19669,19670,19671,19672,19673,19674,19675, -19676,19677,19678,19679,19680,19681,19682,19683,19684,19685,19686,19687, -19688,19689,19690,19691,19692,19693,19694,19695,19696,19697,19698,19699, -19700,19701,19702,19703,19704,19705,19706,19707,19708,19709,19710,19711, -19712,19713,19714,19715,19716,19717,19718,19719,19720,19721,19722,19723, -19724,19725,19726,19727,19728,19729,19730,19731,19732,19733,19734,19735, -19736,19737,19738,19739,19740,19741,19742,19743,19744,19745,19746,19747, -19748,19749,19750,19751,19752,19753,19754,19755,19756,19757,19758,19759, -19760,19761,19762,19763,19764,19765,19766,19767,19768,19769,19770,19771, -19772,19773,19774,19775,19776,19777,19778,19779,19780,19781,19782,19783, -19784,19785,19786,19787,19788,19789,19790,19791,19792,19793,19794,19795, -19796,19797,19798,19799,19800,19801,19802,19803,19804,19805,19806,19807, -19808,19809,19810,19811,19812,19813,19814,19815,19816,19817,19818,19819, -19820,19821,19822,19823,19824,19825,19826,19827,19828,19829,19830,19831, -19832,19833,19834,19835,19836,19837,19838,19839,19840,19841,19842,19843, -19844,19845,19846,19847,19848,19849,19850,19851,19852,19853,19854,19855, -19856,19857,19858,19859,19860,19861,19862,19863,19864,19865,19866,19867, -19868,19869,19870,19871,19872,19873,19874,19875,19876,19877,19878,19879, -19880,19881,19882,19883,19884,19885,19886,19887,19888,19889,19890,19891, -19892,19893,19894,19895,19896,19897,19898,19899,19900,19901,19902,19903, -19904,19905,19906,19907,19908,19909,19910,19911,19912,19913,19914,19915, -19916,19917,19918,19919,19920,19921,19922,19923,19924,19925,19926,19927, -19928,19929,19930,19931,19932,19933,19934,19935,19936,19937,19938,19939, -19940,19941,19942,19943,19944,19945,19946,19947,19948,19949,19950,19951, -19952,19953,19954,19955,19956,19957,19958,19959,19960,19961,19962,19963, -19964,19965,19966,19967,19968,19969,19970,19971,19972,19973,19974,19975, -19976,19977,19978,19979,19980,19981,19982,19983,19984,19985,19986,19987, -19988,19989,19990,19991,19992,19993,19994,19995,19996,19997,19998,19999, -20000,20001,20002,20003,20004,20005,20006,20007,20008,20009,20010,20011, -20012,20013,20014,20015,20016,20017,20018,20019,20020,20021,20022,20023, -20024,20025,20026,20027,20028,20029,20030,20031,20032,20033,20034,20035, -20036,20037,20038,20039,20040,20041,20042,20043,20044,20045,20046,20047, -20048,20049,20050,20051,20052,20053,20054,20055,20056,20057,20058,20059, -20060,20061,20062,20063,20064,20065,20066,20067,20068,20069,20070,20071, -20072,20073,20074,20075,20076,20077,20078,20079,20080,20081,20082,20083, -20084,20085,20086,20087,20088,20089,20090,20091,20092,20093,20094,20095, -20096,20097,20098,20099,20100,20101,20102,20103,20104,20105,20106,20107, -20108,20109,20110,20111,20112,20113,20114,20115,20116,20117,20118,20119, -20120,20121,20122,20123,20124,20125,20126,20127,20128,20129,20130,20131, -20132,20133,20134,20135,20136,20137,20138,20139,20140,20141,20142,20143, -20144,20145,20146,20147,20148,20149,20150,20151,20152,20153,20154,20155, -20156,20157,20158,20159,20160,20161,20162,20163,20164,20165,20166,20167, -20168,20169,20170,20171,20172,20173,20174,20175,20176,20177,20178,20179, -20180,20181,20182,20183,20184,20185,20186,20187,20188,20189,20190,20191, -20192,20193,20194,20195,20196,20197,20198,20199,20200,20201,20202,20203, -20204,20205,20206,20207,20208,20209,20210,20211,20212,20213,20214,20215, -20216,20217,20218,20219,20220,20221,20222,20223,20224,20225,20226,20227, -20228,20229,20230,20231,20232,20233,20234,20235,20236,20237,20238,20239, -20240,20241,20242,20243,20244,20245,20246,20247,20248,20249,20250,20251, -20252,20253,20254,20255,20256,20257,20258,20259,20260,20261,20262,20263, -20264,20265,20266,20267,20268,20269,20270,20271,20272,20273,20274,20275, -20276,20277,20278,20279,20280,20281,20282,20283,20284,20285,20286,20287, -20288,20289,20290,20291,20292,20293,20294,20295,20296,20297,20298,20299, -20300,20301,20302,20303,20304,20305,20306,20307,20308,20309,20310,20311, -20312,20313,20314,20315,20316,20317,20318,20319,20320,20321,20322,20323, -20324,20325,20326,20327,20328,20329,20330,20331,20332,20333,20334,20335, -20336,20337,20338,20339,20340,20341,20342,20343,20344,20345,20346,20347, -20348,20349,20350,20351,20352,20353,20354,20355,20356,20357,20358,20359, -20360,20361,20362,20363,20364,20365,20366,20367,20368,20369,20370,20371, -20372,20373,20374,20375,20376,20377,20378,20379,20380,20381,20382,20383, -20384,20385,20386,20387,20388,20389,20390,20391,20392,20393,20394,20395, -20396,20397,20398,20399,20400,20401,20402,20403,20404,20405,20406,20407, -20408,20409,20410,20411,20412,20413,20414,20415,20416,20417,20418,20419, -20420,20421,20422,20423,20424,20425,20426,20427,20428,20429,20430,20431, -20432,20433,20434,20435,20436,20437,20438,20439,20440,20441,20442,20443, -20444,20445,20446,20447,20448,20449,20450,20451,20452,20453,20454,20455, -20456,20457,20458,20459,20460,20461,20462,20463,20464,20465,20466,20467, -20468,20469,20470,20471,20472,20473,20474,20475,20476,20477,20478,20479, -20480,20481,20482,20483,20484,20485,20486,20487,20488,20489,20490,20491, -20492,20493,20494,20495,20496,20497,20498,20499,20500,20501,20502,20503, -20504,20505,20506,20507,20508,20509,20510,20511,20512,20513,20514,20515, -20516,20517,20518,20519,20520,20521,20522,20523,20524,20525,20526,20527, -20528,20529,20530,20531,20532,20533,20534,20535,20536,20537,20538,20539, -20540,20541,20542,20543,20544,20545,20546,20547,20548,20549,20550,20551, -20552,20553,20554,20555,20556,20557,20558,20559,20560,20561,20562,20563, -20564,20565,20566,20567,20568,20569,20570,20571,20572,20573,20574,20575, -20576,20577,20578,20579,20580,20581,20582,20583,20584,20585,20586,20587, -20588,20589,20590,20591,20592,20593,20594,20595,20596,20597,20598,20599, -20600,20601,20602,20603,20604,20605,20606,20607,20608,20609,20610,20611, -20612,20613,20614,20615,20616,20617,20618,20619,20620,20621,20622,20623, -20624,20625,20626,20627,20628,20629,20630,20631,20632,20633,20634,20635, -20636,20637,20638,20639,20640,20641,20642,20643,20644,20645,20646,20647, -20648,20649,20650,20651,20652,20653,20654,20655,20656,20657,20658,20659, -20660,20661,20662,20663,20664,20665,20666,20667,20668,20669,20670,20671, -20672,20673,20674,20675,20676,20677,20678,20679,20680,20681,20682,20683, -20684,20685,20686,20687,20688,20689,20690,20691,20692,20693,20694,20695, -20696,20697,20698,20699,20700,20701,20702,20703,20704,20705,20706,20707, -20708,20709,20710,20711,20712,20713,20714,20715,20716,20717,20718,20719, -20720,20721,20722,20723,20724,20725,20726,20727,20728,20729,20730,20731, -20732,20733,20734,20735,20736,20737,20738,20739,20740,20741,20742,20743, -20744,20745,20746,20747,20748,20749,20750,20751,20752,20753,20754,20755, -20756,20757,20758,20759,20760,20761,20762,20763,20764,20765,20766,20767, -20768,20769,20770,20771,20772,20773,20774,20775,20776,20777,20778,20779, -20780,20781,20782,20783,20784,20785,20786,20787,20788,20789,20790,20791, -20792,20793,20794,20795,20796,20797,20798,20799,20800,20801,20802,20803, -20804,20805,20806,20807,20808,20809,20810,20811,20812,20813,20814,20815, -20816,20817,20818,20819,20820,20821,20822,20823,20824,20825,20826,20827, -20828,20829,20830,20831,20832,20833,20834,20835,20836,20837,20838,20839, -20840,20841,20842,20843,20844,20845,20846,20847,20848,20849,20850,20851, -20852,20853,20854,20855,20856,20857,20858,20859,20860,20861,20862,20863, -20864,20865,20866,20867,20868,20869,20870,20871,20872,20873,20874,20875, -20876,20877,20878,20879,20880,20881,20882,20883,20884,20885,20886,20887, -20888,20889,20890,20891,20892,20893,20894,20895,20896,20897,20898,20899, -20900,20901,20902,20903,20904,20905,20906,20907,20908,20909,20910,20911, -20912,20913,20914,20915,20916,20917,20918,20919,20920,20921,20922,20923, -20924,20925,20926,20927,20928,20929,20930,20931,20932,20933,20934,20935, -20936,20937,20938,20939,20940,20941,20942,20943,20944,20945,20946,20947, -20948,20949,20950,20951,20952,20953,20954,20955,20956,20957,20958,20959, -20960,20961,20962,20963,20964,20965,20966,20967,20968,20969,20970,20971, -20972,20973,20974,20975,20976,20977,20978,20979,20980,20981,20982,20983, -20984,20985,20986,20987,20988,20989,20990,20991,20992,20993,20994,20995, -20996,20997,20998,20999,21000,21001,21002,21003,21004,21005,21006,21007, -21008,21009,21010,21011,21012,21013,21014,21015,21016,21017,21018,21019, -21020,21021,21022,21023,21024,21025,21026,21027,21028,21029,21030,21031, -21032,21033,21034,21035,21036,21037,21038,21039,21040,21041,21042,21043, -21044,21045,21046,21047,21048,21049,21050,21051,21052,21053,21054,21055, -21056,21057,21058,21059,21060,21061,21062,21063,21064,21065,21066,21067, -21068,21069,21070,21071,21072,21073,21074,21075,21076,21077,21078,21079, -21080,21081,21082,21083,21084,21085,21086,21087,21088,21089,21090,21091, -21092,21093,21094,21095,21096,21097,21098,21099,21100,21101,21102,21103, -21104,21105,21106,21107,21108,21109,21110,21111,21112,21113,21114,21115, -21116,21117,21118,21119,21120,21121,21122,21123,21124,21125,21126,21127, -21128,21129,21130,21131,21132,21133,21134,21135,21136,21137,21138,21139, -21140,21141,21142,21143,21144,21145,21146,21147,21148,21149,21150,21151, -21152,21153,21154,21155,21156,21157,21158,21159,21160,21161,21162,21163, -21164,21165,21166,21167,21168,21169,21170,21171,21172,21173,21174,21175, -21176,21177,21178,21179,21180,21181,21182,21183,21184,21185,21186,21187, -21188,21189,21190,21191,21192,21193,21194,21195,21196,21197,21198,21199, -21200,21201,21202,21203,21204,21205,21206,21207,21208,21209,21210,21211, -21212,21213,21214,21215,21216,21217,21218,21219,21220,21221,21222,21223, -21224,21225,21226,21227,21228,21229,21230,21231,21232,21233,21234,21235, -21236,21237,21238,21239,21240,21241,21242,21243,21244,21245,21246,21247, -21248,21249,21250,21251,21252,21253,21254,21255,21256,21257,21258,21259, -21260,21261,21262,21263,21264,21265,21266,21267,21268,21269,21270,21271, -21272,21273,21274,21275,21276,21277,21278,21279,21280,21281,21282,21283, -21284,21285,21286,21287,21288,21289,21290,21291,21292,21293,21294,21295, -21296,21297,21298,21299,21300,21301,21302,21303,21304,21305,21306,21307, -21308,21309,21310,21311,21312,21313,21314,21315,21316,21317,21318,21319, -21320,21321,21322,21323,21324,21325,21326,21327,21328,21329,21330,21331, -21332,21333,21334,21335,21336,21337,21338,21339,21340,21341,21342,21343, -21344,21345,21346,21347,21348,21349,21350,21351,21352,21353,21354,21355, -21356,21357,21358,21359,21360,21361,21362,21363,21364,21365,21366,21367, -21368,21369,21370,21371,21372,21373,21374,21375,21376,21377,21378,21379, -21380,21381,21382,21383,21384,21385,21386,21387,21388,21389,21390,21391, -21392,21393,21394,21395,21396,21397,21398,21399,21400,21401,21402,21403, -21404,21405,21406,21407,21408,21409,21410,21411,21412,21413,21414,21415, -21416,21417,21418,21419,21420,21421,21422,21423,21424,21425,21426,21427, -21428,21429,21430,21431,21432,21433,21434,21435,21436,21437,21438,21439, -21440,21441,21442,21443,21444,21445,21446,21447,21448,21449,21450,21451, -21452,21453,21454,21455,21456,21457,21458,21459,21460,21461,21462,21463, -21464,21465,21466,21467,21468,21469,21470,21471,21472,21473,21474,21475, -21476,21477,21478,21479,21480,21481,21482,21483,21484,21485,21486,21487, -21488,21489,21490,21491,21492,21493,21494,21495,21496,21497,21498,21499, -21500,21501,21502,21503,21504,21505,21506,21507,21508,21509,21510,21511, -21512,21513,21514,21515,21516,21517,21518,21519,21520,21521,21522,21523, -21524,21525,21526,21527,21528,21529,21530,21531,21532,21533,21534,21535, -21536,21537,21538,21539,21540,21541,21542,21543,21544,21545,21546,21547, -21548,21549,21550,21551,21552,21553,21554,21555,21556,21557,21558,21559, -21560,21561,21562,21563,21564,21565,21566,21567,21568,21569,21570,21571, -21572,21573,21574,21575,21576,21577,21578,21579,21580,21581,21582,21583, -21584,21585,21586,21587,21588,21589,21590,21591,21592,21593,21594,21595, -21596,21597,21598,21599,21600,21601,21602,21603,21604,21605,21606,21607, -21608,21609,21610,21611,21612,21613,21614,21615,21616,21617,21618,21619, -21620,21621,21622,21623,21624,21625,21626,21627,21628,21629,21630,21631, -21632,21633,21634,21635,21636,21637,21638,21639,21640,21641,21642,21643, -21644,21645,21646,21647,21648,21649,21650,21651,21652,21653,21654,21655, -21656,21657,21658,21659,21660,21661,21662,21663,21664,21665,21666,21667, -21668,21669,21670,21671,21672,21673,21674,21675,21676,21677,21678,21679, -21680,21681,21682,21683,21684,21685,21686,21687,21688,21689,21690,21691, -21692,21693,21694,21695,21696,21697,21698,21699,21700,21701,21702,21703, -21704,21705,21706,21707,21708,21709,21710,21711,21712,21713,21714,21715, -21716,21717,21718,21719,21720,21721,21722,21723,21724,21725,21726,21727, -21728,21729,21730,21731,21732,21733,21734,21735,21736,21737,21738,21739, -21740,21741,21742,21743,21744,21745,21746,21747,21748,21749,21750,21751, -21752,21753,21754,21755,21756,21757,21758,21759,21760,21761,21762,21763, -21764,21765,21766,21767,21768,21769,21770,21771,21772,21773,21774,21775, -21776,21777,21778,21779,21780,21781,21782,21783,21784,21785,21786,21787, -21788,21789,21790,21791,21792,21793,21794,21795,21796,21797,21798,21799, -21800,21801,21802,21803,21804,21805,21806,21807,21808,21809,21810,21811, -21812,21813,21814,21815,21816,21817,21818,21819,21820,21821,21822,21823, -21824,21825,21826,21827,21828,21829,21830,21831,21832,21833,21834,21835, -21836,21837,21838,21839,21840,21841,21842,21843,21844,21845,21846,21847, -21848,21849,21850,21851,21852,21853,21854,21855,21856,21857,21858,21859, -21860,21861,21862,21863,21864,21865,21866,21867,21868,21869,21870,21871, -21872,21873,21874,21875,21876,21877,21878,21879,21880,21881,21882,21883, -21884,21885,21886,21887,21888,21889,21890,21891,21892,21893,21894,21895, -21896,21897,21898,21899,21900,21901,21902,21903,21904,21905,21906,21907, -21908,21909,21910,21911,21912,21913,21914,21915,21916,21917,21918,21919, -21920,21921,21922,21923,21924,21925,21926,21927,21928,21929,21930,21931, -21932,21933,21934,21935,21936,21937,21938,21939,21940,21941,21942,21943, -21944,21945,21946,21947,21948,21949,21950,21951,21952,21953,21954,21955, -21956,21957,21958,21959,21960,21961,21962,21963,21964,21965,21966,21967, -21968,21969,21970,21971,21972,21973,21974,21975,21976,21977,21978,21979, -21980,21981,21982,21983,21984,21985,21986,21987,21988,21989,21990,21991, -21992,21993,21994,21995,21996,21997,21998,21999,22000,22001,22002,22003, -22004,22005,22006,22007,22008,22009,22010,22011,22012,22013,22014,22015, -22016,22017,22018,22019,22020,22021,22022,22023,22024,22025,22026,22027, -22028,22029,22030,22031,22032,22033,22034,22035,22036,22037,22038,22039, -22040,22041,22042,22043,22044,22045,22046,22047,22048,22049,22050,22051, -22052,22053,22054,22055,22056,22057,22058,22059,22060,22061,22062,22063, -22064,22065,22066,22067,22068,22069,22070,22071,22072,22073,22074,22075, -22076,22077,22078,22079,22080,22081,22082,22083,22084,22085,22086,22087, -22088,22089,22090,22091,22092,22093,22094,22095,22096,22097,22098,22099, -22100,22101,22102,22103,22104,22105,22106,22107,22108,22109,22110,22111, -22112,22113,22114,22115,22116,22117,22118,22119,22120,22121,22122,22123, -22124,22125,22126,22127,22128,22129,22130,22131,22132,22133,22134,22135, -22136,22137,22138,22139,22140,22141,22142,22143,22144,22145,22146,22147, -22148,22149,22150,22151,22152,22153,22154,22155,22156,22157,22158,22159, -22160,22161,22162,22163,22164,22165,22166,22167,22168,22169,22170,22171, -22172,22173,22174,22175,22176,22177,22178,22179,22180,22181,22182,22183, -22184,22185,22186,22187,22188,22189,22190,22191,22192,22193,22194,22195, -22196,22197,22198,22199,22200,22201,22202,22203,22204,22205,22206,22207, -22208,22209,22210,22211,22212,22213,22214,22215,22216,22217,22218,22219, -22220,22221,22222,22223,22224,22225,22226,22227,22228,22229,22230,22231, -22232,22233,22234,22235,22236,22237,22238,22239,22240,22241,22242,22243, -22244,22245,22246,22247,22248,22249,22250,22251,22252,22253,22254,22255, -22256,22257,22258,22259,22260,22261,22262,22263,22264,22265,22266,22267, -22268,22269,22270,22271,22272,22273,22274,22275,22276,22277,22278,22279, -22280,22281,22282,22283,22284,22285,22286,22287,22288,22289,22290,22291, -22292,22293,22294,22295,22296,22297,22298,22299,22300,22301,22302,22303, -22304,22305,22306,22307,22308,22309,22310,22311,22312,22313,22314,22315, -22316,22317,22318,22319,22320,22321,22322,22323,22324,22325,22326,22327, -22328,22329,22330,22331,22332,22333,22334,22335,22336,22337,22338,22339, -22340,22341,22342,22343,22344,22345,22346,22347,22348,22349,22350,22351, -22352,22353,22354,22355,22356,22357,22358,22359,22360,22361,22362,22363, -22364,22365,22366,22367,22368,22369,22370,22371,22372,22373,22374,22375, -22376,22377,22378,22379,22380,22381,22382,22383,22384,22385,22386,22387, -22388,22389,22390,22391,22392,22393,22394,22395,22396,22397,22398,22399, -22400,22401,22402,22403,22404,22405,22406,22407,22408,22409,22410,22411, -22412,22413,22414,22415,22416,22417,22418,22419,22420,22421,22422,22423, -22424,22425,22426,22427,22428,22429,22430,22431,22432,22433,22434,22435, -22436,22437,22438,22439,22440,22441,22442,22443,22444,22445,22446,22447, -22448,22449,22450,22451,22452,22453,22454,22455,22456,22457,22458,22459, -22460,22461,22462,22463,22464,22465,22466,22467,22468,22469,22470,22471, -22472,22473,22474,22475,22476,22477,22478,22479,22480,22481,22482,22483, -22484,22485,22486,22487,22488,22489,22490,22491,22492,22493,22494,22495, -22496,22497,22498,22499,22500,22501,22502,22503,22504,22505,22506,22507, -22508,22509,22510,22511,22512,22513,22514,22515,22516,22517,22518,22519, -22520,22521,22522,22523,22524,22525,22526,22527,22528,22529,22530,22531, -22532,22533,22534,22535,22536,22537,22538,22539,22540,22541,22542,22543, -22544,22545,22546,22547,22548,22549,22550,22551,22552,22553,22554,22555, -22556,22557,22558,22559,22560,22561,22562,22563,22564,22565,22566,22567, -22568,22569,22570,22571,22572,22573,22574,22575,22576,22577,22578,22579, -22580,22581,22582,22583,22584,22585,22586,22587,22588,22589,22590,22591, -22592,22593,22594,22595,22596,22597,22598,22599,22600,22601,22602,22603, -22604,22605,22606,22607,22608,22609,22610,22611,22612,22613,22614,22615, -22616,22617,22618,22619,22620,22621,22622,22623,22624,22625,22626,22627, -22628,22629,22630,22631,22632,22633,22634,22635,22636,22637,22638,22639, -22640,22641,22642,22643,22644,22645,22646,22647,22648,22649,22650,22651, -22652,22653,22654,22655,22656,22657,22658,22659,22660,22661,22662,22663, -22664,22665,22666,22667,22668,22669,22670,22671,22672,22673,22674,22675, -22676,22677,22678,22679,22680,22681,22682,22683,22684,22685,22686,22687, -22688,22689,22690,22691,22692,22693,22694,22695,22696,22697,22698,22699, -22700,22701,22702,22703,22704,22705,22706,22707,22708,22709,22710,22711, -22712,22713,22714,22715,22716,22717,22718,22719,22720,22721,22722,22723, -22724,22725,22726,22727,22728,22729,22730,22731,22732,22733,22734,22735, -22736,22737,22738,22739,22740,22741,22742,22743,22744,22745,22746,22747, -22748,22749,22750,22751,22752,22753,22754,22755,22756,22757,22758,22759, -22760,22761,22762,22763,22764,22765,22766,22767,22768,22769,22770,22771, -22772,22773,22774,22775,22776,22777,22778,22779,22780,22781,22782,22783, -22784,22785,22786,22787,22788,22789,22790,22791,22792,22793,22794,22795, -22796,22797,22798,22799,22800,22801,22802,22803,22804,22805,22806,22807, -22808,22809,22810,22811,22812,22813,22814,22815,22816,22817,22818,22819, -22820,22821,22822,22823,22824,22825,22826,22827,22828,22829,22830,22831, -22832,22833,22834,22835,22836,22837,22838,22839,22840,22841,22842,22843, -22844,22845,22846,22847,22848,22849,22850,22851,22852,22853,22854,22855, -22856,22857,22858,22859,22860,22861,22862,22863,22864,22865,22866,22867, -22868,22869,22870,22871,22872,22873,22874,22875,22876,22877,22878,22879, -22880,22881,22882,22883,22884,22885,22886,22887,22888,22889,22890,22891, -22892,22893,22894,22895,22896,22897,22898,22899,22900,22901,22902,22903, -22904,22905,22906,22907,22908,22909,22910,22911,22912,22913,22914,22915, -22916,22917,22918,22919,22920,22921,22922,22923,22924,22925,22926,22927, -22928,22929,22930,22931,22932,22933,22934,22935,22936,22937,22938,22939, -22940,22941,22942,22943,22944,22945,22946,22947,22948,22949,22950,22951, -22952,22953,22954,22955,22956,22957,22958,22959,22960,22961,22962,22963, -22964,22965,22966,22967,22968,22969,22970,22971,22972,22973,22974,22975, -22976,22977,22978,22979,22980,22981,22982,22983,22984,22985,22986,22987, -22988,22989,22990,22991,22992,22993,22994,22995,22996,22997,22998,22999, -23000,23001,23002,23003,23004,23005,23006,23007,23008,23009,23010,23011, -23012,23013,23014,23015,23016,23017,23018,23019,23020,23021,23022,23023, -23024,23025,23026,23027,23028,23029,23030,23031,23032,23033,23034,23035, -23036,23037,23038,23039,23040,23041,23042,23043,23044,23045,23046,23047, -23048,23049,23050,23051,23052,23053,23054,23055,23056,23057,23058,23059, -23060,23061,23062,23063,23064,23065,23066,23067,23068,23069,23070,23071, -23072,23073,23074,23075,23076,23077,23078,23079,23080,23081,23082,23083, -23084,23085,23086,23087,23088,23089,23090,23091,23092,23093,23094,23095, -23096,23097,23098,23099,23100,23101,23102,23103,23104,23105,23106,23107, -23108,23109,23110,23111,23112,23113,23114,23115,23116,23117,23118,23119, -23120,23121,23122,23123,23124,23125,23126,23127,23128,23129,23130,23131, -23132,23133,23134,23135,23136,23137,23138,23139,23140,23141,23142,23143, -23144,23145,23146,23147,23148,23149,23150,23151,23152,23153,23154,23155, -23156,23157,23158,23159,23160,23161,23162,23163,23164,23165,23166,23167, -23168,23169,23170,23171,23172,23173,23174,23175,23176,23177,23178,23179, -23180,23181,23182,23183,23184,23185,23186,23187,23188,23189,23190,23191, -23192,23193,23194,23195,23196,23197,23198,23199,23200,23201,23202,23203, -23204,23205,23206,23207,23208,23209,23210,23211,23212,23213,23214,23215, -23216,23217,23218,23219,23220,23221,23222,23223,23224,23225,23226,23227, -23228,23229,23230,23231,23232,23233,23234,23235,23236,23237,23238,23239, -23240,23241,23242,23243,23244,23245,23246,23247,23248,23249,23250,23251, -23252,23253,23254,23255,23256,23257,23258,23259,23260,23261,23262,23263, -23264,23265,23266,23267,23268,23269,23270,23271,23272,23273,23274,23275, -23276,23277,23278,23279,23280,23281,23282,23283,23284,23285,23286,23287, -23288,23289,23290,23291,23292,23293,23294,23295,23296,23297,23298,23299, -23300,23301,23302,23303,23304,23305,23306,23307,23308,23309,23310,23311, -23312,23313,23314,23315,23316,23317,23318,23319,23320,23321,23322,23323, -23324,23325,23326,23327,23328,23329,23330,23331,23332,23333,23334,23335, -23336,23337,23338,23339,23340,23341,23342,23343,23344,23345,23346,23347, -23348,23349,23350,23351,23352,23353,23354,23355,23356,23357,23358,23359, -23360,23361,23362,23363,23364,23365,23366,23367,23368,23369,23370,23371, -23372,23373,23374,23375,23376,23377,23378,23379,23380,23381,23382,23383, -23384,23385,23386,23387,23388,23389,23390,23391,23392,23393,23394,23395, -23396,23397,23398,23399,23400,23401,23402,23403,23404,23405,23406,23407, -23408,23409,23410,23411,23412,23413,23414,23415,23416,23417,23418,23419, -23420,23421,23422,23423,23424,23425,23426,23427,23428,23429,23430,23431, -23432,23433,23434,23435,23436,23437,23438,23439,23440,23441,23442,23443, -23444,23445,23446,23447,23448,23449,23450,23451,23452,23453,23454,23455, -23456,23457,23458,23459,23460,23461,23462,23463,23464,23465,23466,23467, -23468,23469,23470,23471,23472,23473,23474,23475,23476,23477,23478,23479, -23480,23481,23482,23483,23484,23485,23486,23487,23488,23489,23490,23491, -23492,23493,23494,23495,23496,23497,23498,23499,23500,23501,23502,23503, -23504,23505,23506,23507,23508,23509,23510,23511,23512,23513,23514,23515, -23516,23517,23518,23519,23520,23521,23522,23523,23524,23525,23526,23527, -23528,23529,23530,23531,23532,23533,23534,23535,23536,23537,23538,23539, -23540,23541,23542,23543,23544,23545,23546,23547,23548,23549,23550,23551, -23552,23553,23554,23555,23556,23557,23558,23559,23560,23561,23562,23563, -23564,23565,23566,23567,23568,23569,23570,23571,23572,23573,23574,23575, -23576,23577,23578,23579,23580,23581,23582,23583,23584,23585,23586,23587, -23588,23589,23590,23591,23592,23593,23594,23595,23596,23597,23598,23599, -23600,23601,23602,23603,23604,23605,23606,23607,23608,23609,23610,23611, -23612,23613,23614,23615,23616,23617,23618,23619,23620,23621,23622,23623, -23624,23625,23626,23627,23628,23629,23630,23631,23632,23633,23634,23635, -23636,23637,23638,23639,23640,23641,23642,23643,23644,23645,23646,23647, -23648,23649,23650,23651,23652,23653,23654,23655,23656,23657,23658,23659, -23660,23661,23662,23663,23664,23665,23666,23667,23668,23669,23670,23671, -23672,23673,23674,23675,23676,23677,23678,23679,23680,23681,23682,23683, -23684,23685,23686,23687,23688,23689,23690,23691,23692,23693,23694,23695, -23696,23697,23698,23699,23700,23701,23702,23703,23704,23705,23706,23707, -23708,23709,23710,23711,23712,23713,23714,23715,23716,23717,23718,23719, -23720,23721,23722,23723,23724,23725,23726,23727,23728,23729,23730,23731, -23732,23733,23734,23735,23736,23737,23738,23739,23740,23741,23742,23743, -23744,23745,23746,23747,23748,23749,23750,23751,23752,23753,23754,23755, -23756,23757,23758,23759,23760,23761,23762,23763,23764,23765,23766,23767, -23768,23769,23770,23771,23772,23773,23774,23775,23776,23777,23778,23779, -23780,23781,23782,23783,23784,23785,23786,23787,23788,23789,23790,23791, -23792,23793,23794,23795,23796,23797,23798,23799,23800,23801,23802,23803, -23804,23805,23806,23807,23808,23809,23810,23811,23812,23813,23814,23815, -23816,23817,23818,23819,23820,23821,23822,23823,23824,23825,23826,23827, -23828,23829,23830,23831,23832,23833,23834,23835,23836,23837,23838,23839, -23840,23841,23842,23843,23844,23845,23846,23847,23848,23849,23850,23851, -23852,23853,23854,23855,23856,23857,23858,23859,23860,23861,23862,23863, -23864,23865,23866,23867,23868,23869,23870,23871,23872,23873,23874,23875, -23876,23877,23878,23879,23880,23881,23882,23883,23884,23885,23886,23887, -23888,23889,23890,23891,23892,23893,23894,23895,23896,23897,23898,23899, -23900,23901,23902,23903,23904,23905,23906,23907,23908,23909,23910,23911, -23912,23913,23914,23915,23916,23917,23918,23919,23920,23921,23922,23923, -23924,23925,23926,23927,23928,23929,23930,23931,23932,23933,23934,23935, -23936,23937,23938,23939,23940,23941,23942,23943,23944,23945,23946,23947, -23948,23949,23950,23951,23952,23953,23954,23955,23956,23957,23958,23959, -23960,23961,23962,23963,23964,23965,23966,23967,23968,23969,23970,23971, -23972,23973,23974,23975,23976,23977,23978,23979,23980,23981,23982,23983, -23984,23985,23986,23987,23988,23989,23990,23991,23992,23993,23994,23995, -23996,23997,23998,23999,24000,24001,24002,24003,24004,24005,24006,24007, -24008,24009,24010,24011,24012,24013,24014,24015,24016,24017,24018,24019, -24020,24021,24022,24023,24024,24025,24026,24027,24028,24029,24030,24031, -24032,24033,24034,24035,24036,24037,24038,24039,24040,24041,24042,24043, -24044,24045,24046,24047,24048,24049,24050,24051,24052,24053,24054,24055, -24056,24057,24058,24059,24060,24061,24062,24063,24064,24065,24066,24067, -24068,24069,24070,24071,24072,24073,24074,24075,24076,24077,24078,24079, -24080,24081,24082,24083,24084,24085,24086,24087,24088,24089,24090,24091, -24092,24093,24094,24095,24096,24097,24098,24099,24100,24101,24102,24103, -24104,24105,24106,24107,24108,24109,24110,24111,24112,24113,24114,24115, -24116,24117,24118,24119,24120,24121,24122,24123,24124,24125,24126,24127, -24128,24129,24130,24131,24132,24133,24134,24135,24136,24137,24138,24139, -24140,24141,24142,24143,24144,24145,24146,24147,24148,24149,24150,24151, -24152,24153,24154,24155,24156,24157,24158,24159,24160,24161,24162,24163, -24164,24165,24166,24167,24168,24169,24170,24171,24172,24173,24174,24175, -24176,24177,24178,24179,24180,24181,24182,24183,24184,24185,24186,24187, -24188,24189,24190,24191,24192,24193,24194,24195,24196,24197,24198,24199, -24200,24201,24202,24203,24204,24205,24206,24207,24208,24209,24210,24211, -24212,24213,24214,24215,24216,24217,24218,24219,24220,24221,24222,24223, -24224,24225,24226,24227,24228,24229,24230,24231,24232,24233,24234,24235, -24236,24237,24238,24239,24240,24241,24242,24243,24244,24245,24246,24247, -24248,24249,24250,24251,24252,24253,24254,24255,24256,24257,24258,24259, -24260,24261,24262,24263,24264,24265,24266,24267,24268,24269,24270,24271, -24272,24273,24274,24275,24276,24277,24278,24279,24280,24281,24282,24283, -24284,24285,24286,24287,24288,24289,24290,24291,24292,24293,24294,24295, -24296,24297,24298,24299,24300,24301,24302,24303,24304,24305,24306,24307, -24308,24309,24310,24311,24312,24313,24314,24315,24316,24317,24318,24319, -24320,24321,24322,24323,24324,24325,24326,24327,24328,24329,24330,24331, -24332,24333,24334,24335,24336,24337,24338,24339,24340,24341,24342,24343, -24344,24345,24346,24347,24348,24349,24350,24351,24352,24353,24354,24355, -24356,24357,24358,24359,24360,24361,24362,24363,24364,24365,24366,24367, -24368,24369,24370,24371,24372,24373,24374,24375,24376,24377,24378,24379, -24380,24381,24382,24383,24384,24385,24386,24387,24388,24389,24390,24391, -24392,24393,24394,24395,24396,24397,24398,24399,24400,24401,24402,24403, -24404,24405,24406,24407,24408,24409,24410,24411,24412,24413,24414,24415, -24416,24417,24418,24419,24420,24421,24422,24423,24424,24425,24426,24427, -24428,24429,24430,24431,24432,24433,24434,24435,24436,24437,24438,24439, -24440,24441,24442,24443,24444,24445,24446,24447,24448,24449,24450,24451, -24452,24453,24454,24455,24456,24457,24458,24459,24460,24461,24462,24463, -24464,24465,24466,24467,24468,24469,24470,24471,24472,24473,24474,24475, -24476,24477,24478,24479,24480,24481,24482,24483,24484,24485,24486,24487, -24488,24489,24490,24491,24492,24493,24494,24495,24496,24497,24498,24499, -24500,24501,24502,24503,24504,24505,24506,24507,24508,24509,24510,24511, -24512,24513,24514,24515,24516,24517,24518,24519,24520,24521,24522,24523, -24524,24525,24526,24527,24528,24529,24530,24531,24532,24533,24534,24535, -24536,24537,24538,24539,24540,24541,24542,24543,24544,24545,24546,24547, -24548,24549,24550,24551,24552,24553,24554,24555,24556,24557,24558,24559, -24560,24561,24562,24563,24564,24565,24566,24567,24568,24569,24570,24571, -24572,24573,24574,24575,24576,24577,24578,24579,24580,24581,24582,24583, -24584,24585,24586,24587,24588,24589,24590,24591,24592,24593,24594,24595, -24596,24597,24598,24599,24600,24601,24602,24603,24604,24605,24606,24607, -24608,24609,24610,24611,24612,24613,24614,24615,24616,24617,24618,24619, -24620,24621,24622,24623,24624,24625,24626,24627,24628,24629,24630,24631, -24632,24633,24634,24635,24636,24637,24638,24639,24640,24641,24642,24643, -24644,24645,24646,24647,24648,24649,24650,24651,24652,24653,24654,24655, -24656,24657,24658,24659,24660,24661,24662,24663,24664,24665,24666,24667, -24668,24669,24670,24671,24672,24673,24674,24675,24676,24677,24678,24679, -24680,24681,24682,24683,24684,24685,24686,24687,24688,24689,24690,24691, -24692,24693,24694,24695,24696,24697,24698,24699,24700,24701,24702,24703, -24704,24705,24706,24707,24708,24709,24710,24711,24712,24713,24714,24715, -24716,24717,24718,24719,24720,24721,24722,24723,24724,24725,24726,24727, -24728,24729,24730,24731,24732,24733,24734,24735,24736,24737,24738,24739, -24740,24741,24742,24743,24744,24745,24746,24747,24748,24749,24750,24751, -24752,24753,24754,24755,24756,24757,24758,24759,24760,24761,24762,24763, -24764,24765,24766,24767,24768,24769,24770,24771,24772,24773,24774,24775, -24776,24777,24778,24779,24780,24781,24782,24783,24784,24785,24786,24787, -24788,24789,24790,24791,24792,24793,24794,24795,24796,24797,24798,24799, -24800,24801,24802,24803,24804,24805,24806,24807,24808,24809,24810,24811, -24812,24813,24814,24815,24816,24817,24818,24819,24820,24821,24822,24823, -24824,24825,24826,24827,24828,24829,24830,24831,24832,24833,24834,24835, -24836,24837,24838,24839,24840,24841,24842,24843,24844,24845,24846,24847, -24848,24849,24850,24851,24852,24853,24854,24855,24856,24857,24858,24859, -24860,24861,24862,24863,24864,24865,24866,24867,24868,24869,24870,24871, -24872,24873,24874,24875,24876,24877,24878,24879,24880,24881,24882,24883, -24884,24885,24886,24887,24888,24889,24890,24891,24892,24893,24894,24895, -24896,24897,24898,24899,24900,24901,24902,24903,24904,24905,24906,24907, -24908,24909,24910,24911,24912,24913,24914,24915,24916,24917,24918,24919, -24920,24921,24922,24923,24924,24925,24926,24927,24928,24929,24930,24931, -24932,24933,24934,24935,24936,24937,24938,24939,24940,24941,24942,24943, -24944,24945,24946,24947,24948,24949,24950,24951,24952,24953,24954,24955, -24956,24957,24958,24959,24960,24961,24962,24963,24964,24965,24966,24967, -24968,24969,24970,24971,24972,24973,24974,24975,24976,24977,24978,24979, -24980,24981,24982,24983,24984,24985,24986,24987,24988,24989,24990,24991, -24992,24993,24994,24995,24996,24997,24998,24999,25000,25001,25002,25003, -25004,25005,25006,25007,25008,25009,25010,25011,25012,25013,25014,25015, -25016,25017,25018,25019,25020,25021,25022,25023,25024,25025,25026,25027, -25028,25029,25030,25031,25032,25033,25034,25035,25036,25037,25038,25039, -25040,25041,25042,25043,25044,25045,25046,25047,25048,25049,25050,25051, -25052,25053,25054,25055,25056,25057,25058,25059,25060,25061,25062,25063, -25064,25065,25066,25067,25068,25069,25070,25071,25072,25073,25074,25075, -25076,25077,25078,25079,25080,25081,25082,25083,25084,25085,25086,25087, -25088,25089,25090,25091,25092,25093,25094,25095,25096,25097,25098,25099, -25100,25101,25102,25103,25104,25105,25106,25107,25108,25109,25110,25111, -25112,25113,25114,25115,25116,25117,25118,25119,25120,25121,25122,25123, -25124,25125,25126,25127,25128,25129,25130,25131,25132,25133,25134,25135, -25136,25137,25138,25139,25140,25141,25142,25143,25144,25145,25146,25147, -25148,25149,25150,25151,25152,25153,25154,25155,25156,25157,25158,25159, -25160,25161,25162,25163,25164,25165,25166,25167,25168,25169,25170,25171, -25172,25173,25174,25175,25176,25177,25178,25179,25180,25181,25182,25183, -25184,25185,25186,25187,25188,25189,25190,25191,25192,25193,25194,25195, -25196,25197,25198,25199,25200,25201,25202,25203,25204,25205,25206,25207, -25208,25209,25210,25211,25212,25213,25214,25215,25216,25217,25218,25219, -25220,25221,25222,25223,25224,25225,25226,25227,25228,25229,25230,25231, -25232,25233,25234,25235,25236,25237,25238,25239,25240,25241,25242,25243, -25244,25245,25246,25247,25248,25249,25250,25251,25252,25253,25254,25255, -25256,25257,25258,25259,25260,25261,25262,25263,25264,25265,25266,25267, -25268,25269,25270,25271,25272,25273,25274,25275,25276,25277,25278,25279, -25280,25281,25282,25283,25284,25285,25286,25287,25288,25289,25290,25291, -25292,25293,25294,25295,25296,25297,25298,25299,25300,25301,25302,25303, -25304,25305,25306,25307,25308,25309,25310,25311,25312,25313,25314,25315, -25316,25317,25318,25319,25320,25321,25322,25323,25324,25325,25326,25327, -25328,25329,25330,25331,25332,25333,25334,25335,25336,25337,25338,25339, -25340,25341,25342,25343,25344,25345,25346,25347,25348,25349,25350,25351, -25352,25353,25354,25355,25356,25357,25358,25359,25360,25361,25362,25363, -25364,25365,25366,25367,25368,25369,25370,25371,25372,25373,25374,25375, -25376,25377,25378,25379,25380,25381,25382,25383,25384,25385,25386,25387, -25388,25389,25390,25391,25392,25393,25394,25395,25396,25397,25398,25399, -25400,25401,25402,25403,25404,25405,25406,25407,25408,25409,25410,25411, -25412,25413,25414,25415,25416,25417,25418,25419,25420,25421,25422,25423, -25424,25425,25426,25427,25428,25429,25430,25431,25432,25433,25434,25435, -25436,25437,25438,25439,25440,25441,25442,25443,25444,25445,25446,25447, -25448,25449,25450,25451,25452,25453,25454,25455,25456,25457,25458,25459, -25460,25461,25462,25463,25464,25465,25466,25467,25468,25469,25470,25471, -25472,25473,25474,25475,25476,25477,25478,25479,25480,25481,25482,25483, -25484,25485,25486,25487,25488,25489,25490,25491,25492,25493,25494,25495, -25496,25497,25498,25499,25500,25501,25502,25503,25504,25505,25506,25507, -25508,25509,25510,25511,25512,25513,25514,25515,25516,25517,25518,25519, -25520,25521,25522,25523,25524,25525,25526,25527,25528,25529,25530,25531, -25532,25533,25534,25535,25536,25537,25538,25539,25540,25541,25542,25543, -25544,25545,25546,25547,25548,25549,25550,25551,25552,25553,25554,25555, -25556,25557,25558,25559,25560,25561,25562,25563,25564,25565,25566,25567, -25568,25569,25570,25571,25572,25573,25574,25575,25576,25577,25578,25579, -25580,25581,25582,25583,25584,25585,25586,25587,25588,25589,25590,25591, -25592,25593,25594,25595,25596,25597,25598,25599,25600,25601,25602,25603, -25604,25605,25606,25607,25608,25609,25610,25611,25612,25613,25614,25615, -25616,25617,25618,25619,25620,25621,25622,25623,25624,25625,25626,25627, -25628,25629,25630,25631,25632,25633,25634,25635,25636,25637,25638,25639, -25640,25641,25642,25643,25644,25645,25646,25647,25648,25649,25650,25651, -25652,25653,25654,25655,25656,25657,25658,25659,25660,25661,25662,25663, -25664,25665,25666,25667,25668,25669,25670,25671,25672,25673,25674,25675, -25676,25677,25678,25679,25680,25681,25682,25683,25684,25685,25686,25687, -25688,25689,25690,25691,25692,25693,25694,25695,25696,25697,25698,25699, -25700,25701,25702,25703,25704,25705,25706,25707,25708,25709,25710,25711, -25712,25713,25714,25715,25716,25717,25718,25719,25720,25721,25722,25723, -25724,25725,25726,25727,25728,25729,25730,25731,25732,25733,25734,25735, -25736,25737,25738,25739,25740,25741,25742,25743,25744,25745,25746,25747, -25748,25749,25750,25751,25752,25753,25754,25755,25756,25757,25758,25759, -25760,25761,25762,25763,25764,25765,25766,25767,25768,25769,25770,25771, -25772,25773,25774,25775,25776,25777,25778,25779,25780,25781,25782,25783, -25784,25785,25786,25787,25788,25789,25790,25791,25792,25793,25794,25795, -25796,25797,25798,25799,25800,25801,25802,25803,25804,25805,25806,25807, -25808,25809,25810,25811,25812,25813,25814,25815,25816,25817,25818,25819, -25820,25821,25822,25823,25824,25825,25826,25827,25828,25829,25830,25831, -25832,25833,25834,25835,25836,25837,25838,25839,25840,25841,25842,25843, -25844,25845,25846,25847,25848,25849,25850,25851,25852,25853,25854,25855, -25856,25857,25858,25859,25860,25861,25862,25863,25864,25865,25866,25867, -25868,25869,25870,25871,25872,25873,25874,25875,25876,25877,25878,25879, -25880,25881,25882,25883,25884,25885,25886,25887,25888,25889,25890,25891, -25892,25893,25894,25895,25896,25897,25898,25899,25900,25901,25902,25903, -25904,25905,25906,25907,25908,25909,25910,25911,25912,25913,25914,25915, -25916,25917,25918,25919,25920,25921,25922,25923,25924,25925,25926,25927, -25928,25929,25930,25931,25932,25933,25934,25935,25936,25937,25938,25939, -25940,25941,25942,25943,25944,25945,25946,25947,25948,25949,25950,25951, -25952,25953,25954,25955,25956,25957,25958,25959,25960,25961,25962,25963, -25964,25965,25966,25967,25968,25969,25970,25971,25972,25973,25974,25975, -25976,25977,25978,25979,25980,25981,25982,25983,25984,25985,25986,25987, -25988,25989,25990,25991,25992,25993,25994,25995,25996,25997,25998,25999, -26000,26001,26002,26003,26004,26005,26006,26007,26008,26009,26010,26011, -26012,26013,26014,26015,26016,26017,26018,26019,26020,26021,26022,26023, -26024,26025,26026,26027,26028,26029,26030,26031,26032,26033,26034,26035, -26036,26037,26038,26039,26040,26041,26042,26043,26044,26045,26046,26047, -26048,26049,26050,26051,26052,26053,26054,26055,26056,26057,26058,26059, -26060,26061,26062,26063,26064,26065,26066,26067,26068,26069,26070,26071, -26072,26073,26074,26075,26076,26077,26078,26079,26080,26081,26082,26083, -26084,26085,26086,26087,26088,26089,26090,26091,26092,26093,26094,26095, -26096,26097,26098,26099,26100,26101,26102,26103,26104,26105,26106,26107, -26108,26109,26110,26111,26112,26113,26114,26115,26116,26117,26118,26119, -26120,26121,26122,26123,26124,26125,26126,26127,26128,26129,26130,26131, -26132,26133,26134,26135,26136,26137,26138,26139,26140,26141,26142,26143, -26144,26145,26146,26147,26148,26149,26150,26151,26152,26153,26154,26155, -26156,26157,26158,26159,26160,26161,26162,26163,26164,26165,26166,26167, -26168,26169,26170,26171,26172,26173,26174,26175,26176,26177,26178,26179, -26180,26181,26182,26183,26184,26185,26186,26187,26188,26189,26190,26191, -26192,26193,26194,26195,26196,26197,26198,26199,26200,26201,26202,26203, -26204,26205,26206,26207,26208,26209,26210,26211,26212,26213,26214,26215, -26216,26217,26218,26219,26220,26221,26222,26223,26224,26225,26226,26227, -26228,26229,26230,26231,26232,26233,26234,26235,26236,26237,26238,26239, -26240,26241,26242,26243,26244,26245,26246,26247,26248,26249,26250,26251, -26252,26253,26254,26255,26256,26257,26258,26259,26260,26261,26262,26263, -26264,26265,26266,26267,26268,26269,26270,26271,26272,26273,26274,26275, -26276,26277,26278,26279,26280,26281,26282,26283,26284,26285,26286,26287, -26288,26289,26290,26291,26292,26293,26294,26295,26296,26297,26298,26299, -26300,26301,26302,26303,26304,26305,26306,26307,26308,26309,26310,26311, -26312,26313,26314,26315,26316,26317,26318,26319,26320,26321,26322,26323, -26324,26325,26326,26327,26328,26329,26330,26331,26332,26333,26334,26335, -26336,26337,26338,26339,26340,26341,26342,26343,26344,26345,26346,26347, -26348,26349,26350,26351,26352,26353,26354,26355,26356,26357,26358,26359, -26360,26361,26362,26363,26364,26365,26366,26367,26368,26369,26370,26371, -26372,26373,26374,26375,26376,26377,26378,26379,26380,26381,26382,26383, -26384,26385,26386,26387,26388,26389,26390,26391,26392,26393,26394,26395, -26396,26397,26398,26399,26400,26401,26402,26403,26404,26405,26406,26407, -26408,26409,26410,26411,26412,26413,26414,26415,26416,26417,26418,26419, -26420,26421,26422,26423,26424,26425,26426,26427,26428,26429,26430,26431, -26432,26433,26434,26435,26436,26437,26438,26439,26440,26441,26442,26443, -26444,26445,26446,26447,26448,26449,26450,26451,26452,26453,26454,26455, -26456,26457,26458,26459,26460,26461,26462,26463,26464,26465,26466,26467, -26468,26469,26470,26471,26472,26473,26474,26475,26476,26477,26478,26479, -26480,26481,26482,26483,26484,26485,26486,26487,26488,26489,26490,26491, -26492,26493,26494,26495,26496,26497,26498,26499,26500,26501,26502,26503, -26504,26505,26506,26507,26508,26509,26510,26511,26512,26513,26514,26515, -26516,26517,26518,26519,26520,26521,26522,26523,26524,26525,26526,26527, -26528,26529,26530,26531,26532,26533,26534,26535,26536,26537,26538,26539, -26540,26541,26542,26543,26544,26545,26546,26547,26548,26549,26550,26551, -26552,26553,26554,26555,26556,26557,26558,26559,26560,26561,26562,26563, -26564,26565,26566,26567,26568,26569,26570,26571,26572,26573,26574,26575, -26576,26577,26578,26579,26580,26581,26582,26583,26584,26585,26586,26587, -26588,26589,26590,26591,26592,26593,26594,26595,26596,26597,26598,26599, -26600,26601,26602,26603,26604,26605,26606,26607,26608,26609,26610,26611, -26612,26613,26614,26615,26616,26617,26618,26619,26620,26621,26622,26623, -26624,26625,26626,26627,26628,26629,26630,26631,26632,26633,26634,26635, -26636,26637,26638,26639,26640,26641,26642,26643,26644,26645,26646,26647, -26648,26649,26650,26651,26652,26653,26654,26655,26656,26657,26658,26659, -26660,26661,26662,26663,26664,26665,26666,26667,26668,26669,26670,26671, -26672,26673,26674,26675,26676,26677,26678,26679,26680,26681,26682,26683, -26684,26685,26686,26687,26688,26689,26690,26691,26692,26693,26694,26695, -26696,26697,26698,26699,26700,26701,26702,26703,26704,26705,26706,26707, -26708,26709,26710,26711,26712,26713,26714,26715,26716,26717,26718,26719, -26720,26721,26722,26723,26724,26725,26726,26727,26728,26729,26730,26731, -26732,26733,26734,26735,26736,26737,26738,26739,26740,26741,26742,26743, -26744,26745,26746,26747,26748,26749,26750,26751,26752,26753,26754,26755, -26756,26757,26758,26759,26760,26761,26762,26763,26764,26765,26766,26767, -26768,26769,26770,26771,26772,26773,26774,26775,26776,26777,26778,26779, -26780,26781,26782,26783,26784,26785,26786,26787,26788,26789,26790,26791, -26792,26793,26794,26795,26796,26797,26798,26799,26800,26801,26802,26803, -26804,26805,26806,26807,26808,26809,26810,26811,26812,26813,26814,26815, -26816,26817,26818,26819,26820,26821,26822,26823,26824,26825,26826,26827, -26828,26829,26830,26831,26832,26833,26834,26835,26836,26837,26838,26839, -26840,26841,26842,26843,26844,26845,26846,26847,26848,26849,26850,26851, -26852,26853,26854,26855,26856,26857,26858,26859,26860,26861,26862,26863, -26864,26865,26866,26867,26868,26869,26870,26871,26872,26873,26874,26875, -26876,26877,26878,26879,26880,26881,26882,26883,26884,26885,26886,26887, -26888,26889,26890,26891,26892,26893,26894,26895,26896,26897,26898,26899, -26900,26901,26902,26903,26904,26905,26906,26907,26908,26909,26910,26911, -26912,26913,26914,26915,26916,26917,26918,26919,26920,26921,26922,26923, -26924,26925,26926,26927,26928,26929,26930,26931,26932,26933,26934,26935, -26936,26937,26938,26939,26940,26941,26942,26943,26944,26945,26946,26947, -26948,26949,26950,26951,26952,26953,26954,26955,26956,26957,26958,26959, -26960,26961,26962,26963,26964,26965,26966,26967,26968,26969,26970,26971, -26972,26973,26974,26975,26976,26977,26978,26979,26980,26981,26982,26983, -26984,26985,26986,26987,26988,26989,26990,26991,26992,26993,26994,26995, -26996,26997,26998,26999,27000,27001,27002,27003,27004,27005,27006,27007, -27008,27009,27010,27011,27012,27013,27014,27015,27016,27017,27018,27019, -27020,27021,27022,27023,27024,27025,27026,27027,27028,27029,27030,27031, -27032,27033,27034,27035,27036,27037,27038,27039,27040,27041,27042,27043, -27044,27045,27046,27047,27048,27049,27050,27051,27052,27053,27054,27055, -27056,27057,27058,27059,27060,27061,27062,27063,27064,27065,27066,27067, -27068,27069,27070,27071,27072,27073,27074,27075,27076,27077,27078,27079, -27080,27081,27082,27083,27084,27085,27086,27087,27088,27089,27090,27091, -27092,27093,27094,27095,27096,27097,27098,27099,27100,27101,27102,27103, -27104,27105,27106,27107,27108,27109,27110,27111,27112,27113,27114,27115, -27116,27117,27118,27119,27120,27121,27122,27123,27124,27125,27126,27127, -27128,27129,27130,27131,27132,27133,27134,27135,27136,27137,27138,27139, -27140,27141,27142,27143,27144,27145,27146,27147,27148,27149,27150,27151, -27152,27153,27154,27155,27156,27157,27158,27159,27160,27161,27162,27163, -27164,27165,27166,27167,27168,27169,27170,27171,27172,27173,27174,27175, -27176,27177,27178,27179,27180,27181,27182,27183,27184,27185,27186,27187, -27188,27189,27190,27191,27192,27193,27194,27195,27196,27197,27198,27199, -27200,27201,27202,27203,27204,27205,27206,27207,27208,27209,27210,27211, -27212,27213,27214,27215,27216,27217,27218,27219,27220,27221,27222,27223, -27224,27225,27226,27227,27228,27229,27230,27231,27232,27233,27234,27235, -27236,27237,27238,27239,27240,27241,27242,27243,27244,27245,27246,27247, -27248,27249,27250,27251,27252,27253,27254,27255,27256,27257,27258,27259, -27260,27261,27262,27263,27264,27265,27266,27267,27268,27269,27270,27271, -27272,27273,27274,27275,27276,27277,27278,27279,27280,27281,27282,27283, -27284,27285,27286,27287,27288,27289,27290,27291,27292,27293,27294,27295, -27296,27297,27298,27299,27300,27301,27302,27303,27304,27305,27306,27307, -27308,27309,27310,27311,27312,27313,27314,27315,27316,27317,27318,27319, -27320,27321,27322,27323,27324,27325,27326,27327,27328,27329,27330,27331, -27332,27333,27334,27335,27336,27337,27338,27339,27340,27341,27342,27343, -27344,27345,27346,27347,27348,27349,27350,27351,27352,27353,27354,27355, -27356,27357,27358,27359,27360,27361,27362,27363,27364,27365,27366,27367, -27368,27369,27370,27371,27372,27373,27374,27375,27376,27377,27378,27379, -27380,27381,27382,27383,27384,27385,27386,27387,27388,27389,27390,27391, -27392,27393,27394,27395,27396,27397,27398,27399,27400,27401,27402,27403, -27404,27405,27406,27407,27408,27409,27410,27411,27412,27413,27414,27415, -27416,27417,27418,27419,27420,27421,27422,27423,27424,27425,27426,27427, -27428,27429,27430,27431,27432,27433,27434,27435,27436,27437,27438,27439, -27440,27441,27442,27443,27444,27445,27446,27447,27448,27449,27450,27451, -27452,27453,27454,27455,27456,27457,27458,27459,27460,27461,27462,27463, -27464,27465,27466,27467,27468,27469,27470,27471,27472,27473,27474,27475, -27476,27477,27478,27479,27480,27481,27482,27483,27484,27485,27486,27487, -27488,27489,27490,27491,27492,27493,27494,27495,27496,27497,27498,27499, -27500,27501,27502,27503,27504,27505,27506,27507,27508,27509,27510,27511, -27512,27513,27514,27515,27516,27517,27518,27519,27520,27521,27522,27523, -27524,27525,27526,27527,27528,27529,27530,27531,27532,27533,27534,27535, -27536,27537,27538,27539,27540,27541,27542,27543,27544,27545,27546,27547, -27548,27549,27550,27551,27552,27553,27554,27555,27556,27557,27558,27559, -27560,27561,27562,27563,27564,27565,27566,27567,27568,27569,27570,27571, -27572,27573,27574,27575,27576,27577,27578,27579,27580,27581,27582,27583, -27584,27585,27586,27587,27588,27589,27590,27591,27592,27593,27594,27595, -27596,27597,27598,27599,27600,27601,27602,27603,27604,27605,27606,27607, -27608,27609,27610,27611,27612,27613,27614,27615,27616,27617,27618,27619, -27620,27621,27622,27623,27624,27625,27626,27627,27628,27629,27630,27631, -27632,27633,27634,27635,27636,27637,27638,27639,27640,27641,27642,27643, -27644,27645,27646,27647,27648,27649,27650,27651,27652,27653,27654,27655, -27656,27657,27658,27659,27660,27661,27662,27663,27664,27665,27666,27667, -27668,27669,27670,27671,27672,27673,27674,27675,27676,27677,27678,27679, -27680,27681,27682,27683,27684,27685,27686,27687,27688,27689,27690,27691, -27692,27693,27694,27695,27696,27697,27698,27699,27700,27701,27702,27703, -27704,27705,27706,27707,27708,27709,27710,27711,27712,27713,27714,27715, -27716,27717,27718,27719,27720,27721,27722,27723,27724,27725,27726,27727, -27728,27729,27730,27731,27732,27733,27734,27735,27736,27737,27738,27739, -27740,27741,27742,27743,27744,27745,27746,27747,27748,27749,27750,27751, -27752,27753,27754,27755,27756,27757,27758,27759,27760,27761,27762,27763, -27764,27765,27766,27767,27768,27769,27770,27771,27772,27773,27774,27775, -27776,27777,27778,27779,27780,27781,27782,27783,27784,27785,27786,27787, -27788,27789,27790,27791,27792,27793,27794,27795,27796,27797,27798,27799, -27800,27801,27802,27803,27804,27805,27806,27807,27808,27809,27810,27811, -27812,27813,27814,27815,27816,27817,27818,27819,27820,27821,27822,27823, -27824,27825,27826,27827,27828,27829,27830,27831,27832,27833,27834,27835, -27836,27837,27838,27839,27840,27841,27842,27843,27844,27845,27846,27847, -27848,27849,27850,27851,27852,27853,27854,27855,27856,27857,27858,27859, -27860,27861,27862,27863,27864,27865,27866,27867,27868,27869,27870,27871, -27872,27873,27874,27875,27876,27877,27878,27879,27880,27881,27882,27883, -27884,27885,27886,27887,27888,27889,27890,27891,27892,27893,27894,27895, -27896,27897,27898,27899,27900,27901,27902,27903,27904,27905,27906,27907, -27908,27909,27910,27911,27912,27913,27914,27915,27916,27917,27918,27919, -27920,27921,27922,27923,27924,27925,27926,27927,27928,27929,27930,27931, -27932,27933,27934,27935,27936,27937,27938,27939,27940,27941,27942,27943, -27944,27945,27946,27947,27948,27949,27950,27951,27952,27953,27954,27955, -27956,27957,27958,27959,27960,27961,27962,27963,27964,27965,27966,27967, -27968,27969,27970,27971,27972,27973,27974,27975,27976,27977,27978,27979, -27980,27981,27982,27983,27984,27985,27986,27987,27988,27989,27990,27991, -27992,27993,27994,27995,27996,27997,27998,27999,28000,28001,28002,28003, -28004,28005,28006,28007,28008,28009,28010,28011,28012,28013,28014,28015, -28016,28017,28018,28019,28020,28021,28022,28023,28024,28025,28026,28027, -28028,28029,28030,28031,28032,28033,28034,28035,28036,28037,28038,28039, -28040,28041,28042,28043,28044,28045,28046,28047,28048,28049,28050,28051, -28052,28053,28054,28055,28056,28057,28058,28059,28060,28061,28062,28063, -28064,28065,28066,28067,28068,28069,28070,28071,28072,28073,28074,28075, -28076,28077,28078,28079,28080,28081,28082,28083,28084,28085,28086,28087, -28088,28089,28090,28091,28092,28093,28094,28095,28096,28097,28098,28099, -28100,28101,28102,28103,28104,28105,28106,28107,28108,28109,28110,28111, -28112,28113,28114,28115,28116,28117,28118,28119,28120,28121,28122,28123, -28124,28125,28126,28127,28128,28129,28130,28131,28132,28133,28134,28135, -28136,28137,28138,28139,28140,28141,28142,28143,28144,28145,28146,28147, -28148,28149,28150,28151,28152,28153,28154,28155,28156,28157,28158,28159, -28160,28161,28162,28163,28164,28165,28166,28167,28168,28169,28170,28171, -28172,28173,28174,28175,28176,28177,28178,28179,28180,28181,28182,28183, -28184,28185,28186,28187,28188,28189,28190,28191,28192,28193,28194,28195, -28196,28197,28198,28199,28200,28201,28202,28203,28204,28205,28206,28207, -28208,28209,28210,28211,28212,28213,28214,28215,28216,28217,28218,28219, -28220,28221,28222,28223,28224,28225,28226,28227,28228,28229,28230,28231, -28232,28233,28234,28235,28236,28237,28238,28239,28240,28241,28242,28243, -28244,28245,28246,28247,28248,28249,28250,28251,28252,28253,28254,28255, -28256,28257,28258,28259,28260,28261,28262,28263,28264,28265,28266,28267, -28268,28269,28270,28271,28272,28273,28274,28275,28276,28277,28278,28279, -28280,28281,28282,28283,28284,28285,28286,28287,28288,28289,28290,28291, -28292,28293,28294,28295,28296,28297,28298,28299,28300,28301,28302,28303, -28304,28305,28306,28307,28308,28309,28310,28311,28312,28313,28314,28315, -28316,28317,28318,28319,28320,28321,28322,28323,28324,28325,28326,28327, -28328,28329,28330,28331,28332,28333,28334,28335,28336,28337,28338,28339, -28340,28341,28342,28343,28344,28345,28346,28347,28348,28349,28350,28351, -28352,28353,28354,28355,28356,28357,28358,28359,28360,28361,28362,28363, -28364,28365,28366,28367,28368,28369,28370,28371,28372,28373,28374,28375, -28376,28377,28378,28379,28380,28381,28382,28383,28384,28385,28386,28387, -28388,28389,28390,28391,28392,28393,28394,28395,28396,28397,28398,28399, -28400,28401,28402,28403,28404,28405,28406,28407,28408,28409,28410,28411, -28412,28413,28414,28415,28416,28417,28418,28419,28420,28421,28422,28423, -28424,28425,28426,28427,28428,28429,28430,28431,28432,28433,28434,28435, -28436,28437,28438,28439,28440,28441,28442,28443,28444,28445,28446,28447, -28448,28449,28450,28451,28452,28453,28454,28455,28456,28457,28458,28459, -28460,28461,28462,28463,28464,28465,28466,28467,28468,28469,28470,28471, -28472,28473,28474,28475,28476,28477,28478,28479,28480,28481,28482,28483, -28484,28485,28486,28487,28488,28489,28490,28491,28492,28493,28494,28495, -28496,28497,28498,28499,28500,28501,28502,28503,28504,28505,28506,28507, -28508,28509,28510,28511,28512,28513,28514,28515,28516,28517,28518,28519, -28520,28521,28522,28523,28524,28525,28526,28527,28528,28529,28530,28531, -28532,28533,28534,28535,28536,28537,28538,28539,28540,28541,28542,28543, -28544,28545,28546,28547,28548,28549,28550,28551,28552,28553,28554,28555, -28556,28557,28558,28559,28560,28561,28562,28563,28564,28565,28566,28567, -28568,28569,28570,28571,28572,28573,28574,28575,28576,28577,28578,28579, -28580,28581,28582,28583,28584,28585,28586,28587,28588,28589,28590,28591, -28592,28593,28594,28595,28596,28597,28598,28599,28600,28601,28602,28603, -28604,28605,28606,28607,28608,28609,28610,28611,28612,28613,28614,28615, -28616,28617,28618,28619,28620,28621,28622,28623,28624,28625,28626,28627, -28628,28629,28630,28631,28632,28633,28634,28635,28636,28637,28638,28639, -28640,28641,28642,28643,28644,28645,28646,28647,28648,28649,28650,28651, -28652,28653,28654,28655,28656,28657,28658,28659,28660,28661,28662,28663, -28664,28665,28666,28667,28668,28669,28670,28671,28672,28673,28674,28675, -28676,28677,28678,28679,28680,28681,28682,28683,28684,28685,28686,28687, -28688,28689,28690,28691,28692,28693,28694,28695,28696,28697,28698,28699, -28700,28701,28702,28703,28704,28705,28706,28707,28708,28709,28710,28711, -28712,28713,28714,28715,28716,28717,28718,28719,28720,28721,28722,28723, -28724,28725,28726,28727,28728,28729,28730,28731,28732,28733,28734,28735, -28736,28737,28738,28739,28740,28741,28742,28743,28744,28745,28746,28747, -28748,28749,28750,28751,28752,28753,28754,28755,28756,28757,28758,28759, -28760,28761,28762,28763,28764,28765,28766,28767,28768,28769,28770,28771, -28772,28773,28774,28775,28776,28777,28778,28779,28780,28781,28782,28783, -28784,28785,28786,28787,28788,28789,28790,28791,28792,28793,28794,28795, -28796,28797,28798,28799,28800,28801,28802,28803,28804,28805,28806,28807, -28808,28809,28810,28811,28812,28813,28814,28815,28816,28817,28818,28819, -28820,28821,28822,28823,28824,28825,28826,28827,28828,28829,28830,28831, -28832,28833,28834,28835,28836,28837,28838,28839,28840,28841,28842,28843, -28844,28845,28846,28847,28848,28849,28850,28851,28852,28853,28854,28855, -28856,28857,28858,28859,28860,28861,28862,28863,28864,28865,28866,28867, -28868,28869,28870,28871,28872,28873,28874,28875,28876,28877,28878,28879, -28880,28881,28882,28883,28884,28885,28886,28887,28888,28889,28890,28891, -28892,28893,28894,28895,28896,28897,28898,28899,28900,28901,28902,28903, -28904,28905,28906,28907,28908,28909,28910,28911,28912,28913,28914,28915, -28916,28917,28918,28919,28920,28921,28922,28923,28924,28925,28926,28927, -28928,28929,28930,28931,28932,28933,28934,28935,28936,28937,28938,28939, -28940,28941,28942,28943,28944,28945,28946,28947,28948,28949,28950,28951, -28952,28953,28954,28955,28956,28957,28958,28959,28960,28961,28962,28963, -28964,28965,28966,28967,28968,28969,28970,28971,28972,28973,28974,28975, -28976,28977,28978,28979,28980,28981,28982,28983,28984,28985,28986,28987, -28988,28989,28990,28991,28992,28993,28994,28995,28996,28997,28998,28999, -29000,29001,29002,29003,29004,29005,29006,29007,29008,29009,29010,29011, -29012,29013,29014,29015,29016,29017,29018,29019,29020,29021,29022,29023, -29024,29025,29026,29027,29028,29029,29030,29031,29032,29033,29034,29035, -29036,29037,29038,29039,29040,29041,29042,29043,29044,29045,29046,29047, -29048,29049,29050,29051,29052,29053,29054,29055,29056,29057,29058,29059, -29060,29061,29062,29063,29064,29065,29066,29067,29068,29069,29070,29071, -29072,29073,29074,29075,29076,29077,29078,29079,29080,29081,29082,29083, -29084,29085,29086,29087,29088,29089,29090,29091,29092,29093,29094,29095, -29096,29097,29098,29099,29100,29101,29102,29103,29104,29105,29106,29107, -29108,29109,29110,29111,29112,29113,29114,29115,29116,29117,29118,29119, -29120,29121,29122,29123,29124,29125,29126,29127,29128,29129,29130,29131, -29132,29133,29134,29135,29136,29137,29138,29139,29140,29141,29142,29143, -29144,29145,29146,29147,29148,29149,29150,29151,29152,29153,29154,29155, -29156,29157,29158,29159,29160,29161,29162,29163,29164,29165,29166,29167, -29168,29169,29170,29171,29172,29173,29174,29175,29176,29177,29178,29179, -29180,29181,29182,29183,29184,29185,29186,29187,29188,29189,29190,29191, -29192,29193,29194,29195,29196,29197,29198,29199,29200,29201,29202,29203, -29204,29205,29206,29207,29208,29209,29210,29211,29212,29213,29214,29215, -29216,29217,29218,29219,29220,29221,29222,29223,29224,29225,29226,29227, -29228,29229,29230,29231,29232,29233,29234,29235,29236,29237,29238,29239, -29240,29241,29242,29243,29244,29245,29246,29247,29248,29249,29250,29251, -29252,29253,29254,29255,29256,29257,29258,29259,29260,29261,29262,29263, -29264,29265,29266,29267,29268,29269,29270,29271,29272,29273,29274,29275, -29276,29277,29278,29279,29280,29281,29282,29283,29284,29285,29286,29287, -29288,29289,29290,29291,29292,29293,29294,29295,29296,29297,29298,29299, -29300,29301,29302,29303,29304,29305,29306,29307,29308,29309,29310,29311, -29312,29313,29314,29315,29316,29317,29318,29319,29320,29321,29322,29323, -29324,29325,29326,29327,29328,29329,29330,29331,29332,29333,29334,29335, -29336,29337,29338,29339,29340,29341,29342,29343,29344,29345,29346,29347, -29348,29349,29350,29351,29352,29353,29354,29355,29356,29357,29358,29359, -29360,29361,29362,29363,29364,29365,29366,29367,29368,29369,29370,29371, -29372,29373,29374,29375,29376,29377,29378,29379,29380,29381,29382,29383, -29384,29385,29386,29387,29388,29389,29390,29391,29392,29393,29394,29395, -29396,29397,29398,29399,29400,29401,29402,29403,29404,29405,29406,29407, -29408,29409,29410,29411,29412,29413,29414,29415,29416,29417,29418,29419, -29420,29421,29422,29423,29424,29425,29426,29427,29428,29429,29430,29431, -29432,29433,29434,29435,29436,29437,29438,29439,29440,29441,29442,29443, -29444,29445,29446,29447,29448,29449,29450,29451,29452,29453,29454,29455, -29456,29457,29458,29459,29460,29461,29462,29463,29464,29465,29466,29467, -29468,29469,29470,29471,29472,29473,29474,29475,29476,29477,29478,29479, -29480,29481,29482,29483,29484,29485,29486,29487,29488,29489,29490,29491, -29492,29493,29494,29495,29496,29497,29498,29499,29500,29501,29502,29503, -29504,29505,29506,29507,29508,29509,29510,29511,29512,29513,29514,29515, -29516,29517,29518,29519,29520,29521,29522,29523,29524,29525,29526,29527, -29528,29529,29530,29531,29532,29533,29534,29535,29536,29537,29538,29539, -29540,29541,29542,29543,29544,29545,29546,29547,29548,29549,29550,29551, -29552,29553,29554,29555,29556,29557,29558,29559,29560,29561,29562,29563, -29564,29565,29566,29567,29568,29569,29570,29571,29572,29573,29574,29575, -29576,29577,29578,29579,29580,29581,29582,29583,29584,29585,29586,29587, -29588,29589,29590,29591,29592,29593,29594,29595,29596,29597,29598,29599, -29600,29601,29602,29603,29604,29605,29606,29607,29608,29609,29610,29611, -29612,29613,29614,29615,29616,29617,29618,29619,29620,29621,29622,29623, -29624,29625,29626,29627,29628,29629,29630,29631,29632,29633,29634,29635, -29636,29637,29638,29639,29640,29641,29642,29643,29644,29645,29646,29647, -29648,29649,29650,29651,29652,29653,29654,29655,29656,29657,29658,29659, -29660,29661,29662,29663,29664,29665,29666,29667,29668,29669,29670,29671, -29672,29673,29674,29675,29676,29677,29678,29679,29680,29681,29682,29683, -29684,29685,29686,29687,29688,29689,29690,29691,29692,29693,29694,29695, -29696,29697,29698,29699,29700,29701,29702,29703,29704,29705,29706,29707, -29708,29709,29710,29711,29712,29713,29714,29715,29716,29717,29718,29719, -29720,29721,29722,29723,29724,29725,29726,29727,29728,29729,29730,29731, -29732,29733,29734,29735,29736,29737,29738,29739,29740,29741,29742,29743, -29744,29745,29746,29747,29748,29749,29750,29751,29752,29753,29754,29755, -29756,29757,29758,29759,29760,29761,29762,29763,29764,29765,29766,29767, -29768,29769,29770,29771,29772,29773,29774,29775,29776,29777,29778,29779, -29780,29781,29782,29783,29784,29785,29786,29787,29788,29789,29790,29791, -29792,29793,29794,29795,29796,29797,29798,29799,29800,29801,29802,29803, -29804,29805,29806,29807,29808,29809,29810,29811,29812,29813,29814,29815, -29816,29817,29818,29819,29820,29821,29822,29823,29824,29825,29826,29827, -29828,29829,29830,29831,29832,29833,29834,29835,29836,29837,29838,29839, -29840,29841,29842,29843,29844,29845,29846,29847,29848,29849,29850,29851, -29852,29853,29854,29855,29856,29857,29858,29859,29860,29861,29862,29863, -29864,29865,29866,29867,29868,29869,29870,29871,29872,29873,29874,29875, -29876,29877,29878,29879,29880,29881,29882,29883,29884,29885,29886,29887, -29888,29889,29890,29891,29892,29893,29894,29895,29896,29897,29898,29899, -29900,29901,29902,29903,29904,29905,29906,29907,29908,29909,29910,29911, -29912,29913,29914,29915,29916,29917,29918,29919,29920,29921,29922,29923, -29924,29925,29926,29927,29928,29929,29930,29931,29932,29933,29934,29935, -29936,29937,29938,29939,29940,29941,29942,29943,29944,29945,29946,29947, -29948,29949,29950,29951,29952,29953,29954,29955,29956,29957,29958,29959, -29960,29961,29962,29963,29964,29965,29966,29967,29968,29969,29970,29971, -29972,29973,29974,29975,29976,29977,29978,29979,29980,29981,29982,29983, -29984,29985,29986,29987,29988,29989,29990,29991,29992,29993,29994,29995, -29996,29997,29998,29999,30000,30001,30002,30003,30004,30005,30006,30007, -30008,30009,30010,30011,30012,30013,30014,30015,30016,30017,30018,30019, -30020,30021,30022,30023,30024,30025,30026,30027,30028,30029,30030,30031, -30032,30033,30034,30035,30036,30037,30038,30039,30040,30041,30042,30043, -30044,30045,30046,30047,30048,30049,30050,30051,30052,30053,30054,30055, -30056,30057,30058,30059,30060,30061,30062,30063,30064,30065,30066,30067, -30068,30069,30070,30071,30072,30073,30074,30075,30076,30077,30078,30079, -30080,30081,30082,30083,30084,30085,30086,30087,30088,30089,30090,30091, -30092,30093,30094,30095,30096,30097,30098,30099,30100,30101,30102,30103, -30104,30105,30106,30107,30108,30109,30110,30111,30112,30113,30114,30115, -30116,30117,30118,30119,30120,30121,30122,30123,30124,30125,30126,30127, -30128,30129,30130,30131,30132,30133,30134,30135,30136,30137,30138,30139, -30140,30141,30142,30143,30144,30145,30146,30147,30148,30149,30150,30151, -30152,30153,30154,30155,30156,30157,30158,30159,30160,30161,30162,30163, -30164,30165,30166,30167,30168,30169,30170,30171,30172,30173,30174,30175, -30176,30177,30178,30179,30180,30181,30182,30183,30184,30185,30186,30187, -30188,30189,30190,30191,30192,30193,30194,30195,30196,30197,30198,30199, -30200,30201,30202,30203,30204,30205,30206,30207,30208,30209,30210,30211, -30212,30213,30214,30215,30216,30217,30218,30219,30220,30221,30222,30223, -30224,30225,30226,30227,30228,30229,30230,30231,30232,30233,30234,30235, -30236,30237,30238,30239,30240,30241,30242,30243,30244,30245,30246,30247, -30248,30249,30250,30251,30252,30253,30254,30255,30256,30257,30258,30259, -30260,30261,30262,30263,30264,30265,30266,30267,30268,30269,30270,30271, -30272,30273,30274,30275,30276,30277,30278,30279,30280,30281,30282,30283, -30284,30285,30286,30287,30288,30289,30290,30291,30292,30293,30294,30295, -30296,30297,30298,30299,30300,30301,30302,30303,30304,30305,30306,30307, -30308,30309,30310,30311,30312,30313,30314,30315,30316,30317,30318,30319, -30320,30321,30322,30323,30324,30325,30326,30327,30328,30329,30330,30331, -30332,30333,30334,30335,30336,30337,30338,30339,30340,30341,30342,30343, -30344,30345,30346,30347,30348,30349,30350,30351,30352,30353,30354,30355, -30356,30357,30358,30359,30360,30361,30362,30363,30364,30365,30366,30367, -30368,30369,30370,30371,30372,30373,30374,30375,30376,30377,30378,30379, -30380,30381,30382,30383,30384,30385,30386,30387,30388,30389,30390,30391, -30392,30393,30394,30395,30396,30397,30398,30399,30400,30401,30402,30403, -30404,30405,30406,30407,30408,30409,30410,30411,30412,30413,30414,30415, -30416,30417,30418,30419,30420,30421,30422,30423,30424,30425,30426,30427, -30428,30429,30430,30431,30432,30433,30434,30435,30436,30437,30438,30439, -30440,30441,30442,30443,30444,30445,30446,30447,30448,30449,30450,30451, -30452,30453,30454,30455,30456,30457,30458,30459,30460,30461,30462,30463, -30464,30465,30466,30467,30468,30469,30470,30471,30472,30473,30474,30475, -30476,30477,30478,30479,30480,30481,30482,30483,30484,30485,30486,30487, -30488,30489,30490,30491,30492,30493,30494,30495,30496,30497,30498,30499, -30500,30501,30502,30503,30504,30505,30506,30507,30508,30509,30510,30511, -30512,30513,30514,30515,30516,30517,30518,30519,30520,30521,30522,30523, -30524,30525,30526,30527,30528,30529,30530,30531,30532,30533,30534,30535, -30536,30537,30538,30539,30540,30541,30542,30543,30544,30545,30546,30547, -30548,30549,30550,30551,30552,30553,30554,30555,30556,30557,30558,30559, -30560,30561,30562,30563,30564,30565,30566,30567,30568,30569,30570,30571, -30572,30573,30574,30575,30576,30577,30578,30579,30580,30581,30582,30583, -30584,30585,30586,30587,30588,30589,30590,30591,30592,30593,30594,30595, -30596,30597,30598,30599,30600,30601,30602,30603,30604,30605,30606,30607, -30608,30609,30610,30611,30612,30613,30614,30615,30616,30617,30618,30619, -30620,30621,30622,30623,30624,30625,30626,30627,30628,30629,30630,30631, -30632,30633,30634,30635,30636,30637,30638,30639,30640,30641,30642,30643, -30644,30645,30646,30647,30648,30649,30650,30651,30652,30653,30654,30655, -30656,30657,30658,30659,30660,30661,30662,30663,30664,30665,30666,30667, -30668,30669,30670,30671,30672,30673,30674,30675,30676,30677,30678,30679, -30680,30681,30682,30683,30684,30685,30686,30687,30688,30689,30690,30691, -30692,30693,30694,30695,30696,30697,30698,30699,30700,30701,30702,30703, -30704,30705,30706,30707,30708,30709,30710,30711,30712,30713,30714,30715, -30716,30717,30718,30719,30720,30721,30722,30723,30724,30725,30726,30727, -30728,30729,30730,30731,30732,30733,30734,30735,30736,30737,30738,30739, -30740,30741,30742,30743,30744,30745,30746,30747,30748,30749,30750,30751, -30752,30753,30754,30755,30756,30757,30758,30759,30760,30761,30762,30763, -30764,30765,30766,30767,30768,30769,30770,30771,30772,30773,30774,30775, -30776,30777,30778,30779,30780,30781,30782,30783,30784,30785,30786,30787, -30788,30789,30790,30791,30792,30793,30794,30795,30796,30797,30798,30799, -30800,30801,30802,30803,30804,30805,30806,30807,30808,30809,30810,30811, -30812,30813,30814,30815,30816,30817,30818,30819,30820,30821,30822,30823, -30824,30825,30826,30827,30828,30829,30830,30831,30832,30833,30834,30835, -30836,30837,30838,30839,30840,30841,30842,30843,30844,30845,30846,30847, -30848,30849,30850,30851,30852,30853,30854,30855,30856,30857,30858,30859, -30860,30861,30862,30863,30864,30865,30866,30867,30868,30869,30870,30871, -30872,30873,30874,30875,30876,30877,30878,30879,30880,30881,30882,30883, -30884,30885,30886,30887,30888,30889,30890,30891,30892,30893,30894,30895, -30896,30897,30898,30899,30900,30901,30902,30903,30904,30905,30906,30907, -30908,30909,30910,30911,30912,30913,30914,30915,30916,30917,30918,30919, -30920,30921,30922,30923,30924,30925,30926,30927,30928,30929,30930,30931, -30932,30933,30934,30935,30936,30937,30938,30939,30940,30941,30942,30943, -30944,30945,30946,30947,30948,30949,30950,30951,30952,30953,30954,30955, -30956,30957,30958,30959,30960,30961,30962,30963,30964,30965,30966,30967, -30968,30969,30970,30971,30972,30973,30974,30975,30976,30977,30978,30979, -30980,30981,30982,30983,30984,30985,30986,30987,30988,30989,30990,30991, -30992,30993,30994,30995,30996,30997,30998,30999,31000,31001,31002,31003, -31004,31005,31006,31007,31008,31009,31010,31011,31012,31013,31014,31015, -31016,31017,31018,31019,31020,31021,31022,31023,31024,31025,31026,31027, -31028,31029,31030,31031,31032,31033,31034,31035,31036,31037,31038,31039, -31040,31041,31042,31043,31044,31045,31046,31047,31048,31049,31050,31051, -31052,31053,31054,31055,31056,31057,31058,31059,31060,31061,31062,31063, -31064,31065,31066,31067,31068,31069,31070,31071,31072,31073,31074,31075, -31076,31077,31078,31079,31080,31081,31082,31083,31084,31085,31086,31087, -31088,31089,31090,31091,31092,31093,31094,31095,31096,31097,31098,31099, -31100,31101,31102,31103,31104,31105,31106,31107,31108,31109,31110,31111, -31112,31113,31114,31115,31116,31117,31118,31119,31120,31121,31122,31123, -31124,31125,31126,31127,31128,31129,31130,31131,31132,31133,31134,31135, -31136,31137,31138,31139,31140,31141,31142,31143,31144,31145,31146,31147, -31148,31149,31150,31151,31152,31153,31154,31155,31156,31157,31158,31159, -31160,31161,31162,31163,31164,31165,31166,31167,31168,31169,31170,31171, -31172,31173,31174,31175,31176,31177,31178,31179,31180,31181,31182,31183, -31184,31185,31186,31187,31188,31189,31190,31191,31192,31193,31194,31195, -31196,31197,31198,31199,31200,31201,31202,31203,31204,31205,31206,31207, -31208,31209,31210,31211,31212,31213,31214,31215,31216,31217,31218,31219, -31220,31221,31222,31223,31224,31225,31226,31227,31228,31229,31230,31231, -31232,31233,31234,31235,31236,31237,31238,31239,31240,31241,31242,31243, -31244,31245,31246,31247,31248,31249,31250,31251,31252,31253,31254,31255, -31256,31257,31258,31259,31260,31261,31262,31263,31264,31265,31266,31267, -31268,31269,31270,31271,31272,31273,31274,31275,31276,31277,31278,31279, -31280,31281,31282,31283,31284,31285,31286,31287,31288,31289,31290,31291, -31292,31293,31294,31295,31296,31297,31298,31299,31300,31301,31302,31303, -31304,31305,31306,31307,31308,31309,31310,31311,31312,31313,31314,31315, -31316,31317,31318,31319,31320,31321,31322,31323,31324,31325,31326,31327, -31328,31329,31330,31331,31332,31333,31334,31335,31336,31337,31338,31339, -31340,31341,31342,31343,31344,31345,31346,31347,31348,31349,31350,31351, -31352,31353,31354,31355,31356,31357,31358,31359,31360,31361,31362,31363, -31364,31365,31366,31367,31368,31369,31370,31371,31372,31373,31374,31375, -31376,31377,31378,31379,31380,31381,31382,31383,31384,31385,31386,31387, -31388,31389,31390,31391,31392,31393,31394,31395,31396,31397,31398,31399, -31400,31401,31402,31403,31404,31405,31406,31407,31408,31409,31410,31411, -31412,31413,31414,31415,31416,31417,31418,31419,31420,31421,31422,31423, -31424,31425,31426,31427,31428,31429,31430,31431,31432,31433,31434,31435, -31436,31437,31438,31439,31440,31441,31442,31443,31444,31445,31446,31447, -31448,31449,31450,31451,31452,31453,31454,31455,31456,31457,31458,31459, -31460,31461,31462,31463,31464,31465,31466,31467,31468,31469,31470,31471, -31472,31473,31474,31475,31476,31477,31478,31479,31480,31481,31482,31483, -31484,31485,31486,31487,31488,31489,31490,31491,31492,31493,31494,31495, -31496,31497,31498,31499,31500,31501,31502,31503,31504,31505,31506,31507, -31508,31509,31510,31511,31512,31513,31514,31515,31516,31517,31518,31519, -31520,31521,31522,31523,31524,31525,31526,31527,31528,31529,31530,31531, -31532,31533,31534,31535,31536,31537,31538,31539,31540,31541,31542,31543, -31544,31545,31546,31547,31548,31549,31550,31551,31552,31553,31554,31555, -31556,31557,31558,31559,31560,31561,31562,31563,31564,31565,31566,31567, -31568,31569,31570,31571,31572,31573,31574,31575,31576,31577,31578,31579, -31580,31581,31582,31583,31584,31585,31586,31587,31588,31589,31590,31591, -31592,31593,31594,31595,31596,31597,31598,31599,31600,31601,31602,31603, -31604,31605,31606,31607,31608,31609,31610,31611,31612,31613,31614,31615, -31616,31617,31618,31619,31620,31621,31622,31623,31624,31625,31626,31627, -31628,31629,31630,31631,31632,31633,31634,31635,31636,31637,31638,31639, -31640,31641,31642,31643,31644,31645,31646,31647,31648,31649,31650,31651, -31652,31653,31654,31655,31656,31657,31658,31659,31660,31661,31662,31663, -31664,31665,31666,31667,31668,31669,31670,31671,31672,31673,31674,31675, -31676,31677,31678,31679,31680,31681,31682,31683,31684,31685,31686,31687, -31688,31689,31690,31691,31692,31693,31694,31695,31696,31697,31698,31699, -31700,31701,31702,31703,31704,31705,31706,31707,31708,31709,31710,31711, -31712,31713,31714,31715,31716,31717,31718,31719,31720,31721,31722,31723, -31724,31725,31726,31727,31728,31729,31730,31731,31732,31733,31734,31735, -31736,31737,31738,31739,31740,31741,31742,31743,31744,31745,31746,31747, -31748,31749,31750,31751,31752,31753,31754,31755,31756,31757,31758,31759, -31760,31761,31762,31763,31764,31765,31766,31767,31768,31769,31770,31771, -31772,31773,31774,31775,31776,31777,31778,31779,31780,31781,31782,31783, -31784,31785,31786,31787,31788,31789,31790,31791,31792,31793,31794,31795, -31796,31797,31798,31799,31800,31801,31802,31803,31804,31805,31806,31807, -31808,31809,31810,31811,31812,31813,31814,31815,31816,31817,31818,31819, -31820,31821,31822,31823,31824,31825,31826,31827,31828,31829,31830,31831, -31832,31833,31834,31835,31836,31837,31838,31839,31840,31841,31842,31843, -31844,31845,31846,31847,31848,31849,31850,31851,31852,31853,31854,31855, -31856,31857,31858,31859,31860,31861,31862,31863,31864,31865,31866,31867, -31868,31869,31870,31871,31872,31873,31874,31875,31876,31877,31878,31879, -31880,31881,31882,31883,31884,31885,31886,31887,31888,31889,31890,31891, -31892,31893,31894,31895,31896,31897,31898,31899,31900,31901,31902,31903, -31904,31905,31906,31907,31908,31909,31910,31911,31912,31913,31914,31915, -31916,31917,31918,31919,31920,31921,31922,31923,31924,31925,31926,31927, -31928,31929,31930,31931,31932,31933,31934,31935,31936,31937,31938,31939, -31940,31941,31942,31943,31944,31945,31946,31947,31948,31949,31950,31951, -31952,31953,31954,31955,31956,31957,31958,31959,31960,31961,31962,31963, -31964,31965,31966,31967,31968,31969,31970,31971,31972,31973,31974,31975, -31976,31977,31978,31979,31980,31981,31982,31983,31984,31985,31986,31987, -31988,31989,31990,31991,31992,31993,31994,31995,31996,31997,31998,31999, -32000,32001,32002,32003,32004,32005,32006,32007,32008,32009,32010,32011, -32012,32013,32014,32015,32016,32017,32018,32019,32020,32021,32022,32023, -32024,32025,32026,32027,32028,32029,32030,32031,32032,32033,32034,32035, -32036,32037,32038,32039,32040,32041,32042,32043,32044,32045,32046,32047, -32048,32049,32050,32051,32052,32053,32054,32055,32056,32057,32058,32059, -32060,32061,32062,32063,32064,32065,32066,32067,32068,32069,32070,32071, -32072,32073,32074,32075,32076,32077,32078,32079,32080,32081,32082,32083, -32084,32085,32086,32087,32088,32089,32090,32091,32092,32093,32094,32095, -32096,32097,32098,32099,32100,32101,32102,32103,32104,32105,32106,32107, -32108,32109,32110,32111,32112,32113,32114,32115,32116,32117,32118,32119, -32120,32121,32122,32123,32124,32125,32126,32127,32128,32129,32130,32131, -32132,32133,32134,32135,32136,32137,32138,32139,32140,32141,32142,32143, -32144,32145,32146,32147,32148,32149,32150,32151,32152,32153,32154,32155, -32156,32157,32158,32159,32160,32161,32162,32163,32164,32165,32166,32167, -32168,32169,32170,32171,32172,32173,32174,32175,32176,32177,32178,32179, -32180,32181,32182,32183,32184,32185,32186,32187,32188,32189,32190,32191, -32192,32193,32194,32195,32196,32197,32198,32199,32200,32201,32202,32203, -32204,32205,32206,32207,32208,32209,32210,32211,32212,32213,32214,32215, -32216,32217,32218,32219,32220,32221,32222,32223,32224,32225,32226,32227, -32228,32229,32230,32231,32232,32233,32234,32235,32236,32237,32238,32239, -32240,32241,32242,32243,32244,32245,32246,32247,32248,32249,32250,32251, -32252,32253,32254,32255,32256,32257,32258,32259,32260,32261,32262,32263, -32264,32265,32266,32267,32268,32269,32270,32271,32272,32273,32274,32275, -32276,32277,32278,32279,32280,32281,32282,32283,32284,32285,32286,32287, -32288,32289,32290,32291,32292,32293,32294,32295,32296,32297,32298,32299, -32300,32301,32302,32303,32304,32305,32306,32307,32308,32309,32310,32311, -32312,32313,32314,32315,32316,32317,32318,32319,32320,32321,32322,32323, -32324,32325,32326,32327,32328,32329,32330,32331,32332,32333,32334,32335, -32336,32337,32338,32339,32340,32341,32342,32343,32344,32345,32346,32347, -32348,32349,32350,32351,32352,32353,32354,32355,32356,32357,32358,32359, -32360,32361,32362,32363,32364,32365,32366,32367,32368,32369,32370,32371, -32372,32373,32374,32375,32376,32377,32378,32379,32380,32381,32382,32383, -32384,32385,32386,32387,32388,32389,32390,32391,32392,32393,32394,32395, -32396,32397,32398,32399,32400,32401,32402,32403,32404,32405,32406,32407, -32408,32409,32410,32411,32412,32413,32414,32415,32416,32417,32418,32419, -32420,32421,32422,32423,32424,32425,32426,32427,32428,32429,32430,32431, -32432,32433,32434,32435,32436,32437,32438,32439,32440,32441,32442,32443, -32444,32445,32446,32447,32448,32449,32450,32451,32452,32453,32454,32455, -32456,32457,32458,32459,32460,32461,32462,32463,32464,32465,32466,32467, -32468,32469,32470,32471,32472,32473,32474,32475,32476,32477,32478,32479, -32480,32481,32482,32483,32484,32485,32486,32487,32488,32489,32490,32491, -32492,32493,32494,32495,32496,32497,32498,32499,32500,32501,32502,32503, -32504,32505,32506,32507,32508,32509,32510,32511,32512,32513,32514,32515, -32516,32517,32518,32519,32520,32521,32522,32523,32524,32525,32526,32527, -32528,32529,32530,32531,32532,32533,32534,32535,32536,32537,32538,32539, -32540,32541,32542,32543,32544,32545,32546,32547,32548,32549,32550,32551, -32552,32553,32554,32555,32556,32557,32558,32559,32560,32561,32562,32563, -32564,32565,32566,32567,32568,32569,32570,32571,32572,32573,32574,32575, -32576,32577,32578,32579,32580,32581,32582,32583,32584,32585,32586,32587, -32588,32589,32590,32591,32592,32593,32594,32595,32596,32597,32598,32599, -32600,32601,32602,32603,32604,32605,32606,32607,32608,32609,32610,32611, -32612,32613,32614,32615,32616,32617,32618,32619,32620,32621,32622,32623, -32624,32625,32626,32627,32628,32629,32630,32631,32632,32633,32634,32635, -32636,32637,32638,32639,32640,32641,32642,32643,32644,32645,32646,32647, -32648,32649,32650,32651,32652,32653,32654,32655,32656,32657,32658,32659, -32660,32661,32662,32663,32664,32665,32666,32667,32668,32669,32670,32671, -32672,32673,32674,32675,32676,32677,32678,32679,32680,32681,32682,32683, -32684,32685,32686,32687,32688,32689,32690,32691,32692,32693,32694,32695, -32696,32697,32698,32699,32700,32701,32702,32703,32704,32705,32706,32707, -32708,32709,32710,32711,32712,32713,32714,32715,32716,32717,32718,32719, -32720,32721,32722,32723,32724,32725,32726,32727,32728,32729,32730,32731, -32732,32733,32734,32735,32736,32737,32738,32739,32740,32741,32742,32743, -32744,32745,32746,32747,32748,32749,32750,32751,32752,32753,32754,32755, -32756,32757,32758,32759,32760,32761,32762,32763,32764,32765,32766,32767, -32768L,32769L,32770L,32771L,32772L,32773L,32774L,32775L,32776L,32777L, -32778L,32779L,32780L,32781L,32782L,32783L,32784L,32785L,32786L,32787L, -32788L,32789L,32790L,32791L,32792L,32793L,32794L,32795L,32796L,32797L, -32798L,32799L,32800L,32801L,32802L,32803L,32804L,32805L,32806L,32807L, -32808L,32809L,32810L,32811L,32812L,32813L,32814L,32815L,32816L,32817L, -32818L,32819L,32820L,32821L,32822L,32823L,32824L,32825L,32826L,32827L, -32828L,32829L,32830L,32831L,32832L,32833L,32834L,32835L,32836L,32837L, -32838L,32839L,32840L,32841L,32842L,32843L,32844L,32845L,32846L,32847L, -32848L,32849L,32850L,32851L,32852L,32853L,32854L,32855L,32856L,32857L, -32858L,32859L,32860L,32861L,32862L,32863L,32864L,32865L,32866L,32867L, -32868L,32869L,32870L,32871L,32872L,32873L,32874L,32875L,32876L,32877L, -32878L,32879L,32880L,32881L,32882L,32883L,32884L,32885L,32886L,32887L, -32888L,32889L,32890L,32891L,32892L,32893L,32894L,32895L,32896L,32897L, -32898L,32899L,32900L,32901L,32902L,32903L,32904L,32905L,32906L,32907L, -32908L,32909L,32910L,32911L,32912L,32913L,32914L,32915L,32916L,32917L, -32918L,32919L,32920L,32921L,32922L,32923L,32924L,32925L,32926L,32927L, -32928L,32929L,32930L,32931L,32932L,32933L,32934L,32935L,32936L,32937L, -32938L,32939L,32940L,32941L,32942L,32943L,32944L,32945L,32946L,32947L, -32948L,32949L,32950L,32951L,32952L,32953L,32954L,32955L,32956L,32957L, -32958L,32959L,32960L,32961L,32962L,32963L,32964L,32965L,32966L,32967L, -32968L,32969L,32970L,32971L,32972L,32973L,32974L,32975L,32976L,32977L, -32978L,32979L,32980L,32981L,32982L,32983L,32984L,32985L,32986L,32987L, -32988L,32989L,32990L,32991L,32992L,32993L,32994L,32995L,32996L,32997L, -32998L,32999L,33000L,33001L,33002L,33003L,33004L,33005L,33006L,33007L, -33008L,33009L,33010L,33011L,33012L,33013L,33014L,33015L,33016L,33017L, -33018L,33019L,33020L,33021L,33022L,33023L,33024L,33025L,33026L,33027L, -33028L,33029L,33030L,33031L,33032L,33033L,33034L,33035L,33036L,33037L, -33038L,33039L,33040L,33041L,33042L,33043L,33044L,33045L,33046L,33047L, -33048L,33049L,33050L,33051L,33052L,33053L,33054L,33055L,33056L,33057L, -33058L,33059L,33060L,33061L,33062L,33063L,33064L,33065L,33066L,33067L, -33068L,33069L,33070L,33071L,33072L,33073L,33074L,33075L,33076L,33077L, -33078L,33079L,33080L,33081L,33082L,33083L,33084L,33085L,33086L,33087L, -33088L,33089L,33090L,33091L,33092L,33093L,33094L,33095L,33096L,33097L, -33098L,33099L,33100L,33101L,33102L,33103L,33104L,33105L,33106L,33107L, -33108L,33109L,33110L,33111L,33112L,33113L,33114L,33115L,33116L,33117L, -33118L,33119L,33120L,33121L,33122L,33123L,33124L,33125L,33126L,33127L, -33128L,33129L,33130L,33131L,33132L,33133L,33134L,33135L,33136L,33137L, -33138L,33139L,33140L,33141L,33142L,33143L,33144L,33145L,33146L,33147L, -33148L,33149L,33150L,33151L,33152L,33153L,33154L,33155L,33156L,33157L, -33158L,33159L,33160L,33161L,33162L,33163L,33164L,33165L,33166L,33167L, -33168L,33169L,33170L,33171L,33172L,33173L,33174L,33175L,33176L,33177L, -33178L,33179L,33180L,33181L,33182L,33183L,33184L,33185L,33186L,33187L, -33188L,33189L,33190L,33191L,33192L,33193L,33194L,33195L,33196L,33197L, -33198L,33199L,33200L,33201L,33202L,33203L,33204L,33205L,33206L,33207L, -33208L,33209L,33210L,33211L,33212L,33213L,33214L,33215L,33216L,33217L, -33218L,33219L,33220L,33221L,33222L,33223L,33224L,33225L,33226L,33227L, -33228L,33229L,33230L,33231L,33232L,33233L,33234L,33235L,33236L,33237L, -33238L,33239L,33240L,33241L,33242L,33243L,33244L,33245L,33246L,33247L, -33248L,33249L,33250L,33251L,33252L,33253L,33254L,33255L,33256L,33257L, -33258L,33259L,33260L,33261L,33262L,33263L,33264L,33265L,33266L,33267L, -33268L,33269L,33270L,33271L,33272L,33273L,33274L,33275L,33276L,33277L, -33278L,33279L,33280L,33281L,33282L,33283L,33284L,33285L,33286L,33287L, -33288L,33289L,33290L,33291L,33292L,33293L,33294L,33295L,33296L,33297L, -33298L,33299L,33300L,33301L,33302L,33303L,33304L,33305L,33306L,33307L, -33308L,33309L,33310L,33311L,33312L,33313L,33314L,33315L,33316L,33317L, -33318L,33319L,33320L,33321L,33322L,33323L,33324L,33325L,33326L,33327L, -33328L,33329L,33330L,33331L,33332L,33333L,33334L,33335L,33336L,33337L, -33338L,33339L,33340L,33341L,33342L,33343L,33344L,33345L,33346L,33347L, -33348L,33349L,33350L,33351L,33352L,33353L,33354L,33355L,33356L,33357L, -33358L,33359L,33360L,33361L,33362L,33363L,33364L,33365L,33366L,33367L, -33368L,33369L,33370L,33371L,33372L,33373L,33374L,33375L,33376L,33377L, -33378L,33379L,33380L,33381L,33382L,33383L,33384L,33385L,33386L,33387L, -33388L,33389L,33390L,33391L,33392L,33393L,33394L,33395L,33396L,33397L, -33398L,33399L,33400L,33401L,33402L,33403L,33404L,33405L,33406L,33407L, -33408L,33409L,33410L,33411L,33412L,33413L,33414L,33415L,33416L,33417L, -33418L,33419L,33420L,33421L,33422L,33423L,33424L,33425L,33426L,33427L, -33428L,33429L,33430L,33431L,33432L,33433L,33434L,33435L,33436L,33437L, -33438L,33439L,33440L,33441L,33442L,33443L,33444L,33445L,33446L,33447L, -33448L,33449L,33450L,33451L,33452L,33453L,33454L,33455L,33456L,33457L, -33458L,33459L,33460L,33461L,33462L,33463L,33464L,33465L,33466L,33467L, -33468L,33469L,33470L,33471L,33472L,33473L,33474L,33475L,33476L,33477L, -33478L,33479L,33480L,33481L,33482L,33483L,33484L,33485L,33486L,33487L, -33488L,33489L,33490L,33491L,33492L,33493L,33494L,33495L,33496L,33497L, -33498L,33499L,33500L,33501L,33502L,33503L,33504L,33505L,33506L,33507L, -33508L,33509L,33510L,33511L,33512L,33513L,33514L,33515L,33516L,33517L, -33518L,33519L,33520L,33521L,33522L,33523L,33524L,33525L,33526L,33527L, -33528L,33529L,33530L,33531L,33532L,33533L,33534L,33535L,33536L,33537L, -33538L,33539L,33540L,33541L,33542L,33543L,33544L,33545L,33546L,33547L, -33548L,33549L,33550L,33551L,33552L,33553L,33554L,33555L,33556L,33557L, -33558L,33559L,33560L,33561L,33562L,33563L,33564L,33565L,33566L,33567L, -33568L,33569L,33570L,33571L,33572L,33573L,33574L,33575L,33576L,33577L, -33578L,33579L,33580L,33581L,33582L,33583L,33584L,33585L,33586L,33587L, -33588L,33589L,33590L,33591L,33592L,33593L,33594L,33595L,33596L,33597L, -33598L,33599L,33600L,33601L,33602L,33603L,33604L,33605L,33606L,33607L, -33608L,33609L,33610L,33611L,33612L,33613L,33614L,33615L,33616L,33617L, -33618L,33619L,33620L,33621L,33622L,33623L,33624L,33625L,33626L,33627L, -33628L,33629L,33630L,33631L,33632L,33633L,33634L,33635L,33636L,33637L, -33638L,33639L,33640L,33641L,33642L,33643L,33644L,33645L,33646L,33647L, -33648L,33649L,33650L,33651L,33652L,33653L,33654L,33655L,33656L,33657L, -33658L,33659L,33660L,33661L,33662L,33663L,33664L,33665L,33666L,33667L, -33668L,33669L,33670L,33671L,33672L,33673L,33674L,33675L,33676L,33677L, -33678L,33679L,33680L,33681L,33682L,33683L,33684L,33685L,33686L,33687L, -33688L,33689L,33690L,33691L,33692L,33693L,33694L,33695L,33696L,33697L, -33698L,33699L,33700L,33701L,33702L,33703L,33704L,33705L,33706L,33707L, -33708L,33709L,33710L,33711L,33712L,33713L,33714L,33715L,33716L,33717L, -33718L,33719L,33720L,33721L,33722L,33723L,33724L,33725L,33726L,33727L, -33728L,33729L,33730L,33731L,33732L,33733L,33734L,33735L,33736L,33737L, -33738L,33739L,33740L,33741L,33742L,33743L,33744L,33745L,33746L,33747L, -33748L,33749L,33750L,33751L,33752L,33753L,33754L,33755L,33756L,33757L, -33758L,33759L,33760L,33761L,33762L,33763L,33764L,33765L,33766L,33767L, -33768L,33769L,33770L,33771L,33772L,33773L,33774L,33775L,33776L,33777L, -33778L,33779L,33780L,33781L,33782L,33783L,33784L,33785L,33786L,33787L, -33788L,33789L,33790L,33791L,33792L,33793L,33794L,33795L,33796L,33797L, -33798L,33799L,33800L,33801L,33802L,33803L,33804L,33805L,33806L,33807L, -33808L,33809L,33810L,33811L,33812L,33813L,33814L,33815L,33816L,33817L, -33818L,33819L,33820L,33821L,33822L,33823L,33824L,33825L,33826L,33827L, -33828L,33829L,33830L,33831L,33832L,33833L,33834L,33835L,33836L,33837L, -33838L,33839L,33840L,33841L,33842L,33843L,33844L,33845L,33846L,33847L, -33848L,33849L,33850L,33851L,33852L,33853L,33854L,33855L,33856L,33857L, -33858L,33859L,33860L,33861L,33862L,33863L,33864L,33865L,33866L,33867L, -33868L,33869L,33870L,33871L,33872L,33873L,33874L,33875L,33876L,33877L, -33878L,33879L,33880L,33881L,33882L,33883L,33884L,33885L,33886L,33887L, -33888L,33889L,33890L,33891L,33892L,33893L,33894L,33895L,33896L,33897L, -33898L,33899L,33900L,33901L,33902L,33903L,33904L,33905L,33906L,33907L, -33908L,33909L,33910L,33911L,33912L,33913L,33914L,33915L,33916L,33917L, -33918L,33919L,33920L,33921L,33922L,33923L,33924L,33925L,33926L,33927L, -33928L,33929L,33930L,33931L,33932L,33933L,33934L,33935L,33936L,33937L, -33938L,33939L,33940L,33941L,33942L,33943L,33944L,33945L,33946L,33947L, -33948L,33949L,33950L,33951L,33952L,33953L,33954L,33955L,33956L,33957L, -33958L,33959L,33960L,33961L,33962L,33963L,33964L,33965L,33966L,33967L, -33968L,33969L,33970L,33971L,33972L,33973L,33974L,33975L,33976L,33977L, -33978L,33979L,33980L,33981L,33982L,33983L,33984L,33985L,33986L,33987L, -33988L,33989L,33990L,33991L,33992L,33993L,33994L,33995L,33996L,33997L, -33998L,33999L,34000L,34001L,34002L,34003L,34004L,34005L,34006L,34007L, -34008L,34009L,34010L,34011L,34012L,34013L,34014L,34015L,34016L,34017L, -34018L,34019L,34020L,34021L,34022L,34023L,34024L,34025L,34026L,34027L, -34028L,34029L,34030L,34031L,34032L,34033L,34034L,34035L,34036L,34037L, -34038L,34039L,34040L,34041L,34042L,34043L,34044L,34045L,34046L,34047L, -34048L,34049L,34050L,34051L,34052L,34053L,34054L,34055L,34056L,34057L, -34058L,34059L,34060L,34061L,34062L,34063L,34064L,34065L,34066L,34067L, -34068L,34069L,34070L,34071L,34072L,34073L,34074L,34075L,34076L,34077L, -34078L,34079L,34080L,34081L,34082L,34083L,34084L,34085L,34086L,34087L, -34088L,34089L,34090L,34091L,34092L,34093L,34094L,34095L,34096L,34097L, -34098L,34099L,34100L,34101L,34102L,34103L,34104L,34105L,34106L,34107L, -34108L,34109L,34110L,34111L,34112L,34113L,34114L,34115L,34116L,34117L, -34118L,34119L,34120L,34121L,34122L,34123L,34124L,34125L,34126L,34127L, -34128L,34129L,34130L,34131L,34132L,34133L,34134L,34135L,34136L,34137L, -34138L,34139L,34140L,34141L,34142L,34143L,34144L,34145L,34146L,34147L, -34148L,34149L,34150L,34151L,34152L,34153L,34154L,34155L,34156L,34157L, -34158L,34159L,34160L,34161L,34162L,34163L,34164L,34165L,34166L,34167L, -34168L,34169L,34170L,34171L,34172L,34173L,34174L,34175L,34176L,34177L, -34178L,34179L,34180L,34181L,34182L,34183L,34184L,34185L,34186L,34187L, -34188L,34189L,34190L,34191L,34192L,34193L,34194L,34195L,34196L,34197L, -34198L,34199L,34200L,34201L,34202L,34203L,34204L,34205L,34206L,34207L, -34208L,34209L,34210L,34211L,34212L,34213L,34214L,34215L,34216L,34217L, -34218L,34219L,34220L,34221L,34222L,34223L,34224L,34225L,34226L,34227L, -34228L,34229L,34230L,34231L,34232L,34233L,34234L,34235L,34236L,34237L, -34238L,34239L,34240L,34241L,34242L,34243L,34244L,34245L,34246L,34247L, -34248L,34249L,34250L,34251L,34252L,34253L,34254L,34255L,34256L,34257L, -34258L,34259L,34260L,34261L,34262L,34263L,34264L,34265L,34266L,34267L, -34268L,34269L,34270L,34271L,34272L,34273L,34274L,34275L,34276L,34277L, -34278L,34279L,34280L,34281L,34282L,34283L,34284L,34285L,34286L,34287L, -34288L,34289L,34290L,34291L,34292L,34293L,34294L,34295L,34296L,34297L, -34298L,34299L,34300L,34301L,34302L,34303L,34304L,34305L,34306L,34307L, -34308L,34309L,34310L,34311L,34312L,34313L,34314L,34315L,34316L,34317L, -34318L,34319L,34320L,34321L,34322L,34323L,34324L,34325L,34326L,34327L, -34328L,34329L,34330L,34331L,34332L,34333L,34334L,34335L,34336L,34337L, -34338L,34339L,34340L,34341L,34342L,34343L,34344L,34345L,34346L,34347L, -34348L,34349L,34350L,34351L,34352L,34353L,34354L,34355L,34356L,34357L, -34358L,34359L,34360L,34361L,34362L,34363L,34364L,34365L,34366L,34367L, -34368L,34369L,34370L,34371L,34372L,34373L,34374L,34375L,34376L,34377L, -34378L,34379L,34380L,34381L,34382L,34383L,34384L,34385L,34386L,34387L, -34388L,34389L,34390L,34391L,34392L,34393L,34394L,34395L,34396L,34397L, -34398L,34399L,34400L,34401L,34402L,34403L,34404L,34405L,34406L,34407L, -34408L,34409L,34410L,34411L,34412L,34413L,34414L,34415L,34416L,34417L, -34418L,34419L,34420L,34421L,34422L,34423L,34424L,34425L,34426L,34427L, -34428L,34429L,34430L,34431L,34432L,34433L,34434L,34435L,34436L,34437L, -34438L,34439L,34440L,34441L,34442L,34443L,34444L,34445L,34446L,34447L, -34448L,34449L,34450L,34451L,34452L,34453L,34454L,34455L,34456L,34457L, -34458L,34459L,34460L,34461L,34462L,34463L,34464L,34465L,34466L,34467L, -34468L,34469L,34470L,34471L,34472L,34473L,34474L,34475L,34476L,34477L, -34478L,34479L,34480L,34481L,34482L,34483L,34484L,34485L,34486L,34487L, -34488L,34489L,34490L,34491L,34492L,34493L,34494L,34495L,34496L,34497L, -34498L,34499L,34500L,34501L,34502L,34503L,34504L,34505L,34506L,34507L, -34508L,34509L,34510L,34511L,34512L,34513L,34514L,34515L,34516L,34517L, -34518L,34519L,34520L,34521L,34522L,34523L,34524L,34525L,34526L,34527L, -34528L,34529L,34530L,34531L,34532L,34533L,34534L,34535L,34536L,34537L, -34538L,34539L,34540L,34541L,34542L,34543L,34544L,34545L,34546L,34547L, -34548L,34549L,34550L,34551L,34552L,34553L,34554L,34555L,34556L,34557L, -34558L,34559L,34560L,34561L,34562L,34563L,34564L,34565L,34566L,34567L, -34568L,34569L,34570L,34571L,34572L,34573L,34574L,34575L,34576L,34577L, -34578L,34579L,34580L,34581L,34582L,34583L,34584L,34585L,34586L,34587L, -34588L,34589L,34590L,34591L,34592L,34593L,34594L,34595L,34596L,34597L, -34598L,34599L,34600L,34601L,34602L,34603L,34604L,34605L,34606L,34607L, -34608L,34609L,34610L,34611L,34612L,34613L,34614L,34615L,34616L,34617L, -34618L,34619L,34620L,34621L,34622L,34623L,34624L,34625L,34626L,34627L, -34628L,34629L,34630L,34631L,34632L,34633L,34634L,34635L,34636L,34637L, -34638L,34639L,34640L,34641L,34642L,34643L,34644L,34645L,34646L,34647L, -34648L,34649L,34650L,34651L,34652L,34653L,34654L,34655L,34656L,34657L, -34658L,34659L,34660L,34661L,34662L,34663L,34664L,34665L,34666L,34667L, -34668L,34669L,34670L,34671L,34672L,34673L,34674L,34675L,34676L,34677L, -34678L,34679L,34680L,34681L,34682L,34683L,34684L,34685L,34686L,34687L, -34688L,34689L,34690L,34691L,34692L,34693L,34694L,34695L,34696L,34697L, -34698L,34699L,34700L,34701L,34702L,34703L,34704L,34705L,34706L,34707L, -34708L,34709L,34710L,34711L,34712L,34713L,34714L,34715L,34716L,34717L, -34718L,34719L,34720L,34721L,34722L,34723L,34724L,34725L,34726L,34727L, -34728L,34729L,34730L,34731L,34732L,34733L,34734L,34735L,34736L,34737L, -34738L,34739L,34740L,34741L,34742L,34743L,34744L,34745L,34746L,34747L, -34748L,34749L,34750L,34751L,34752L,34753L,34754L,34755L,34756L,34757L, -34758L,34759L,34760L,34761L,34762L,34763L,34764L,34765L,34766L,34767L, -34768L,34769L,34770L,34771L,34772L,34773L,34774L,34775L,34776L,34777L, -34778L,34779L,34780L,34781L,34782L,34783L,34784L,34785L,34786L,34787L, -34788L,34789L,34790L,34791L,34792L,34793L,34794L,34795L,34796L,34797L, -34798L,34799L,34800L,34801L,34802L,34803L,34804L,34805L,34806L,34807L, -34808L,34809L,34810L,34811L,34812L,34813L,34814L,34815L,34816L,34817L, -34818L,34819L,34820L,34821L,34822L,34823L,34824L,34825L,34826L,34827L, -34828L,34829L,34830L,34831L,34832L,34833L,34834L,34835L,34836L,34837L, -34838L,34839L,34840L,34841L,34842L,34843L,34844L,34845L,34846L,34847L, -34848L,34849L,34850L,34851L,34852L,34853L,34854L,34855L,34856L,34857L, -34858L,34859L,34860L,34861L,34862L,34863L,34864L,34865L,34866L,34867L, -34868L,34869L,34870L,34871L,34872L,34873L,34874L,34875L,34876L,34877L, -34878L,34879L,34880L,34881L,34882L,34883L,34884L,34885L,34886L,34887L, -34888L,34889L,34890L,34891L,34892L,34893L,34894L,34895L,34896L,34897L, -34898L,34899L,34900L,34901L,34902L,34903L,34904L,34905L,34906L,34907L, -34908L,34909L,34910L,34911L,34912L,34913L,34914L,34915L,34916L,34917L, -34918L,34919L,34920L,34921L,34922L,34923L,34924L,34925L,34926L,34927L, -34928L,34929L,34930L,34931L,34932L,34933L,34934L,34935L,34936L,34937L, -34938L,34939L,34940L,34941L,34942L,34943L,34944L,34945L,34946L,34947L, -34948L,34949L,34950L,34951L,34952L,34953L,34954L,34955L,34956L,34957L, -34958L,34959L,34960L,34961L,34962L,34963L,34964L,34965L,34966L,34967L, -34968L,34969L,34970L,34971L,34972L,34973L,34974L,34975L,34976L,34977L, -34978L,34979L,34980L,34981L,34982L,34983L,34984L,34985L,34986L,34987L, -34988L,34989L,34990L,34991L,34992L,34993L,34994L,34995L,34996L,34997L, -34998L,34999L,35000L,35001L,35002L,35003L,35004L,35005L,35006L,35007L, -35008L,35009L,35010L,35011L,35012L,35013L,35014L,35015L,35016L,35017L, -35018L,35019L,35020L,35021L,35022L,35023L,35024L,35025L,35026L,35027L, -35028L,35029L,35030L,35031L,35032L,35033L,35034L,35035L,35036L,35037L, -35038L,35039L,35040L,35041L,35042L,35043L,35044L,35045L,35046L,35047L, -35048L,35049L,35050L,35051L,35052L,35053L,35054L,35055L,35056L,35057L, -35058L,35059L,35060L,35061L,35062L,35063L,35064L,35065L,35066L,35067L, -35068L,35069L,35070L,35071L,35072L,35073L,35074L,35075L,35076L,35077L, -35078L,35079L,35080L,35081L,35082L,35083L,35084L,35085L,35086L,35087L, -35088L,35089L,35090L,35091L,35092L,35093L,35094L,35095L,35096L,35097L, -35098L,35099L,35100L,35101L,35102L,35103L,35104L,35105L,35106L,35107L, -35108L,35109L,35110L,35111L,35112L,35113L,35114L,35115L,35116L,35117L, -35118L,35119L,35120L,35121L,35122L,35123L,35124L,35125L,35126L,35127L, -35128L,35129L,35130L,35131L,35132L,35133L,35134L,35135L,35136L,35137L, -35138L,35139L,35140L,35141L,35142L,35143L,35144L,35145L,35146L,35147L, -35148L,35149L,35150L,35151L,35152L,35153L,35154L,35155L,35156L,35157L, -35158L,35159L,35160L,35161L,35162L,35163L,35164L,35165L,35166L,35167L, -35168L,35169L,35170L,35171L,35172L,35173L,35174L,35175L,35176L,35177L, -35178L,35179L,35180L,35181L,35182L,35183L,35184L,35185L,35186L,35187L, -35188L,35189L,35190L,35191L,35192L,35193L,35194L,35195L,35196L,35197L, -35198L,35199L,35200L,35201L,35202L,35203L,35204L,35205L,35206L,35207L, -35208L,35209L,35210L,35211L,35212L,35213L,35214L,35215L,35216L,35217L, -35218L,35219L,35220L,35221L,35222L,35223L,35224L,35225L,35226L,35227L, -35228L,35229L,35230L,35231L,35232L,35233L,35234L,35235L,35236L,35237L, -35238L,35239L,35240L,35241L,35242L,35243L,35244L,35245L,35246L,35247L, -35248L,35249L,35250L,35251L,35252L,35253L,35254L,35255L,35256L,35257L, -35258L,35259L,35260L,35261L,35262L,35263L,35264L,35265L,35266L,35267L, -35268L,35269L,35270L,35271L,35272L,35273L,35274L,35275L,35276L,35277L, -35278L,35279L,35280L,35281L,35282L,35283L,35284L,35285L,35286L,35287L, -35288L,35289L,35290L,35291L,35292L,35293L,35294L,35295L,35296L,35297L, -35298L,35299L,35300L,35301L,35302L,35303L,35304L,35305L,35306L,35307L, -35308L,35309L,35310L,35311L,35312L,35313L,35314L,35315L,35316L,35317L, -35318L,35319L,35320L,35321L,35322L,35323L,35324L,35325L,35326L,35327L, -35328L,35329L,35330L,35331L,35332L,35333L,35334L,35335L,35336L,35337L, -35338L,35339L,35340L,35341L,35342L,35343L,35344L,35345L,35346L,35347L, -35348L,35349L,35350L,35351L,35352L,35353L,35354L,35355L,35356L,35357L, -35358L,35359L,35360L,35361L,35362L,35363L,35364L,35365L,35366L,35367L, -35368L,35369L,35370L,35371L,35372L,35373L,35374L,35375L,35376L,35377L, -35378L,35379L,35380L,35381L,35382L,35383L,35384L,35385L,35386L,35387L, -35388L,35389L,35390L,35391L,35392L,35393L,35394L,35395L,35396L,35397L, -35398L,35399L,35400L,35401L,35402L,35403L,35404L,35405L,35406L,35407L, -35408L,35409L,35410L,35411L,35412L,35413L,35414L,35415L,35416L,35417L, -35418L,35419L,35420L,35421L,35422L,35423L,35424L,35425L,35426L,35427L, -35428L,35429L,35430L,35431L,35432L,35433L,35434L,35435L,35436L,35437L, -35438L,35439L,35440L,35441L,35442L,35443L,35444L,35445L,35446L,35447L, -35448L,35449L,35450L,35451L,35452L,35453L,35454L,35455L,35456L,35457L, -35458L,35459L,35460L,35461L,35462L,35463L,35464L,35465L,35466L,35467L, -35468L,35469L,35470L,35471L,35472L,35473L,35474L,35475L,35476L,35477L, -35478L,35479L,35480L,35481L,35482L,35483L,35484L,35485L,35486L,35487L, -35488L,35489L,35490L,35491L,35492L,35493L,35494L,35495L,35496L,35497L, -35498L,35499L,35500L,35501L,35502L,35503L,35504L,35505L,35506L,35507L, -35508L,35509L,35510L,35511L,35512L,35513L,35514L,35515L,35516L,35517L, -35518L,35519L,35520L,35521L,35522L,35523L,35524L,35525L,35526L,35527L, -35528L,35529L,35530L,35531L,35532L,35533L,35534L,35535L,35536L,35537L, -35538L,35539L,35540L,35541L,35542L,35543L,35544L,35545L,35546L,35547L, -35548L,35549L,35550L,35551L,35552L,35553L,35554L,35555L,35556L,35557L, -35558L,35559L,35560L,35561L,35562L,35563L,35564L,35565L,35566L,35567L, -35568L,35569L,35570L,35571L,35572L,35573L,35574L,35575L,35576L,35577L, -35578L,35579L,35580L,35581L,35582L,35583L,35584L,35585L,35586L,35587L, -35588L,35589L,35590L,35591L,35592L,35593L,35594L,35595L,35596L,35597L, -35598L,35599L,35600L,35601L,35602L,35603L,35604L,35605L,35606L,35607L, -35608L,35609L,35610L,35611L,35612L,35613L,35614L,35615L,35616L,35617L, -35618L,35619L,35620L,35621L,35622L,35623L,35624L,35625L,35626L,35627L, -35628L,35629L,35630L,35631L,35632L,35633L,35634L,35635L,35636L,35637L, -35638L,35639L,35640L,35641L,35642L,35643L,35644L,35645L,35646L,35647L, -35648L,35649L,35650L,35651L,35652L,35653L,35654L,35655L,35656L,35657L, -35658L,35659L,35660L,35661L,35662L,35663L,35664L,35665L,35666L,35667L, -35668L,35669L,35670L,35671L,35672L,35673L,35674L,35675L,35676L,35677L, -35678L,35679L,35680L,35681L,35682L,35683L,35684L,35685L,35686L,35687L, -35688L,35689L,35690L,35691L,35692L,35693L,35694L,35695L,35696L,35697L, -35698L,35699L,35700L,35701L,35702L,35703L,35704L,35705L,35706L,35707L, -35708L,35709L,35710L,35711L,35712L,35713L,35714L,35715L,35716L,35717L, -35718L,35719L,35720L,35721L,35722L,35723L,35724L,35725L,35726L,35727L, -35728L,35729L,35730L,35731L,35732L,35733L,35734L,35735L,35736L,35737L, -35738L,35739L,35740L,35741L,35742L,35743L,35744L,35745L,35746L,35747L, -35748L,35749L,35750L,35751L,35752L,35753L,35754L,35755L,35756L,35757L, -35758L,35759L,35760L,35761L,35762L,35763L,35764L,35765L,35766L,35767L, -35768L,35769L,35770L,35771L,35772L,35773L,35774L,35775L,35776L,35777L, -35778L,35779L,35780L,35781L,35782L,35783L,35784L,35785L,35786L,35787L, -35788L,35789L,35790L,35791L,35792L,35793L,35794L,35795L,35796L,35797L, -35798L,35799L,35800L,35801L,35802L,35803L,35804L,35805L,35806L,35807L, -35808L,35809L,35810L,35811L,35812L,35813L,35814L,35815L,35816L,35817L, -35818L,35819L,35820L,35821L,35822L,35823L,35824L,35825L,35826L,35827L, -35828L,35829L,35830L,35831L,35832L,35833L,35834L,35835L,35836L,35837L, -35838L,35839L,35840L,35841L,35842L,35843L,35844L,35845L,35846L,35847L, -35848L,35849L,35850L,35851L,35852L,35853L,35854L,35855L,35856L,35857L, -35858L,35859L,35860L,35861L,35862L,35863L,35864L,35865L,35866L,35867L, -35868L,35869L,35870L,35871L,35872L,35873L,35874L,35875L,35876L,35877L, -35878L,35879L,35880L,35881L,35882L,35883L,35884L,35885L,35886L,35887L, -35888L,35889L,35890L,35891L,35892L,35893L,35894L,35895L,35896L,35897L, -35898L,35899L,35900L,35901L,35902L,35903L,35904L,35905L,35906L,35907L, -35908L,35909L,35910L,35911L,35912L,35913L,35914L,35915L,35916L,35917L, -35918L,35919L,35920L,35921L,35922L,35923L,35924L,35925L,35926L,35927L, -35928L,35929L,35930L,35931L,35932L,35933L,35934L,35935L,35936L,35937L, -35938L,35939L,35940L,35941L,35942L,35943L,35944L,35945L,35946L,35947L, -35948L,35949L,35950L,35951L,35952L,35953L,35954L,35955L,35956L,35957L, -35958L,35959L,35960L,35961L,35962L,35963L,35964L,35965L,35966L,35967L, -35968L,35969L,35970L,35971L,35972L,35973L,35974L,35975L,35976L,35977L, -35978L,35979L,35980L,35981L,35982L,35983L,35984L,35985L,35986L,35987L, -35988L,35989L,35990L,35991L,35992L,35993L,35994L,35995L,35996L,35997L, -35998L,35999L,36000L,36001L,36002L,36003L,36004L,36005L,36006L,36007L, -36008L,36009L,36010L,36011L,36012L,36013L,36014L,36015L,36016L,36017L, -36018L,36019L,36020L,36021L,36022L,36023L,36024L,36025L,36026L,36027L, -36028L,36029L,36030L,36031L,36032L,36033L,36034L,36035L,36036L,36037L, -36038L,36039L,36040L,36041L,36042L,36043L,36044L,36045L,36046L,36047L, -36048L,36049L,36050L,36051L,36052L,36053L,36054L,36055L,36056L,36057L, -36058L,36059L,36060L,36061L,36062L,36063L,36064L,36065L,36066L,36067L, -36068L,36069L,36070L,36071L,36072L,36073L,36074L,36075L,36076L,36077L, -36078L,36079L,36080L,36081L,36082L,36083L,36084L,36085L,36086L,36087L, -36088L,36089L,36090L,36091L,36092L,36093L,36094L,36095L,36096L,36097L, -36098L,36099L,36100L,36101L,36102L,36103L,36104L,36105L,36106L,36107L, -36108L,36109L,36110L,36111L,36112L,36113L,36114L,36115L,36116L,36117L, -36118L,36119L,36120L,36121L,36122L,36123L,36124L,36125L,36126L,36127L, -36128L,36129L,36130L,36131L,36132L,36133L,36134L,36135L,36136L,36137L, -36138L,36139L,36140L,36141L,36142L,36143L,36144L,36145L,36146L,36147L, -36148L,36149L,36150L,36151L,36152L,36153L,36154L,36155L,36156L,36157L, -36158L,36159L,36160L,36161L,36162L,36163L,36164L,36165L,36166L,36167L, -36168L,36169L,36170L,36171L,36172L,36173L,36174L,36175L,36176L,36177L, -36178L,36179L,36180L,36181L,36182L,36183L,36184L,36185L,36186L,36187L, -36188L,36189L,36190L,36191L,36192L,36193L,36194L,36195L,36196L,36197L, -36198L,36199L,36200L,36201L,36202L,36203L,36204L,36205L,36206L,36207L, -36208L,36209L,36210L,36211L,36212L,36213L,36214L,36215L,36216L,36217L, -36218L,36219L,36220L,36221L,36222L,36223L,36224L,36225L,36226L,36227L, -36228L,36229L,36230L,36231L,36232L,36233L,36234L,36235L,36236L,36237L, -36238L,36239L,36240L,36241L,36242L,36243L,36244L,36245L,36246L,36247L, -36248L,36249L,36250L,36251L,36252L,36253L,36254L,36255L,36256L,36257L, -36258L,36259L,36260L,36261L,36262L,36263L,36264L,36265L,36266L,36267L, -36268L,36269L,36270L,36271L,36272L,36273L,36274L,36275L,36276L,36277L, -36278L,36279L,36280L,36281L,36282L,36283L,36284L,36285L,36286L,36287L, -36288L,36289L,36290L,36291L,36292L,36293L,36294L,36295L,36296L,36297L, -36298L,36299L,36300L,36301L,36302L,36303L,36304L,36305L,36306L,36307L, -36308L,36309L,36310L,36311L,36312L,36313L,36314L,36315L,36316L,36317L, -36318L,36319L,36320L,36321L,36322L,36323L,36324L,36325L,36326L,36327L, -36328L,36329L,36330L,36331L,36332L,36333L,36334L,36335L,36336L,36337L, -36338L,36339L,36340L,36341L,36342L,36343L,36344L,36345L,36346L,36347L, -36348L,36349L,36350L,36351L,36352L,36353L,36354L,36355L,36356L,36357L, -36358L,36359L,36360L,36361L,36362L,36363L,36364L,36365L,36366L,36367L, -36368L,36369L,36370L,36371L,36372L,36373L,36374L,36375L,36376L,36377L, -36378L,36379L,36380L,36381L,36382L,36383L,36384L,36385L,36386L,36387L, -36388L,36389L,36390L,36391L,36392L,36393L,36394L,36395L,36396L,36397L, -36398L,36399L,36400L,36401L,36402L,36403L,36404L,36405L,36406L,36407L, -36408L,36409L,36410L,36411L,36412L,36413L,36414L,36415L,36416L,36417L, -36418L,36419L,36420L,36421L,36422L,36423L,36424L,36425L,36426L,36427L, -36428L,36429L,36430L,36431L,36432L,36433L,36434L,36435L,36436L,36437L, -36438L,36439L,36440L,36441L,36442L,36443L,36444L,36445L,36446L,36447L, -36448L,36449L,36450L,36451L,36452L,36453L,36454L,36455L,36456L,36457L, -36458L,36459L,36460L,36461L,36462L,36463L,36464L,36465L,36466L,36467L, -36468L,36469L,36470L,36471L,36472L,36473L,36474L,36475L,36476L,36477L, -36478L,36479L,36480L,36481L,36482L,36483L,36484L,36485L,36486L,36487L, -36488L,36489L,36490L,36491L,36492L,36493L,36494L,36495L,36496L,36497L, -36498L,36499L,36500L,36501L,36502L,36503L,36504L,36505L,36506L,36507L, -36508L,36509L,36510L,36511L,36512L,36513L,36514L,36515L,36516L,36517L, -36518L,36519L,36520L,36521L,36522L,36523L,36524L,36525L,36526L,36527L, -36528L,36529L,36530L,36531L,36532L,36533L,36534L,36535L,36536L,36537L, -36538L,36539L,36540L,36541L,36542L,36543L,36544L,36545L,36546L,36547L, -36548L,36549L,36550L,36551L,36552L,36553L,36554L,36555L,36556L,36557L, -36558L,36559L,36560L,36561L,36562L,36563L,36564L,36565L,36566L,36567L, -36568L,36569L,36570L,36571L,36572L,36573L,36574L,36575L,36576L,36577L, -36578L,36579L,36580L,36581L,36582L,36583L,36584L,36585L,36586L,36587L, -36588L,36589L,36590L,36591L,36592L,36593L,36594L,36595L,36596L,36597L, -36598L,36599L,36600L,36601L,36602L,36603L,36604L,36605L,36606L,36607L, -36608L,36609L,36610L,36611L,36612L,36613L,36614L,36615L,36616L,36617L, -36618L,36619L,36620L,36621L,36622L,36623L,36624L,36625L,36626L,36627L, -36628L,36629L,36630L,36631L,36632L,36633L,36634L,36635L,36636L,36637L, -36638L,36639L,36640L,36641L,36642L,36643L,36644L,36645L,36646L,36647L, -36648L,36649L,36650L,36651L,36652L,36653L,36654L,36655L,36656L,36657L, -36658L,36659L,36660L,36661L,36662L,36663L,36664L,36665L,36666L,36667L, -36668L,36669L,36670L,36671L,36672L,36673L,36674L,36675L,36676L,36677L, -36678L,36679L,36680L,36681L,36682L,36683L,36684L,36685L,36686L,36687L, -36688L,36689L,36690L,36691L,36692L,36693L,36694L,36695L,36696L,36697L, -36698L,36699L,36700L,36701L,36702L,36703L,36704L,36705L,36706L,36707L, -36708L,36709L,36710L,36711L,36712L,36713L,36714L,36715L,36716L,36717L, -36718L,36719L,36720L,36721L,36722L,36723L,36724L,36725L,36726L,36727L, -36728L,36729L,36730L,36731L,36732L,36733L,36734L,36735L,36736L,36737L, -36738L,36739L,36740L,36741L,36742L,36743L,36744L,36745L,36746L,36747L, -36748L,36749L,36750L,36751L,36752L,36753L,36754L,36755L,36756L,36757L, -36758L,36759L,36760L,36761L,36762L,36763L,36764L,36765L,36766L,36767L, -36768L,36769L,36770L,36771L,36772L,36773L,36774L,36775L,36776L,36777L, -36778L,36779L,36780L,36781L,36782L,36783L,36784L,36785L,36786L,36787L, -36788L,36789L,36790L,36791L,36792L,36793L,36794L,36795L,36796L,36797L, -36798L,36799L,36800L,36801L,36802L,36803L,36804L,36805L,36806L,36807L, -36808L,36809L,36810L,36811L,36812L,36813L,36814L,36815L,36816L,36817L, -36818L,36819L,36820L,36821L,36822L,36823L,36824L,36825L,36826L,36827L, -36828L,36829L,36830L,36831L,36832L,36833L,36834L,36835L,36836L,36837L, -36838L,36839L,36840L,36841L,36842L,36843L,36844L,36845L,36846L,36847L, -36848L,36849L,36850L,36851L,36852L,36853L,36854L,36855L,36856L,36857L, -36858L,36859L,36860L,36861L,36862L,36863L,36864L,36865L,36866L,36867L, -36868L,36869L,36870L,36871L,36872L,36873L,36874L,36875L,36876L,36877L, -36878L,36879L,36880L,36881L,36882L,36883L,36884L,36885L,36886L,36887L, -36888L,36889L,36890L,36891L,36892L,36893L,36894L,36895L,36896L,36897L, -36898L,36899L,36900L,36901L,36902L,36903L,36904L,36905L,36906L,36907L, -36908L,36909L,36910L,36911L,36912L,36913L,36914L,36915L,36916L,36917L, -36918L,36919L,36920L,36921L,36922L,36923L,36924L,36925L,36926L,36927L, -36928L,36929L,36930L,36931L,36932L,36933L,36934L,36935L,36936L,36937L, -36938L,36939L,36940L,36941L,36942L,36943L,36944L,36945L,36946L,36947L, -36948L,36949L,36950L,36951L,36952L,36953L,36954L,36955L,36956L,36957L, -36958L,36959L,36960L,36961L,36962L,36963L,36964L,36965L,36966L,36967L, -36968L,36969L,36970L,36971L,36972L,36973L,36974L,36975L,36976L,36977L, -36978L,36979L,36980L,36981L,36982L,36983L,36984L,36985L,36986L,36987L, -36988L,36989L,36990L,36991L,36992L,36993L,36994L,36995L,36996L,36997L, -36998L,36999L,37000L,37001L,37002L,37003L,37004L,37005L,37006L,37007L, -37008L,37009L,37010L,37011L,37012L,37013L,37014L,37015L,37016L,37017L, -37018L,37019L,37020L,37021L,37022L,37023L,37024L,37025L,37026L,37027L, -37028L,37029L,37030L,37031L,37032L,37033L,37034L,37035L,37036L,37037L, -37038L,37039L,37040L,37041L,37042L,37043L,37044L,37045L,37046L,37047L, -37048L,37049L,37050L,37051L,37052L,37053L,37054L,37055L,37056L,37057L, -37058L,37059L,37060L,37061L,37062L,37063L,37064L,37065L,37066L,37067L, -37068L,37069L,37070L,37071L,37072L,37073L,37074L,37075L,37076L,37077L, -37078L,37079L,37080L,37081L,37082L,37083L,37084L,37085L,37086L,37087L, -37088L,37089L,37090L,37091L,37092L,37093L,37094L,37095L,37096L,37097L, -37098L,37099L,37100L,37101L,37102L,37103L,37104L,37105L,37106L,37107L, -37108L,37109L,37110L,37111L,37112L,37113L,37114L,37115L,37116L,37117L, -37118L,37119L,37120L,37121L,37122L,37123L,37124L,37125L,37126L,37127L, -37128L,37129L,37130L,37131L,37132L,37133L,37134L,37135L,37136L,37137L, -37138L,37139L,37140L,37141L,37142L,37143L,37144L,37145L,37146L,37147L, -37148L,37149L,37150L,37151L,37152L,37153L,37154L,37155L,37156L,37157L, -37158L,37159L,37160L,37161L,37162L,37163L,37164L,37165L,37166L,37167L, -37168L,37169L,37170L,37171L,37172L,37173L,37174L,37175L,37176L,37177L, -37178L,37179L,37180L,37181L,37182L,37183L,37184L,37185L,37186L,37187L, -37188L,37189L,37190L,37191L,37192L,37193L,37194L,37195L,37196L,37197L, -37198L,37199L,37200L,37201L,37202L,37203L,37204L,37205L,37206L,37207L, -37208L,37209L,37210L,37211L,37212L,37213L,37214L,37215L,37216L,37217L, -37218L,37219L,37220L,37221L,37222L,37223L,37224L,37225L,37226L,37227L, -37228L,37229L,37230L,37231L,37232L,37233L,37234L,37235L,37236L,37237L, -37238L,37239L,37240L,37241L,37242L,37243L,37244L,37245L,37246L,37247L, -37248L,37249L,37250L,37251L,37252L,37253L,37254L,37255L,37256L,37257L, -37258L,37259L,37260L,37261L,37262L,37263L,37264L,37265L,37266L,37267L, -37268L,37269L,37270L,37271L,37272L,37273L,37274L,37275L,37276L,37277L, -37278L,37279L,37280L,37281L,37282L,37283L,37284L,37285L,37286L,37287L, -37288L,37289L,37290L,37291L,37292L,37293L,37294L,37295L,37296L,37297L, -37298L,37299L,37300L,37301L,37302L,37303L,37304L,37305L,37306L,37307L, -37308L,37309L,37310L,37311L,37312L,37313L,37314L,37315L,37316L,37317L, -37318L,37319L,37320L,37321L,37322L,37323L,37324L,37325L,37326L,37327L, -37328L,37329L,37330L,37331L,37332L,37333L,37334L,37335L,37336L,37337L, -37338L,37339L,37340L,37341L,37342L,37343L,37344L,37345L,37346L,37347L, -37348L,37349L,37350L,37351L,37352L,37353L,37354L,37355L,37356L,37357L, -37358L,37359L,37360L,37361L,37362L,37363L,37364L,37365L,37366L,37367L, -37368L,37369L,37370L,37371L,37372L,37373L,37374L,37375L,37376L,37377L, -37378L,37379L,37380L,37381L,37382L,37383L,37384L,37385L,37386L,37387L, -37388L,37389L,37390L,37391L,37392L,37393L,37394L,37395L,37396L,37397L, -37398L,37399L,37400L,37401L,37402L,37403L,37404L,37405L,37406L,37407L, -37408L,37409L,37410L,37411L,37412L,37413L,37414L,37415L,37416L,37417L, -37418L,37419L,37420L,37421L,37422L,37423L,37424L,37425L,37426L,37427L, -37428L,37429L,37430L,37431L,37432L,37433L,37434L,37435L,37436L,37437L, -37438L,37439L,37440L,37441L,37442L,37443L,37444L,37445L,37446L,37447L, -37448L,37449L,37450L,37451L,37452L,37453L,37454L,37455L,37456L,37457L, -37458L,37459L,37460L,37461L,37462L,37463L,37464L,37465L,37466L,37467L, -37468L,37469L,37470L,37471L,37472L,37473L,37474L,37475L,37476L,37477L, -37478L,37479L,37480L,37481L,37482L,37483L,37484L,37485L,37486L,37487L, -37488L,37489L,37490L,37491L,37492L,37493L,37494L,37495L,37496L,37497L, -37498L,37499L,37500L,37501L,37502L,37503L,37504L,37505L,37506L,37507L, -37508L,37509L,37510L,37511L,37512L,37513L,37514L,37515L,37516L,37517L, -37518L,37519L,37520L,37521L,37522L,37523L,37524L,37525L,37526L,37527L, -37528L,37529L,37530L,37531L,37532L,37533L,37534L,37535L,37536L,37537L, -37538L,37539L,37540L,37541L,37542L,37543L,37544L,37545L,37546L,37547L, -37548L,37549L,37550L,37551L,37552L,37553L,37554L,37555L,37556L,37557L, -37558L,37559L,37560L,37561L,37562L,37563L,37564L,37565L,37566L,37567L, -37568L,37569L,37570L,37571L,37572L,37573L,37574L,37575L,37576L,37577L, -37578L,37579L,37580L,37581L,37582L,37583L,37584L,37585L,37586L,37587L, -37588L,37589L,37590L,37591L,37592L,37593L,37594L,37595L,37596L,37597L, -37598L,37599L,37600L,37601L,37602L,37603L,37604L,37605L,37606L,37607L, -37608L,37609L,37610L,37611L,37612L,37613L,37614L,37615L,37616L,37617L, -37618L,37619L,37620L,37621L,37622L,37623L,37624L,37625L,37626L,37627L, -37628L,37629L,37630L,37631L,37632L,37633L,37634L,37635L,37636L,37637L, -37638L,37639L,37640L,37641L,37642L,37643L,37644L,37645L,37646L,37647L, -37648L,37649L,37650L,37651L,37652L,37653L,37654L,37655L,37656L,37657L, -37658L,37659L,37660L,37661L,37662L,37663L,37664L,37665L,37666L,37667L, -37668L,37669L,37670L,37671L,37672L,37673L,37674L,37675L,37676L,37677L, -37678L,37679L,37680L,37681L,37682L,37683L,37684L,37685L,37686L,37687L, -37688L,37689L,37690L,37691L,37692L,37693L,37694L,37695L,37696L,37697L, -37698L,37699L,37700L,37701L,37702L,37703L,37704L,37705L,37706L,37707L, -37708L,37709L,37710L,37711L,37712L,37713L,37714L,37715L,37716L,37717L, -37718L,37719L,37720L,37721L,37722L,37723L,37724L,37725L,37726L,37727L, -37728L,37729L,37730L,37731L,37732L,37733L,37734L,37735L,37736L,37737L, -37738L,37739L,37740L,37741L,37742L,37743L,37744L,37745L,37746L,37747L, -37748L,37749L,37750L,37751L,37752L,37753L,37754L,37755L,37756L,37757L, -37758L,37759L,37760L,37761L,37762L,37763L,37764L,37765L,37766L,37767L, -37768L,37769L,37770L,37771L,37772L,37773L,37774L,37775L,37776L,37777L, -37778L,37779L,37780L,37781L,37782L,37783L,37784L,37785L,37786L,37787L, -37788L,37789L,37790L,37791L,37792L,37793L,37794L,37795L,37796L,37797L, -37798L,37799L,37800L,37801L,37802L,37803L,37804L,37805L,37806L,37807L, -37808L,37809L,37810L,37811L,37812L,37813L,37814L,37815L,37816L,37817L, -37818L,37819L,37820L,37821L,37822L,37823L,37824L,37825L,37826L,37827L, -37828L,37829L,37830L,37831L,37832L,37833L,37834L,37835L,37836L,37837L, -37838L,37839L,37840L,37841L,37842L,37843L,37844L,37845L,37846L,37847L, -37848L,37849L,37850L,37851L,37852L,37853L,37854L,37855L,37856L,37857L, -37858L,37859L,37860L,37861L,37862L,37863L,37864L,37865L,37866L,37867L, -37868L,37869L,37870L,37871L,37872L,37873L,37874L,37875L,37876L,37877L, -37878L,37879L,37880L,37881L,37882L,37883L,37884L,37885L,37886L,37887L, -37888L,37889L,37890L,37891L,37892L,37893L,37894L,37895L,37896L,37897L, -37898L,37899L,37900L,37901L,37902L,37903L,37904L,37905L,37906L,37907L, -37908L,37909L,37910L,37911L,37912L,37913L,37914L,37915L,37916L,37917L, -37918L,37919L,37920L,37921L,37922L,37923L,37924L,37925L,37926L,37927L, -37928L,37929L,37930L,37931L,37932L,37933L,37934L,37935L,37936L,37937L, -37938L,37939L,37940L,37941L,37942L,37943L,37944L,37945L,37946L,37947L, -37948L,37949L,37950L,37951L,37952L,37953L,37954L,37955L,37956L,37957L, -37958L,37959L,37960L,37961L,37962L,37963L,37964L,37965L,37966L,37967L, -37968L,37969L,37970L,37971L,37972L,37973L,37974L,37975L,37976L,37977L, -37978L,37979L,37980L,37981L,37982L,37983L,37984L,37985L,37986L,37987L, -37988L,37989L,37990L,37991L,37992L,37993L,37994L,37995L,37996L,37997L, -37998L,37999L,38000L,38001L,38002L,38003L,38004L,38005L,38006L,38007L, -38008L,38009L,38010L,38011L,38012L,38013L,38014L,38015L,38016L,38017L, -38018L,38019L,38020L,38021L,38022L,38023L,38024L,38025L,38026L,38027L, -38028L,38029L,38030L,38031L,38032L,38033L,38034L,38035L,38036L,38037L, -38038L,38039L,38040L,38041L,38042L,38043L,38044L,38045L,38046L,38047L, -38048L,38049L,38050L,38051L,38052L,38053L,38054L,38055L,38056L,38057L, -38058L,38059L,38060L,38061L,38062L,38063L,38064L,38065L,38066L,38067L, -38068L,38069L,38070L,38071L,38072L,38073L,38074L,38075L,38076L,38077L, -38078L,38079L,38080L,38081L,38082L,38083L,38084L,38085L,38086L,38087L, -38088L,38089L,38090L,38091L,38092L,38093L,38094L,38095L,38096L,38097L, -38098L,38099L,38100L,38101L,38102L,38103L,38104L,38105L,38106L,38107L, -38108L,38109L,38110L,38111L,38112L,38113L,38114L,38115L,38116L,38117L, -38118L,38119L,38120L,38121L,38122L,38123L,38124L,38125L,38126L,38127L, -38128L,38129L,38130L,38131L,38132L,38133L,38134L,38135L,38136L,38137L, -38138L,38139L,38140L,38141L,38142L,38143L,38144L,38145L,38146L,38147L, -38148L,38149L,38150L,38151L,38152L,38153L,38154L,38155L,38156L,38157L, -38158L,38159L,38160L,38161L,38162L,38163L,38164L,38165L,38166L,38167L, -38168L,38169L,38170L,38171L,38172L,38173L,38174L,38175L,38176L,38177L, -38178L,38179L,38180L,38181L,38182L,38183L,38184L,38185L,38186L,38187L, -38188L,38189L,38190L,38191L,38192L,38193L,38194L,38195L,38196L,38197L, -38198L,38199L,38200L,38201L,38202L,38203L,38204L,38205L,38206L,38207L, -38208L,38209L,38210L,38211L,38212L,38213L,38214L,38215L,38216L,38217L, -38218L,38219L,38220L,38221L,38222L,38223L,38224L,38225L,38226L,38227L, -38228L,38229L,38230L,38231L,38232L,38233L,38234L,38235L,38236L,38237L, -38238L,38239L,38240L,38241L,38242L,38243L,38244L,38245L,38246L,38247L, -38248L,38249L,38250L,38251L,38252L,38253L,38254L,38255L,38256L,38257L, -38258L,38259L,38260L,38261L,38262L,38263L,38264L,38265L,38266L,38267L, -38268L,38269L,38270L,38271L,38272L,38273L,38274L,38275L,38276L,38277L, -38278L,38279L,38280L,38281L,38282L,38283L,38284L,38285L,38286L,38287L, -38288L,38289L,38290L,38291L,38292L,38293L,38294L,38295L,38296L,38297L, -38298L,38299L,38300L,38301L,38302L,38303L,38304L,38305L,38306L,38307L, -38308L,38309L,38310L,38311L,38312L,38313L,38314L,38315L,38316L,38317L, -38318L,38319L,38320L,38321L,38322L,38323L,38324L,38325L,38326L,38327L, -38328L,38329L,38330L,38331L,38332L,38333L,38334L,38335L,38336L,38337L, -38338L,38339L,38340L,38341L,38342L,38343L,38344L,38345L,38346L,38347L, -38348L,38349L,38350L,38351L,38352L,38353L,38354L,38355L,38356L,38357L, -38358L,38359L,38360L,38361L,38362L,38363L,38364L,38365L,38366L,38367L, -38368L,38369L,38370L,38371L,38372L,38373L,38374L,38375L,38376L,38377L, -38378L,38379L,38380L,38381L,38382L,38383L,38384L,38385L,38386L,38387L, -38388L,38389L,38390L,38391L,38392L,38393L,38394L,38395L,38396L,38397L, -38398L,38399L,38400L,38401L,38402L,38403L,38404L,38405L,38406L,38407L, -38408L,38409L,38410L,38411L,38412L,38413L,38414L,38415L,38416L,38417L, -38418L,38419L,38420L,38421L,38422L,38423L,38424L,38425L,38426L,38427L, -38428L,38429L,38430L,38431L,38432L,38433L,38434L,38435L,38436L,38437L, -38438L,38439L,38440L,38441L,38442L,38443L,38444L,38445L,38446L,38447L, -38448L,38449L,38450L,38451L,38452L,38453L,38454L,38455L,38456L,38457L, -38458L,38459L,38460L,38461L,38462L,38463L,38464L,38465L,38466L,38467L, -38468L,38469L,38470L,38471L,38472L,38473L,38474L,38475L,38476L,38477L, -38478L,38479L,38480L,38481L,38482L,38483L,38484L,38485L,38486L,38487L, -38488L,38489L,38490L,38491L,38492L,38493L,38494L,38495L,38496L,38497L, -38498L,38499L,38500L,38501L,38502L,38503L,38504L,38505L,38506L,38507L, -38508L,38509L,38510L,38511L,38512L,38513L,38514L,38515L,38516L,38517L, -38518L,38519L,38520L,38521L,38522L,38523L,38524L,38525L,38526L,38527L, -38528L,38529L,38530L,38531L,38532L,38533L,38534L,38535L,38536L,38537L, -38538L,38539L,38540L,38541L,38542L,38543L,38544L,38545L,38546L,38547L, -38548L,38549L,38550L,38551L,38552L,38553L,38554L,38555L,38556L,38557L, -38558L,38559L,38560L,38561L,38562L,38563L,38564L,38565L,38566L,38567L, -38568L,38569L,38570L,38571L,38572L,38573L,38574L,38575L,38576L,38577L, -38578L,38579L,38580L,38581L,38582L,38583L,38584L,38585L,38586L,38587L, -38588L,38589L,38590L,38591L,38592L,38593L,38594L,38595L,38596L,38597L, -38598L,38599L,38600L,38601L,38602L,38603L,38604L,38605L,38606L,38607L, -38608L,38609L,38610L,38611L,38612L,38613L,38614L,38615L,38616L,38617L, -38618L,38619L,38620L,38621L,38622L,38623L,38624L,38625L,38626L,38627L, -38628L,38629L,38630L,38631L,38632L,38633L,38634L,38635L,38636L,38637L, -38638L,38639L,38640L,38641L,38642L,38643L,38644L,38645L,38646L,38647L, -38648L,38649L,38650L,38651L,38652L,38653L,38654L,38655L,38656L,38657L, -38658L,38659L,38660L,38661L,38662L,38663L,38664L,38665L,38666L,38667L, -38668L,38669L,38670L,38671L,38672L,38673L,38674L,38675L,38676L,38677L, -38678L,38679L,38680L,38681L,38682L,38683L,38684L,38685L,38686L,38687L, -38688L,38689L,38690L,38691L,38692L,38693L,38694L,38695L,38696L,38697L, -38698L,38699L,38700L,38701L,38702L,38703L,38704L,38705L,38706L,38707L, -38708L,38709L,38710L,38711L,38712L,38713L,38714L,38715L,38716L,38717L, -38718L,38719L,38720L,38721L,38722L,38723L,38724L,38725L,38726L,38727L, -38728L,38729L,38730L,38731L,38732L,38733L,38734L,38735L,38736L,38737L, -38738L,38739L,38740L,38741L,38742L,38743L,38744L,38745L,38746L,38747L, -38748L,38749L,38750L,38751L,38752L,38753L,38754L,38755L,38756L,38757L, -38758L,38759L,38760L,38761L,38762L,38763L,38764L,38765L,38766L,38767L, -38768L,38769L,38770L,38771L,38772L,38773L,38774L,38775L,38776L,38777L, -38778L,38779L,38780L,38781L,38782L,38783L,38784L,38785L,38786L,38787L, -38788L,38789L,38790L,38791L,38792L,38793L,38794L,38795L,38796L,38797L, -38798L,38799L,38800L,38801L,38802L,38803L,38804L,38805L,38806L,38807L, -38808L,38809L,38810L,38811L,38812L,38813L,38814L,38815L,38816L,38817L, -38818L,38819L,38820L,38821L,38822L,38823L,38824L,38825L,38826L,38827L, -38828L,38829L,38830L,38831L,38832L,38833L,38834L,38835L,38836L,38837L, -38838L,38839L,38840L,38841L,38842L,38843L,38844L,38845L,38846L,38847L, -38848L,38849L,38850L,38851L,38852L,38853L,38854L,38855L,38856L,38857L, -38858L,38859L,38860L,38861L,38862L,38863L,38864L,38865L,38866L,38867L, -38868L,38869L,38870L,38871L,38872L,38873L,38874L,38875L,38876L,38877L, -38878L,38879L,38880L,38881L,38882L,38883L,38884L,38885L,38886L,38887L, -38888L,38889L,38890L,38891L,38892L,38893L,38894L,38895L,38896L,38897L, -38898L,38899L,38900L,38901L,38902L,38903L,38904L,38905L,38906L,38907L, -38908L,38909L,38910L,38911L,38912L,38913L,38914L,38915L,38916L,38917L, -38918L,38919L,38920L,38921L,38922L,38923L,38924L,38925L,38926L,38927L, -38928L,38929L,38930L,38931L,38932L,38933L,38934L,38935L,38936L,38937L, -38938L,38939L,38940L,38941L,38942L,38943L,38944L,38945L,38946L,38947L, -38948L,38949L,38950L,38951L,38952L,38953L,38954L,38955L,38956L,38957L, -38958L,38959L,38960L,38961L,38962L,38963L,38964L,38965L,38966L,38967L, -38968L,38969L,38970L,38971L,38972L,38973L,38974L,38975L,38976L,38977L, -38978L,38979L,38980L,38981L,38982L,38983L,38984L,38985L,38986L,38987L, -38988L,38989L,38990L,38991L,38992L,38993L,38994L,38995L,38996L,38997L, -38998L,38999L,39000L,39001L,39002L,39003L,39004L,39005L,39006L,39007L, -39008L,39009L,39010L,39011L,39012L,39013L,39014L,39015L,39016L,39017L, -39018L,39019L,39020L,39021L,39022L,39023L,39024L,39025L,39026L,39027L, -39028L,39029L,39030L,39031L,39032L,39033L,39034L,39035L,39036L,39037L, -39038L,39039L,39040L,39041L,39042L,39043L,39044L,39045L,39046L,39047L, -39048L,39049L,39050L,39051L,39052L,39053L,39054L,39055L,39056L,39057L, -39058L,39059L,39060L,39061L,39062L,39063L,39064L,39065L,39066L,39067L, -39068L,39069L,39070L,39071L,39072L,39073L,39074L,39075L,39076L,39077L, -39078L,39079L,39080L,39081L,39082L,39083L,39084L,39085L,39086L,39087L, -39088L,39089L,39090L,39091L,39092L,39093L,39094L,39095L,39096L,39097L, -39098L,39099L,39100L,39101L,39102L,39103L,39104L,39105L,39106L,39107L, -39108L,39109L,39110L,39111L,39112L,39113L,39114L,39115L,39116L,39117L, -39118L,39119L,39120L,39121L,39122L,39123L,39124L,39125L,39126L,39127L, -39128L,39129L,39130L,39131L,39132L,39133L,39134L,39135L,39136L,39137L, -39138L,39139L,39140L,39141L,39142L,39143L,39144L,39145L,39146L,39147L, -39148L,39149L,39150L,39151L,39152L,39153L,39154L,39155L,39156L,39157L, -39158L,39159L,39160L,39161L,39162L,39163L,39164L,39165L,39166L,39167L, -39168L,39169L,39170L,39171L,39172L,39173L,39174L,39175L,39176L,39177L, -39178L,39179L,39180L,39181L,39182L,39183L,39184L,39185L,39186L,39187L, -39188L,39189L,39190L,39191L,39192L,39193L,39194L,39195L,39196L,39197L, -39198L,39199L,39200L,39201L,39202L,39203L,39204L,39205L,39206L,39207L, -39208L,39209L,39210L,39211L,39212L,39213L,39214L,39215L,39216L,39217L, -39218L,39219L,39220L,39221L,39222L,39223L,39224L,39225L,39226L,39227L, -39228L,39229L,39230L,39231L,39232L,39233L,39234L,39235L,39236L,39237L, -39238L,39239L,39240L,39241L,39242L,39243L,39244L,39245L,39246L,39247L, -39248L,39249L,39250L,39251L,39252L,39253L,39254L,39255L,39256L,39257L, -39258L,39259L,39260L,39261L,39262L,39263L,39264L,39265L,39266L,39267L, -39268L,39269L,39270L,39271L,39272L,39273L,39274L,39275L,39276L,39277L, -39278L,39279L,39280L,39281L,39282L,39283L,39284L,39285L,39286L,39287L, -39288L,39289L,39290L,39291L,39292L,39293L,39294L,39295L,39296L,39297L, -39298L,39299L,39300L,39301L,39302L,39303L,39304L,39305L,39306L,39307L, -39308L,39309L,39310L,39311L,39312L,39313L,39314L,39315L,39316L,39317L, -39318L,39319L,39320L,39321L,39322L,39323L,39324L,39325L,39326L,39327L, -39328L,39329L,39330L,39331L,39332L,39333L,39334L,39335L,39336L,39337L, -39338L,39339L,39340L,39341L,39342L,39343L,39344L,39345L,39346L,39347L, -39348L,39349L,39350L,39351L,39352L,39353L,39354L,39355L,39356L,39357L, -39358L,39359L,39360L,39361L,39362L,39363L,39364L,39365L,39366L,39367L, -39368L,39369L,39370L,39371L,39372L,39373L,39374L,39375L,39376L,39377L, -39378L,39379L,39380L,39381L,39382L,39383L,39384L,39385L,39386L,39387L, -39388L,39389L,39390L,39391L,39392L,39393L,39394L,39395L,39396L,39397L, -39398L,39399L,39400L,39401L,39402L,39403L,39404L,39405L,39406L,39407L, -39408L,39409L,39410L,39411L,39412L,39413L,39414L,39415L,39416L,39417L, -39418L,39419L,39420L,39421L,39422L,39423L,39424L,39425L,39426L,39427L, -39428L,39429L,39430L,39431L,39432L,39433L,39434L,39435L,39436L,39437L, -39438L,39439L,39440L,39441L,39442L,39443L,39444L,39445L,39446L,39447L, -39448L,39449L,39450L,39451L,39452L,39453L,39454L,39455L,39456L,39457L, -39458L,39459L,39460L,39461L,39462L,39463L,39464L,39465L,39466L,39467L, -39468L,39469L,39470L,39471L,39472L,39473L,39474L,39475L,39476L,39477L, -39478L,39479L,39480L,39481L,39482L,39483L,39484L,39485L,39486L,39487L, -39488L,39489L,39490L,39491L,39492L,39493L,39494L,39495L,39496L,39497L, -39498L,39499L,39500L,39501L,39502L,39503L,39504L,39505L,39506L,39507L, -39508L,39509L,39510L,39511L,39512L,39513L,39514L,39515L,39516L,39517L, -39518L,39519L,39520L,39521L,39522L,39523L,39524L,39525L,39526L,39527L, -39528L,39529L,39530L,39531L,39532L,39533L,39534L,39535L,39536L,39537L, -39538L,39539L,39540L,39541L,39542L,39543L,39544L,39545L,39546L,39547L, -39548L,39549L,39550L,39551L,39552L,39553L,39554L,39555L,39556L,39557L, -39558L,39559L,39560L,39561L,39562L,39563L,39564L,39565L,39566L,39567L, -39568L,39569L,39570L,39571L,39572L,39573L,39574L,39575L,39576L,39577L, -39578L,39579L,39580L,39581L,39582L,39583L,39584L,39585L,39586L,39587L, -39588L,39589L,39590L,39591L,39592L,39593L,39594L,39595L,39596L,39597L, -39598L,39599L,39600L,39601L,39602L,39603L,39604L,39605L,39606L,39607L, -39608L,39609L,39610L,39611L,39612L,39613L,39614L,39615L,39616L,39617L, -39618L,39619L,39620L,39621L,39622L,39623L,39624L,39625L,39626L,39627L, -39628L,39629L,39630L,39631L,39632L,39633L,39634L,39635L,39636L,39637L, -39638L,39639L,39640L,39641L,39642L,39643L,39644L,39645L,39646L,39647L, -39648L,39649L,39650L,39651L,39652L,39653L,39654L,39655L,39656L,39657L, -39658L,39659L,39660L,39661L,39662L,39663L,39664L,39665L,39666L,39667L, -39668L,39669L,39670L,39671L,39672L,39673L,39674L,39675L,39676L,39677L, -39678L,39679L,39680L,39681L,39682L,39683L,39684L,39685L,39686L,39687L, -39688L,39689L,39690L,39691L,39692L,39693L,39694L,39695L,39696L,39697L, -39698L,39699L,39700L,39701L,39702L,39703L,39704L,39705L,39706L,39707L, -39708L,39709L,39710L,39711L,39712L,39713L,39714L,39715L,39716L,39717L, -39718L,39719L,39720L,39721L,39722L,39723L,39724L,39725L,39726L,39727L, -39728L,39729L,39730L,39731L,39732L,39733L,39734L,39735L,39736L,39737L, -39738L,39739L,39740L,39741L,39742L,39743L,39744L,39745L,39746L,39747L, -39748L,39749L,39750L,39751L,39752L,39753L,39754L,39755L,39756L,39757L, -39758L,39759L,39760L,39761L,39762L,39763L,39764L,39765L,39766L,39767L, -39768L,39769L,39770L,39771L,39772L,39773L,39774L,39775L,39776L,39777L, -39778L,39779L,39780L,39781L,39782L,39783L,39784L,39785L,39786L,39787L, -39788L,39789L,39790L,39791L,39792L,39793L,39794L,39795L,39796L,39797L, -39798L,39799L,39800L,39801L,39802L,39803L,39804L,39805L,39806L,39807L, -39808L,39809L,39810L,39811L,39812L,39813L,39814L,39815L,39816L,39817L, -39818L,39819L,39820L,39821L,39822L,39823L,39824L,39825L,39826L,39827L, -39828L,39829L,39830L,39831L,39832L,39833L,39834L,39835L,39836L,39837L, -39838L,39839L,39840L,39841L,39842L,39843L,39844L,39845L,39846L,39847L, -39848L,39849L,39850L,39851L,39852L,39853L,39854L,39855L,39856L,39857L, -39858L,39859L,39860L,39861L,39862L,39863L,39864L,39865L,39866L,39867L, -39868L,39869L,39870L,39871L,39872L,39873L,39874L,39875L,39876L,39877L, -39878L,39879L,39880L,39881L,39882L,39883L,39884L,39885L,39886L,39887L, -39888L,39889L,39890L,39891L,39892L,39893L,39894L,39895L,39896L,39897L, -39898L,39899L,39900L,39901L,39902L,39903L,39904L,39905L,39906L,39907L, -39908L,39909L,39910L,39911L,39912L,39913L,39914L,39915L,39916L,39917L, -39918L,39919L,39920L,39921L,39922L,39923L,39924L,39925L,39926L,39927L, -39928L,39929L,39930L,39931L,39932L,39933L,39934L,39935L,39936L,39937L, -39938L,39939L,39940L,39941L,39942L,39943L,39944L,39945L,39946L,39947L, -39948L,39949L,39950L,39951L,39952L,39953L,39954L,39955L,39956L,39957L, -39958L,39959L,39960L,39961L,39962L,39963L,39964L,39965L,39966L,39967L, -39968L,39969L,39970L,39971L,39972L,39973L,39974L,39975L,39976L,39977L, -39978L,39979L,39980L,39981L,39982L,39983L,39984L,39985L,39986L,39987L, -39988L,39989L,39990L,39991L,39992L,39993L,39994L,39995L,39996L,39997L, -39998L,39999L,40000L,40001L,40002L,40003L,40004L,40005L,40006L,40007L, -40008L,40009L,40010L,40011L,40012L,40013L,40014L,40015L,40016L,40017L, -40018L,40019L,40020L,40021L,40022L,40023L,40024L,40025L,40026L,40027L, -40028L,40029L,40030L,40031L,40032L,40033L,40034L,40035L,40036L,40037L, -40038L,40039L,40040L,40041L,40042L,40043L,40044L,40045L,40046L,40047L, -40048L,40049L,40050L,40051L,40052L,40053L,40054L,40055L,40056L,40057L, -40058L,40059L,40060L,40061L,40062L,40063L,40064L,40065L,40066L,40067L, -40068L,40069L,40070L,40071L,40072L,40073L,40074L,40075L,40076L,40077L, -40078L,40079L,40080L,40081L,40082L,40083L,40084L,40085L,40086L,40087L, -40088L,40089L,40090L,40091L,40092L,40093L,40094L,40095L,40096L,40097L, -40098L,40099L,40100L,40101L,40102L,40103L,40104L,40105L,40106L,40107L, -40108L,40109L,40110L,40111L,40112L,40113L,40114L,40115L,40116L,40117L, -40118L,40119L,40120L,40121L,40122L,40123L,40124L,40125L,40126L,40127L, -40128L,40129L,40130L,40131L,40132L,40133L,40134L,40135L,40136L,40137L, -40138L,40139L,40140L,40141L,40142L,40143L,40144L,40145L,40146L,40147L, -40148L,40149L,40150L,40151L,40152L,40153L,40154L,40155L,40156L,40157L, -40158L,40159L,40160L,40161L,40162L,40163L,40164L,40165L,40166L,40167L, -40168L,40169L,40170L,40171L,40172L,40173L,40174L,40175L,40176L,40177L, -40178L,40179L,40180L,40181L,40182L,40183L,40184L,40185L,40186L,40187L, -40188L,40189L,40190L,40191L,40192L,40193L,40194L,40195L,40196L,40197L, -40198L,40199L,40200L,40201L,40202L,40203L,40204L,40205L,40206L,40207L, -40208L,40209L,40210L,40211L,40212L,40213L,40214L,40215L,40216L,40217L, -40218L,40219L,40220L,40221L,40222L,40223L,40224L,40225L,40226L,40227L, -40228L,40229L,40230L,40231L,40232L,40233L,40234L,40235L,40236L,40237L, -40238L,40239L,40240L,40241L,40242L,40243L,40244L,40245L,40246L,40247L, -40248L,40249L,40250L,40251L,40252L,40253L,40254L,40255L,40256L,40257L, -40258L,40259L,40260L,40261L,40262L,40263L,40264L,40265L,40266L,40267L, -40268L,40269L,40270L,40271L,40272L,40273L,40274L,40275L,40276L,40277L, -40278L,40279L,40280L,40281L,40282L,40283L,40284L,40285L,40286L,40287L, -40288L,40289L,40290L,40291L,40292L,40293L,40294L,40295L,40296L,40297L, -40298L,40299L,40300L,40301L,40302L,40303L,40304L,40305L,40306L,40307L, -40308L,40309L,40310L,40311L,40312L,40313L,40314L,40315L,40316L,40317L, -40318L,40319L,40320L,40321L,40322L,40323L,40324L,40325L,40326L,40327L, -40328L,40329L,40330L,40331L,40332L,40333L,40334L,40335L,40336L,40337L, -40338L,40339L,40340L,40341L,40342L,40343L,40344L,40345L,40346L,40347L, -40348L,40349L,40350L,40351L,40352L,40353L,40354L,40355L,40356L,40357L, -40358L,40359L,40360L,40361L,40362L,40363L,40364L,40365L,40366L,40367L, -40368L,40369L,40370L,40371L,40372L,40373L,40374L,40375L,40376L,40377L, -40378L,40379L,40380L,40381L,40382L,40383L,40384L,40385L,40386L,40387L, -40388L,40389L,40390L,40391L,40392L,40393L,40394L,40395L,40396L,40397L, -40398L,40399L,40400L,40401L,40402L,40403L,40404L,40405L,40406L,40407L, -40408L,40409L,40410L,40411L,40412L,40413L,40414L,40415L,40416L,40417L, -40418L,40419L,40420L,40421L,40422L,40423L,40424L,40425L,40426L,40427L, -40428L,40429L,40430L,40431L,40432L,40433L,40434L,40435L,40436L,40437L, -40438L,40439L,40440L,40441L,40442L,40443L,40444L,40445L,40446L,40447L, -40448L,40449L,40450L,40451L,40452L,40453L,40454L,40455L,40456L,40457L, -40458L,40459L,40460L,40461L,40462L,40463L,40464L,40465L,40466L,40467L, -40468L,40469L,40470L,40471L,40472L,40473L,40474L,40475L,40476L,40477L, -40478L,40479L,40480L,40481L,40482L,40483L,40484L,40485L,40486L,40487L, -40488L,40489L,40490L,40491L,40492L,40493L,40494L,40495L,40496L,40497L, -40498L,40499L,40500L,40501L,40502L,40503L,40504L,40505L,40506L,40507L, -40508L,40509L,40510L,40511L,40512L,40513L,40514L,40515L,40516L,40517L, -40518L,40519L,40520L,40521L,40522L,40523L,40524L,40525L,40526L,40527L, -40528L,40529L,40530L,40531L,40532L,40533L,40534L,40535L,40536L,40537L, -40538L,40539L,40540L,40541L,40542L,40543L,40544L,40545L,40546L,40547L, -40548L,40549L,40550L,40551L,40552L,40553L,40554L,40555L,40556L,40557L, -40558L,40559L,40560L,40561L,40562L,40563L,40564L,40565L,40566L,40567L, -40568L,40569L,40570L,40571L,40572L,40573L,40574L,40575L,40576L,40577L, -40578L,40579L,40580L,40581L,40582L,40583L,40584L,40585L,40586L,40587L, -40588L,40589L,40590L,40591L,40592L,40593L,40594L,40595L,40596L,40597L, -40598L,40599L,40600L,40601L,40602L,40603L,40604L,40605L,40606L,40607L, -40608L,40609L,40610L,40611L,40612L,40613L,40614L,40615L,40616L,40617L, -40618L,40619L,40620L,40621L,40622L,40623L,40624L,40625L,40626L,40627L, -40628L,40629L,40630L,40631L,40632L,40633L,40634L,40635L,40636L,40637L, -40638L,40639L,40640L,40641L,40642L,40643L,40644L,40645L,40646L,40647L, -40648L,40649L,40650L,40651L,40652L,40653L,40654L,40655L,40656L,40657L, -40658L,40659L,40660L,40661L,40662L,40663L,40664L,40665L,40666L,40667L, -40668L,40669L,40670L,40671L,40672L,40673L,40674L,40675L,40676L,40677L, -40678L,40679L,40680L,40681L,40682L,40683L,40684L,40685L,40686L,40687L, -40688L,40689L,40690L,40691L,40692L,40693L,40694L,40695L,40696L,40697L, -40698L,40699L,40700L,40701L,40702L,40703L,40704L,40705L,40706L,40707L, -40708L,40709L,40710L,40711L,40712L,40713L,40714L,40715L,40716L,40717L, -40718L,40719L,40720L,40721L,40722L,40723L,40724L,40725L,40726L,40727L, -40728L,40729L,40730L,40731L,40732L,40733L,40734L,40735L,40736L,40737L, -40738L,40739L,40740L,40741L,40742L,40743L,40744L,40745L,40746L,40747L, -40748L,40749L,40750L,40751L,40752L,40753L,40754L,40755L,40756L,40757L, -40758L,40759L,40760L,40761L,40762L,40763L,40764L,40765L,40766L,40767L, -40768L,40769L,40770L,40771L,40772L,40773L,40774L,40775L,40776L,40777L, -40778L,40779L,40780L,40781L,40782L,40783L,40784L,40785L,40786L,40787L, -40788L,40789L,40790L,40791L,40792L,40793L,40794L,40795L,40796L,40797L, -40798L,40799L,40800L,40801L,40802L,40803L,40804L,40805L,40806L,40807L, -40808L,40809L,40810L,40811L,40812L,40813L,40814L,40815L,40816L,40817L, -40818L,40819L,40820L,40821L,40822L,40823L,40824L,40825L,40826L,40827L, -40828L,40829L,40830L,40831L,40832L,40833L,40834L,40835L,40836L,40837L, -40838L,40839L,40840L,40841L,40842L,40843L,40844L,40845L,40846L,40847L, -40848L,40849L,40850L,40851L,40852L,40853L,40854L,40855L,40856L,40857L, -40858L,40859L,40860L,40861L,40862L,40863L,40864L,40865L,40866L,40867L, -40868L,40869L,40870L,40871L,40872L,40873L,40874L,40875L,40876L,40877L, -40878L,40879L,40880L,40881L,40882L,40883L,40884L,40885L,40886L,40887L, -40888L,40889L,40890L,40891L,40892L,40893L,40894L,40895L,40896L,40897L, -40898L,40899L,40900L,40901L,40902L,40903L,40904L,40905L,40906L,40907L, -40908L,40909L,40910L,40911L,40912L,40913L,40914L,40915L,40916L,40917L, -40918L,40919L,40920L,40921L,40922L,40923L,40924L,40925L,40926L,40927L, -40928L,40929L,40930L,40931L,40932L,40933L,40934L,40935L,40936L,40937L, -40938L,40939L,40940L,40941L,40942L,40943L,40944L,40945L,40946L,40947L, -40948L,40949L,40950L,40951L,40952L,40953L,40954L,40955L,40956L,40957L, -40958L,40959L,40960L,40961L,40962L,40963L,40964L,40965L,40966L,40967L, -40968L,40969L,40970L,40971L,40972L,40973L,40974L,40975L,40976L,40977L, -40978L,40979L,40980L,40981L,40982L,40983L,40984L,40985L,40986L,40987L, -40988L,40989L,40990L,40991L,40992L,40993L,40994L,40995L,40996L,40997L, -40998L,40999L,41000L,41001L,41002L,41003L,41004L,41005L,41006L,41007L, -41008L,41009L,41010L,41011L,41012L,41013L,41014L,41015L,41016L,41017L, -41018L,41019L,41020L,41021L,41022L,41023L,41024L,41025L,41026L,41027L, -41028L,41029L,41030L,41031L,41032L,41033L,41034L,41035L,41036L,41037L, -41038L,41039L,41040L,41041L,41042L,41043L,41044L,41045L,41046L,41047L, -41048L,41049L,41050L,41051L,41052L,41053L,41054L,41055L,41056L,41057L, -41058L,41059L,41060L,41061L,41062L,41063L,41064L,41065L,41066L,41067L, -41068L,41069L,41070L,41071L,41072L,41073L,41074L,41075L,41076L,41077L, -41078L,41079L,41080L,41081L,41082L,41083L,41084L,41085L,41086L,41087L, -41088L,41089L,41090L,41091L,41092L,41093L,41094L,41095L,41096L,41097L, -41098L,41099L,41100L,41101L,41102L,41103L,41104L,41105L,41106L,41107L, -41108L,41109L,41110L,41111L,41112L,41113L,41114L,41115L,41116L,41117L, -41118L,41119L,41120L,41121L,41122L,41123L,41124L,41125L,41126L,41127L, -41128L,41129L,41130L,41131L,41132L,41133L,41134L,41135L,41136L,41137L, -41138L,41139L,41140L,41141L,41142L,41143L,41144L,41145L,41146L,41147L, -41148L,41149L,41150L,41151L,41152L,41153L,41154L,41155L,41156L,41157L, -41158L,41159L,41160L,41161L,41162L,41163L,41164L,41165L,41166L,41167L, -41168L,41169L,41170L,41171L,41172L,41173L,41174L,41175L,41176L,41177L, -41178L,41179L,41180L,41181L,41182L,41183L,41184L,41185L,41186L,41187L, -41188L,41189L,41190L,41191L,41192L,41193L,41194L,41195L,41196L,41197L, -41198L,41199L,41200L,41201L,41202L,41203L,41204L,41205L,41206L,41207L, -41208L,41209L,41210L,41211L,41212L,41213L,41214L,41215L,41216L,41217L, -41218L,41219L,41220L,41221L,41222L,41223L,41224L,41225L,41226L,41227L, -41228L,41229L,41230L,41231L,41232L,41233L,41234L,41235L,41236L,41237L, -41238L,41239L,41240L,41241L,41242L,41243L,41244L,41245L,41246L,41247L, -41248L,41249L,41250L,41251L,41252L,41253L,41254L,41255L,41256L,41257L, -41258L,41259L,41260L,41261L,41262L,41263L,41264L,41265L,41266L,41267L, -41268L,41269L,41270L,41271L,41272L,41273L,41274L,41275L,41276L,41277L, -41278L,41279L,41280L,41281L,41282L,41283L,41284L,41285L,41286L,41287L, -41288L,41289L,41290L,41291L,41292L,41293L,41294L,41295L,41296L,41297L, -41298L,41299L,41300L,41301L,41302L,41303L,41304L,41305L,41306L,41307L, -41308L,41309L,41310L,41311L,41312L,41313L,41314L,41315L,41316L,41317L, -41318L,41319L,41320L,41321L,41322L,41323L,41324L,41325L,41326L,41327L, -41328L,41329L,41330L,41331L,41332L,41333L,41334L,41335L,41336L,41337L, -41338L,41339L,41340L,41341L,41342L,41343L,41344L,41345L,41346L,41347L, -41348L,41349L,41350L,41351L,41352L,41353L,41354L,41355L,41356L,41357L, -41358L,41359L,41360L,41361L,41362L,41363L,41364L,41365L,41366L,41367L, -41368L,41369L,41370L,41371L,41372L,41373L,41374L,41375L,41376L,41377L, -41378L,41379L,41380L,41381L,41382L,41383L,41384L,41385L,41386L,41387L, -41388L,41389L,41390L,41391L,41392L,41393L,41394L,41395L,41396L,41397L, -41398L,41399L,41400L,41401L,41402L,41403L,41404L,41405L,41406L,41407L, -41408L,41409L,41410L,41411L,41412L,41413L,41414L,41415L,41416L,41417L, -41418L,41419L,41420L,41421L,41422L,41423L,41424L,41425L,41426L,41427L, -41428L,41429L,41430L,41431L,41432L,41433L,41434L,41435L,41436L,41437L, -41438L,41439L,41440L,41441L,41442L,41443L,41444L,41445L,41446L,41447L, -41448L,41449L,41450L,41451L,41452L,41453L,41454L,41455L,41456L,41457L, -41458L,41459L,41460L,41461L,41462L,41463L,41464L,41465L,41466L,41467L, -41468L,41469L,41470L,41471L,41472L,41473L,41474L,41475L,41476L,41477L, -41478L,41479L,41480L,41481L,41482L,41483L,41484L,41485L,41486L,41487L, -41488L,41489L,41490L,41491L,41492L,41493L,41494L,41495L,41496L,41497L, -41498L,41499L,41500L,41501L,41502L,41503L,41504L,41505L,41506L,41507L, -41508L,41509L,41510L,41511L,41512L,41513L,41514L,41515L,41516L,41517L, -41518L,41519L,41520L,41521L,41522L,41523L,41524L,41525L,41526L,41527L, -41528L,41529L,41530L,41531L,41532L,41533L,41534L,41535L,41536L,41537L, -41538L,41539L,41540L,41541L,41542L,41543L,41544L,41545L,41546L,41547L, -41548L,41549L,41550L,41551L,41552L,41553L,41554L,41555L,41556L,41557L, -41558L,41559L,41560L,41561L,41562L,41563L,41564L,41565L,41566L,41567L, -41568L,41569L,41570L,41571L,41572L,41573L,41574L,41575L,41576L,41577L, -41578L,41579L,41580L,41581L,41582L,41583L,41584L,41585L,41586L,41587L, -41588L,41589L,41590L,41591L,41592L,41593L,41594L,41595L,41596L,41597L, -41598L,41599L,41600L,41601L,41602L,41603L,41604L,41605L,41606L,41607L, -41608L,41609L,41610L,41611L,41612L,41613L,41614L,41615L,41616L,41617L, -41618L,41619L,41620L,41621L,41622L,41623L,41624L,41625L,41626L,41627L, -41628L,41629L,41630L,41631L,41632L,41633L,41634L,41635L,41636L,41637L, -41638L,41639L,41640L,41641L,41642L,41643L,41644L,41645L,41646L,41647L, -41648L,41649L,41650L,41651L,41652L,41653L,41654L,41655L,41656L,41657L, -41658L,41659L,41660L,41661L,41662L,41663L,41664L,41665L,41666L,41667L, -41668L,41669L,41670L,41671L,41672L,41673L,41674L,41675L,41676L,41677L, -41678L,41679L,41680L,41681L,41682L,41683L,41684L,41685L,41686L,41687L, -41688L,41689L,41690L,41691L,41692L,41693L,41694L,41695L,41696L,41697L, -41698L,41699L,41700L,41701L,41702L,41703L,41704L,41705L,41706L,41707L, -41708L,41709L,41710L,41711L,41712L,41713L,41714L,41715L,41716L,41717L, -41718L,41719L,41720L,41721L,41722L,41723L,41724L,41725L,41726L,41727L, -41728L,41729L,41730L,41731L,41732L,41733L,41734L,41735L,41736L,41737L, -41738L,41739L,41740L,41741L,41742L,41743L,41744L,41745L,41746L,41747L, -41748L,41749L,41750L,41751L,41752L,41753L,41754L,41755L,41756L,41757L, -41758L,41759L,41760L,41761L,41762L,41763L,41764L,41765L,41766L,41767L, -41768L,41769L,41770L,41771L,41772L,41773L,41774L,41775L,41776L,41777L, -41778L,41779L,41780L,41781L,41782L,41783L,41784L,41785L,41786L,41787L, -41788L,41789L,41790L,41791L,41792L,41793L,41794L,41795L,41796L,41797L, -41798L,41799L,41800L,41801L,41802L,41803L,41804L,41805L,41806L,41807L, -41808L,41809L,41810L,41811L,41812L,41813L,41814L,41815L,41816L,41817L, -41818L,41819L,41820L,41821L,41822L,41823L,41824L,41825L,41826L,41827L, -41828L,41829L,41830L,41831L,41832L,41833L,41834L,41835L,41836L,41837L, -41838L,41839L,41840L,41841L,41842L,41843L,41844L,41845L,41846L,41847L, -41848L,41849L,41850L,41851L,41852L,41853L,41854L,41855L,41856L,41857L, -41858L,41859L,41860L,41861L,41862L,41863L,41864L,41865L,41866L,41867L, -41868L,41869L,41870L,41871L,41872L,41873L,41874L,41875L,41876L,41877L, -41878L,41879L,41880L,41881L,41882L,41883L,41884L,41885L,41886L,41887L, -41888L,41889L,41890L,41891L,41892L,41893L,41894L,41895L,41896L,41897L, -41898L,41899L,41900L,41901L,41902L,41903L,41904L,41905L,41906L,41907L, -41908L,41909L,41910L,41911L,41912L,41913L,41914L,41915L,41916L,41917L, -41918L,41919L,41920L,41921L,41922L,41923L,41924L,41925L,41926L,41927L, -41928L,41929L,41930L,41931L,41932L,41933L,41934L,41935L,41936L,41937L, -41938L,41939L,41940L,41941L,41942L,41943L,41944L,41945L,41946L,41947L, -41948L,41949L,41950L,41951L,41952L,41953L,41954L,41955L,41956L,41957L, -41958L,41959L,41960L,41961L,41962L,41963L,41964L,41965L,41966L,41967L, -41968L,41969L,41970L,41971L,41972L,41973L,41974L,41975L,41976L,41977L, -41978L,41979L,41980L,41981L,41982L,41983L,41984L,41985L,41986L,41987L, -41988L,41989L,41990L,41991L,41992L,41993L,41994L,41995L,41996L,41997L, -41998L,41999L,42000L,42001L,42002L,42003L,42004L,42005L,42006L,42007L, -42008L,42009L,42010L,42011L,42012L,42013L,42014L,42015L,42016L,42017L, -42018L,42019L,42020L,42021L,42022L,42023L,42024L,42025L,42026L,42027L, -42028L,42029L,42030L,42031L,42032L,42033L,42034L,42035L,42036L,42037L, -42038L,42039L,42040L,42041L,42042L,42043L,42044L,42045L,42046L,42047L, -42048L,42049L,42050L,42051L,42052L,42053L,42054L,42055L,42056L,42057L, -42058L,42059L,42060L,42061L,42062L,42063L,42064L,42065L,42066L,42067L, -42068L,42069L,42070L,42071L,42072L,42073L,42074L,42075L,42076L,42077L, -42078L,42079L,42080L,42081L,42082L,42083L,42084L,42085L,42086L,42087L, -42088L,42089L,42090L,42091L,42092L,42093L,42094L,42095L,42096L,42097L, -42098L,42099L,42100L,42101L,42102L,42103L,42104L,42105L,42106L,42107L, -42108L,42109L,42110L,42111L,42112L,42113L,42114L,42115L,42116L,42117L, -42118L,42119L,42120L,42121L,42122L,42123L,42124L,42125L,42126L,42127L, -42128L,42129L,42130L,42131L,42132L,42133L,42134L,42135L,42136L,42137L, -42138L,42139L,42140L,42141L,42142L,42143L,42144L,42145L,42146L,42147L, -42148L,42149L,42150L,42151L,42152L,42153L,42154L,42155L,42156L,42157L, -42158L,42159L,42160L,42161L,42162L,42163L,42164L,42165L,42166L,42167L, -42168L,42169L,42170L,42171L,42172L,42173L,42174L,42175L,42176L,42177L, -42178L,42179L,42180L,42181L,42182L,42183L,42184L,42185L,42186L,42187L, -42188L,42189L,42190L,42191L,42192L,42193L,42194L,42195L,42196L,42197L, -42198L,42199L,42200L,42201L,42202L,42203L,42204L,42205L,42206L,42207L, -42208L,42209L,42210L,42211L,42212L,42213L,42214L,42215L,42216L,42217L, -42218L,42219L,42220L,42221L,42222L,42223L,42224L,42225L,42226L,42227L, -42228L,42229L,42230L,42231L,42232L,42233L,42234L,42235L,42236L,42237L, -42238L,42239L,42240L,42241L,42242L,42243L,42244L,42245L,42246L,42247L, -42248L,42249L,42250L,42251L,42252L,42253L,42254L,42255L,42256L,42257L, -42258L,42259L,42260L,42261L,42262L,42263L,42264L,42265L,42266L,42267L, -42268L,42269L,42270L,42271L,42272L,42273L,42274L,42275L,42276L,42277L, -42278L,42279L,42280L,42281L,42282L,42283L,42284L,42285L,42286L,42287L, -42288L,42289L,42290L,42291L,42292L,42293L,42294L,42295L,42296L,42297L, -42298L,42299L,42300L,42301L,42302L,42303L,42304L,42305L,42306L,42307L, -42308L,42309L,42310L,42311L,42312L,42313L,42314L,42315L,42316L,42317L, -42318L,42319L,42320L,42321L,42322L,42323L,42324L,42325L,42326L,42327L, -42328L,42329L,42330L,42331L,42332L,42333L,42334L,42335L,42336L,42337L, -42338L,42339L,42340L,42341L,42342L,42343L,42344L,42345L,42346L,42347L, -42348L,42349L,42350L,42351L,42352L,42353L,42354L,42355L,42356L,42357L, -42358L,42359L,42360L,42361L,42362L,42363L,42364L,42365L,42366L,42367L, -42368L,42369L,42370L,42371L,42372L,42373L,42374L,42375L,42376L,42377L, -42378L,42379L,42380L,42381L,42382L,42383L,42384L,42385L,42386L,42387L, -42388L,42389L,42390L,42391L,42392L,42393L,42394L,42395L,42396L,42397L, -42398L,42399L,42400L,42401L,42402L,42403L,42404L,42405L,42406L,42407L, -42408L,42409L,42410L,42411L,42412L,42413L,42414L,42415L,42416L,42417L, -42418L,42419L,42420L,42421L,42422L,42423L,42424L,42425L,42426L,42427L, -42428L,42429L,42430L,42431L,42432L,42433L,42434L,42435L,42436L,42437L, -42438L,42439L,42440L,42441L,42442L,42443L,42444L,42445L,42446L,42447L, -42448L,42449L,42450L,42451L,42452L,42453L,42454L,42455L,42456L,42457L, -42458L,42459L,42460L,42461L,42462L,42463L,42464L,42465L,42466L,42467L, -42468L,42469L,42470L,42471L,42472L,42473L,42474L,42475L,42476L,42477L, -42478L,42479L,42480L,42481L,42482L,42483L,42484L,42485L,42486L,42487L, -42488L,42489L,42490L,42491L,42492L,42493L,42494L,42495L,42496L,42497L, -42498L,42499L,42500L,42501L,42502L,42503L,42504L,42505L,42506L,42507L, -42508L,42509L,42510L,42511L,42512L,42513L,42514L,42515L,42516L,42517L, -42518L,42519L,42520L,42521L,42522L,42523L,42524L,42525L,42526L,42527L, -42528L,42529L,42530L,42531L,42532L,42533L,42534L,42535L,42536L,42537L, -42538L,42539L,42540L,42541L,42542L,42543L,42544L,42545L,42546L,42547L, -42548L,42549L,42550L,42551L,42552L,42553L,42554L,42555L,42556L,42557L, -42558L,42559L,42560L,42560L,42562L,42562L,42564L,42564L,42566L,42566L, -42568L,42568L,42570L,42570L,42572L,42572L,42574L,42574L,42576L,42576L, -42578L,42578L,42580L,42580L,42582L,42582L,42584L,42584L,42586L,42586L, -42588L,42588L,42590L,42590L,42592L,42592L,42594L,42594L,42596L,42596L, -42598L,42598L,42600L,42600L,42602L,42602L,42604L,42604L,42606L,42607L, -42608L,42609L,42610L,42611L,42612L,42613L,42614L,42615L,42616L,42617L, -42618L,42619L,42620L,42621L,42622L,42623L,42624L,42624L,42626L,42626L, -42628L,42628L,42630L,42630L,42632L,42632L,42634L,42634L,42636L,42636L, -42638L,42638L,42640L,42640L,42642L,42642L,42644L,42644L,42646L,42646L, -42648L,42648L,42650L,42650L,42652L,42653L,42654L,42655L,42656L,42657L, -42658L,42659L,42660L,42661L,42662L,42663L,42664L,42665L,42666L,42667L, -42668L,42669L,42670L,42671L,42672L,42673L,42674L,42675L,42676L,42677L, -42678L,42679L,42680L,42681L,42682L,42683L,42684L,42685L,42686L,42687L, -42688L,42689L,42690L,42691L,42692L,42693L,42694L,42695L,42696L,42697L, -42698L,42699L,42700L,42701L,42702L,42703L,42704L,42705L,42706L,42707L, -42708L,42709L,42710L,42711L,42712L,42713L,42714L,42715L,42716L,42717L, -42718L,42719L,42720L,42721L,42722L,42723L,42724L,42725L,42726L,42727L, -42728L,42729L,42730L,42731L,42732L,42733L,42734L,42735L,42736L,42737L, -42738L,42739L,42740L,42741L,42742L,42743L,42744L,42745L,42746L,42747L, -42748L,42749L,42750L,42751L,42752L,42753L,42754L,42755L,42756L,42757L, -42758L,42759L,42760L,42761L,42762L,42763L,42764L,42765L,42766L,42767L, -42768L,42769L,42770L,42771L,42772L,42773L,42774L,42775L,42776L,42777L, -42778L,42779L,42780L,42781L,42782L,42783L,42784L,42785L,42786L,42786L, -42788L,42788L,42790L,42790L,42792L,42792L,42794L,42794L,42796L,42796L, -42798L,42798L,42800L,42801L,42802L,42802L,42804L,42804L,42806L,42806L, -42808L,42808L,42810L,42810L,42812L,42812L,42814L,42814L,42816L,42816L, -42818L,42818L,42820L,42820L,42822L,42822L,42824L,42824L,42826L,42826L, -42828L,42828L,42830L,42830L,42832L,42832L,42834L,42834L,42836L,42836L, -42838L,42838L,42840L,42840L,42842L,42842L,42844L,42844L,42846L,42846L, -42848L,42848L,42850L,42850L,42852L,42852L,42854L,42854L,42856L,42856L, -42858L,42858L,42860L,42860L,42862L,42862L,42864L,42865L,42866L,42867L, -42868L,42869L,42870L,42871L,42872L,42873L,42873L,42875L,42875L,42877L, -42878L,42878L,42880L,42880L,42882L,42882L,42884L,42884L,42886L,42886L, -42888L,42889L,42890L,42891L,42891L,42893L,42894L,42895L,42896L,42896L, -42898L,42898L,42948L,42901L,42902L,42902L,42904L,42904L,42906L,42906L, -42908L,42908L,42910L,42910L,42912L,42912L,42914L,42914L,42916L,42916L, -42918L,42918L,42920L,42920L,42922L,42923L,42924L,42925L,42926L,42927L, -42928L,42929L,42930L,42931L,42932L,42932L,42934L,42934L,42936L,42936L, -42938L,42938L,42940L,42940L,42942L,42942L,42944L,42945L,42946L,42946L, -42948L,42949L,42950L,42951L,42952L,42953L,42954L,42955L,42956L,42957L, -42958L,42959L,42960L,42961L,42962L,42963L,42964L,42965L,42966L,42967L, -42968L,42969L,42970L,42971L,42972L,42973L,42974L,42975L,42976L,42977L, -42978L,42979L,42980L,42981L,42982L,42983L,42984L,42985L,42986L,42987L, -42988L,42989L,42990L,42991L,42992L,42993L,42994L,42995L,42996L,42997L, -42998L,42999L,43000L,43001L,43002L,43003L,43004L,43005L,43006L,43007L, -43008L,43009L,43010L,43011L,43012L,43013L,43014L,43015L,43016L,43017L, -43018L,43019L,43020L,43021L,43022L,43023L,43024L,43025L,43026L,43027L, -43028L,43029L,43030L,43031L,43032L,43033L,43034L,43035L,43036L,43037L, -43038L,43039L,43040L,43041L,43042L,43043L,43044L,43045L,43046L,43047L, -43048L,43049L,43050L,43051L,43052L,43053L,43054L,43055L,43056L,43057L, -43058L,43059L,43060L,43061L,43062L,43063L,43064L,43065L,43066L,43067L, -43068L,43069L,43070L,43071L,43072L,43073L,43074L,43075L,43076L,43077L, -43078L,43079L,43080L,43081L,43082L,43083L,43084L,43085L,43086L,43087L, -43088L,43089L,43090L,43091L,43092L,43093L,43094L,43095L,43096L,43097L, -43098L,43099L,43100L,43101L,43102L,43103L,43104L,43105L,43106L,43107L, -43108L,43109L,43110L,43111L,43112L,43113L,43114L,43115L,43116L,43117L, -43118L,43119L,43120L,43121L,43122L,43123L,43124L,43125L,43126L,43127L, -43128L,43129L,43130L,43131L,43132L,43133L,43134L,43135L,43136L,43137L, -43138L,43139L,43140L,43141L,43142L,43143L,43144L,43145L,43146L,43147L, -43148L,43149L,43150L,43151L,43152L,43153L,43154L,43155L,43156L,43157L, -43158L,43159L,43160L,43161L,43162L,43163L,43164L,43165L,43166L,43167L, -43168L,43169L,43170L,43171L,43172L,43173L,43174L,43175L,43176L,43177L, -43178L,43179L,43180L,43181L,43182L,43183L,43184L,43185L,43186L,43187L, -43188L,43189L,43190L,43191L,43192L,43193L,43194L,43195L,43196L,43197L, -43198L,43199L,43200L,43201L,43202L,43203L,43204L,43205L,43206L,43207L, -43208L,43209L,43210L,43211L,43212L,43213L,43214L,43215L,43216L,43217L, -43218L,43219L,43220L,43221L,43222L,43223L,43224L,43225L,43226L,43227L, -43228L,43229L,43230L,43231L,43232L,43233L,43234L,43235L,43236L,43237L, -43238L,43239L,43240L,43241L,43242L,43243L,43244L,43245L,43246L,43247L, -43248L,43249L,43250L,43251L,43252L,43253L,43254L,43255L,43256L,43257L, -43258L,43259L,43260L,43261L,43262L,43263L,43264L,43265L,43266L,43267L, -43268L,43269L,43270L,43271L,43272L,43273L,43274L,43275L,43276L,43277L, -43278L,43279L,43280L,43281L,43282L,43283L,43284L,43285L,43286L,43287L, -43288L,43289L,43290L,43291L,43292L,43293L,43294L,43295L,43296L,43297L, -43298L,43299L,43300L,43301L,43302L,43303L,43304L,43305L,43306L,43307L, -43308L,43309L,43310L,43311L,43312L,43313L,43314L,43315L,43316L,43317L, -43318L,43319L,43320L,43321L,43322L,43323L,43324L,43325L,43326L,43327L, -43328L,43329L,43330L,43331L,43332L,43333L,43334L,43335L,43336L,43337L, -43338L,43339L,43340L,43341L,43342L,43343L,43344L,43345L,43346L,43347L, -43348L,43349L,43350L,43351L,43352L,43353L,43354L,43355L,43356L,43357L, -43358L,43359L,43360L,43361L,43362L,43363L,43364L,43365L,43366L,43367L, -43368L,43369L,43370L,43371L,43372L,43373L,43374L,43375L,43376L,43377L, -43378L,43379L,43380L,43381L,43382L,43383L,43384L,43385L,43386L,43387L, -43388L,43389L,43390L,43391L,43392L,43393L,43394L,43395L,43396L,43397L, -43398L,43399L,43400L,43401L,43402L,43403L,43404L,43405L,43406L,43407L, -43408L,43409L,43410L,43411L,43412L,43413L,43414L,43415L,43416L,43417L, -43418L,43419L,43420L,43421L,43422L,43423L,43424L,43425L,43426L,43427L, -43428L,43429L,43430L,43431L,43432L,43433L,43434L,43435L,43436L,43437L, -43438L,43439L,43440L,43441L,43442L,43443L,43444L,43445L,43446L,43447L, -43448L,43449L,43450L,43451L,43452L,43453L,43454L,43455L,43456L,43457L, -43458L,43459L,43460L,43461L,43462L,43463L,43464L,43465L,43466L,43467L, -43468L,43469L,43470L,43471L,43472L,43473L,43474L,43475L,43476L,43477L, -43478L,43479L,43480L,43481L,43482L,43483L,43484L,43485L,43486L,43487L, -43488L,43489L,43490L,43491L,43492L,43493L,43494L,43495L,43496L,43497L, -43498L,43499L,43500L,43501L,43502L,43503L,43504L,43505L,43506L,43507L, -43508L,43509L,43510L,43511L,43512L,43513L,43514L,43515L,43516L,43517L, -43518L,43519L,43520L,43521L,43522L,43523L,43524L,43525L,43526L,43527L, -43528L,43529L,43530L,43531L,43532L,43533L,43534L,43535L,43536L,43537L, -43538L,43539L,43540L,43541L,43542L,43543L,43544L,43545L,43546L,43547L, -43548L,43549L,43550L,43551L,43552L,43553L,43554L,43555L,43556L,43557L, -43558L,43559L,43560L,43561L,43562L,43563L,43564L,43565L,43566L,43567L, -43568L,43569L,43570L,43571L,43572L,43573L,43574L,43575L,43576L,43577L, -43578L,43579L,43580L,43581L,43582L,43583L,43584L,43585L,43586L,43587L, -43588L,43589L,43590L,43591L,43592L,43593L,43594L,43595L,43596L,43597L, -43598L,43599L,43600L,43601L,43602L,43603L,43604L,43605L,43606L,43607L, -43608L,43609L,43610L,43611L,43612L,43613L,43614L,43615L,43616L,43617L, -43618L,43619L,43620L,43621L,43622L,43623L,43624L,43625L,43626L,43627L, -43628L,43629L,43630L,43631L,43632L,43633L,43634L,43635L,43636L,43637L, -43638L,43639L,43640L,43641L,43642L,43643L,43644L,43645L,43646L,43647L, -43648L,43649L,43650L,43651L,43652L,43653L,43654L,43655L,43656L,43657L, -43658L,43659L,43660L,43661L,43662L,43663L,43664L,43665L,43666L,43667L, -43668L,43669L,43670L,43671L,43672L,43673L,43674L,43675L,43676L,43677L, -43678L,43679L,43680L,43681L,43682L,43683L,43684L,43685L,43686L,43687L, -43688L,43689L,43690L,43691L,43692L,43693L,43694L,43695L,43696L,43697L, -43698L,43699L,43700L,43701L,43702L,43703L,43704L,43705L,43706L,43707L, -43708L,43709L,43710L,43711L,43712L,43713L,43714L,43715L,43716L,43717L, -43718L,43719L,43720L,43721L,43722L,43723L,43724L,43725L,43726L,43727L, -43728L,43729L,43730L,43731L,43732L,43733L,43734L,43735L,43736L,43737L, -43738L,43739L,43740L,43741L,43742L,43743L,43744L,43745L,43746L,43747L, -43748L,43749L,43750L,43751L,43752L,43753L,43754L,43755L,43756L,43757L, -43758L,43759L,43760L,43761L,43762L,43763L,43764L,43765L,43766L,43767L, -43768L,43769L,43770L,43771L,43772L,43773L,43774L,43775L,43776L,43777L, -43778L,43779L,43780L,43781L,43782L,43783L,43784L,43785L,43786L,43787L, -43788L,43789L,43790L,43791L,43792L,43793L,43794L,43795L,43796L,43797L, -43798L,43799L,43800L,43801L,43802L,43803L,43804L,43805L,43806L,43807L, -43808L,43809L,43810L,43811L,43812L,43813L,43814L,43815L,43816L,43817L, -43818L,43819L,43820L,43821L,43822L,43823L,43824L,43825L,43826L,43827L, -43828L,43829L,43830L,43831L,43832L,43833L,43834L,43835L,43836L,43837L, -43838L,43839L,43840L,43841L,43842L,43843L,43844L,43845L,43846L,43847L, -43848L,43849L,43850L,43851L,43852L,43853L,43854L,43855L,43856L,43857L, -43858L,42931L,43860L,43861L,43862L,43863L,43864L,43865L,43866L,43867L, -43868L,43869L,43870L,43871L,43872L,43873L,43874L,43875L,43876L,43877L, -43878L,43879L,43880L,43881L,43882L,43883L,43884L,43885L,43886L,43887L,5024, -5025,5026,5027,5028,5029,5030,5031,5032,5033,5034,5035,5036,5037,5038,5039, -5040,5041,5042,5043,5044,5045,5046,5047,5048,5049,5050,5051,5052,5053,5054, -5055,5056,5057,5058,5059,5060,5061,5062,5063,5064,5065,5066,5067,5068,5069, -5070,5071,5072,5073,5074,5075,5076,5077,5078,5079,5080,5081,5082,5083,5084, -5085,5086,5087,5088,5089,5090,5091,5092,5093,5094,5095,5096,5097,5098,5099, -5100,5101,5102,5103,43968L,43969L,43970L,43971L,43972L,43973L,43974L, -43975L,43976L,43977L,43978L,43979L,43980L,43981L,43982L,43983L,43984L, -43985L,43986L,43987L,43988L,43989L,43990L,43991L,43992L,43993L,43994L, -43995L,43996L,43997L,43998L,43999L,44000L,44001L,44002L,44003L,44004L, -44005L,44006L,44007L,44008L,44009L,44010L,44011L,44012L,44013L,44014L, -44015L,44016L,44017L,44018L,44019L,44020L,44021L,44022L,44023L,44024L, -44025L,44026L,44027L,44028L,44029L,44030L,44031L,44032L,44033L,44034L, -44035L,44036L,44037L,44038L,44039L,44040L,44041L,44042L,44043L,44044L, -44045L,44046L,44047L,44048L,44049L,44050L,44051L,44052L,44053L,44054L, -44055L,44056L,44057L,44058L,44059L,44060L,44061L,44062L,44063L,44064L, -44065L,44066L,44067L,44068L,44069L,44070L,44071L,44072L,44073L,44074L, -44075L,44076L,44077L,44078L,44079L,44080L,44081L,44082L,44083L,44084L, -44085L,44086L,44087L,44088L,44089L,44090L,44091L,44092L,44093L,44094L, -44095L,44096L,44097L,44098L,44099L,44100L,44101L,44102L,44103L,44104L, -44105L,44106L,44107L,44108L,44109L,44110L,44111L,44112L,44113L,44114L, -44115L,44116L,44117L,44118L,44119L,44120L,44121L,44122L,44123L,44124L, -44125L,44126L,44127L,44128L,44129L,44130L,44131L,44132L,44133L,44134L, -44135L,44136L,44137L,44138L,44139L,44140L,44141L,44142L,44143L,44144L, -44145L,44146L,44147L,44148L,44149L,44150L,44151L,44152L,44153L,44154L, -44155L,44156L,44157L,44158L,44159L,44160L,44161L,44162L,44163L,44164L, -44165L,44166L,44167L,44168L,44169L,44170L,44171L,44172L,44173L,44174L, -44175L,44176L,44177L,44178L,44179L,44180L,44181L,44182L,44183L,44184L, -44185L,44186L,44187L,44188L,44189L,44190L,44191L,44192L,44193L,44194L, -44195L,44196L,44197L,44198L,44199L,44200L,44201L,44202L,44203L,44204L, -44205L,44206L,44207L,44208L,44209L,44210L,44211L,44212L,44213L,44214L, -44215L,44216L,44217L,44218L,44219L,44220L,44221L,44222L,44223L,44224L, -44225L,44226L,44227L,44228L,44229L,44230L,44231L,44232L,44233L,44234L, -44235L,44236L,44237L,44238L,44239L,44240L,44241L,44242L,44243L,44244L, -44245L,44246L,44247L,44248L,44249L,44250L,44251L,44252L,44253L,44254L, -44255L,44256L,44257L,44258L,44259L,44260L,44261L,44262L,44263L,44264L, -44265L,44266L,44267L,44268L,44269L,44270L,44271L,44272L,44273L,44274L, -44275L,44276L,44277L,44278L,44279L,44280L,44281L,44282L,44283L,44284L, -44285L,44286L,44287L,44288L,44289L,44290L,44291L,44292L,44293L,44294L, -44295L,44296L,44297L,44298L,44299L,44300L,44301L,44302L,44303L,44304L, -44305L,44306L,44307L,44308L,44309L,44310L,44311L,44312L,44313L,44314L, -44315L,44316L,44317L,44318L,44319L,44320L,44321L,44322L,44323L,44324L, -44325L,44326L,44327L,44328L,44329L,44330L,44331L,44332L,44333L,44334L, -44335L,44336L,44337L,44338L,44339L,44340L,44341L,44342L,44343L,44344L, -44345L,44346L,44347L,44348L,44349L,44350L,44351L,44352L,44353L,44354L, -44355L,44356L,44357L,44358L,44359L,44360L,44361L,44362L,44363L,44364L, -44365L,44366L,44367L,44368L,44369L,44370L,44371L,44372L,44373L,44374L, -44375L,44376L,44377L,44378L,44379L,44380L,44381L,44382L,44383L,44384L, -44385L,44386L,44387L,44388L,44389L,44390L,44391L,44392L,44393L,44394L, -44395L,44396L,44397L,44398L,44399L,44400L,44401L,44402L,44403L,44404L, -44405L,44406L,44407L,44408L,44409L,44410L,44411L,44412L,44413L,44414L, -44415L,44416L,44417L,44418L,44419L,44420L,44421L,44422L,44423L,44424L, -44425L,44426L,44427L,44428L,44429L,44430L,44431L,44432L,44433L,44434L, -44435L,44436L,44437L,44438L,44439L,44440L,44441L,44442L,44443L,44444L, -44445L,44446L,44447L,44448L,44449L,44450L,44451L,44452L,44453L,44454L, -44455L,44456L,44457L,44458L,44459L,44460L,44461L,44462L,44463L,44464L, -44465L,44466L,44467L,44468L,44469L,44470L,44471L,44472L,44473L,44474L, -44475L,44476L,44477L,44478L,44479L,44480L,44481L,44482L,44483L,44484L, -44485L,44486L,44487L,44488L,44489L,44490L,44491L,44492L,44493L,44494L, -44495L,44496L,44497L,44498L,44499L,44500L,44501L,44502L,44503L,44504L, -44505L,44506L,44507L,44508L,44509L,44510L,44511L,44512L,44513L,44514L, -44515L,44516L,44517L,44518L,44519L,44520L,44521L,44522L,44523L,44524L, -44525L,44526L,44527L,44528L,44529L,44530L,44531L,44532L,44533L,44534L, -44535L,44536L,44537L,44538L,44539L,44540L,44541L,44542L,44543L,44544L, -44545L,44546L,44547L,44548L,44549L,44550L,44551L,44552L,44553L,44554L, -44555L,44556L,44557L,44558L,44559L,44560L,44561L,44562L,44563L,44564L, -44565L,44566L,44567L,44568L,44569L,44570L,44571L,44572L,44573L,44574L, -44575L,44576L,44577L,44578L,44579L,44580L,44581L,44582L,44583L,44584L, -44585L,44586L,44587L,44588L,44589L,44590L,44591L,44592L,44593L,44594L, -44595L,44596L,44597L,44598L,44599L,44600L,44601L,44602L,44603L,44604L, -44605L,44606L,44607L,44608L,44609L,44610L,44611L,44612L,44613L,44614L, -44615L,44616L,44617L,44618L,44619L,44620L,44621L,44622L,44623L,44624L, -44625L,44626L,44627L,44628L,44629L,44630L,44631L,44632L,44633L,44634L, -44635L,44636L,44637L,44638L,44639L,44640L,44641L,44642L,44643L,44644L, -44645L,44646L,44647L,44648L,44649L,44650L,44651L,44652L,44653L,44654L, -44655L,44656L,44657L,44658L,44659L,44660L,44661L,44662L,44663L,44664L, -44665L,44666L,44667L,44668L,44669L,44670L,44671L,44672L,44673L,44674L, -44675L,44676L,44677L,44678L,44679L,44680L,44681L,44682L,44683L,44684L, -44685L,44686L,44687L,44688L,44689L,44690L,44691L,44692L,44693L,44694L, -44695L,44696L,44697L,44698L,44699L,44700L,44701L,44702L,44703L,44704L, -44705L,44706L,44707L,44708L,44709L,44710L,44711L,44712L,44713L,44714L, -44715L,44716L,44717L,44718L,44719L,44720L,44721L,44722L,44723L,44724L, -44725L,44726L,44727L,44728L,44729L,44730L,44731L,44732L,44733L,44734L, -44735L,44736L,44737L,44738L,44739L,44740L,44741L,44742L,44743L,44744L, -44745L,44746L,44747L,44748L,44749L,44750L,44751L,44752L,44753L,44754L, -44755L,44756L,44757L,44758L,44759L,44760L,44761L,44762L,44763L,44764L, -44765L,44766L,44767L,44768L,44769L,44770L,44771L,44772L,44773L,44774L, -44775L,44776L,44777L,44778L,44779L,44780L,44781L,44782L,44783L,44784L, -44785L,44786L,44787L,44788L,44789L,44790L,44791L,44792L,44793L,44794L, -44795L,44796L,44797L,44798L,44799L,44800L,44801L,44802L,44803L,44804L, -44805L,44806L,44807L,44808L,44809L,44810L,44811L,44812L,44813L,44814L, -44815L,44816L,44817L,44818L,44819L,44820L,44821L,44822L,44823L,44824L, -44825L,44826L,44827L,44828L,44829L,44830L,44831L,44832L,44833L,44834L, -44835L,44836L,44837L,44838L,44839L,44840L,44841L,44842L,44843L,44844L, -44845L,44846L,44847L,44848L,44849L,44850L,44851L,44852L,44853L,44854L, -44855L,44856L,44857L,44858L,44859L,44860L,44861L,44862L,44863L,44864L, -44865L,44866L,44867L,44868L,44869L,44870L,44871L,44872L,44873L,44874L, -44875L,44876L,44877L,44878L,44879L,44880L,44881L,44882L,44883L,44884L, -44885L,44886L,44887L,44888L,44889L,44890L,44891L,44892L,44893L,44894L, -44895L,44896L,44897L,44898L,44899L,44900L,44901L,44902L,44903L,44904L, -44905L,44906L,44907L,44908L,44909L,44910L,44911L,44912L,44913L,44914L, -44915L,44916L,44917L,44918L,44919L,44920L,44921L,44922L,44923L,44924L, -44925L,44926L,44927L,44928L,44929L,44930L,44931L,44932L,44933L,44934L, -44935L,44936L,44937L,44938L,44939L,44940L,44941L,44942L,44943L,44944L, -44945L,44946L,44947L,44948L,44949L,44950L,44951L,44952L,44953L,44954L, -44955L,44956L,44957L,44958L,44959L,44960L,44961L,44962L,44963L,44964L, -44965L,44966L,44967L,44968L,44969L,44970L,44971L,44972L,44973L,44974L, -44975L,44976L,44977L,44978L,44979L,44980L,44981L,44982L,44983L,44984L, -44985L,44986L,44987L,44988L,44989L,44990L,44991L,44992L,44993L,44994L, -44995L,44996L,44997L,44998L,44999L,45000L,45001L,45002L,45003L,45004L, -45005L,45006L,45007L,45008L,45009L,45010L,45011L,45012L,45013L,45014L, -45015L,45016L,45017L,45018L,45019L,45020L,45021L,45022L,45023L,45024L, -45025L,45026L,45027L,45028L,45029L,45030L,45031L,45032L,45033L,45034L, -45035L,45036L,45037L,45038L,45039L,45040L,45041L,45042L,45043L,45044L, -45045L,45046L,45047L,45048L,45049L,45050L,45051L,45052L,45053L,45054L, -45055L,45056L,45057L,45058L,45059L,45060L,45061L,45062L,45063L,45064L, -45065L,45066L,45067L,45068L,45069L,45070L,45071L,45072L,45073L,45074L, -45075L,45076L,45077L,45078L,45079L,45080L,45081L,45082L,45083L,45084L, -45085L,45086L,45087L,45088L,45089L,45090L,45091L,45092L,45093L,45094L, -45095L,45096L,45097L,45098L,45099L,45100L,45101L,45102L,45103L,45104L, -45105L,45106L,45107L,45108L,45109L,45110L,45111L,45112L,45113L,45114L, -45115L,45116L,45117L,45118L,45119L,45120L,45121L,45122L,45123L,45124L, -45125L,45126L,45127L,45128L,45129L,45130L,45131L,45132L,45133L,45134L, -45135L,45136L,45137L,45138L,45139L,45140L,45141L,45142L,45143L,45144L, -45145L,45146L,45147L,45148L,45149L,45150L,45151L,45152L,45153L,45154L, -45155L,45156L,45157L,45158L,45159L,45160L,45161L,45162L,45163L,45164L, -45165L,45166L,45167L,45168L,45169L,45170L,45171L,45172L,45173L,45174L, -45175L,45176L,45177L,45178L,45179L,45180L,45181L,45182L,45183L,45184L, -45185L,45186L,45187L,45188L,45189L,45190L,45191L,45192L,45193L,45194L, -45195L,45196L,45197L,45198L,45199L,45200L,45201L,45202L,45203L,45204L, -45205L,45206L,45207L,45208L,45209L,45210L,45211L,45212L,45213L,45214L, -45215L,45216L,45217L,45218L,45219L,45220L,45221L,45222L,45223L,45224L, -45225L,45226L,45227L,45228L,45229L,45230L,45231L,45232L,45233L,45234L, -45235L,45236L,45237L,45238L,45239L,45240L,45241L,45242L,45243L,45244L, -45245L,45246L,45247L,45248L,45249L,45250L,45251L,45252L,45253L,45254L, -45255L,45256L,45257L,45258L,45259L,45260L,45261L,45262L,45263L,45264L, -45265L,45266L,45267L,45268L,45269L,45270L,45271L,45272L,45273L,45274L, -45275L,45276L,45277L,45278L,45279L,45280L,45281L,45282L,45283L,45284L, -45285L,45286L,45287L,45288L,45289L,45290L,45291L,45292L,45293L,45294L, -45295L,45296L,45297L,45298L,45299L,45300L,45301L,45302L,45303L,45304L, -45305L,45306L,45307L,45308L,45309L,45310L,45311L,45312L,45313L,45314L, -45315L,45316L,45317L,45318L,45319L,45320L,45321L,45322L,45323L,45324L, -45325L,45326L,45327L,45328L,45329L,45330L,45331L,45332L,45333L,45334L, -45335L,45336L,45337L,45338L,45339L,45340L,45341L,45342L,45343L,45344L, -45345L,45346L,45347L,45348L,45349L,45350L,45351L,45352L,45353L,45354L, -45355L,45356L,45357L,45358L,45359L,45360L,45361L,45362L,45363L,45364L, -45365L,45366L,45367L,45368L,45369L,45370L,45371L,45372L,45373L,45374L, -45375L,45376L,45377L,45378L,45379L,45380L,45381L,45382L,45383L,45384L, -45385L,45386L,45387L,45388L,45389L,45390L,45391L,45392L,45393L,45394L, -45395L,45396L,45397L,45398L,45399L,45400L,45401L,45402L,45403L,45404L, -45405L,45406L,45407L,45408L,45409L,45410L,45411L,45412L,45413L,45414L, -45415L,45416L,45417L,45418L,45419L,45420L,45421L,45422L,45423L,45424L, -45425L,45426L,45427L,45428L,45429L,45430L,45431L,45432L,45433L,45434L, -45435L,45436L,45437L,45438L,45439L,45440L,45441L,45442L,45443L,45444L, -45445L,45446L,45447L,45448L,45449L,45450L,45451L,45452L,45453L,45454L, -45455L,45456L,45457L,45458L,45459L,45460L,45461L,45462L,45463L,45464L, -45465L,45466L,45467L,45468L,45469L,45470L,45471L,45472L,45473L,45474L, -45475L,45476L,45477L,45478L,45479L,45480L,45481L,45482L,45483L,45484L, -45485L,45486L,45487L,45488L,45489L,45490L,45491L,45492L,45493L,45494L, -45495L,45496L,45497L,45498L,45499L,45500L,45501L,45502L,45503L,45504L, -45505L,45506L,45507L,45508L,45509L,45510L,45511L,45512L,45513L,45514L, -45515L,45516L,45517L,45518L,45519L,45520L,45521L,45522L,45523L,45524L, -45525L,45526L,45527L,45528L,45529L,45530L,45531L,45532L,45533L,45534L, -45535L,45536L,45537L,45538L,45539L,45540L,45541L,45542L,45543L,45544L, -45545L,45546L,45547L,45548L,45549L,45550L,45551L,45552L,45553L,45554L, -45555L,45556L,45557L,45558L,45559L,45560L,45561L,45562L,45563L,45564L, -45565L,45566L,45567L,45568L,45569L,45570L,45571L,45572L,45573L,45574L, -45575L,45576L,45577L,45578L,45579L,45580L,45581L,45582L,45583L,45584L, -45585L,45586L,45587L,45588L,45589L,45590L,45591L,45592L,45593L,45594L, -45595L,45596L,45597L,45598L,45599L,45600L,45601L,45602L,45603L,45604L, -45605L,45606L,45607L,45608L,45609L,45610L,45611L,45612L,45613L,45614L, -45615L,45616L,45617L,45618L,45619L,45620L,45621L,45622L,45623L,45624L, -45625L,45626L,45627L,45628L,45629L,45630L,45631L,45632L,45633L,45634L, -45635L,45636L,45637L,45638L,45639L,45640L,45641L,45642L,45643L,45644L, -45645L,45646L,45647L,45648L,45649L,45650L,45651L,45652L,45653L,45654L, -45655L,45656L,45657L,45658L,45659L,45660L,45661L,45662L,45663L,45664L, -45665L,45666L,45667L,45668L,45669L,45670L,45671L,45672L,45673L,45674L, -45675L,45676L,45677L,45678L,45679L,45680L,45681L,45682L,45683L,45684L, -45685L,45686L,45687L,45688L,45689L,45690L,45691L,45692L,45693L,45694L, -45695L,45696L,45697L,45698L,45699L,45700L,45701L,45702L,45703L,45704L, -45705L,45706L,45707L,45708L,45709L,45710L,45711L,45712L,45713L,45714L, -45715L,45716L,45717L,45718L,45719L,45720L,45721L,45722L,45723L,45724L, -45725L,45726L,45727L,45728L,45729L,45730L,45731L,45732L,45733L,45734L, -45735L,45736L,45737L,45738L,45739L,45740L,45741L,45742L,45743L,45744L, -45745L,45746L,45747L,45748L,45749L,45750L,45751L,45752L,45753L,45754L, -45755L,45756L,45757L,45758L,45759L,45760L,45761L,45762L,45763L,45764L, -45765L,45766L,45767L,45768L,45769L,45770L,45771L,45772L,45773L,45774L, -45775L,45776L,45777L,45778L,45779L,45780L,45781L,45782L,45783L,45784L, -45785L,45786L,45787L,45788L,45789L,45790L,45791L,45792L,45793L,45794L, -45795L,45796L,45797L,45798L,45799L,45800L,45801L,45802L,45803L,45804L, -45805L,45806L,45807L,45808L,45809L,45810L,45811L,45812L,45813L,45814L, -45815L,45816L,45817L,45818L,45819L,45820L,45821L,45822L,45823L,45824L, -45825L,45826L,45827L,45828L,45829L,45830L,45831L,45832L,45833L,45834L, -45835L,45836L,45837L,45838L,45839L,45840L,45841L,45842L,45843L,45844L, -45845L,45846L,45847L,45848L,45849L,45850L,45851L,45852L,45853L,45854L, -45855L,45856L,45857L,45858L,45859L,45860L,45861L,45862L,45863L,45864L, -45865L,45866L,45867L,45868L,45869L,45870L,45871L,45872L,45873L,45874L, -45875L,45876L,45877L,45878L,45879L,45880L,45881L,45882L,45883L,45884L, -45885L,45886L,45887L,45888L,45889L,45890L,45891L,45892L,45893L,45894L, -45895L,45896L,45897L,45898L,45899L,45900L,45901L,45902L,45903L,45904L, -45905L,45906L,45907L,45908L,45909L,45910L,45911L,45912L,45913L,45914L, -45915L,45916L,45917L,45918L,45919L,45920L,45921L,45922L,45923L,45924L, -45925L,45926L,45927L,45928L,45929L,45930L,45931L,45932L,45933L,45934L, -45935L,45936L,45937L,45938L,45939L,45940L,45941L,45942L,45943L,45944L, -45945L,45946L,45947L,45948L,45949L,45950L,45951L,45952L,45953L,45954L, -45955L,45956L,45957L,45958L,45959L,45960L,45961L,45962L,45963L,45964L, -45965L,45966L,45967L,45968L,45969L,45970L,45971L,45972L,45973L,45974L, -45975L,45976L,45977L,45978L,45979L,45980L,45981L,45982L,45983L,45984L, -45985L,45986L,45987L,45988L,45989L,45990L,45991L,45992L,45993L,45994L, -45995L,45996L,45997L,45998L,45999L,46000L,46001L,46002L,46003L,46004L, -46005L,46006L,46007L,46008L,46009L,46010L,46011L,46012L,46013L,46014L, -46015L,46016L,46017L,46018L,46019L,46020L,46021L,46022L,46023L,46024L, -46025L,46026L,46027L,46028L,46029L,46030L,46031L,46032L,46033L,46034L, -46035L,46036L,46037L,46038L,46039L,46040L,46041L,46042L,46043L,46044L, -46045L,46046L,46047L,46048L,46049L,46050L,46051L,46052L,46053L,46054L, -46055L,46056L,46057L,46058L,46059L,46060L,46061L,46062L,46063L,46064L, -46065L,46066L,46067L,46068L,46069L,46070L,46071L,46072L,46073L,46074L, -46075L,46076L,46077L,46078L,46079L,46080L,46081L,46082L,46083L,46084L, -46085L,46086L,46087L,46088L,46089L,46090L,46091L,46092L,46093L,46094L, -46095L,46096L,46097L,46098L,46099L,46100L,46101L,46102L,46103L,46104L, -46105L,46106L,46107L,46108L,46109L,46110L,46111L,46112L,46113L,46114L, -46115L,46116L,46117L,46118L,46119L,46120L,46121L,46122L,46123L,46124L, -46125L,46126L,46127L,46128L,46129L,46130L,46131L,46132L,46133L,46134L, -46135L,46136L,46137L,46138L,46139L,46140L,46141L,46142L,46143L,46144L, -46145L,46146L,46147L,46148L,46149L,46150L,46151L,46152L,46153L,46154L, -46155L,46156L,46157L,46158L,46159L,46160L,46161L,46162L,46163L,46164L, -46165L,46166L,46167L,46168L,46169L,46170L,46171L,46172L,46173L,46174L, -46175L,46176L,46177L,46178L,46179L,46180L,46181L,46182L,46183L,46184L, -46185L,46186L,46187L,46188L,46189L,46190L,46191L,46192L,46193L,46194L, -46195L,46196L,46197L,46198L,46199L,46200L,46201L,46202L,46203L,46204L, -46205L,46206L,46207L,46208L,46209L,46210L,46211L,46212L,46213L,46214L, -46215L,46216L,46217L,46218L,46219L,46220L,46221L,46222L,46223L,46224L, -46225L,46226L,46227L,46228L,46229L,46230L,46231L,46232L,46233L,46234L, -46235L,46236L,46237L,46238L,46239L,46240L,46241L,46242L,46243L,46244L, -46245L,46246L,46247L,46248L,46249L,46250L,46251L,46252L,46253L,46254L, -46255L,46256L,46257L,46258L,46259L,46260L,46261L,46262L,46263L,46264L, -46265L,46266L,46267L,46268L,46269L,46270L,46271L,46272L,46273L,46274L, -46275L,46276L,46277L,46278L,46279L,46280L,46281L,46282L,46283L,46284L, -46285L,46286L,46287L,46288L,46289L,46290L,46291L,46292L,46293L,46294L, -46295L,46296L,46297L,46298L,46299L,46300L,46301L,46302L,46303L,46304L, -46305L,46306L,46307L,46308L,46309L,46310L,46311L,46312L,46313L,46314L, -46315L,46316L,46317L,46318L,46319L,46320L,46321L,46322L,46323L,46324L, -46325L,46326L,46327L,46328L,46329L,46330L,46331L,46332L,46333L,46334L, -46335L,46336L,46337L,46338L,46339L,46340L,46341L,46342L,46343L,46344L, -46345L,46346L,46347L,46348L,46349L,46350L,46351L,46352L,46353L,46354L, -46355L,46356L,46357L,46358L,46359L,46360L,46361L,46362L,46363L,46364L, -46365L,46366L,46367L,46368L,46369L,46370L,46371L,46372L,46373L,46374L, -46375L,46376L,46377L,46378L,46379L,46380L,46381L,46382L,46383L,46384L, -46385L,46386L,46387L,46388L,46389L,46390L,46391L,46392L,46393L,46394L, -46395L,46396L,46397L,46398L,46399L,46400L,46401L,46402L,46403L,46404L, -46405L,46406L,46407L,46408L,46409L,46410L,46411L,46412L,46413L,46414L, -46415L,46416L,46417L,46418L,46419L,46420L,46421L,46422L,46423L,46424L, -46425L,46426L,46427L,46428L,46429L,46430L,46431L,46432L,46433L,46434L, -46435L,46436L,46437L,46438L,46439L,46440L,46441L,46442L,46443L,46444L, -46445L,46446L,46447L,46448L,46449L,46450L,46451L,46452L,46453L,46454L, -46455L,46456L,46457L,46458L,46459L,46460L,46461L,46462L,46463L,46464L, -46465L,46466L,46467L,46468L,46469L,46470L,46471L,46472L,46473L,46474L, -46475L,46476L,46477L,46478L,46479L,46480L,46481L,46482L,46483L,46484L, -46485L,46486L,46487L,46488L,46489L,46490L,46491L,46492L,46493L,46494L, -46495L,46496L,46497L,46498L,46499L,46500L,46501L,46502L,46503L,46504L, -46505L,46506L,46507L,46508L,46509L,46510L,46511L,46512L,46513L,46514L, -46515L,46516L,46517L,46518L,46519L,46520L,46521L,46522L,46523L,46524L, -46525L,46526L,46527L,46528L,46529L,46530L,46531L,46532L,46533L,46534L, -46535L,46536L,46537L,46538L,46539L,46540L,46541L,46542L,46543L,46544L, -46545L,46546L,46547L,46548L,46549L,46550L,46551L,46552L,46553L,46554L, -46555L,46556L,46557L,46558L,46559L,46560L,46561L,46562L,46563L,46564L, -46565L,46566L,46567L,46568L,46569L,46570L,46571L,46572L,46573L,46574L, -46575L,46576L,46577L,46578L,46579L,46580L,46581L,46582L,46583L,46584L, -46585L,46586L,46587L,46588L,46589L,46590L,46591L,46592L,46593L,46594L, -46595L,46596L,46597L,46598L,46599L,46600L,46601L,46602L,46603L,46604L, -46605L,46606L,46607L,46608L,46609L,46610L,46611L,46612L,46613L,46614L, -46615L,46616L,46617L,46618L,46619L,46620L,46621L,46622L,46623L,46624L, -46625L,46626L,46627L,46628L,46629L,46630L,46631L,46632L,46633L,46634L, -46635L,46636L,46637L,46638L,46639L,46640L,46641L,46642L,46643L,46644L, -46645L,46646L,46647L,46648L,46649L,46650L,46651L,46652L,46653L,46654L, -46655L,46656L,46657L,46658L,46659L,46660L,46661L,46662L,46663L,46664L, -46665L,46666L,46667L,46668L,46669L,46670L,46671L,46672L,46673L,46674L, -46675L,46676L,46677L,46678L,46679L,46680L,46681L,46682L,46683L,46684L, -46685L,46686L,46687L,46688L,46689L,46690L,46691L,46692L,46693L,46694L, -46695L,46696L,46697L,46698L,46699L,46700L,46701L,46702L,46703L,46704L, -46705L,46706L,46707L,46708L,46709L,46710L,46711L,46712L,46713L,46714L, -46715L,46716L,46717L,46718L,46719L,46720L,46721L,46722L,46723L,46724L, -46725L,46726L,46727L,46728L,46729L,46730L,46731L,46732L,46733L,46734L, -46735L,46736L,46737L,46738L,46739L,46740L,46741L,46742L,46743L,46744L, -46745L,46746L,46747L,46748L,46749L,46750L,46751L,46752L,46753L,46754L, -46755L,46756L,46757L,46758L,46759L,46760L,46761L,46762L,46763L,46764L, -46765L,46766L,46767L,46768L,46769L,46770L,46771L,46772L,46773L,46774L, -46775L,46776L,46777L,46778L,46779L,46780L,46781L,46782L,46783L,46784L, -46785L,46786L,46787L,46788L,46789L,46790L,46791L,46792L,46793L,46794L, -46795L,46796L,46797L,46798L,46799L,46800L,46801L,46802L,46803L,46804L, -46805L,46806L,46807L,46808L,46809L,46810L,46811L,46812L,46813L,46814L, -46815L,46816L,46817L,46818L,46819L,46820L,46821L,46822L,46823L,46824L, -46825L,46826L,46827L,46828L,46829L,46830L,46831L,46832L,46833L,46834L, -46835L,46836L,46837L,46838L,46839L,46840L,46841L,46842L,46843L,46844L, -46845L,46846L,46847L,46848L,46849L,46850L,46851L,46852L,46853L,46854L, -46855L,46856L,46857L,46858L,46859L,46860L,46861L,46862L,46863L,46864L, -46865L,46866L,46867L,46868L,46869L,46870L,46871L,46872L,46873L,46874L, -46875L,46876L,46877L,46878L,46879L,46880L,46881L,46882L,46883L,46884L, -46885L,46886L,46887L,46888L,46889L,46890L,46891L,46892L,46893L,46894L, -46895L,46896L,46897L,46898L,46899L,46900L,46901L,46902L,46903L,46904L, -46905L,46906L,46907L,46908L,46909L,46910L,46911L,46912L,46913L,46914L, -46915L,46916L,46917L,46918L,46919L,46920L,46921L,46922L,46923L,46924L, -46925L,46926L,46927L,46928L,46929L,46930L,46931L,46932L,46933L,46934L, -46935L,46936L,46937L,46938L,46939L,46940L,46941L,46942L,46943L,46944L, -46945L,46946L,46947L,46948L,46949L,46950L,46951L,46952L,46953L,46954L, -46955L,46956L,46957L,46958L,46959L,46960L,46961L,46962L,46963L,46964L, -46965L,46966L,46967L,46968L,46969L,46970L,46971L,46972L,46973L,46974L, -46975L,46976L,46977L,46978L,46979L,46980L,46981L,46982L,46983L,46984L, -46985L,46986L,46987L,46988L,46989L,46990L,46991L,46992L,46993L,46994L, -46995L,46996L,46997L,46998L,46999L,47000L,47001L,47002L,47003L,47004L, -47005L,47006L,47007L,47008L,47009L,47010L,47011L,47012L,47013L,47014L, -47015L,47016L,47017L,47018L,47019L,47020L,47021L,47022L,47023L,47024L, -47025L,47026L,47027L,47028L,47029L,47030L,47031L,47032L,47033L,47034L, -47035L,47036L,47037L,47038L,47039L,47040L,47041L,47042L,47043L,47044L, -47045L,47046L,47047L,47048L,47049L,47050L,47051L,47052L,47053L,47054L, -47055L,47056L,47057L,47058L,47059L,47060L,47061L,47062L,47063L,47064L, -47065L,47066L,47067L,47068L,47069L,47070L,47071L,47072L,47073L,47074L, -47075L,47076L,47077L,47078L,47079L,47080L,47081L,47082L,47083L,47084L, -47085L,47086L,47087L,47088L,47089L,47090L,47091L,47092L,47093L,47094L, -47095L,47096L,47097L,47098L,47099L,47100L,47101L,47102L,47103L,47104L, -47105L,47106L,47107L,47108L,47109L,47110L,47111L,47112L,47113L,47114L, -47115L,47116L,47117L,47118L,47119L,47120L,47121L,47122L,47123L,47124L, -47125L,47126L,47127L,47128L,47129L,47130L,47131L,47132L,47133L,47134L, -47135L,47136L,47137L,47138L,47139L,47140L,47141L,47142L,47143L,47144L, -47145L,47146L,47147L,47148L,47149L,47150L,47151L,47152L,47153L,47154L, -47155L,47156L,47157L,47158L,47159L,47160L,47161L,47162L,47163L,47164L, -47165L,47166L,47167L,47168L,47169L,47170L,47171L,47172L,47173L,47174L, -47175L,47176L,47177L,47178L,47179L,47180L,47181L,47182L,47183L,47184L, -47185L,47186L,47187L,47188L,47189L,47190L,47191L,47192L,47193L,47194L, -47195L,47196L,47197L,47198L,47199L,47200L,47201L,47202L,47203L,47204L, -47205L,47206L,47207L,47208L,47209L,47210L,47211L,47212L,47213L,47214L, -47215L,47216L,47217L,47218L,47219L,47220L,47221L,47222L,47223L,47224L, -47225L,47226L,47227L,47228L,47229L,47230L,47231L,47232L,47233L,47234L, -47235L,47236L,47237L,47238L,47239L,47240L,47241L,47242L,47243L,47244L, -47245L,47246L,47247L,47248L,47249L,47250L,47251L,47252L,47253L,47254L, -47255L,47256L,47257L,47258L,47259L,47260L,47261L,47262L,47263L,47264L, -47265L,47266L,47267L,47268L,47269L,47270L,47271L,47272L,47273L,47274L, -47275L,47276L,47277L,47278L,47279L,47280L,47281L,47282L,47283L,47284L, -47285L,47286L,47287L,47288L,47289L,47290L,47291L,47292L,47293L,47294L, -47295L,47296L,47297L,47298L,47299L,47300L,47301L,47302L,47303L,47304L, -47305L,47306L,47307L,47308L,47309L,47310L,47311L,47312L,47313L,47314L, -47315L,47316L,47317L,47318L,47319L,47320L,47321L,47322L,47323L,47324L, -47325L,47326L,47327L,47328L,47329L,47330L,47331L,47332L,47333L,47334L, -47335L,47336L,47337L,47338L,47339L,47340L,47341L,47342L,47343L,47344L, -47345L,47346L,47347L,47348L,47349L,47350L,47351L,47352L,47353L,47354L, -47355L,47356L,47357L,47358L,47359L,47360L,47361L,47362L,47363L,47364L, -47365L,47366L,47367L,47368L,47369L,47370L,47371L,47372L,47373L,47374L, -47375L,47376L,47377L,47378L,47379L,47380L,47381L,47382L,47383L,47384L, -47385L,47386L,47387L,47388L,47389L,47390L,47391L,47392L,47393L,47394L, -47395L,47396L,47397L,47398L,47399L,47400L,47401L,47402L,47403L,47404L, -47405L,47406L,47407L,47408L,47409L,47410L,47411L,47412L,47413L,47414L, -47415L,47416L,47417L,47418L,47419L,47420L,47421L,47422L,47423L,47424L, -47425L,47426L,47427L,47428L,47429L,47430L,47431L,47432L,47433L,47434L, -47435L,47436L,47437L,47438L,47439L,47440L,47441L,47442L,47443L,47444L, -47445L,47446L,47447L,47448L,47449L,47450L,47451L,47452L,47453L,47454L, -47455L,47456L,47457L,47458L,47459L,47460L,47461L,47462L,47463L,47464L, -47465L,47466L,47467L,47468L,47469L,47470L,47471L,47472L,47473L,47474L, -47475L,47476L,47477L,47478L,47479L,47480L,47481L,47482L,47483L,47484L, -47485L,47486L,47487L,47488L,47489L,47490L,47491L,47492L,47493L,47494L, -47495L,47496L,47497L,47498L,47499L,47500L,47501L,47502L,47503L,47504L, -47505L,47506L,47507L,47508L,47509L,47510L,47511L,47512L,47513L,47514L, -47515L,47516L,47517L,47518L,47519L,47520L,47521L,47522L,47523L,47524L, -47525L,47526L,47527L,47528L,47529L,47530L,47531L,47532L,47533L,47534L, -47535L,47536L,47537L,47538L,47539L,47540L,47541L,47542L,47543L,47544L, -47545L,47546L,47547L,47548L,47549L,47550L,47551L,47552L,47553L,47554L, -47555L,47556L,47557L,47558L,47559L,47560L,47561L,47562L,47563L,47564L, -47565L,47566L,47567L,47568L,47569L,47570L,47571L,47572L,47573L,47574L, -47575L,47576L,47577L,47578L,47579L,47580L,47581L,47582L,47583L,47584L, -47585L,47586L,47587L,47588L,47589L,47590L,47591L,47592L,47593L,47594L, -47595L,47596L,47597L,47598L,47599L,47600L,47601L,47602L,47603L,47604L, -47605L,47606L,47607L,47608L,47609L,47610L,47611L,47612L,47613L,47614L, -47615L,47616L,47617L,47618L,47619L,47620L,47621L,47622L,47623L,47624L, -47625L,47626L,47627L,47628L,47629L,47630L,47631L,47632L,47633L,47634L, -47635L,47636L,47637L,47638L,47639L,47640L,47641L,47642L,47643L,47644L, -47645L,47646L,47647L,47648L,47649L,47650L,47651L,47652L,47653L,47654L, -47655L,47656L,47657L,47658L,47659L,47660L,47661L,47662L,47663L,47664L, -47665L,47666L,47667L,47668L,47669L,47670L,47671L,47672L,47673L,47674L, -47675L,47676L,47677L,47678L,47679L,47680L,47681L,47682L,47683L,47684L, -47685L,47686L,47687L,47688L,47689L,47690L,47691L,47692L,47693L,47694L, -47695L,47696L,47697L,47698L,47699L,47700L,47701L,47702L,47703L,47704L, -47705L,47706L,47707L,47708L,47709L,47710L,47711L,47712L,47713L,47714L, -47715L,47716L,47717L,47718L,47719L,47720L,47721L,47722L,47723L,47724L, -47725L,47726L,47727L,47728L,47729L,47730L,47731L,47732L,47733L,47734L, -47735L,47736L,47737L,47738L,47739L,47740L,47741L,47742L,47743L,47744L, -47745L,47746L,47747L,47748L,47749L,47750L,47751L,47752L,47753L,47754L, -47755L,47756L,47757L,47758L,47759L,47760L,47761L,47762L,47763L,47764L, -47765L,47766L,47767L,47768L,47769L,47770L,47771L,47772L,47773L,47774L, -47775L,47776L,47777L,47778L,47779L,47780L,47781L,47782L,47783L,47784L, -47785L,47786L,47787L,47788L,47789L,47790L,47791L,47792L,47793L,47794L, -47795L,47796L,47797L,47798L,47799L,47800L,47801L,47802L,47803L,47804L, -47805L,47806L,47807L,47808L,47809L,47810L,47811L,47812L,47813L,47814L, -47815L,47816L,47817L,47818L,47819L,47820L,47821L,47822L,47823L,47824L, -47825L,47826L,47827L,47828L,47829L,47830L,47831L,47832L,47833L,47834L, -47835L,47836L,47837L,47838L,47839L,47840L,47841L,47842L,47843L,47844L, -47845L,47846L,47847L,47848L,47849L,47850L,47851L,47852L,47853L,47854L, -47855L,47856L,47857L,47858L,47859L,47860L,47861L,47862L,47863L,47864L, -47865L,47866L,47867L,47868L,47869L,47870L,47871L,47872L,47873L,47874L, -47875L,47876L,47877L,47878L,47879L,47880L,47881L,47882L,47883L,47884L, -47885L,47886L,47887L,47888L,47889L,47890L,47891L,47892L,47893L,47894L, -47895L,47896L,47897L,47898L,47899L,47900L,47901L,47902L,47903L,47904L, -47905L,47906L,47907L,47908L,47909L,47910L,47911L,47912L,47913L,47914L, -47915L,47916L,47917L,47918L,47919L,47920L,47921L,47922L,47923L,47924L, -47925L,47926L,47927L,47928L,47929L,47930L,47931L,47932L,47933L,47934L, -47935L,47936L,47937L,47938L,47939L,47940L,47941L,47942L,47943L,47944L, -47945L,47946L,47947L,47948L,47949L,47950L,47951L,47952L,47953L,47954L, -47955L,47956L,47957L,47958L,47959L,47960L,47961L,47962L,47963L,47964L, -47965L,47966L,47967L,47968L,47969L,47970L,47971L,47972L,47973L,47974L, -47975L,47976L,47977L,47978L,47979L,47980L,47981L,47982L,47983L,47984L, -47985L,47986L,47987L,47988L,47989L,47990L,47991L,47992L,47993L,47994L, -47995L,47996L,47997L,47998L,47999L,48000L,48001L,48002L,48003L,48004L, -48005L,48006L,48007L,48008L,48009L,48010L,48011L,48012L,48013L,48014L, -48015L,48016L,48017L,48018L,48019L,48020L,48021L,48022L,48023L,48024L, -48025L,48026L,48027L,48028L,48029L,48030L,48031L,48032L,48033L,48034L, -48035L,48036L,48037L,48038L,48039L,48040L,48041L,48042L,48043L,48044L, -48045L,48046L,48047L,48048L,48049L,48050L,48051L,48052L,48053L,48054L, -48055L,48056L,48057L,48058L,48059L,48060L,48061L,48062L,48063L,48064L, -48065L,48066L,48067L,48068L,48069L,48070L,48071L,48072L,48073L,48074L, -48075L,48076L,48077L,48078L,48079L,48080L,48081L,48082L,48083L,48084L, -48085L,48086L,48087L,48088L,48089L,48090L,48091L,48092L,48093L,48094L, -48095L,48096L,48097L,48098L,48099L,48100L,48101L,48102L,48103L,48104L, -48105L,48106L,48107L,48108L,48109L,48110L,48111L,48112L,48113L,48114L, -48115L,48116L,48117L,48118L,48119L,48120L,48121L,48122L,48123L,48124L, -48125L,48126L,48127L,48128L,48129L,48130L,48131L,48132L,48133L,48134L, -48135L,48136L,48137L,48138L,48139L,48140L,48141L,48142L,48143L,48144L, -48145L,48146L,48147L,48148L,48149L,48150L,48151L,48152L,48153L,48154L, -48155L,48156L,48157L,48158L,48159L,48160L,48161L,48162L,48163L,48164L, -48165L,48166L,48167L,48168L,48169L,48170L,48171L,48172L,48173L,48174L, -48175L,48176L,48177L,48178L,48179L,48180L,48181L,48182L,48183L,48184L, -48185L,48186L,48187L,48188L,48189L,48190L,48191L,48192L,48193L,48194L, -48195L,48196L,48197L,48198L,48199L,48200L,48201L,48202L,48203L,48204L, -48205L,48206L,48207L,48208L,48209L,48210L,48211L,48212L,48213L,48214L, -48215L,48216L,48217L,48218L,48219L,48220L,48221L,48222L,48223L,48224L, -48225L,48226L,48227L,48228L,48229L,48230L,48231L,48232L,48233L,48234L, -48235L,48236L,48237L,48238L,48239L,48240L,48241L,48242L,48243L,48244L, -48245L,48246L,48247L,48248L,48249L,48250L,48251L,48252L,48253L,48254L, -48255L,48256L,48257L,48258L,48259L,48260L,48261L,48262L,48263L,48264L, -48265L,48266L,48267L,48268L,48269L,48270L,48271L,48272L,48273L,48274L, -48275L,48276L,48277L,48278L,48279L,48280L,48281L,48282L,48283L,48284L, -48285L,48286L,48287L,48288L,48289L,48290L,48291L,48292L,48293L,48294L, -48295L,48296L,48297L,48298L,48299L,48300L,48301L,48302L,48303L,48304L, -48305L,48306L,48307L,48308L,48309L,48310L,48311L,48312L,48313L,48314L, -48315L,48316L,48317L,48318L,48319L,48320L,48321L,48322L,48323L,48324L, -48325L,48326L,48327L,48328L,48329L,48330L,48331L,48332L,48333L,48334L, -48335L,48336L,48337L,48338L,48339L,48340L,48341L,48342L,48343L,48344L, -48345L,48346L,48347L,48348L,48349L,48350L,48351L,48352L,48353L,48354L, -48355L,48356L,48357L,48358L,48359L,48360L,48361L,48362L,48363L,48364L, -48365L,48366L,48367L,48368L,48369L,48370L,48371L,48372L,48373L,48374L, -48375L,48376L,48377L,48378L,48379L,48380L,48381L,48382L,48383L,48384L, -48385L,48386L,48387L,48388L,48389L,48390L,48391L,48392L,48393L,48394L, -48395L,48396L,48397L,48398L,48399L,48400L,48401L,48402L,48403L,48404L, -48405L,48406L,48407L,48408L,48409L,48410L,48411L,48412L,48413L,48414L, -48415L,48416L,48417L,48418L,48419L,48420L,48421L,48422L,48423L,48424L, -48425L,48426L,48427L,48428L,48429L,48430L,48431L,48432L,48433L,48434L, -48435L,48436L,48437L,48438L,48439L,48440L,48441L,48442L,48443L,48444L, -48445L,48446L,48447L,48448L,48449L,48450L,48451L,48452L,48453L,48454L, -48455L,48456L,48457L,48458L,48459L,48460L,48461L,48462L,48463L,48464L, -48465L,48466L,48467L,48468L,48469L,48470L,48471L,48472L,48473L,48474L, -48475L,48476L,48477L,48478L,48479L,48480L,48481L,48482L,48483L,48484L, -48485L,48486L,48487L,48488L,48489L,48490L,48491L,48492L,48493L,48494L, -48495L,48496L,48497L,48498L,48499L,48500L,48501L,48502L,48503L,48504L, -48505L,48506L,48507L,48508L,48509L,48510L,48511L,48512L,48513L,48514L, -48515L,48516L,48517L,48518L,48519L,48520L,48521L,48522L,48523L,48524L, -48525L,48526L,48527L,48528L,48529L,48530L,48531L,48532L,48533L,48534L, -48535L,48536L,48537L,48538L,48539L,48540L,48541L,48542L,48543L,48544L, -48545L,48546L,48547L,48548L,48549L,48550L,48551L,48552L,48553L,48554L, -48555L,48556L,48557L,48558L,48559L,48560L,48561L,48562L,48563L,48564L, -48565L,48566L,48567L,48568L,48569L,48570L,48571L,48572L,48573L,48574L, -48575L,48576L,48577L,48578L,48579L,48580L,48581L,48582L,48583L,48584L, -48585L,48586L,48587L,48588L,48589L,48590L,48591L,48592L,48593L,48594L, -48595L,48596L,48597L,48598L,48599L,48600L,48601L,48602L,48603L,48604L, -48605L,48606L,48607L,48608L,48609L,48610L,48611L,48612L,48613L,48614L, -48615L,48616L,48617L,48618L,48619L,48620L,48621L,48622L,48623L,48624L, -48625L,48626L,48627L,48628L,48629L,48630L,48631L,48632L,48633L,48634L, -48635L,48636L,48637L,48638L,48639L,48640L,48641L,48642L,48643L,48644L, -48645L,48646L,48647L,48648L,48649L,48650L,48651L,48652L,48653L,48654L, -48655L,48656L,48657L,48658L,48659L,48660L,48661L,48662L,48663L,48664L, -48665L,48666L,48667L,48668L,48669L,48670L,48671L,48672L,48673L,48674L, -48675L,48676L,48677L,48678L,48679L,48680L,48681L,48682L,48683L,48684L, -48685L,48686L,48687L,48688L,48689L,48690L,48691L,48692L,48693L,48694L, -48695L,48696L,48697L,48698L,48699L,48700L,48701L,48702L,48703L,48704L, -48705L,48706L,48707L,48708L,48709L,48710L,48711L,48712L,48713L,48714L, -48715L,48716L,48717L,48718L,48719L,48720L,48721L,48722L,48723L,48724L, -48725L,48726L,48727L,48728L,48729L,48730L,48731L,48732L,48733L,48734L, -48735L,48736L,48737L,48738L,48739L,48740L,48741L,48742L,48743L,48744L, -48745L,48746L,48747L,48748L,48749L,48750L,48751L,48752L,48753L,48754L, -48755L,48756L,48757L,48758L,48759L,48760L,48761L,48762L,48763L,48764L, -48765L,48766L,48767L,48768L,48769L,48770L,48771L,48772L,48773L,48774L, -48775L,48776L,48777L,48778L,48779L,48780L,48781L,48782L,48783L,48784L, -48785L,48786L,48787L,48788L,48789L,48790L,48791L,48792L,48793L,48794L, -48795L,48796L,48797L,48798L,48799L,48800L,48801L,48802L,48803L,48804L, -48805L,48806L,48807L,48808L,48809L,48810L,48811L,48812L,48813L,48814L, -48815L,48816L,48817L,48818L,48819L,48820L,48821L,48822L,48823L,48824L, -48825L,48826L,48827L,48828L,48829L,48830L,48831L,48832L,48833L,48834L, -48835L,48836L,48837L,48838L,48839L,48840L,48841L,48842L,48843L,48844L, -48845L,48846L,48847L,48848L,48849L,48850L,48851L,48852L,48853L,48854L, -48855L,48856L,48857L,48858L,48859L,48860L,48861L,48862L,48863L,48864L, -48865L,48866L,48867L,48868L,48869L,48870L,48871L,48872L,48873L,48874L, -48875L,48876L,48877L,48878L,48879L,48880L,48881L,48882L,48883L,48884L, -48885L,48886L,48887L,48888L,48889L,48890L,48891L,48892L,48893L,48894L, -48895L,48896L,48897L,48898L,48899L,48900L,48901L,48902L,48903L,48904L, -48905L,48906L,48907L,48908L,48909L,48910L,48911L,48912L,48913L,48914L, -48915L,48916L,48917L,48918L,48919L,48920L,48921L,48922L,48923L,48924L, -48925L,48926L,48927L,48928L,48929L,48930L,48931L,48932L,48933L,48934L, -48935L,48936L,48937L,48938L,48939L,48940L,48941L,48942L,48943L,48944L, -48945L,48946L,48947L,48948L,48949L,48950L,48951L,48952L,48953L,48954L, -48955L,48956L,48957L,48958L,48959L,48960L,48961L,48962L,48963L,48964L, -48965L,48966L,48967L,48968L,48969L,48970L,48971L,48972L,48973L,48974L, -48975L,48976L,48977L,48978L,48979L,48980L,48981L,48982L,48983L,48984L, -48985L,48986L,48987L,48988L,48989L,48990L,48991L,48992L,48993L,48994L, -48995L,48996L,48997L,48998L,48999L,49000L,49001L,49002L,49003L,49004L, -49005L,49006L,49007L,49008L,49009L,49010L,49011L,49012L,49013L,49014L, -49015L,49016L,49017L,49018L,49019L,49020L,49021L,49022L,49023L,49024L, -49025L,49026L,49027L,49028L,49029L,49030L,49031L,49032L,49033L,49034L, -49035L,49036L,49037L,49038L,49039L,49040L,49041L,49042L,49043L,49044L, -49045L,49046L,49047L,49048L,49049L,49050L,49051L,49052L,49053L,49054L, -49055L,49056L,49057L,49058L,49059L,49060L,49061L,49062L,49063L,49064L, -49065L,49066L,49067L,49068L,49069L,49070L,49071L,49072L,49073L,49074L, -49075L,49076L,49077L,49078L,49079L,49080L,49081L,49082L,49083L,49084L, -49085L,49086L,49087L,49088L,49089L,49090L,49091L,49092L,49093L,49094L, -49095L,49096L,49097L,49098L,49099L,49100L,49101L,49102L,49103L,49104L, -49105L,49106L,49107L,49108L,49109L,49110L,49111L,49112L,49113L,49114L, -49115L,49116L,49117L,49118L,49119L,49120L,49121L,49122L,49123L,49124L, -49125L,49126L,49127L,49128L,49129L,49130L,49131L,49132L,49133L,49134L, -49135L,49136L,49137L,49138L,49139L,49140L,49141L,49142L,49143L,49144L, -49145L,49146L,49147L,49148L,49149L,49150L,49151L,49152L,49153L,49154L, -49155L,49156L,49157L,49158L,49159L,49160L,49161L,49162L,49163L,49164L, -49165L,49166L,49167L,49168L,49169L,49170L,49171L,49172L,49173L,49174L, -49175L,49176L,49177L,49178L,49179L,49180L,49181L,49182L,49183L,49184L, -49185L,49186L,49187L,49188L,49189L,49190L,49191L,49192L,49193L,49194L, -49195L,49196L,49197L,49198L,49199L,49200L,49201L,49202L,49203L,49204L, -49205L,49206L,49207L,49208L,49209L,49210L,49211L,49212L,49213L,49214L, -49215L,49216L,49217L,49218L,49219L,49220L,49221L,49222L,49223L,49224L, -49225L,49226L,49227L,49228L,49229L,49230L,49231L,49232L,49233L,49234L, -49235L,49236L,49237L,49238L,49239L,49240L,49241L,49242L,49243L,49244L, -49245L,49246L,49247L,49248L,49249L,49250L,49251L,49252L,49253L,49254L, -49255L,49256L,49257L,49258L,49259L,49260L,49261L,49262L,49263L,49264L, -49265L,49266L,49267L,49268L,49269L,49270L,49271L,49272L,49273L,49274L, -49275L,49276L,49277L,49278L,49279L,49280L,49281L,49282L,49283L,49284L, -49285L,49286L,49287L,49288L,49289L,49290L,49291L,49292L,49293L,49294L, -49295L,49296L,49297L,49298L,49299L,49300L,49301L,49302L,49303L,49304L, -49305L,49306L,49307L,49308L,49309L,49310L,49311L,49312L,49313L,49314L, -49315L,49316L,49317L,49318L,49319L,49320L,49321L,49322L,49323L,49324L, -49325L,49326L,49327L,49328L,49329L,49330L,49331L,49332L,49333L,49334L, -49335L,49336L,49337L,49338L,49339L,49340L,49341L,49342L,49343L,49344L, -49345L,49346L,49347L,49348L,49349L,49350L,49351L,49352L,49353L,49354L, -49355L,49356L,49357L,49358L,49359L,49360L,49361L,49362L,49363L,49364L, -49365L,49366L,49367L,49368L,49369L,49370L,49371L,49372L,49373L,49374L, -49375L,49376L,49377L,49378L,49379L,49380L,49381L,49382L,49383L,49384L, -49385L,49386L,49387L,49388L,49389L,49390L,49391L,49392L,49393L,49394L, -49395L,49396L,49397L,49398L,49399L,49400L,49401L,49402L,49403L,49404L, -49405L,49406L,49407L,49408L,49409L,49410L,49411L,49412L,49413L,49414L, -49415L,49416L,49417L,49418L,49419L,49420L,49421L,49422L,49423L,49424L, -49425L,49426L,49427L,49428L,49429L,49430L,49431L,49432L,49433L,49434L, -49435L,49436L,49437L,49438L,49439L,49440L,49441L,49442L,49443L,49444L, -49445L,49446L,49447L,49448L,49449L,49450L,49451L,49452L,49453L,49454L, -49455L,49456L,49457L,49458L,49459L,49460L,49461L,49462L,49463L,49464L, -49465L,49466L,49467L,49468L,49469L,49470L,49471L,49472L,49473L,49474L, -49475L,49476L,49477L,49478L,49479L,49480L,49481L,49482L,49483L,49484L, -49485L,49486L,49487L,49488L,49489L,49490L,49491L,49492L,49493L,49494L, -49495L,49496L,49497L,49498L,49499L,49500L,49501L,49502L,49503L,49504L, -49505L,49506L,49507L,49508L,49509L,49510L,49511L,49512L,49513L,49514L, -49515L,49516L,49517L,49518L,49519L,49520L,49521L,49522L,49523L,49524L, -49525L,49526L,49527L,49528L,49529L,49530L,49531L,49532L,49533L,49534L, -49535L,49536L,49537L,49538L,49539L,49540L,49541L,49542L,49543L,49544L, -49545L,49546L,49547L,49548L,49549L,49550L,49551L,49552L,49553L,49554L, -49555L,49556L,49557L,49558L,49559L,49560L,49561L,49562L,49563L,49564L, -49565L,49566L,49567L,49568L,49569L,49570L,49571L,49572L,49573L,49574L, -49575L,49576L,49577L,49578L,49579L,49580L,49581L,49582L,49583L,49584L, -49585L,49586L,49587L,49588L,49589L,49590L,49591L,49592L,49593L,49594L, -49595L,49596L,49597L,49598L,49599L,49600L,49601L,49602L,49603L,49604L, -49605L,49606L,49607L,49608L,49609L,49610L,49611L,49612L,49613L,49614L, -49615L,49616L,49617L,49618L,49619L,49620L,49621L,49622L,49623L,49624L, -49625L,49626L,49627L,49628L,49629L,49630L,49631L,49632L,49633L,49634L, -49635L,49636L,49637L,49638L,49639L,49640L,49641L,49642L,49643L,49644L, -49645L,49646L,49647L,49648L,49649L,49650L,49651L,49652L,49653L,49654L, -49655L,49656L,49657L,49658L,49659L,49660L,49661L,49662L,49663L,49664L, -49665L,49666L,49667L,49668L,49669L,49670L,49671L,49672L,49673L,49674L, -49675L,49676L,49677L,49678L,49679L,49680L,49681L,49682L,49683L,49684L, -49685L,49686L,49687L,49688L,49689L,49690L,49691L,49692L,49693L,49694L, -49695L,49696L,49697L,49698L,49699L,49700L,49701L,49702L,49703L,49704L, -49705L,49706L,49707L,49708L,49709L,49710L,49711L,49712L,49713L,49714L, -49715L,49716L,49717L,49718L,49719L,49720L,49721L,49722L,49723L,49724L, -49725L,49726L,49727L,49728L,49729L,49730L,49731L,49732L,49733L,49734L, -49735L,49736L,49737L,49738L,49739L,49740L,49741L,49742L,49743L,49744L, -49745L,49746L,49747L,49748L,49749L,49750L,49751L,49752L,49753L,49754L, -49755L,49756L,49757L,49758L,49759L,49760L,49761L,49762L,49763L,49764L, -49765L,49766L,49767L,49768L,49769L,49770L,49771L,49772L,49773L,49774L, -49775L,49776L,49777L,49778L,49779L,49780L,49781L,49782L,49783L,49784L, -49785L,49786L,49787L,49788L,49789L,49790L,49791L,49792L,49793L,49794L, -49795L,49796L,49797L,49798L,49799L,49800L,49801L,49802L,49803L,49804L, -49805L,49806L,49807L,49808L,49809L,49810L,49811L,49812L,49813L,49814L, -49815L,49816L,49817L,49818L,49819L,49820L,49821L,49822L,49823L,49824L, -49825L,49826L,49827L,49828L,49829L,49830L,49831L,49832L,49833L,49834L, -49835L,49836L,49837L,49838L,49839L,49840L,49841L,49842L,49843L,49844L, -49845L,49846L,49847L,49848L,49849L,49850L,49851L,49852L,49853L,49854L, -49855L,49856L,49857L,49858L,49859L,49860L,49861L,49862L,49863L,49864L, -49865L,49866L,49867L,49868L,49869L,49870L,49871L,49872L,49873L,49874L, -49875L,49876L,49877L,49878L,49879L,49880L,49881L,49882L,49883L,49884L, -49885L,49886L,49887L,49888L,49889L,49890L,49891L,49892L,49893L,49894L, -49895L,49896L,49897L,49898L,49899L,49900L,49901L,49902L,49903L,49904L, -49905L,49906L,49907L,49908L,49909L,49910L,49911L,49912L,49913L,49914L, -49915L,49916L,49917L,49918L,49919L,49920L,49921L,49922L,49923L,49924L, -49925L,49926L,49927L,49928L,49929L,49930L,49931L,49932L,49933L,49934L, -49935L,49936L,49937L,49938L,49939L,49940L,49941L,49942L,49943L,49944L, -49945L,49946L,49947L,49948L,49949L,49950L,49951L,49952L,49953L,49954L, -49955L,49956L,49957L,49958L,49959L,49960L,49961L,49962L,49963L,49964L, -49965L,49966L,49967L,49968L,49969L,49970L,49971L,49972L,49973L,49974L, -49975L,49976L,49977L,49978L,49979L,49980L,49981L,49982L,49983L,49984L, -49985L,49986L,49987L,49988L,49989L,49990L,49991L,49992L,49993L,49994L, -49995L,49996L,49997L,49998L,49999L,50000L,50001L,50002L,50003L,50004L, -50005L,50006L,50007L,50008L,50009L,50010L,50011L,50012L,50013L,50014L, -50015L,50016L,50017L,50018L,50019L,50020L,50021L,50022L,50023L,50024L, -50025L,50026L,50027L,50028L,50029L,50030L,50031L,50032L,50033L,50034L, -50035L,50036L,50037L,50038L,50039L,50040L,50041L,50042L,50043L,50044L, -50045L,50046L,50047L,50048L,50049L,50050L,50051L,50052L,50053L,50054L, -50055L,50056L,50057L,50058L,50059L,50060L,50061L,50062L,50063L,50064L, -50065L,50066L,50067L,50068L,50069L,50070L,50071L,50072L,50073L,50074L, -50075L,50076L,50077L,50078L,50079L,50080L,50081L,50082L,50083L,50084L, -50085L,50086L,50087L,50088L,50089L,50090L,50091L,50092L,50093L,50094L, -50095L,50096L,50097L,50098L,50099L,50100L,50101L,50102L,50103L,50104L, -50105L,50106L,50107L,50108L,50109L,50110L,50111L,50112L,50113L,50114L, -50115L,50116L,50117L,50118L,50119L,50120L,50121L,50122L,50123L,50124L, -50125L,50126L,50127L,50128L,50129L,50130L,50131L,50132L,50133L,50134L, -50135L,50136L,50137L,50138L,50139L,50140L,50141L,50142L,50143L,50144L, -50145L,50146L,50147L,50148L,50149L,50150L,50151L,50152L,50153L,50154L, -50155L,50156L,50157L,50158L,50159L,50160L,50161L,50162L,50163L,50164L, -50165L,50166L,50167L,50168L,50169L,50170L,50171L,50172L,50173L,50174L, -50175L,50176L,50177L,50178L,50179L,50180L,50181L,50182L,50183L,50184L, -50185L,50186L,50187L,50188L,50189L,50190L,50191L,50192L,50193L,50194L, -50195L,50196L,50197L,50198L,50199L,50200L,50201L,50202L,50203L,50204L, -50205L,50206L,50207L,50208L,50209L,50210L,50211L,50212L,50213L,50214L, -50215L,50216L,50217L,50218L,50219L,50220L,50221L,50222L,50223L,50224L, -50225L,50226L,50227L,50228L,50229L,50230L,50231L,50232L,50233L,50234L, -50235L,50236L,50237L,50238L,50239L,50240L,50241L,50242L,50243L,50244L, -50245L,50246L,50247L,50248L,50249L,50250L,50251L,50252L,50253L,50254L, -50255L,50256L,50257L,50258L,50259L,50260L,50261L,50262L,50263L,50264L, -50265L,50266L,50267L,50268L,50269L,50270L,50271L,50272L,50273L,50274L, -50275L,50276L,50277L,50278L,50279L,50280L,50281L,50282L,50283L,50284L, -50285L,50286L,50287L,50288L,50289L,50290L,50291L,50292L,50293L,50294L, -50295L,50296L,50297L,50298L,50299L,50300L,50301L,50302L,50303L,50304L, -50305L,50306L,50307L,50308L,50309L,50310L,50311L,50312L,50313L,50314L, -50315L,50316L,50317L,50318L,50319L,50320L,50321L,50322L,50323L,50324L, -50325L,50326L,50327L,50328L,50329L,50330L,50331L,50332L,50333L,50334L, -50335L,50336L,50337L,50338L,50339L,50340L,50341L,50342L,50343L,50344L, -50345L,50346L,50347L,50348L,50349L,50350L,50351L,50352L,50353L,50354L, -50355L,50356L,50357L,50358L,50359L,50360L,50361L,50362L,50363L,50364L, -50365L,50366L,50367L,50368L,50369L,50370L,50371L,50372L,50373L,50374L, -50375L,50376L,50377L,50378L,50379L,50380L,50381L,50382L,50383L,50384L, -50385L,50386L,50387L,50388L,50389L,50390L,50391L,50392L,50393L,50394L, -50395L,50396L,50397L,50398L,50399L,50400L,50401L,50402L,50403L,50404L, -50405L,50406L,50407L,50408L,50409L,50410L,50411L,50412L,50413L,50414L, -50415L,50416L,50417L,50418L,50419L,50420L,50421L,50422L,50423L,50424L, -50425L,50426L,50427L,50428L,50429L,50430L,50431L,50432L,50433L,50434L, -50435L,50436L,50437L,50438L,50439L,50440L,50441L,50442L,50443L,50444L, -50445L,50446L,50447L,50448L,50449L,50450L,50451L,50452L,50453L,50454L, -50455L,50456L,50457L,50458L,50459L,50460L,50461L,50462L,50463L,50464L, -50465L,50466L,50467L,50468L,50469L,50470L,50471L,50472L,50473L,50474L, -50475L,50476L,50477L,50478L,50479L,50480L,50481L,50482L,50483L,50484L, -50485L,50486L,50487L,50488L,50489L,50490L,50491L,50492L,50493L,50494L, -50495L,50496L,50497L,50498L,50499L,50500L,50501L,50502L,50503L,50504L, -50505L,50506L,50507L,50508L,50509L,50510L,50511L,50512L,50513L,50514L, -50515L,50516L,50517L,50518L,50519L,50520L,50521L,50522L,50523L,50524L, -50525L,50526L,50527L,50528L,50529L,50530L,50531L,50532L,50533L,50534L, -50535L,50536L,50537L,50538L,50539L,50540L,50541L,50542L,50543L,50544L, -50545L,50546L,50547L,50548L,50549L,50550L,50551L,50552L,50553L,50554L, -50555L,50556L,50557L,50558L,50559L,50560L,50561L,50562L,50563L,50564L, -50565L,50566L,50567L,50568L,50569L,50570L,50571L,50572L,50573L,50574L, -50575L,50576L,50577L,50578L,50579L,50580L,50581L,50582L,50583L,50584L, -50585L,50586L,50587L,50588L,50589L,50590L,50591L,50592L,50593L,50594L, -50595L,50596L,50597L,50598L,50599L,50600L,50601L,50602L,50603L,50604L, -50605L,50606L,50607L,50608L,50609L,50610L,50611L,50612L,50613L,50614L, -50615L,50616L,50617L,50618L,50619L,50620L,50621L,50622L,50623L,50624L, -50625L,50626L,50627L,50628L,50629L,50630L,50631L,50632L,50633L,50634L, -50635L,50636L,50637L,50638L,50639L,50640L,50641L,50642L,50643L,50644L, -50645L,50646L,50647L,50648L,50649L,50650L,50651L,50652L,50653L,50654L, -50655L,50656L,50657L,50658L,50659L,50660L,50661L,50662L,50663L,50664L, -50665L,50666L,50667L,50668L,50669L,50670L,50671L,50672L,50673L,50674L, -50675L,50676L,50677L,50678L,50679L,50680L,50681L,50682L,50683L,50684L, -50685L,50686L,50687L,50688L,50689L,50690L,50691L,50692L,50693L,50694L, -50695L,50696L,50697L,50698L,50699L,50700L,50701L,50702L,50703L,50704L, -50705L,50706L,50707L,50708L,50709L,50710L,50711L,50712L,50713L,50714L, -50715L,50716L,50717L,50718L,50719L,50720L,50721L,50722L,50723L,50724L, -50725L,50726L,50727L,50728L,50729L,50730L,50731L,50732L,50733L,50734L, -50735L,50736L,50737L,50738L,50739L,50740L,50741L,50742L,50743L,50744L, -50745L,50746L,50747L,50748L,50749L,50750L,50751L,50752L,50753L,50754L, -50755L,50756L,50757L,50758L,50759L,50760L,50761L,50762L,50763L,50764L, -50765L,50766L,50767L,50768L,50769L,50770L,50771L,50772L,50773L,50774L, -50775L,50776L,50777L,50778L,50779L,50780L,50781L,50782L,50783L,50784L, -50785L,50786L,50787L,50788L,50789L,50790L,50791L,50792L,50793L,50794L, -50795L,50796L,50797L,50798L,50799L,50800L,50801L,50802L,50803L,50804L, -50805L,50806L,50807L,50808L,50809L,50810L,50811L,50812L,50813L,50814L, -50815L,50816L,50817L,50818L,50819L,50820L,50821L,50822L,50823L,50824L, -50825L,50826L,50827L,50828L,50829L,50830L,50831L,50832L,50833L,50834L, -50835L,50836L,50837L,50838L,50839L,50840L,50841L,50842L,50843L,50844L, -50845L,50846L,50847L,50848L,50849L,50850L,50851L,50852L,50853L,50854L, -50855L,50856L,50857L,50858L,50859L,50860L,50861L,50862L,50863L,50864L, -50865L,50866L,50867L,50868L,50869L,50870L,50871L,50872L,50873L,50874L, -50875L,50876L,50877L,50878L,50879L,50880L,50881L,50882L,50883L,50884L, -50885L,50886L,50887L,50888L,50889L,50890L,50891L,50892L,50893L,50894L, -50895L,50896L,50897L,50898L,50899L,50900L,50901L,50902L,50903L,50904L, -50905L,50906L,50907L,50908L,50909L,50910L,50911L,50912L,50913L,50914L, -50915L,50916L,50917L,50918L,50919L,50920L,50921L,50922L,50923L,50924L, -50925L,50926L,50927L,50928L,50929L,50930L,50931L,50932L,50933L,50934L, -50935L,50936L,50937L,50938L,50939L,50940L,50941L,50942L,50943L,50944L, -50945L,50946L,50947L,50948L,50949L,50950L,50951L,50952L,50953L,50954L, -50955L,50956L,50957L,50958L,50959L,50960L,50961L,50962L,50963L,50964L, -50965L,50966L,50967L,50968L,50969L,50970L,50971L,50972L,50973L,50974L, -50975L,50976L,50977L,50978L,50979L,50980L,50981L,50982L,50983L,50984L, -50985L,50986L,50987L,50988L,50989L,50990L,50991L,50992L,50993L,50994L, -50995L,50996L,50997L,50998L,50999L,51000L,51001L,51002L,51003L,51004L, -51005L,51006L,51007L,51008L,51009L,51010L,51011L,51012L,51013L,51014L, -51015L,51016L,51017L,51018L,51019L,51020L,51021L,51022L,51023L,51024L, -51025L,51026L,51027L,51028L,51029L,51030L,51031L,51032L,51033L,51034L, -51035L,51036L,51037L,51038L,51039L,51040L,51041L,51042L,51043L,51044L, -51045L,51046L,51047L,51048L,51049L,51050L,51051L,51052L,51053L,51054L, -51055L,51056L,51057L,51058L,51059L,51060L,51061L,51062L,51063L,51064L, -51065L,51066L,51067L,51068L,51069L,51070L,51071L,51072L,51073L,51074L, -51075L,51076L,51077L,51078L,51079L,51080L,51081L,51082L,51083L,51084L, -51085L,51086L,51087L,51088L,51089L,51090L,51091L,51092L,51093L,51094L, -51095L,51096L,51097L,51098L,51099L,51100L,51101L,51102L,51103L,51104L, -51105L,51106L,51107L,51108L,51109L,51110L,51111L,51112L,51113L,51114L, -51115L,51116L,51117L,51118L,51119L,51120L,51121L,51122L,51123L,51124L, -51125L,51126L,51127L,51128L,51129L,51130L,51131L,51132L,51133L,51134L, -51135L,51136L,51137L,51138L,51139L,51140L,51141L,51142L,51143L,51144L, -51145L,51146L,51147L,51148L,51149L,51150L,51151L,51152L,51153L,51154L, -51155L,51156L,51157L,51158L,51159L,51160L,51161L,51162L,51163L,51164L, -51165L,51166L,51167L,51168L,51169L,51170L,51171L,51172L,51173L,51174L, -51175L,51176L,51177L,51178L,51179L,51180L,51181L,51182L,51183L,51184L, -51185L,51186L,51187L,51188L,51189L,51190L,51191L,51192L,51193L,51194L, -51195L,51196L,51197L,51198L,51199L,51200L,51201L,51202L,51203L,51204L, -51205L,51206L,51207L,51208L,51209L,51210L,51211L,51212L,51213L,51214L, -51215L,51216L,51217L,51218L,51219L,51220L,51221L,51222L,51223L,51224L, -51225L,51226L,51227L,51228L,51229L,51230L,51231L,51232L,51233L,51234L, -51235L,51236L,51237L,51238L,51239L,51240L,51241L,51242L,51243L,51244L, -51245L,51246L,51247L,51248L,51249L,51250L,51251L,51252L,51253L,51254L, -51255L,51256L,51257L,51258L,51259L,51260L,51261L,51262L,51263L,51264L, -51265L,51266L,51267L,51268L,51269L,51270L,51271L,51272L,51273L,51274L, -51275L,51276L,51277L,51278L,51279L,51280L,51281L,51282L,51283L,51284L, -51285L,51286L,51287L,51288L,51289L,51290L,51291L,51292L,51293L,51294L, -51295L,51296L,51297L,51298L,51299L,51300L,51301L,51302L,51303L,51304L, -51305L,51306L,51307L,51308L,51309L,51310L,51311L,51312L,51313L,51314L, -51315L,51316L,51317L,51318L,51319L,51320L,51321L,51322L,51323L,51324L, -51325L,51326L,51327L,51328L,51329L,51330L,51331L,51332L,51333L,51334L, -51335L,51336L,51337L,51338L,51339L,51340L,51341L,51342L,51343L,51344L, -51345L,51346L,51347L,51348L,51349L,51350L,51351L,51352L,51353L,51354L, -51355L,51356L,51357L,51358L,51359L,51360L,51361L,51362L,51363L,51364L, -51365L,51366L,51367L,51368L,51369L,51370L,51371L,51372L,51373L,51374L, -51375L,51376L,51377L,51378L,51379L,51380L,51381L,51382L,51383L,51384L, -51385L,51386L,51387L,51388L,51389L,51390L,51391L,51392L,51393L,51394L, -51395L,51396L,51397L,51398L,51399L,51400L,51401L,51402L,51403L,51404L, -51405L,51406L,51407L,51408L,51409L,51410L,51411L,51412L,51413L,51414L, -51415L,51416L,51417L,51418L,51419L,51420L,51421L,51422L,51423L,51424L, -51425L,51426L,51427L,51428L,51429L,51430L,51431L,51432L,51433L,51434L, -51435L,51436L,51437L,51438L,51439L,51440L,51441L,51442L,51443L,51444L, -51445L,51446L,51447L,51448L,51449L,51450L,51451L,51452L,51453L,51454L, -51455L,51456L,51457L,51458L,51459L,51460L,51461L,51462L,51463L,51464L, -51465L,51466L,51467L,51468L,51469L,51470L,51471L,51472L,51473L,51474L, -51475L,51476L,51477L,51478L,51479L,51480L,51481L,51482L,51483L,51484L, -51485L,51486L,51487L,51488L,51489L,51490L,51491L,51492L,51493L,51494L, -51495L,51496L,51497L,51498L,51499L,51500L,51501L,51502L,51503L,51504L, -51505L,51506L,51507L,51508L,51509L,51510L,51511L,51512L,51513L,51514L, -51515L,51516L,51517L,51518L,51519L,51520L,51521L,51522L,51523L,51524L, -51525L,51526L,51527L,51528L,51529L,51530L,51531L,51532L,51533L,51534L, -51535L,51536L,51537L,51538L,51539L,51540L,51541L,51542L,51543L,51544L, -51545L,51546L,51547L,51548L,51549L,51550L,51551L,51552L,51553L,51554L, -51555L,51556L,51557L,51558L,51559L,51560L,51561L,51562L,51563L,51564L, -51565L,51566L,51567L,51568L,51569L,51570L,51571L,51572L,51573L,51574L, -51575L,51576L,51577L,51578L,51579L,51580L,51581L,51582L,51583L,51584L, -51585L,51586L,51587L,51588L,51589L,51590L,51591L,51592L,51593L,51594L, -51595L,51596L,51597L,51598L,51599L,51600L,51601L,51602L,51603L,51604L, -51605L,51606L,51607L,51608L,51609L,51610L,51611L,51612L,51613L,51614L, -51615L,51616L,51617L,51618L,51619L,51620L,51621L,51622L,51623L,51624L, -51625L,51626L,51627L,51628L,51629L,51630L,51631L,51632L,51633L,51634L, -51635L,51636L,51637L,51638L,51639L,51640L,51641L,51642L,51643L,51644L, -51645L,51646L,51647L,51648L,51649L,51650L,51651L,51652L,51653L,51654L, -51655L,51656L,51657L,51658L,51659L,51660L,51661L,51662L,51663L,51664L, -51665L,51666L,51667L,51668L,51669L,51670L,51671L,51672L,51673L,51674L, -51675L,51676L,51677L,51678L,51679L,51680L,51681L,51682L,51683L,51684L, -51685L,51686L,51687L,51688L,51689L,51690L,51691L,51692L,51693L,51694L, -51695L,51696L,51697L,51698L,51699L,51700L,51701L,51702L,51703L,51704L, -51705L,51706L,51707L,51708L,51709L,51710L,51711L,51712L,51713L,51714L, -51715L,51716L,51717L,51718L,51719L,51720L,51721L,51722L,51723L,51724L, -51725L,51726L,51727L,51728L,51729L,51730L,51731L,51732L,51733L,51734L, -51735L,51736L,51737L,51738L,51739L,51740L,51741L,51742L,51743L,51744L, -51745L,51746L,51747L,51748L,51749L,51750L,51751L,51752L,51753L,51754L, -51755L,51756L,51757L,51758L,51759L,51760L,51761L,51762L,51763L,51764L, -51765L,51766L,51767L,51768L,51769L,51770L,51771L,51772L,51773L,51774L, -51775L,51776L,51777L,51778L,51779L,51780L,51781L,51782L,51783L,51784L, -51785L,51786L,51787L,51788L,51789L,51790L,51791L,51792L,51793L,51794L, -51795L,51796L,51797L,51798L,51799L,51800L,51801L,51802L,51803L,51804L, -51805L,51806L,51807L,51808L,51809L,51810L,51811L,51812L,51813L,51814L, -51815L,51816L,51817L,51818L,51819L,51820L,51821L,51822L,51823L,51824L, -51825L,51826L,51827L,51828L,51829L,51830L,51831L,51832L,51833L,51834L, -51835L,51836L,51837L,51838L,51839L,51840L,51841L,51842L,51843L,51844L, -51845L,51846L,51847L,51848L,51849L,51850L,51851L,51852L,51853L,51854L, -51855L,51856L,51857L,51858L,51859L,51860L,51861L,51862L,51863L,51864L, -51865L,51866L,51867L,51868L,51869L,51870L,51871L,51872L,51873L,51874L, -51875L,51876L,51877L,51878L,51879L,51880L,51881L,51882L,51883L,51884L, -51885L,51886L,51887L,51888L,51889L,51890L,51891L,51892L,51893L,51894L, -51895L,51896L,51897L,51898L,51899L,51900L,51901L,51902L,51903L,51904L, -51905L,51906L,51907L,51908L,51909L,51910L,51911L,51912L,51913L,51914L, -51915L,51916L,51917L,51918L,51919L,51920L,51921L,51922L,51923L,51924L, -51925L,51926L,51927L,51928L,51929L,51930L,51931L,51932L,51933L,51934L, -51935L,51936L,51937L,51938L,51939L,51940L,51941L,51942L,51943L,51944L, -51945L,51946L,51947L,51948L,51949L,51950L,51951L,51952L,51953L,51954L, -51955L,51956L,51957L,51958L,51959L,51960L,51961L,51962L,51963L,51964L, -51965L,51966L,51967L,51968L,51969L,51970L,51971L,51972L,51973L,51974L, -51975L,51976L,51977L,51978L,51979L,51980L,51981L,51982L,51983L,51984L, -51985L,51986L,51987L,51988L,51989L,51990L,51991L,51992L,51993L,51994L, -51995L,51996L,51997L,51998L,51999L,52000L,52001L,52002L,52003L,52004L, -52005L,52006L,52007L,52008L,52009L,52010L,52011L,52012L,52013L,52014L, -52015L,52016L,52017L,52018L,52019L,52020L,52021L,52022L,52023L,52024L, -52025L,52026L,52027L,52028L,52029L,52030L,52031L,52032L,52033L,52034L, -52035L,52036L,52037L,52038L,52039L,52040L,52041L,52042L,52043L,52044L, -52045L,52046L,52047L,52048L,52049L,52050L,52051L,52052L,52053L,52054L, -52055L,52056L,52057L,52058L,52059L,52060L,52061L,52062L,52063L,52064L, -52065L,52066L,52067L,52068L,52069L,52070L,52071L,52072L,52073L,52074L, -52075L,52076L,52077L,52078L,52079L,52080L,52081L,52082L,52083L,52084L, -52085L,52086L,52087L,52088L,52089L,52090L,52091L,52092L,52093L,52094L, -52095L,52096L,52097L,52098L,52099L,52100L,52101L,52102L,52103L,52104L, -52105L,52106L,52107L,52108L,52109L,52110L,52111L,52112L,52113L,52114L, -52115L,52116L,52117L,52118L,52119L,52120L,52121L,52122L,52123L,52124L, -52125L,52126L,52127L,52128L,52129L,52130L,52131L,52132L,52133L,52134L, -52135L,52136L,52137L,52138L,52139L,52140L,52141L,52142L,52143L,52144L, -52145L,52146L,52147L,52148L,52149L,52150L,52151L,52152L,52153L,52154L, -52155L,52156L,52157L,52158L,52159L,52160L,52161L,52162L,52163L,52164L, -52165L,52166L,52167L,52168L,52169L,52170L,52171L,52172L,52173L,52174L, -52175L,52176L,52177L,52178L,52179L,52180L,52181L,52182L,52183L,52184L, -52185L,52186L,52187L,52188L,52189L,52190L,52191L,52192L,52193L,52194L, -52195L,52196L,52197L,52198L,52199L,52200L,52201L,52202L,52203L,52204L, -52205L,52206L,52207L,52208L,52209L,52210L,52211L,52212L,52213L,52214L, -52215L,52216L,52217L,52218L,52219L,52220L,52221L,52222L,52223L,52224L, -52225L,52226L,52227L,52228L,52229L,52230L,52231L,52232L,52233L,52234L, -52235L,52236L,52237L,52238L,52239L,52240L,52241L,52242L,52243L,52244L, -52245L,52246L,52247L,52248L,52249L,52250L,52251L,52252L,52253L,52254L, -52255L,52256L,52257L,52258L,52259L,52260L,52261L,52262L,52263L,52264L, -52265L,52266L,52267L,52268L,52269L,52270L,52271L,52272L,52273L,52274L, -52275L,52276L,52277L,52278L,52279L,52280L,52281L,52282L,52283L,52284L, -52285L,52286L,52287L,52288L,52289L,52290L,52291L,52292L,52293L,52294L, -52295L,52296L,52297L,52298L,52299L,52300L,52301L,52302L,52303L,52304L, -52305L,52306L,52307L,52308L,52309L,52310L,52311L,52312L,52313L,52314L, -52315L,52316L,52317L,52318L,52319L,52320L,52321L,52322L,52323L,52324L, -52325L,52326L,52327L,52328L,52329L,52330L,52331L,52332L,52333L,52334L, -52335L,52336L,52337L,52338L,52339L,52340L,52341L,52342L,52343L,52344L, -52345L,52346L,52347L,52348L,52349L,52350L,52351L,52352L,52353L,52354L, -52355L,52356L,52357L,52358L,52359L,52360L,52361L,52362L,52363L,52364L, -52365L,52366L,52367L,52368L,52369L,52370L,52371L,52372L,52373L,52374L, -52375L,52376L,52377L,52378L,52379L,52380L,52381L,52382L,52383L,52384L, -52385L,52386L,52387L,52388L,52389L,52390L,52391L,52392L,52393L,52394L, -52395L,52396L,52397L,52398L,52399L,52400L,52401L,52402L,52403L,52404L, -52405L,52406L,52407L,52408L,52409L,52410L,52411L,52412L,52413L,52414L, -52415L,52416L,52417L,52418L,52419L,52420L,52421L,52422L,52423L,52424L, -52425L,52426L,52427L,52428L,52429L,52430L,52431L,52432L,52433L,52434L, -52435L,52436L,52437L,52438L,52439L,52440L,52441L,52442L,52443L,52444L, -52445L,52446L,52447L,52448L,52449L,52450L,52451L,52452L,52453L,52454L, -52455L,52456L,52457L,52458L,52459L,52460L,52461L,52462L,52463L,52464L, -52465L,52466L,52467L,52468L,52469L,52470L,52471L,52472L,52473L,52474L, -52475L,52476L,52477L,52478L,52479L,52480L,52481L,52482L,52483L,52484L, -52485L,52486L,52487L,52488L,52489L,52490L,52491L,52492L,52493L,52494L, -52495L,52496L,52497L,52498L,52499L,52500L,52501L,52502L,52503L,52504L, -52505L,52506L,52507L,52508L,52509L,52510L,52511L,52512L,52513L,52514L, -52515L,52516L,52517L,52518L,52519L,52520L,52521L,52522L,52523L,52524L, -52525L,52526L,52527L,52528L,52529L,52530L,52531L,52532L,52533L,52534L, -52535L,52536L,52537L,52538L,52539L,52540L,52541L,52542L,52543L,52544L, -52545L,52546L,52547L,52548L,52549L,52550L,52551L,52552L,52553L,52554L, -52555L,52556L,52557L,52558L,52559L,52560L,52561L,52562L,52563L,52564L, -52565L,52566L,52567L,52568L,52569L,52570L,52571L,52572L,52573L,52574L, -52575L,52576L,52577L,52578L,52579L,52580L,52581L,52582L,52583L,52584L, -52585L,52586L,52587L,52588L,52589L,52590L,52591L,52592L,52593L,52594L, -52595L,52596L,52597L,52598L,52599L,52600L,52601L,52602L,52603L,52604L, -52605L,52606L,52607L,52608L,52609L,52610L,52611L,52612L,52613L,52614L, -52615L,52616L,52617L,52618L,52619L,52620L,52621L,52622L,52623L,52624L, -52625L,52626L,52627L,52628L,52629L,52630L,52631L,52632L,52633L,52634L, -52635L,52636L,52637L,52638L,52639L,52640L,52641L,52642L,52643L,52644L, -52645L,52646L,52647L,52648L,52649L,52650L,52651L,52652L,52653L,52654L, -52655L,52656L,52657L,52658L,52659L,52660L,52661L,52662L,52663L,52664L, -52665L,52666L,52667L,52668L,52669L,52670L,52671L,52672L,52673L,52674L, -52675L,52676L,52677L,52678L,52679L,52680L,52681L,52682L,52683L,52684L, -52685L,52686L,52687L,52688L,52689L,52690L,52691L,52692L,52693L,52694L, -52695L,52696L,52697L,52698L,52699L,52700L,52701L,52702L,52703L,52704L, -52705L,52706L,52707L,52708L,52709L,52710L,52711L,52712L,52713L,52714L, -52715L,52716L,52717L,52718L,52719L,52720L,52721L,52722L,52723L,52724L, -52725L,52726L,52727L,52728L,52729L,52730L,52731L,52732L,52733L,52734L, -52735L,52736L,52737L,52738L,52739L,52740L,52741L,52742L,52743L,52744L, -52745L,52746L,52747L,52748L,52749L,52750L,52751L,52752L,52753L,52754L, -52755L,52756L,52757L,52758L,52759L,52760L,52761L,52762L,52763L,52764L, -52765L,52766L,52767L,52768L,52769L,52770L,52771L,52772L,52773L,52774L, -52775L,52776L,52777L,52778L,52779L,52780L,52781L,52782L,52783L,52784L, -52785L,52786L,52787L,52788L,52789L,52790L,52791L,52792L,52793L,52794L, -52795L,52796L,52797L,52798L,52799L,52800L,52801L,52802L,52803L,52804L, -52805L,52806L,52807L,52808L,52809L,52810L,52811L,52812L,52813L,52814L, -52815L,52816L,52817L,52818L,52819L,52820L,52821L,52822L,52823L,52824L, -52825L,52826L,52827L,52828L,52829L,52830L,52831L,52832L,52833L,52834L, -52835L,52836L,52837L,52838L,52839L,52840L,52841L,52842L,52843L,52844L, -52845L,52846L,52847L,52848L,52849L,52850L,52851L,52852L,52853L,52854L, -52855L,52856L,52857L,52858L,52859L,52860L,52861L,52862L,52863L,52864L, -52865L,52866L,52867L,52868L,52869L,52870L,52871L,52872L,52873L,52874L, -52875L,52876L,52877L,52878L,52879L,52880L,52881L,52882L,52883L,52884L, -52885L,52886L,52887L,52888L,52889L,52890L,52891L,52892L,52893L,52894L, -52895L,52896L,52897L,52898L,52899L,52900L,52901L,52902L,52903L,52904L, -52905L,52906L,52907L,52908L,52909L,52910L,52911L,52912L,52913L,52914L, -52915L,52916L,52917L,52918L,52919L,52920L,52921L,52922L,52923L,52924L, -52925L,52926L,52927L,52928L,52929L,52930L,52931L,52932L,52933L,52934L, -52935L,52936L,52937L,52938L,52939L,52940L,52941L,52942L,52943L,52944L, -52945L,52946L,52947L,52948L,52949L,52950L,52951L,52952L,52953L,52954L, -52955L,52956L,52957L,52958L,52959L,52960L,52961L,52962L,52963L,52964L, -52965L,52966L,52967L,52968L,52969L,52970L,52971L,52972L,52973L,52974L, -52975L,52976L,52977L,52978L,52979L,52980L,52981L,52982L,52983L,52984L, -52985L,52986L,52987L,52988L,52989L,52990L,52991L,52992L,52993L,52994L, -52995L,52996L,52997L,52998L,52999L,53000L,53001L,53002L,53003L,53004L, -53005L,53006L,53007L,53008L,53009L,53010L,53011L,53012L,53013L,53014L, -53015L,53016L,53017L,53018L,53019L,53020L,53021L,53022L,53023L,53024L, -53025L,53026L,53027L,53028L,53029L,53030L,53031L,53032L,53033L,53034L, -53035L,53036L,53037L,53038L,53039L,53040L,53041L,53042L,53043L,53044L, -53045L,53046L,53047L,53048L,53049L,53050L,53051L,53052L,53053L,53054L, -53055L,53056L,53057L,53058L,53059L,53060L,53061L,53062L,53063L,53064L, -53065L,53066L,53067L,53068L,53069L,53070L,53071L,53072L,53073L,53074L, -53075L,53076L,53077L,53078L,53079L,53080L,53081L,53082L,53083L,53084L, -53085L,53086L,53087L,53088L,53089L,53090L,53091L,53092L,53093L,53094L, -53095L,53096L,53097L,53098L,53099L,53100L,53101L,53102L,53103L,53104L, -53105L,53106L,53107L,53108L,53109L,53110L,53111L,53112L,53113L,53114L, -53115L,53116L,53117L,53118L,53119L,53120L,53121L,53122L,53123L,53124L, -53125L,53126L,53127L,53128L,53129L,53130L,53131L,53132L,53133L,53134L, -53135L,53136L,53137L,53138L,53139L,53140L,53141L,53142L,53143L,53144L, -53145L,53146L,53147L,53148L,53149L,53150L,53151L,53152L,53153L,53154L, -53155L,53156L,53157L,53158L,53159L,53160L,53161L,53162L,53163L,53164L, -53165L,53166L,53167L,53168L,53169L,53170L,53171L,53172L,53173L,53174L, -53175L,53176L,53177L,53178L,53179L,53180L,53181L,53182L,53183L,53184L, -53185L,53186L,53187L,53188L,53189L,53190L,53191L,53192L,53193L,53194L, -53195L,53196L,53197L,53198L,53199L,53200L,53201L,53202L,53203L,53204L, -53205L,53206L,53207L,53208L,53209L,53210L,53211L,53212L,53213L,53214L, -53215L,53216L,53217L,53218L,53219L,53220L,53221L,53222L,53223L,53224L, -53225L,53226L,53227L,53228L,53229L,53230L,53231L,53232L,53233L,53234L, -53235L,53236L,53237L,53238L,53239L,53240L,53241L,53242L,53243L,53244L, -53245L,53246L,53247L,53248L,53249L,53250L,53251L,53252L,53253L,53254L, -53255L,53256L,53257L,53258L,53259L,53260L,53261L,53262L,53263L,53264L, -53265L,53266L,53267L,53268L,53269L,53270L,53271L,53272L,53273L,53274L, -53275L,53276L,53277L,53278L,53279L,53280L,53281L,53282L,53283L,53284L, -53285L,53286L,53287L,53288L,53289L,53290L,53291L,53292L,53293L,53294L, -53295L,53296L,53297L,53298L,53299L,53300L,53301L,53302L,53303L,53304L, -53305L,53306L,53307L,53308L,53309L,53310L,53311L,53312L,53313L,53314L, -53315L,53316L,53317L,53318L,53319L,53320L,53321L,53322L,53323L,53324L, -53325L,53326L,53327L,53328L,53329L,53330L,53331L,53332L,53333L,53334L, -53335L,53336L,53337L,53338L,53339L,53340L,53341L,53342L,53343L,53344L, -53345L,53346L,53347L,53348L,53349L,53350L,53351L,53352L,53353L,53354L, -53355L,53356L,53357L,53358L,53359L,53360L,53361L,53362L,53363L,53364L, -53365L,53366L,53367L,53368L,53369L,53370L,53371L,53372L,53373L,53374L, -53375L,53376L,53377L,53378L,53379L,53380L,53381L,53382L,53383L,53384L, -53385L,53386L,53387L,53388L,53389L,53390L,53391L,53392L,53393L,53394L, -53395L,53396L,53397L,53398L,53399L,53400L,53401L,53402L,53403L,53404L, -53405L,53406L,53407L,53408L,53409L,53410L,53411L,53412L,53413L,53414L, -53415L,53416L,53417L,53418L,53419L,53420L,53421L,53422L,53423L,53424L, -53425L,53426L,53427L,53428L,53429L,53430L,53431L,53432L,53433L,53434L, -53435L,53436L,53437L,53438L,53439L,53440L,53441L,53442L,53443L,53444L, -53445L,53446L,53447L,53448L,53449L,53450L,53451L,53452L,53453L,53454L, -53455L,53456L,53457L,53458L,53459L,53460L,53461L,53462L,53463L,53464L, -53465L,53466L,53467L,53468L,53469L,53470L,53471L,53472L,53473L,53474L, -53475L,53476L,53477L,53478L,53479L,53480L,53481L,53482L,53483L,53484L, -53485L,53486L,53487L,53488L,53489L,53490L,53491L,53492L,53493L,53494L, -53495L,53496L,53497L,53498L,53499L,53500L,53501L,53502L,53503L,53504L, -53505L,53506L,53507L,53508L,53509L,53510L,53511L,53512L,53513L,53514L, -53515L,53516L,53517L,53518L,53519L,53520L,53521L,53522L,53523L,53524L, -53525L,53526L,53527L,53528L,53529L,53530L,53531L,53532L,53533L,53534L, -53535L,53536L,53537L,53538L,53539L,53540L,53541L,53542L,53543L,53544L, -53545L,53546L,53547L,53548L,53549L,53550L,53551L,53552L,53553L,53554L, -53555L,53556L,53557L,53558L,53559L,53560L,53561L,53562L,53563L,53564L, -53565L,53566L,53567L,53568L,53569L,53570L,53571L,53572L,53573L,53574L, -53575L,53576L,53577L,53578L,53579L,53580L,53581L,53582L,53583L,53584L, -53585L,53586L,53587L,53588L,53589L,53590L,53591L,53592L,53593L,53594L, -53595L,53596L,53597L,53598L,53599L,53600L,53601L,53602L,53603L,53604L, -53605L,53606L,53607L,53608L,53609L,53610L,53611L,53612L,53613L,53614L, -53615L,53616L,53617L,53618L,53619L,53620L,53621L,53622L,53623L,53624L, -53625L,53626L,53627L,53628L,53629L,53630L,53631L,53632L,53633L,53634L, -53635L,53636L,53637L,53638L,53639L,53640L,53641L,53642L,53643L,53644L, -53645L,53646L,53647L,53648L,53649L,53650L,53651L,53652L,53653L,53654L, -53655L,53656L,53657L,53658L,53659L,53660L,53661L,53662L,53663L,53664L, -53665L,53666L,53667L,53668L,53669L,53670L,53671L,53672L,53673L,53674L, -53675L,53676L,53677L,53678L,53679L,53680L,53681L,53682L,53683L,53684L, -53685L,53686L,53687L,53688L,53689L,53690L,53691L,53692L,53693L,53694L, -53695L,53696L,53697L,53698L,53699L,53700L,53701L,53702L,53703L,53704L, -53705L,53706L,53707L,53708L,53709L,53710L,53711L,53712L,53713L,53714L, -53715L,53716L,53717L,53718L,53719L,53720L,53721L,53722L,53723L,53724L, -53725L,53726L,53727L,53728L,53729L,53730L,53731L,53732L,53733L,53734L, -53735L,53736L,53737L,53738L,53739L,53740L,53741L,53742L,53743L,53744L, -53745L,53746L,53747L,53748L,53749L,53750L,53751L,53752L,53753L,53754L, -53755L,53756L,53757L,53758L,53759L,53760L,53761L,53762L,53763L,53764L, -53765L,53766L,53767L,53768L,53769L,53770L,53771L,53772L,53773L,53774L, -53775L,53776L,53777L,53778L,53779L,53780L,53781L,53782L,53783L,53784L, -53785L,53786L,53787L,53788L,53789L,53790L,53791L,53792L,53793L,53794L, -53795L,53796L,53797L,53798L,53799L,53800L,53801L,53802L,53803L,53804L, -53805L,53806L,53807L,53808L,53809L,53810L,53811L,53812L,53813L,53814L, -53815L,53816L,53817L,53818L,53819L,53820L,53821L,53822L,53823L,53824L, -53825L,53826L,53827L,53828L,53829L,53830L,53831L,53832L,53833L,53834L, -53835L,53836L,53837L,53838L,53839L,53840L,53841L,53842L,53843L,53844L, -53845L,53846L,53847L,53848L,53849L,53850L,53851L,53852L,53853L,53854L, -53855L,53856L,53857L,53858L,53859L,53860L,53861L,53862L,53863L,53864L, -53865L,53866L,53867L,53868L,53869L,53870L,53871L,53872L,53873L,53874L, -53875L,53876L,53877L,53878L,53879L,53880L,53881L,53882L,53883L,53884L, -53885L,53886L,53887L,53888L,53889L,53890L,53891L,53892L,53893L,53894L, -53895L,53896L,53897L,53898L,53899L,53900L,53901L,53902L,53903L,53904L, -53905L,53906L,53907L,53908L,53909L,53910L,53911L,53912L,53913L,53914L, -53915L,53916L,53917L,53918L,53919L,53920L,53921L,53922L,53923L,53924L, -53925L,53926L,53927L,53928L,53929L,53930L,53931L,53932L,53933L,53934L, -53935L,53936L,53937L,53938L,53939L,53940L,53941L,53942L,53943L,53944L, -53945L,53946L,53947L,53948L,53949L,53950L,53951L,53952L,53953L,53954L, -53955L,53956L,53957L,53958L,53959L,53960L,53961L,53962L,53963L,53964L, -53965L,53966L,53967L,53968L,53969L,53970L,53971L,53972L,53973L,53974L, -53975L,53976L,53977L,53978L,53979L,53980L,53981L,53982L,53983L,53984L, -53985L,53986L,53987L,53988L,53989L,53990L,53991L,53992L,53993L,53994L, -53995L,53996L,53997L,53998L,53999L,54000L,54001L,54002L,54003L,54004L, -54005L,54006L,54007L,54008L,54009L,54010L,54011L,54012L,54013L,54014L, -54015L,54016L,54017L,54018L,54019L,54020L,54021L,54022L,54023L,54024L, -54025L,54026L,54027L,54028L,54029L,54030L,54031L,54032L,54033L,54034L, -54035L,54036L,54037L,54038L,54039L,54040L,54041L,54042L,54043L,54044L, -54045L,54046L,54047L,54048L,54049L,54050L,54051L,54052L,54053L,54054L, -54055L,54056L,54057L,54058L,54059L,54060L,54061L,54062L,54063L,54064L, -54065L,54066L,54067L,54068L,54069L,54070L,54071L,54072L,54073L,54074L, -54075L,54076L,54077L,54078L,54079L,54080L,54081L,54082L,54083L,54084L, -54085L,54086L,54087L,54088L,54089L,54090L,54091L,54092L,54093L,54094L, -54095L,54096L,54097L,54098L,54099L,54100L,54101L,54102L,54103L,54104L, -54105L,54106L,54107L,54108L,54109L,54110L,54111L,54112L,54113L,54114L, -54115L,54116L,54117L,54118L,54119L,54120L,54121L,54122L,54123L,54124L, -54125L,54126L,54127L,54128L,54129L,54130L,54131L,54132L,54133L,54134L, -54135L,54136L,54137L,54138L,54139L,54140L,54141L,54142L,54143L,54144L, -54145L,54146L,54147L,54148L,54149L,54150L,54151L,54152L,54153L,54154L, -54155L,54156L,54157L,54158L,54159L,54160L,54161L,54162L,54163L,54164L, -54165L,54166L,54167L,54168L,54169L,54170L,54171L,54172L,54173L,54174L, -54175L,54176L,54177L,54178L,54179L,54180L,54181L,54182L,54183L,54184L, -54185L,54186L,54187L,54188L,54189L,54190L,54191L,54192L,54193L,54194L, -54195L,54196L,54197L,54198L,54199L,54200L,54201L,54202L,54203L,54204L, -54205L,54206L,54207L,54208L,54209L,54210L,54211L,54212L,54213L,54214L, -54215L,54216L,54217L,54218L,54219L,54220L,54221L,54222L,54223L,54224L, -54225L,54226L,54227L,54228L,54229L,54230L,54231L,54232L,54233L,54234L, -54235L,54236L,54237L,54238L,54239L,54240L,54241L,54242L,54243L,54244L, -54245L,54246L,54247L,54248L,54249L,54250L,54251L,54252L,54253L,54254L, -54255L,54256L,54257L,54258L,54259L,54260L,54261L,54262L,54263L,54264L, -54265L,54266L,54267L,54268L,54269L,54270L,54271L,54272L,54273L,54274L, -54275L,54276L,54277L,54278L,54279L,54280L,54281L,54282L,54283L,54284L, -54285L,54286L,54287L,54288L,54289L,54290L,54291L,54292L,54293L,54294L, -54295L,54296L,54297L,54298L,54299L,54300L,54301L,54302L,54303L,54304L, -54305L,54306L,54307L,54308L,54309L,54310L,54311L,54312L,54313L,54314L, -54315L,54316L,54317L,54318L,54319L,54320L,54321L,54322L,54323L,54324L, -54325L,54326L,54327L,54328L,54329L,54330L,54331L,54332L,54333L,54334L, -54335L,54336L,54337L,54338L,54339L,54340L,54341L,54342L,54343L,54344L, -54345L,54346L,54347L,54348L,54349L,54350L,54351L,54352L,54353L,54354L, -54355L,54356L,54357L,54358L,54359L,54360L,54361L,54362L,54363L,54364L, -54365L,54366L,54367L,54368L,54369L,54370L,54371L,54372L,54373L,54374L, -54375L,54376L,54377L,54378L,54379L,54380L,54381L,54382L,54383L,54384L, -54385L,54386L,54387L,54388L,54389L,54390L,54391L,54392L,54393L,54394L, -54395L,54396L,54397L,54398L,54399L,54400L,54401L,54402L,54403L,54404L, -54405L,54406L,54407L,54408L,54409L,54410L,54411L,54412L,54413L,54414L, -54415L,54416L,54417L,54418L,54419L,54420L,54421L,54422L,54423L,54424L, -54425L,54426L,54427L,54428L,54429L,54430L,54431L,54432L,54433L,54434L, -54435L,54436L,54437L,54438L,54439L,54440L,54441L,54442L,54443L,54444L, -54445L,54446L,54447L,54448L,54449L,54450L,54451L,54452L,54453L,54454L, -54455L,54456L,54457L,54458L,54459L,54460L,54461L,54462L,54463L,54464L, -54465L,54466L,54467L,54468L,54469L,54470L,54471L,54472L,54473L,54474L, -54475L,54476L,54477L,54478L,54479L,54480L,54481L,54482L,54483L,54484L, -54485L,54486L,54487L,54488L,54489L,54490L,54491L,54492L,54493L,54494L, -54495L,54496L,54497L,54498L,54499L,54500L,54501L,54502L,54503L,54504L, -54505L,54506L,54507L,54508L,54509L,54510L,54511L,54512L,54513L,54514L, -54515L,54516L,54517L,54518L,54519L,54520L,54521L,54522L,54523L,54524L, -54525L,54526L,54527L,54528L,54529L,54530L,54531L,54532L,54533L,54534L, -54535L,54536L,54537L,54538L,54539L,54540L,54541L,54542L,54543L,54544L, -54545L,54546L,54547L,54548L,54549L,54550L,54551L,54552L,54553L,54554L, -54555L,54556L,54557L,54558L,54559L,54560L,54561L,54562L,54563L,54564L, -54565L,54566L,54567L,54568L,54569L,54570L,54571L,54572L,54573L,54574L, -54575L,54576L,54577L,54578L,54579L,54580L,54581L,54582L,54583L,54584L, -54585L,54586L,54587L,54588L,54589L,54590L,54591L,54592L,54593L,54594L, -54595L,54596L,54597L,54598L,54599L,54600L,54601L,54602L,54603L,54604L, -54605L,54606L,54607L,54608L,54609L,54610L,54611L,54612L,54613L,54614L, -54615L,54616L,54617L,54618L,54619L,54620L,54621L,54622L,54623L,54624L, -54625L,54626L,54627L,54628L,54629L,54630L,54631L,54632L,54633L,54634L, -54635L,54636L,54637L,54638L,54639L,54640L,54641L,54642L,54643L,54644L, -54645L,54646L,54647L,54648L,54649L,54650L,54651L,54652L,54653L,54654L, -54655L,54656L,54657L,54658L,54659L,54660L,54661L,54662L,54663L,54664L, -54665L,54666L,54667L,54668L,54669L,54670L,54671L,54672L,54673L,54674L, -54675L,54676L,54677L,54678L,54679L,54680L,54681L,54682L,54683L,54684L, -54685L,54686L,54687L,54688L,54689L,54690L,54691L,54692L,54693L,54694L, -54695L,54696L,54697L,54698L,54699L,54700L,54701L,54702L,54703L,54704L, -54705L,54706L,54707L,54708L,54709L,54710L,54711L,54712L,54713L,54714L, -54715L,54716L,54717L,54718L,54719L,54720L,54721L,54722L,54723L,54724L, -54725L,54726L,54727L,54728L,54729L,54730L,54731L,54732L,54733L,54734L, -54735L,54736L,54737L,54738L,54739L,54740L,54741L,54742L,54743L,54744L, -54745L,54746L,54747L,54748L,54749L,54750L,54751L,54752L,54753L,54754L, -54755L,54756L,54757L,54758L,54759L,54760L,54761L,54762L,54763L,54764L, -54765L,54766L,54767L,54768L,54769L,54770L,54771L,54772L,54773L,54774L, -54775L,54776L,54777L,54778L,54779L,54780L,54781L,54782L,54783L,54784L, -54785L,54786L,54787L,54788L,54789L,54790L,54791L,54792L,54793L,54794L, -54795L,54796L,54797L,54798L,54799L,54800L,54801L,54802L,54803L,54804L, -54805L,54806L,54807L,54808L,54809L,54810L,54811L,54812L,54813L,54814L, -54815L,54816L,54817L,54818L,54819L,54820L,54821L,54822L,54823L,54824L, -54825L,54826L,54827L,54828L,54829L,54830L,54831L,54832L,54833L,54834L, -54835L,54836L,54837L,54838L,54839L,54840L,54841L,54842L,54843L,54844L, -54845L,54846L,54847L,54848L,54849L,54850L,54851L,54852L,54853L,54854L, -54855L,54856L,54857L,54858L,54859L,54860L,54861L,54862L,54863L,54864L, -54865L,54866L,54867L,54868L,54869L,54870L,54871L,54872L,54873L,54874L, -54875L,54876L,54877L,54878L,54879L,54880L,54881L,54882L,54883L,54884L, -54885L,54886L,54887L,54888L,54889L,54890L,54891L,54892L,54893L,54894L, -54895L,54896L,54897L,54898L,54899L,54900L,54901L,54902L,54903L,54904L, -54905L,54906L,54907L,54908L,54909L,54910L,54911L,54912L,54913L,54914L, -54915L,54916L,54917L,54918L,54919L,54920L,54921L,54922L,54923L,54924L, -54925L,54926L,54927L,54928L,54929L,54930L,54931L,54932L,54933L,54934L, -54935L,54936L,54937L,54938L,54939L,54940L,54941L,54942L,54943L,54944L, -54945L,54946L,54947L,54948L,54949L,54950L,54951L,54952L,54953L,54954L, -54955L,54956L,54957L,54958L,54959L,54960L,54961L,54962L,54963L,54964L, -54965L,54966L,54967L,54968L,54969L,54970L,54971L,54972L,54973L,54974L, -54975L,54976L,54977L,54978L,54979L,54980L,54981L,54982L,54983L,54984L, -54985L,54986L,54987L,54988L,54989L,54990L,54991L,54992L,54993L,54994L, -54995L,54996L,54997L,54998L,54999L,55000L,55001L,55002L,55003L,55004L, -55005L,55006L,55007L,55008L,55009L,55010L,55011L,55012L,55013L,55014L, -55015L,55016L,55017L,55018L,55019L,55020L,55021L,55022L,55023L,55024L, -55025L,55026L,55027L,55028L,55029L,55030L,55031L,55032L,55033L,55034L, -55035L,55036L,55037L,55038L,55039L,55040L,55041L,55042L,55043L,55044L, -55045L,55046L,55047L,55048L,55049L,55050L,55051L,55052L,55053L,55054L, -55055L,55056L,55057L,55058L,55059L,55060L,55061L,55062L,55063L,55064L, -55065L,55066L,55067L,55068L,55069L,55070L,55071L,55072L,55073L,55074L, -55075L,55076L,55077L,55078L,55079L,55080L,55081L,55082L,55083L,55084L, -55085L,55086L,55087L,55088L,55089L,55090L,55091L,55092L,55093L,55094L, -55095L,55096L,55097L,55098L,55099L,55100L,55101L,55102L,55103L,55104L, -55105L,55106L,55107L,55108L,55109L,55110L,55111L,55112L,55113L,55114L, -55115L,55116L,55117L,55118L,55119L,55120L,55121L,55122L,55123L,55124L, -55125L,55126L,55127L,55128L,55129L,55130L,55131L,55132L,55133L,55134L, -55135L,55136L,55137L,55138L,55139L,55140L,55141L,55142L,55143L,55144L, -55145L,55146L,55147L,55148L,55149L,55150L,55151L,55152L,55153L,55154L, -55155L,55156L,55157L,55158L,55159L,55160L,55161L,55162L,55163L,55164L, -55165L,55166L,55167L,55168L,55169L,55170L,55171L,55172L,55173L,55174L, -55175L,55176L,55177L,55178L,55179L,55180L,55181L,55182L,55183L,55184L, -55185L,55186L,55187L,55188L,55189L,55190L,55191L,55192L,55193L,55194L, -55195L,55196L,55197L,55198L,55199L,55200L,55201L,55202L,55203L,55204L, -55205L,55206L,55207L,55208L,55209L,55210L,55211L,55212L,55213L,55214L, -55215L,55216L,55217L,55218L,55219L,55220L,55221L,55222L,55223L,55224L, -55225L,55226L,55227L,55228L,55229L,55230L,55231L,55232L,55233L,55234L, -55235L,55236L,55237L,55238L,55239L,55240L,55241L,55242L,55243L,55244L, -55245L,55246L,55247L,55248L,55249L,55250L,55251L,55252L,55253L,55254L, -55255L,55256L,55257L,55258L,55259L,55260L,55261L,55262L,55263L,55264L, -55265L,55266L,55267L,55268L,55269L,55270L,55271L,55272L,55273L,55274L, -55275L,55276L,55277L,55278L,55279L,55280L,55281L,55282L,55283L,55284L, -55285L,55286L,55287L,55288L,55289L,55290L,55291L,55292L,55293L,55294L, -55295L,55296L,55297L,55298L,55299L,55300L,55301L,55302L,55303L,55304L, -55305L,55306L,55307L,55308L,55309L,55310L,55311L,55312L,55313L,55314L, -55315L,55316L,55317L,55318L,55319L,55320L,55321L,55322L,55323L,55324L, -55325L,55326L,55327L,55328L,55329L,55330L,55331L,55332L,55333L,55334L, -55335L,55336L,55337L,55338L,55339L,55340L,55341L,55342L,55343L,55344L, -55345L,55346L,55347L,55348L,55349L,55350L,55351L,55352L,55353L,55354L, -55355L,55356L,55357L,55358L,55359L,55360L,55361L,55362L,55363L,55364L, -55365L,55366L,55367L,55368L,55369L,55370L,55371L,55372L,55373L,55374L, -55375L,55376L,55377L,55378L,55379L,55380L,55381L,55382L,55383L,55384L, -55385L,55386L,55387L,55388L,55389L,55390L,55391L,55392L,55393L,55394L, -55395L,55396L,55397L,55398L,55399L,55400L,55401L,55402L,55403L,55404L, -55405L,55406L,55407L,55408L,55409L,55410L,55411L,55412L,55413L,55414L, -55415L,55416L,55417L,55418L,55419L,55420L,55421L,55422L,55423L,55424L, -55425L,55426L,55427L,55428L,55429L,55430L,55431L,55432L,55433L,55434L, -55435L,55436L,55437L,55438L,55439L,55440L,55441L,55442L,55443L,55444L, -55445L,55446L,55447L,55448L,55449L,55450L,55451L,55452L,55453L,55454L, -55455L,55456L,55457L,55458L,55459L,55460L,55461L,55462L,55463L,55464L, -55465L,55466L,55467L,55468L,55469L,55470L,55471L,55472L,55473L,55474L, -55475L,55476L,55477L,55478L,55479L,55480L,55481L,55482L,55483L,55484L, -55485L,55486L,55487L,55488L,55489L,55490L,55491L,55492L,55493L,55494L, -55495L,55496L,55497L,55498L,55499L,55500L,55501L,55502L,55503L,55504L, -55505L,55506L,55507L,55508L,55509L,55510L,55511L,55512L,55513L,55514L, -55515L,55516L,55517L,55518L,55519L,55520L,55521L,55522L,55523L,55524L, -55525L,55526L,55527L,55528L,55529L,55530L,55531L,55532L,55533L,55534L, -55535L,55536L,55537L,55538L,55539L,55540L,55541L,55542L,55543L,55544L, -55545L,55546L,55547L,55548L,55549L,55550L,55551L,55552L,55553L,55554L, -55555L,55556L,55557L,55558L,55559L,55560L,55561L,55562L,55563L,55564L, -55565L,55566L,55567L,55568L,55569L,55570L,55571L,55572L,55573L,55574L, -55575L,55576L,55577L,55578L,55579L,55580L,55581L,55582L,55583L,55584L, -55585L,55586L,55587L,55588L,55589L,55590L,55591L,55592L,55593L,55594L, -55595L,55596L,55597L,55598L,55599L,55600L,55601L,55602L,55603L,55604L, -55605L,55606L,55607L,55608L,55609L,55610L,55611L,55612L,55613L,55614L, -55615L,55616L,55617L,55618L,55619L,55620L,55621L,55622L,55623L,55624L, -55625L,55626L,55627L,55628L,55629L,55630L,55631L,55632L,55633L,55634L, -55635L,55636L,55637L,55638L,55639L,55640L,55641L,55642L,55643L,55644L, -55645L,55646L,55647L,55648L,55649L,55650L,55651L,55652L,55653L,55654L, -55655L,55656L,55657L,55658L,55659L,55660L,55661L,55662L,55663L,55664L, -55665L,55666L,55667L,55668L,55669L,55670L,55671L,55672L,55673L,55674L, -55675L,55676L,55677L,55678L,55679L,55680L,55681L,55682L,55683L,55684L, -55685L,55686L,55687L,55688L,55689L,55690L,55691L,55692L,55693L,55694L, -55695L,55696L,55697L,55698L,55699L,55700L,55701L,55702L,55703L,55704L, -55705L,55706L,55707L,55708L,55709L,55710L,55711L,55712L,55713L,55714L, -55715L,55716L,55717L,55718L,55719L,55720L,55721L,55722L,55723L,55724L, -55725L,55726L,55727L,55728L,55729L,55730L,55731L,55732L,55733L,55734L, -55735L,55736L,55737L,55738L,55739L,55740L,55741L,55742L,55743L,55744L, -55745L,55746L,55747L,55748L,55749L,55750L,55751L,55752L,55753L,55754L, -55755L,55756L,55757L,55758L,55759L,55760L,55761L,55762L,55763L,55764L, -55765L,55766L,55767L,55768L,55769L,55770L,55771L,55772L,55773L,55774L, -55775L,55776L,55777L,55778L,55779L,55780L,55781L,55782L,55783L,55784L, -55785L,55786L,55787L,55788L,55789L,55790L,55791L,55792L,55793L,55794L, -55795L,55796L,55797L,55798L,55799L,55800L,55801L,55802L,55803L,55804L, -55805L,55806L,55807L,55808L,55809L,55810L,55811L,55812L,55813L,55814L, -55815L,55816L,55817L,55818L,55819L,55820L,55821L,55822L,55823L,55824L, -55825L,55826L,55827L,55828L,55829L,55830L,55831L,55832L,55833L,55834L, -55835L,55836L,55837L,55838L,55839L,55840L,55841L,55842L,55843L,55844L, -55845L,55846L,55847L,55848L,55849L,55850L,55851L,55852L,55853L,55854L, -55855L,55856L,55857L,55858L,55859L,55860L,55861L,55862L,55863L,55864L, -55865L,55866L,55867L,55868L,55869L,55870L,55871L,55872L,55873L,55874L, -55875L,55876L,55877L,55878L,55879L,55880L,55881L,55882L,55883L,55884L, -55885L,55886L,55887L,55888L,55889L,55890L,55891L,55892L,55893L,55894L, -55895L,55896L,55897L,55898L,55899L,55900L,55901L,55902L,55903L,55904L, -55905L,55906L,55907L,55908L,55909L,55910L,55911L,55912L,55913L,55914L, -55915L,55916L,55917L,55918L,55919L,55920L,55921L,55922L,55923L,55924L, -55925L,55926L,55927L,55928L,55929L,55930L,55931L,55932L,55933L,55934L, -55935L,55936L,55937L,55938L,55939L,55940L,55941L,55942L,55943L,55944L, -55945L,55946L,55947L,55948L,55949L,55950L,55951L,55952L,55953L,55954L, -55955L,55956L,55957L,55958L,55959L,55960L,55961L,55962L,55963L,55964L, -55965L,55966L,55967L,55968L,55969L,55970L,55971L,55972L,55973L,55974L, -55975L,55976L,55977L,55978L,55979L,55980L,55981L,55982L,55983L,55984L, -55985L,55986L,55987L,55988L,55989L,55990L,55991L,55992L,55993L,55994L, -55995L,55996L,55997L,55998L,55999L,56000L,56001L,56002L,56003L,56004L, -56005L,56006L,56007L,56008L,56009L,56010L,56011L,56012L,56013L,56014L, -56015L,56016L,56017L,56018L,56019L,56020L,56021L,56022L,56023L,56024L, -56025L,56026L,56027L,56028L,56029L,56030L,56031L,56032L,56033L,56034L, -56035L,56036L,56037L,56038L,56039L,56040L,56041L,56042L,56043L,56044L, -56045L,56046L,56047L,56048L,56049L,56050L,56051L,56052L,56053L,56054L, -56055L,56056L,56057L,56058L,56059L,56060L,56061L,56062L,56063L,56064L, -56065L,56066L,56067L,56068L,56069L,56070L,56071L,56072L,56073L,56074L, -56075L,56076L,56077L,56078L,56079L,56080L,56081L,56082L,56083L,56084L, -56085L,56086L,56087L,56088L,56089L,56090L,56091L,56092L,56093L,56094L, -56095L,56096L,56097L,56098L,56099L,56100L,56101L,56102L,56103L,56104L, -56105L,56106L,56107L,56108L,56109L,56110L,56111L,56112L,56113L,56114L, -56115L,56116L,56117L,56118L,56119L,56120L,56121L,56122L,56123L,56124L, -56125L,56126L,56127L,56128L,56129L,56130L,56131L,56132L,56133L,56134L, -56135L,56136L,56137L,56138L,56139L,56140L,56141L,56142L,56143L,56144L, -56145L,56146L,56147L,56148L,56149L,56150L,56151L,56152L,56153L,56154L, -56155L,56156L,56157L,56158L,56159L,56160L,56161L,56162L,56163L,56164L, -56165L,56166L,56167L,56168L,56169L,56170L,56171L,56172L,56173L,56174L, -56175L,56176L,56177L,56178L,56179L,56180L,56181L,56182L,56183L,56184L, -56185L,56186L,56187L,56188L,56189L,56190L,56191L,56192L,56193L,56194L, -56195L,56196L,56197L,56198L,56199L,56200L,56201L,56202L,56203L,56204L, -56205L,56206L,56207L,56208L,56209L,56210L,56211L,56212L,56213L,56214L, -56215L,56216L,56217L,56218L,56219L,56220L,56221L,56222L,56223L,56224L, -56225L,56226L,56227L,56228L,56229L,56230L,56231L,56232L,56233L,56234L, -56235L,56236L,56237L,56238L,56239L,56240L,56241L,56242L,56243L,56244L, -56245L,56246L,56247L,56248L,56249L,56250L,56251L,56252L,56253L,56254L, -56255L,56256L,56257L,56258L,56259L,56260L,56261L,56262L,56263L,56264L, -56265L,56266L,56267L,56268L,56269L,56270L,56271L,56272L,56273L,56274L, -56275L,56276L,56277L,56278L,56279L,56280L,56281L,56282L,56283L,56284L, -56285L,56286L,56287L,56288L,56289L,56290L,56291L,56292L,56293L,56294L, -56295L,56296L,56297L,56298L,56299L,56300L,56301L,56302L,56303L,56304L, -56305L,56306L,56307L,56308L,56309L,56310L,56311L,56312L,56313L,56314L, -56315L,56316L,56317L,56318L,56319L,56320L,56321L,56322L,56323L,56324L, -56325L,56326L,56327L,56328L,56329L,56330L,56331L,56332L,56333L,56334L, -56335L,56336L,56337L,56338L,56339L,56340L,56341L,56342L,56343L,56344L, -56345L,56346L,56347L,56348L,56349L,56350L,56351L,56352L,56353L,56354L, -56355L,56356L,56357L,56358L,56359L,56360L,56361L,56362L,56363L,56364L, -56365L,56366L,56367L,56368L,56369L,56370L,56371L,56372L,56373L,56374L, -56375L,56376L,56377L,56378L,56379L,56380L,56381L,56382L,56383L,56384L, -56385L,56386L,56387L,56388L,56389L,56390L,56391L,56392L,56393L,56394L, -56395L,56396L,56397L,56398L,56399L,56400L,56401L,56402L,56403L,56404L, -56405L,56406L,56407L,56408L,56409L,56410L,56411L,56412L,56413L,56414L, -56415L,56416L,56417L,56418L,56419L,56420L,56421L,56422L,56423L,56424L, -56425L,56426L,56427L,56428L,56429L,56430L,56431L,56432L,56433L,56434L, -56435L,56436L,56437L,56438L,56439L,56440L,56441L,56442L,56443L,56444L, -56445L,56446L,56447L,56448L,56449L,56450L,56451L,56452L,56453L,56454L, -56455L,56456L,56457L,56458L,56459L,56460L,56461L,56462L,56463L,56464L, -56465L,56466L,56467L,56468L,56469L,56470L,56471L,56472L,56473L,56474L, -56475L,56476L,56477L,56478L,56479L,56480L,56481L,56482L,56483L,56484L, -56485L,56486L,56487L,56488L,56489L,56490L,56491L,56492L,56493L,56494L, -56495L,56496L,56497L,56498L,56499L,56500L,56501L,56502L,56503L,56504L, -56505L,56506L,56507L,56508L,56509L,56510L,56511L,56512L,56513L,56514L, -56515L,56516L,56517L,56518L,56519L,56520L,56521L,56522L,56523L,56524L, -56525L,56526L,56527L,56528L,56529L,56530L,56531L,56532L,56533L,56534L, -56535L,56536L,56537L,56538L,56539L,56540L,56541L,56542L,56543L,56544L, -56545L,56546L,56547L,56548L,56549L,56550L,56551L,56552L,56553L,56554L, -56555L,56556L,56557L,56558L,56559L,56560L,56561L,56562L,56563L,56564L, -56565L,56566L,56567L,56568L,56569L,56570L,56571L,56572L,56573L,56574L, -56575L,56576L,56577L,56578L,56579L,56580L,56581L,56582L,56583L,56584L, -56585L,56586L,56587L,56588L,56589L,56590L,56591L,56592L,56593L,56594L, -56595L,56596L,56597L,56598L,56599L,56600L,56601L,56602L,56603L,56604L, -56605L,56606L,56607L,56608L,56609L,56610L,56611L,56612L,56613L,56614L, -56615L,56616L,56617L,56618L,56619L,56620L,56621L,56622L,56623L,56624L, -56625L,56626L,56627L,56628L,56629L,56630L,56631L,56632L,56633L,56634L, -56635L,56636L,56637L,56638L,56639L,56640L,56641L,56642L,56643L,56644L, -56645L,56646L,56647L,56648L,56649L,56650L,56651L,56652L,56653L,56654L, -56655L,56656L,56657L,56658L,56659L,56660L,56661L,56662L,56663L,56664L, -56665L,56666L,56667L,56668L,56669L,56670L,56671L,56672L,56673L,56674L, -56675L,56676L,56677L,56678L,56679L,56680L,56681L,56682L,56683L,56684L, -56685L,56686L,56687L,56688L,56689L,56690L,56691L,56692L,56693L,56694L, -56695L,56696L,56697L,56698L,56699L,56700L,56701L,56702L,56703L,56704L, -56705L,56706L,56707L,56708L,56709L,56710L,56711L,56712L,56713L,56714L, -56715L,56716L,56717L,56718L,56719L,56720L,56721L,56722L,56723L,56724L, -56725L,56726L,56727L,56728L,56729L,56730L,56731L,56732L,56733L,56734L, -56735L,56736L,56737L,56738L,56739L,56740L,56741L,56742L,56743L,56744L, -56745L,56746L,56747L,56748L,56749L,56750L,56751L,56752L,56753L,56754L, -56755L,56756L,56757L,56758L,56759L,56760L,56761L,56762L,56763L,56764L, -56765L,56766L,56767L,56768L,56769L,56770L,56771L,56772L,56773L,56774L, -56775L,56776L,56777L,56778L,56779L,56780L,56781L,56782L,56783L,56784L, -56785L,56786L,56787L,56788L,56789L,56790L,56791L,56792L,56793L,56794L, -56795L,56796L,56797L,56798L,56799L,56800L,56801L,56802L,56803L,56804L, -56805L,56806L,56807L,56808L,56809L,56810L,56811L,56812L,56813L,56814L, -56815L,56816L,56817L,56818L,56819L,56820L,56821L,56822L,56823L,56824L, -56825L,56826L,56827L,56828L,56829L,56830L,56831L,56832L,56833L,56834L, -56835L,56836L,56837L,56838L,56839L,56840L,56841L,56842L,56843L,56844L, -56845L,56846L,56847L,56848L,56849L,56850L,56851L,56852L,56853L,56854L, -56855L,56856L,56857L,56858L,56859L,56860L,56861L,56862L,56863L,56864L, -56865L,56866L,56867L,56868L,56869L,56870L,56871L,56872L,56873L,56874L, -56875L,56876L,56877L,56878L,56879L,56880L,56881L,56882L,56883L,56884L, -56885L,56886L,56887L,56888L,56889L,56890L,56891L,56892L,56893L,56894L, -56895L,56896L,56897L,56898L,56899L,56900L,56901L,56902L,56903L,56904L, -56905L,56906L,56907L,56908L,56909L,56910L,56911L,56912L,56913L,56914L, -56915L,56916L,56917L,56918L,56919L,56920L,56921L,56922L,56923L,56924L, -56925L,56926L,56927L,56928L,56929L,56930L,56931L,56932L,56933L,56934L, -56935L,56936L,56937L,56938L,56939L,56940L,56941L,56942L,56943L,56944L, -56945L,56946L,56947L,56948L,56949L,56950L,56951L,56952L,56953L,56954L, -56955L,56956L,56957L,56958L,56959L,56960L,56961L,56962L,56963L,56964L, -56965L,56966L,56967L,56968L,56969L,56970L,56971L,56972L,56973L,56974L, -56975L,56976L,56977L,56978L,56979L,56980L,56981L,56982L,56983L,56984L, -56985L,56986L,56987L,56988L,56989L,56990L,56991L,56992L,56993L,56994L, -56995L,56996L,56997L,56998L,56999L,57000L,57001L,57002L,57003L,57004L, -57005L,57006L,57007L,57008L,57009L,57010L,57011L,57012L,57013L,57014L, -57015L,57016L,57017L,57018L,57019L,57020L,57021L,57022L,57023L,57024L, -57025L,57026L,57027L,57028L,57029L,57030L,57031L,57032L,57033L,57034L, -57035L,57036L,57037L,57038L,57039L,57040L,57041L,57042L,57043L,57044L, -57045L,57046L,57047L,57048L,57049L,57050L,57051L,57052L,57053L,57054L, -57055L,57056L,57057L,57058L,57059L,57060L,57061L,57062L,57063L,57064L, -57065L,57066L,57067L,57068L,57069L,57070L,57071L,57072L,57073L,57074L, -57075L,57076L,57077L,57078L,57079L,57080L,57081L,57082L,57083L,57084L, -57085L,57086L,57087L,57088L,57089L,57090L,57091L,57092L,57093L,57094L, -57095L,57096L,57097L,57098L,57099L,57100L,57101L,57102L,57103L,57104L, -57105L,57106L,57107L,57108L,57109L,57110L,57111L,57112L,57113L,57114L, -57115L,57116L,57117L,57118L,57119L,57120L,57121L,57122L,57123L,57124L, -57125L,57126L,57127L,57128L,57129L,57130L,57131L,57132L,57133L,57134L, -57135L,57136L,57137L,57138L,57139L,57140L,57141L,57142L,57143L,57144L, -57145L,57146L,57147L,57148L,57149L,57150L,57151L,57152L,57153L,57154L, -57155L,57156L,57157L,57158L,57159L,57160L,57161L,57162L,57163L,57164L, -57165L,57166L,57167L,57168L,57169L,57170L,57171L,57172L,57173L,57174L, -57175L,57176L,57177L,57178L,57179L,57180L,57181L,57182L,57183L,57184L, -57185L,57186L,57187L,57188L,57189L,57190L,57191L,57192L,57193L,57194L, -57195L,57196L,57197L,57198L,57199L,57200L,57201L,57202L,57203L,57204L, -57205L,57206L,57207L,57208L,57209L,57210L,57211L,57212L,57213L,57214L, -57215L,57216L,57217L,57218L,57219L,57220L,57221L,57222L,57223L,57224L, -57225L,57226L,57227L,57228L,57229L,57230L,57231L,57232L,57233L,57234L, -57235L,57236L,57237L,57238L,57239L,57240L,57241L,57242L,57243L,57244L, -57245L,57246L,57247L,57248L,57249L,57250L,57251L,57252L,57253L,57254L, -57255L,57256L,57257L,57258L,57259L,57260L,57261L,57262L,57263L,57264L, -57265L,57266L,57267L,57268L,57269L,57270L,57271L,57272L,57273L,57274L, -57275L,57276L,57277L,57278L,57279L,57280L,57281L,57282L,57283L,57284L, -57285L,57286L,57287L,57288L,57289L,57290L,57291L,57292L,57293L,57294L, -57295L,57296L,57297L,57298L,57299L,57300L,57301L,57302L,57303L,57304L, -57305L,57306L,57307L,57308L,57309L,57310L,57311L,57312L,57313L,57314L, -57315L,57316L,57317L,57318L,57319L,57320L,57321L,57322L,57323L,57324L, -57325L,57326L,57327L,57328L,57329L,57330L,57331L,57332L,57333L,57334L, -57335L,57336L,57337L,57338L,57339L,57340L,57341L,57342L,57343L,57344L, -57345L,57346L,57347L,57348L,57349L,57350L,57351L,57352L,57353L,57354L, -57355L,57356L,57357L,57358L,57359L,57360L,57361L,57362L,57363L,57364L, -57365L,57366L,57367L,57368L,57369L,57370L,57371L,57372L,57373L,57374L, -57375L,57376L,57377L,57378L,57379L,57380L,57381L,57382L,57383L,57384L, -57385L,57386L,57387L,57388L,57389L,57390L,57391L,57392L,57393L,57394L, -57395L,57396L,57397L,57398L,57399L,57400L,57401L,57402L,57403L,57404L, -57405L,57406L,57407L,57408L,57409L,57410L,57411L,57412L,57413L,57414L, -57415L,57416L,57417L,57418L,57419L,57420L,57421L,57422L,57423L,57424L, -57425L,57426L,57427L,57428L,57429L,57430L,57431L,57432L,57433L,57434L, -57435L,57436L,57437L,57438L,57439L,57440L,57441L,57442L,57443L,57444L, -57445L,57446L,57447L,57448L,57449L,57450L,57451L,57452L,57453L,57454L, -57455L,57456L,57457L,57458L,57459L,57460L,57461L,57462L,57463L,57464L, -57465L,57466L,57467L,57468L,57469L,57470L,57471L,57472L,57473L,57474L, -57475L,57476L,57477L,57478L,57479L,57480L,57481L,57482L,57483L,57484L, -57485L,57486L,57487L,57488L,57489L,57490L,57491L,57492L,57493L,57494L, -57495L,57496L,57497L,57498L,57499L,57500L,57501L,57502L,57503L,57504L, -57505L,57506L,57507L,57508L,57509L,57510L,57511L,57512L,57513L,57514L, -57515L,57516L,57517L,57518L,57519L,57520L,57521L,57522L,57523L,57524L, -57525L,57526L,57527L,57528L,57529L,57530L,57531L,57532L,57533L,57534L, -57535L,57536L,57537L,57538L,57539L,57540L,57541L,57542L,57543L,57544L, -57545L,57546L,57547L,57548L,57549L,57550L,57551L,57552L,57553L,57554L, -57555L,57556L,57557L,57558L,57559L,57560L,57561L,57562L,57563L,57564L, -57565L,57566L,57567L,57568L,57569L,57570L,57571L,57572L,57573L,57574L, -57575L,57576L,57577L,57578L,57579L,57580L,57581L,57582L,57583L,57584L, -57585L,57586L,57587L,57588L,57589L,57590L,57591L,57592L,57593L,57594L, -57595L,57596L,57597L,57598L,57599L,57600L,57601L,57602L,57603L,57604L, -57605L,57606L,57607L,57608L,57609L,57610L,57611L,57612L,57613L,57614L, -57615L,57616L,57617L,57618L,57619L,57620L,57621L,57622L,57623L,57624L, -57625L,57626L,57627L,57628L,57629L,57630L,57631L,57632L,57633L,57634L, -57635L,57636L,57637L,57638L,57639L,57640L,57641L,57642L,57643L,57644L, -57645L,57646L,57647L,57648L,57649L,57650L,57651L,57652L,57653L,57654L, -57655L,57656L,57657L,57658L,57659L,57660L,57661L,57662L,57663L,57664L, -57665L,57666L,57667L,57668L,57669L,57670L,57671L,57672L,57673L,57674L, -57675L,57676L,57677L,57678L,57679L,57680L,57681L,57682L,57683L,57684L, -57685L,57686L,57687L,57688L,57689L,57690L,57691L,57692L,57693L,57694L, -57695L,57696L,57697L,57698L,57699L,57700L,57701L,57702L,57703L,57704L, -57705L,57706L,57707L,57708L,57709L,57710L,57711L,57712L,57713L,57714L, -57715L,57716L,57717L,57718L,57719L,57720L,57721L,57722L,57723L,57724L, -57725L,57726L,57727L,57728L,57729L,57730L,57731L,57732L,57733L,57734L, -57735L,57736L,57737L,57738L,57739L,57740L,57741L,57742L,57743L,57744L, -57745L,57746L,57747L,57748L,57749L,57750L,57751L,57752L,57753L,57754L, -57755L,57756L,57757L,57758L,57759L,57760L,57761L,57762L,57763L,57764L, -57765L,57766L,57767L,57768L,57769L,57770L,57771L,57772L,57773L,57774L, -57775L,57776L,57777L,57778L,57779L,57780L,57781L,57782L,57783L,57784L, -57785L,57786L,57787L,57788L,57789L,57790L,57791L,57792L,57793L,57794L, -57795L,57796L,57797L,57798L,57799L,57800L,57801L,57802L,57803L,57804L, -57805L,57806L,57807L,57808L,57809L,57810L,57811L,57812L,57813L,57814L, -57815L,57816L,57817L,57818L,57819L,57820L,57821L,57822L,57823L,57824L, -57825L,57826L,57827L,57828L,57829L,57830L,57831L,57832L,57833L,57834L, -57835L,57836L,57837L,57838L,57839L,57840L,57841L,57842L,57843L,57844L, -57845L,57846L,57847L,57848L,57849L,57850L,57851L,57852L,57853L,57854L, -57855L,57856L,57857L,57858L,57859L,57860L,57861L,57862L,57863L,57864L, -57865L,57866L,57867L,57868L,57869L,57870L,57871L,57872L,57873L,57874L, -57875L,57876L,57877L,57878L,57879L,57880L,57881L,57882L,57883L,57884L, -57885L,57886L,57887L,57888L,57889L,57890L,57891L,57892L,57893L,57894L, -57895L,57896L,57897L,57898L,57899L,57900L,57901L,57902L,57903L,57904L, -57905L,57906L,57907L,57908L,57909L,57910L,57911L,57912L,57913L,57914L, -57915L,57916L,57917L,57918L,57919L,57920L,57921L,57922L,57923L,57924L, -57925L,57926L,57927L,57928L,57929L,57930L,57931L,57932L,57933L,57934L, -57935L,57936L,57937L,57938L,57939L,57940L,57941L,57942L,57943L,57944L, -57945L,57946L,57947L,57948L,57949L,57950L,57951L,57952L,57953L,57954L, -57955L,57956L,57957L,57958L,57959L,57960L,57961L,57962L,57963L,57964L, -57965L,57966L,57967L,57968L,57969L,57970L,57971L,57972L,57973L,57974L, -57975L,57976L,57977L,57978L,57979L,57980L,57981L,57982L,57983L,57984L, -57985L,57986L,57987L,57988L,57989L,57990L,57991L,57992L,57993L,57994L, -57995L,57996L,57997L,57998L,57999L,58000L,58001L,58002L,58003L,58004L, -58005L,58006L,58007L,58008L,58009L,58010L,58011L,58012L,58013L,58014L, -58015L,58016L,58017L,58018L,58019L,58020L,58021L,58022L,58023L,58024L, -58025L,58026L,58027L,58028L,58029L,58030L,58031L,58032L,58033L,58034L, -58035L,58036L,58037L,58038L,58039L,58040L,58041L,58042L,58043L,58044L, -58045L,58046L,58047L,58048L,58049L,58050L,58051L,58052L,58053L,58054L, -58055L,58056L,58057L,58058L,58059L,58060L,58061L,58062L,58063L,58064L, -58065L,58066L,58067L,58068L,58069L,58070L,58071L,58072L,58073L,58074L, -58075L,58076L,58077L,58078L,58079L,58080L,58081L,58082L,58083L,58084L, -58085L,58086L,58087L,58088L,58089L,58090L,58091L,58092L,58093L,58094L, -58095L,58096L,58097L,58098L,58099L,58100L,58101L,58102L,58103L,58104L, -58105L,58106L,58107L,58108L,58109L,58110L,58111L,58112L,58113L,58114L, -58115L,58116L,58117L,58118L,58119L,58120L,58121L,58122L,58123L,58124L, -58125L,58126L,58127L,58128L,58129L,58130L,58131L,58132L,58133L,58134L, -58135L,58136L,58137L,58138L,58139L,58140L,58141L,58142L,58143L,58144L, -58145L,58146L,58147L,58148L,58149L,58150L,58151L,58152L,58153L,58154L, -58155L,58156L,58157L,58158L,58159L,58160L,58161L,58162L,58163L,58164L, -58165L,58166L,58167L,58168L,58169L,58170L,58171L,58172L,58173L,58174L, -58175L,58176L,58177L,58178L,58179L,58180L,58181L,58182L,58183L,58184L, -58185L,58186L,58187L,58188L,58189L,58190L,58191L,58192L,58193L,58194L, -58195L,58196L,58197L,58198L,58199L,58200L,58201L,58202L,58203L,58204L, -58205L,58206L,58207L,58208L,58209L,58210L,58211L,58212L,58213L,58214L, -58215L,58216L,58217L,58218L,58219L,58220L,58221L,58222L,58223L,58224L, -58225L,58226L,58227L,58228L,58229L,58230L,58231L,58232L,58233L,58234L, -58235L,58236L,58237L,58238L,58239L,58240L,58241L,58242L,58243L,58244L, -58245L,58246L,58247L,58248L,58249L,58250L,58251L,58252L,58253L,58254L, -58255L,58256L,58257L,58258L,58259L,58260L,58261L,58262L,58263L,58264L, -58265L,58266L,58267L,58268L,58269L,58270L,58271L,58272L,58273L,58274L, -58275L,58276L,58277L,58278L,58279L,58280L,58281L,58282L,58283L,58284L, -58285L,58286L,58287L,58288L,58289L,58290L,58291L,58292L,58293L,58294L, -58295L,58296L,58297L,58298L,58299L,58300L,58301L,58302L,58303L,58304L, -58305L,58306L,58307L,58308L,58309L,58310L,58311L,58312L,58313L,58314L, -58315L,58316L,58317L,58318L,58319L,58320L,58321L,58322L,58323L,58324L, -58325L,58326L,58327L,58328L,58329L,58330L,58331L,58332L,58333L,58334L, -58335L,58336L,58337L,58338L,58339L,58340L,58341L,58342L,58343L,58344L, -58345L,58346L,58347L,58348L,58349L,58350L,58351L,58352L,58353L,58354L, -58355L,58356L,58357L,58358L,58359L,58360L,58361L,58362L,58363L,58364L, -58365L,58366L,58367L,58368L,58369L,58370L,58371L,58372L,58373L,58374L, -58375L,58376L,58377L,58378L,58379L,58380L,58381L,58382L,58383L,58384L, -58385L,58386L,58387L,58388L,58389L,58390L,58391L,58392L,58393L,58394L, -58395L,58396L,58397L,58398L,58399L,58400L,58401L,58402L,58403L,58404L, -58405L,58406L,58407L,58408L,58409L,58410L,58411L,58412L,58413L,58414L, -58415L,58416L,58417L,58418L,58419L,58420L,58421L,58422L,58423L,58424L, -58425L,58426L,58427L,58428L,58429L,58430L,58431L,58432L,58433L,58434L, -58435L,58436L,58437L,58438L,58439L,58440L,58441L,58442L,58443L,58444L, -58445L,58446L,58447L,58448L,58449L,58450L,58451L,58452L,58453L,58454L, -58455L,58456L,58457L,58458L,58459L,58460L,58461L,58462L,58463L,58464L, -58465L,58466L,58467L,58468L,58469L,58470L,58471L,58472L,58473L,58474L, -58475L,58476L,58477L,58478L,58479L,58480L,58481L,58482L,58483L,58484L, -58485L,58486L,58487L,58488L,58489L,58490L,58491L,58492L,58493L,58494L, -58495L,58496L,58497L,58498L,58499L,58500L,58501L,58502L,58503L,58504L, -58505L,58506L,58507L,58508L,58509L,58510L,58511L,58512L,58513L,58514L, -58515L,58516L,58517L,58518L,58519L,58520L,58521L,58522L,58523L,58524L, -58525L,58526L,58527L,58528L,58529L,58530L,58531L,58532L,58533L,58534L, -58535L,58536L,58537L,58538L,58539L,58540L,58541L,58542L,58543L,58544L, -58545L,58546L,58547L,58548L,58549L,58550L,58551L,58552L,58553L,58554L, -58555L,58556L,58557L,58558L,58559L,58560L,58561L,58562L,58563L,58564L, -58565L,58566L,58567L,58568L,58569L,58570L,58571L,58572L,58573L,58574L, -58575L,58576L,58577L,58578L,58579L,58580L,58581L,58582L,58583L,58584L, -58585L,58586L,58587L,58588L,58589L,58590L,58591L,58592L,58593L,58594L, -58595L,58596L,58597L,58598L,58599L,58600L,58601L,58602L,58603L,58604L, -58605L,58606L,58607L,58608L,58609L,58610L,58611L,58612L,58613L,58614L, -58615L,58616L,58617L,58618L,58619L,58620L,58621L,58622L,58623L,58624L, -58625L,58626L,58627L,58628L,58629L,58630L,58631L,58632L,58633L,58634L, -58635L,58636L,58637L,58638L,58639L,58640L,58641L,58642L,58643L,58644L, -58645L,58646L,58647L,58648L,58649L,58650L,58651L,58652L,58653L,58654L, -58655L,58656L,58657L,58658L,58659L,58660L,58661L,58662L,58663L,58664L, -58665L,58666L,58667L,58668L,58669L,58670L,58671L,58672L,58673L,58674L, -58675L,58676L,58677L,58678L,58679L,58680L,58681L,58682L,58683L,58684L, -58685L,58686L,58687L,58688L,58689L,58690L,58691L,58692L,58693L,58694L, -58695L,58696L,58697L,58698L,58699L,58700L,58701L,58702L,58703L,58704L, -58705L,58706L,58707L,58708L,58709L,58710L,58711L,58712L,58713L,58714L, -58715L,58716L,58717L,58718L,58719L,58720L,58721L,58722L,58723L,58724L, -58725L,58726L,58727L,58728L,58729L,58730L,58731L,58732L,58733L,58734L, -58735L,58736L,58737L,58738L,58739L,58740L,58741L,58742L,58743L,58744L, -58745L,58746L,58747L,58748L,58749L,58750L,58751L,58752L,58753L,58754L, -58755L,58756L,58757L,58758L,58759L,58760L,58761L,58762L,58763L,58764L, -58765L,58766L,58767L,58768L,58769L,58770L,58771L,58772L,58773L,58774L, -58775L,58776L,58777L,58778L,58779L,58780L,58781L,58782L,58783L,58784L, -58785L,58786L,58787L,58788L,58789L,58790L,58791L,58792L,58793L,58794L, -58795L,58796L,58797L,58798L,58799L,58800L,58801L,58802L,58803L,58804L, -58805L,58806L,58807L,58808L,58809L,58810L,58811L,58812L,58813L,58814L, -58815L,58816L,58817L,58818L,58819L,58820L,58821L,58822L,58823L,58824L, -58825L,58826L,58827L,58828L,58829L,58830L,58831L,58832L,58833L,58834L, -58835L,58836L,58837L,58838L,58839L,58840L,58841L,58842L,58843L,58844L, -58845L,58846L,58847L,58848L,58849L,58850L,58851L,58852L,58853L,58854L, -58855L,58856L,58857L,58858L,58859L,58860L,58861L,58862L,58863L,58864L, -58865L,58866L,58867L,58868L,58869L,58870L,58871L,58872L,58873L,58874L, -58875L,58876L,58877L,58878L,58879L,58880L,58881L,58882L,58883L,58884L, -58885L,58886L,58887L,58888L,58889L,58890L,58891L,58892L,58893L,58894L, -58895L,58896L,58897L,58898L,58899L,58900L,58901L,58902L,58903L,58904L, -58905L,58906L,58907L,58908L,58909L,58910L,58911L,58912L,58913L,58914L, -58915L,58916L,58917L,58918L,58919L,58920L,58921L,58922L,58923L,58924L, -58925L,58926L,58927L,58928L,58929L,58930L,58931L,58932L,58933L,58934L, -58935L,58936L,58937L,58938L,58939L,58940L,58941L,58942L,58943L,58944L, -58945L,58946L,58947L,58948L,58949L,58950L,58951L,58952L,58953L,58954L, -58955L,58956L,58957L,58958L,58959L,58960L,58961L,58962L,58963L,58964L, -58965L,58966L,58967L,58968L,58969L,58970L,58971L,58972L,58973L,58974L, -58975L,58976L,58977L,58978L,58979L,58980L,58981L,58982L,58983L,58984L, -58985L,58986L,58987L,58988L,58989L,58990L,58991L,58992L,58993L,58994L, -58995L,58996L,58997L,58998L,58999L,59000L,59001L,59002L,59003L,59004L, -59005L,59006L,59007L,59008L,59009L,59010L,59011L,59012L,59013L,59014L, -59015L,59016L,59017L,59018L,59019L,59020L,59021L,59022L,59023L,59024L, -59025L,59026L,59027L,59028L,59029L,59030L,59031L,59032L,59033L,59034L, -59035L,59036L,59037L,59038L,59039L,59040L,59041L,59042L,59043L,59044L, -59045L,59046L,59047L,59048L,59049L,59050L,59051L,59052L,59053L,59054L, -59055L,59056L,59057L,59058L,59059L,59060L,59061L,59062L,59063L,59064L, -59065L,59066L,59067L,59068L,59069L,59070L,59071L,59072L,59073L,59074L, -59075L,59076L,59077L,59078L,59079L,59080L,59081L,59082L,59083L,59084L, -59085L,59086L,59087L,59088L,59089L,59090L,59091L,59092L,59093L,59094L, -59095L,59096L,59097L,59098L,59099L,59100L,59101L,59102L,59103L,59104L, -59105L,59106L,59107L,59108L,59109L,59110L,59111L,59112L,59113L,59114L, -59115L,59116L,59117L,59118L,59119L,59120L,59121L,59122L,59123L,59124L, -59125L,59126L,59127L,59128L,59129L,59130L,59131L,59132L,59133L,59134L, -59135L,59136L,59137L,59138L,59139L,59140L,59141L,59142L,59143L,59144L, -59145L,59146L,59147L,59148L,59149L,59150L,59151L,59152L,59153L,59154L, -59155L,59156L,59157L,59158L,59159L,59160L,59161L,59162L,59163L,59164L, -59165L,59166L,59167L,59168L,59169L,59170L,59171L,59172L,59173L,59174L, -59175L,59176L,59177L,59178L,59179L,59180L,59181L,59182L,59183L,59184L, -59185L,59186L,59187L,59188L,59189L,59190L,59191L,59192L,59193L,59194L, -59195L,59196L,59197L,59198L,59199L,59200L,59201L,59202L,59203L,59204L, -59205L,59206L,59207L,59208L,59209L,59210L,59211L,59212L,59213L,59214L, -59215L,59216L,59217L,59218L,59219L,59220L,59221L,59222L,59223L,59224L, -59225L,59226L,59227L,59228L,59229L,59230L,59231L,59232L,59233L,59234L, -59235L,59236L,59237L,59238L,59239L,59240L,59241L,59242L,59243L,59244L, -59245L,59246L,59247L,59248L,59249L,59250L,59251L,59252L,59253L,59254L, -59255L,59256L,59257L,59258L,59259L,59260L,59261L,59262L,59263L,59264L, -59265L,59266L,59267L,59268L,59269L,59270L,59271L,59272L,59273L,59274L, -59275L,59276L,59277L,59278L,59279L,59280L,59281L,59282L,59283L,59284L, -59285L,59286L,59287L,59288L,59289L,59290L,59291L,59292L,59293L,59294L, -59295L,59296L,59297L,59298L,59299L,59300L,59301L,59302L,59303L,59304L, -59305L,59306L,59307L,59308L,59309L,59310L,59311L,59312L,59313L,59314L, -59315L,59316L,59317L,59318L,59319L,59320L,59321L,59322L,59323L,59324L, -59325L,59326L,59327L,59328L,59329L,59330L,59331L,59332L,59333L,59334L, -59335L,59336L,59337L,59338L,59339L,59340L,59341L,59342L,59343L,59344L, -59345L,59346L,59347L,59348L,59349L,59350L,59351L,59352L,59353L,59354L, -59355L,59356L,59357L,59358L,59359L,59360L,59361L,59362L,59363L,59364L, -59365L,59366L,59367L,59368L,59369L,59370L,59371L,59372L,59373L,59374L, -59375L,59376L,59377L,59378L,59379L,59380L,59381L,59382L,59383L,59384L, -59385L,59386L,59387L,59388L,59389L,59390L,59391L,59392L,59393L,59394L, -59395L,59396L,59397L,59398L,59399L,59400L,59401L,59402L,59403L,59404L, -59405L,59406L,59407L,59408L,59409L,59410L,59411L,59412L,59413L,59414L, -59415L,59416L,59417L,59418L,59419L,59420L,59421L,59422L,59423L,59424L, -59425L,59426L,59427L,59428L,59429L,59430L,59431L,59432L,59433L,59434L, -59435L,59436L,59437L,59438L,59439L,59440L,59441L,59442L,59443L,59444L, -59445L,59446L,59447L,59448L,59449L,59450L,59451L,59452L,59453L,59454L, -59455L,59456L,59457L,59458L,59459L,59460L,59461L,59462L,59463L,59464L, -59465L,59466L,59467L,59468L,59469L,59470L,59471L,59472L,59473L,59474L, -59475L,59476L,59477L,59478L,59479L,59480L,59481L,59482L,59483L,59484L, -59485L,59486L,59487L,59488L,59489L,59490L,59491L,59492L,59493L,59494L, -59495L,59496L,59497L,59498L,59499L,59500L,59501L,59502L,59503L,59504L, -59505L,59506L,59507L,59508L,59509L,59510L,59511L,59512L,59513L,59514L, -59515L,59516L,59517L,59518L,59519L,59520L,59521L,59522L,59523L,59524L, -59525L,59526L,59527L,59528L,59529L,59530L,59531L,59532L,59533L,59534L, -59535L,59536L,59537L,59538L,59539L,59540L,59541L,59542L,59543L,59544L, -59545L,59546L,59547L,59548L,59549L,59550L,59551L,59552L,59553L,59554L, -59555L,59556L,59557L,59558L,59559L,59560L,59561L,59562L,59563L,59564L, -59565L,59566L,59567L,59568L,59569L,59570L,59571L,59572L,59573L,59574L, -59575L,59576L,59577L,59578L,59579L,59580L,59581L,59582L,59583L,59584L, -59585L,59586L,59587L,59588L,59589L,59590L,59591L,59592L,59593L,59594L, -59595L,59596L,59597L,59598L,59599L,59600L,59601L,59602L,59603L,59604L, -59605L,59606L,59607L,59608L,59609L,59610L,59611L,59612L,59613L,59614L, -59615L,59616L,59617L,59618L,59619L,59620L,59621L,59622L,59623L,59624L, -59625L,59626L,59627L,59628L,59629L,59630L,59631L,59632L,59633L,59634L, -59635L,59636L,59637L,59638L,59639L,59640L,59641L,59642L,59643L,59644L, -59645L,59646L,59647L,59648L,59649L,59650L,59651L,59652L,59653L,59654L, -59655L,59656L,59657L,59658L,59659L,59660L,59661L,59662L,59663L,59664L, -59665L,59666L,59667L,59668L,59669L,59670L,59671L,59672L,59673L,59674L, -59675L,59676L,59677L,59678L,59679L,59680L,59681L,59682L,59683L,59684L, -59685L,59686L,59687L,59688L,59689L,59690L,59691L,59692L,59693L,59694L, -59695L,59696L,59697L,59698L,59699L,59700L,59701L,59702L,59703L,59704L, -59705L,59706L,59707L,59708L,59709L,59710L,59711L,59712L,59713L,59714L, -59715L,59716L,59717L,59718L,59719L,59720L,59721L,59722L,59723L,59724L, -59725L,59726L,59727L,59728L,59729L,59730L,59731L,59732L,59733L,59734L, -59735L,59736L,59737L,59738L,59739L,59740L,59741L,59742L,59743L,59744L, -59745L,59746L,59747L,59748L,59749L,59750L,59751L,59752L,59753L,59754L, -59755L,59756L,59757L,59758L,59759L,59760L,59761L,59762L,59763L,59764L, -59765L,59766L,59767L,59768L,59769L,59770L,59771L,59772L,59773L,59774L, -59775L,59776L,59777L,59778L,59779L,59780L,59781L,59782L,59783L,59784L, -59785L,59786L,59787L,59788L,59789L,59790L,59791L,59792L,59793L,59794L, -59795L,59796L,59797L,59798L,59799L,59800L,59801L,59802L,59803L,59804L, -59805L,59806L,59807L,59808L,59809L,59810L,59811L,59812L,59813L,59814L, -59815L,59816L,59817L,59818L,59819L,59820L,59821L,59822L,59823L,59824L, -59825L,59826L,59827L,59828L,59829L,59830L,59831L,59832L,59833L,59834L, -59835L,59836L,59837L,59838L,59839L,59840L,59841L,59842L,59843L,59844L, -59845L,59846L,59847L,59848L,59849L,59850L,59851L,59852L,59853L,59854L, -59855L,59856L,59857L,59858L,59859L,59860L,59861L,59862L,59863L,59864L, -59865L,59866L,59867L,59868L,59869L,59870L,59871L,59872L,59873L,59874L, -59875L,59876L,59877L,59878L,59879L,59880L,59881L,59882L,59883L,59884L, -59885L,59886L,59887L,59888L,59889L,59890L,59891L,59892L,59893L,59894L, -59895L,59896L,59897L,59898L,59899L,59900L,59901L,59902L,59903L,59904L, -59905L,59906L,59907L,59908L,59909L,59910L,59911L,59912L,59913L,59914L, -59915L,59916L,59917L,59918L,59919L,59920L,59921L,59922L,59923L,59924L, -59925L,59926L,59927L,59928L,59929L,59930L,59931L,59932L,59933L,59934L, -59935L,59936L,59937L,59938L,59939L,59940L,59941L,59942L,59943L,59944L, -59945L,59946L,59947L,59948L,59949L,59950L,59951L,59952L,59953L,59954L, -59955L,59956L,59957L,59958L,59959L,59960L,59961L,59962L,59963L,59964L, -59965L,59966L,59967L,59968L,59969L,59970L,59971L,59972L,59973L,59974L, -59975L,59976L,59977L,59978L,59979L,59980L,59981L,59982L,59983L,59984L, -59985L,59986L,59987L,59988L,59989L,59990L,59991L,59992L,59993L,59994L, -59995L,59996L,59997L,59998L,59999L,60000L,60001L,60002L,60003L,60004L, -60005L,60006L,60007L,60008L,60009L,60010L,60011L,60012L,60013L,60014L, -60015L,60016L,60017L,60018L,60019L,60020L,60021L,60022L,60023L,60024L, -60025L,60026L,60027L,60028L,60029L,60030L,60031L,60032L,60033L,60034L, -60035L,60036L,60037L,60038L,60039L,60040L,60041L,60042L,60043L,60044L, -60045L,60046L,60047L,60048L,60049L,60050L,60051L,60052L,60053L,60054L, -60055L,60056L,60057L,60058L,60059L,60060L,60061L,60062L,60063L,60064L, -60065L,60066L,60067L,60068L,60069L,60070L,60071L,60072L,60073L,60074L, -60075L,60076L,60077L,60078L,60079L,60080L,60081L,60082L,60083L,60084L, -60085L,60086L,60087L,60088L,60089L,60090L,60091L,60092L,60093L,60094L, -60095L,60096L,60097L,60098L,60099L,60100L,60101L,60102L,60103L,60104L, -60105L,60106L,60107L,60108L,60109L,60110L,60111L,60112L,60113L,60114L, -60115L,60116L,60117L,60118L,60119L,60120L,60121L,60122L,60123L,60124L, -60125L,60126L,60127L,60128L,60129L,60130L,60131L,60132L,60133L,60134L, -60135L,60136L,60137L,60138L,60139L,60140L,60141L,60142L,60143L,60144L, -60145L,60146L,60147L,60148L,60149L,60150L,60151L,60152L,60153L,60154L, -60155L,60156L,60157L,60158L,60159L,60160L,60161L,60162L,60163L,60164L, -60165L,60166L,60167L,60168L,60169L,60170L,60171L,60172L,60173L,60174L, -60175L,60176L,60177L,60178L,60179L,60180L,60181L,60182L,60183L,60184L, -60185L,60186L,60187L,60188L,60189L,60190L,60191L,60192L,60193L,60194L, -60195L,60196L,60197L,60198L,60199L,60200L,60201L,60202L,60203L,60204L, -60205L,60206L,60207L,60208L,60209L,60210L,60211L,60212L,60213L,60214L, -60215L,60216L,60217L,60218L,60219L,60220L,60221L,60222L,60223L,60224L, -60225L,60226L,60227L,60228L,60229L,60230L,60231L,60232L,60233L,60234L, -60235L,60236L,60237L,60238L,60239L,60240L,60241L,60242L,60243L,60244L, -60245L,60246L,60247L,60248L,60249L,60250L,60251L,60252L,60253L,60254L, -60255L,60256L,60257L,60258L,60259L,60260L,60261L,60262L,60263L,60264L, -60265L,60266L,60267L,60268L,60269L,60270L,60271L,60272L,60273L,60274L, -60275L,60276L,60277L,60278L,60279L,60280L,60281L,60282L,60283L,60284L, -60285L,60286L,60287L,60288L,60289L,60290L,60291L,60292L,60293L,60294L, -60295L,60296L,60297L,60298L,60299L,60300L,60301L,60302L,60303L,60304L, -60305L,60306L,60307L,60308L,60309L,60310L,60311L,60312L,60313L,60314L, -60315L,60316L,60317L,60318L,60319L,60320L,60321L,60322L,60323L,60324L, -60325L,60326L,60327L,60328L,60329L,60330L,60331L,60332L,60333L,60334L, -60335L,60336L,60337L,60338L,60339L,60340L,60341L,60342L,60343L,60344L, -60345L,60346L,60347L,60348L,60349L,60350L,60351L,60352L,60353L,60354L, -60355L,60356L,60357L,60358L,60359L,60360L,60361L,60362L,60363L,60364L, -60365L,60366L,60367L,60368L,60369L,60370L,60371L,60372L,60373L,60374L, -60375L,60376L,60377L,60378L,60379L,60380L,60381L,60382L,60383L,60384L, -60385L,60386L,60387L,60388L,60389L,60390L,60391L,60392L,60393L,60394L, -60395L,60396L,60397L,60398L,60399L,60400L,60401L,60402L,60403L,60404L, -60405L,60406L,60407L,60408L,60409L,60410L,60411L,60412L,60413L,60414L, -60415L,60416L,60417L,60418L,60419L,60420L,60421L,60422L,60423L,60424L, -60425L,60426L,60427L,60428L,60429L,60430L,60431L,60432L,60433L,60434L, -60435L,60436L,60437L,60438L,60439L,60440L,60441L,60442L,60443L,60444L, -60445L,60446L,60447L,60448L,60449L,60450L,60451L,60452L,60453L,60454L, -60455L,60456L,60457L,60458L,60459L,60460L,60461L,60462L,60463L,60464L, -60465L,60466L,60467L,60468L,60469L,60470L,60471L,60472L,60473L,60474L, -60475L,60476L,60477L,60478L,60479L,60480L,60481L,60482L,60483L,60484L, -60485L,60486L,60487L,60488L,60489L,60490L,60491L,60492L,60493L,60494L, -60495L,60496L,60497L,60498L,60499L,60500L,60501L,60502L,60503L,60504L, -60505L,60506L,60507L,60508L,60509L,60510L,60511L,60512L,60513L,60514L, -60515L,60516L,60517L,60518L,60519L,60520L,60521L,60522L,60523L,60524L, -60525L,60526L,60527L,60528L,60529L,60530L,60531L,60532L,60533L,60534L, -60535L,60536L,60537L,60538L,60539L,60540L,60541L,60542L,60543L,60544L, -60545L,60546L,60547L,60548L,60549L,60550L,60551L,60552L,60553L,60554L, -60555L,60556L,60557L,60558L,60559L,60560L,60561L,60562L,60563L,60564L, -60565L,60566L,60567L,60568L,60569L,60570L,60571L,60572L,60573L,60574L, -60575L,60576L,60577L,60578L,60579L,60580L,60581L,60582L,60583L,60584L, -60585L,60586L,60587L,60588L,60589L,60590L,60591L,60592L,60593L,60594L, -60595L,60596L,60597L,60598L,60599L,60600L,60601L,60602L,60603L,60604L, -60605L,60606L,60607L,60608L,60609L,60610L,60611L,60612L,60613L,60614L, -60615L,60616L,60617L,60618L,60619L,60620L,60621L,60622L,60623L,60624L, -60625L,60626L,60627L,60628L,60629L,60630L,60631L,60632L,60633L,60634L, -60635L,60636L,60637L,60638L,60639L,60640L,60641L,60642L,60643L,60644L, -60645L,60646L,60647L,60648L,60649L,60650L,60651L,60652L,60653L,60654L, -60655L,60656L,60657L,60658L,60659L,60660L,60661L,60662L,60663L,60664L, -60665L,60666L,60667L,60668L,60669L,60670L,60671L,60672L,60673L,60674L, -60675L,60676L,60677L,60678L,60679L,60680L,60681L,60682L,60683L,60684L, -60685L,60686L,60687L,60688L,60689L,60690L,60691L,60692L,60693L,60694L, -60695L,60696L,60697L,60698L,60699L,60700L,60701L,60702L,60703L,60704L, -60705L,60706L,60707L,60708L,60709L,60710L,60711L,60712L,60713L,60714L, -60715L,60716L,60717L,60718L,60719L,60720L,60721L,60722L,60723L,60724L, -60725L,60726L,60727L,60728L,60729L,60730L,60731L,60732L,60733L,60734L, -60735L,60736L,60737L,60738L,60739L,60740L,60741L,60742L,60743L,60744L, -60745L,60746L,60747L,60748L,60749L,60750L,60751L,60752L,60753L,60754L, -60755L,60756L,60757L,60758L,60759L,60760L,60761L,60762L,60763L,60764L, -60765L,60766L,60767L,60768L,60769L,60770L,60771L,60772L,60773L,60774L, -60775L,60776L,60777L,60778L,60779L,60780L,60781L,60782L,60783L,60784L, -60785L,60786L,60787L,60788L,60789L,60790L,60791L,60792L,60793L,60794L, -60795L,60796L,60797L,60798L,60799L,60800L,60801L,60802L,60803L,60804L, -60805L,60806L,60807L,60808L,60809L,60810L,60811L,60812L,60813L,60814L, -60815L,60816L,60817L,60818L,60819L,60820L,60821L,60822L,60823L,60824L, -60825L,60826L,60827L,60828L,60829L,60830L,60831L,60832L,60833L,60834L, -60835L,60836L,60837L,60838L,60839L,60840L,60841L,60842L,60843L,60844L, -60845L,60846L,60847L,60848L,60849L,60850L,60851L,60852L,60853L,60854L, -60855L,60856L,60857L,60858L,60859L,60860L,60861L,60862L,60863L,60864L, -60865L,60866L,60867L,60868L,60869L,60870L,60871L,60872L,60873L,60874L, -60875L,60876L,60877L,60878L,60879L,60880L,60881L,60882L,60883L,60884L, -60885L,60886L,60887L,60888L,60889L,60890L,60891L,60892L,60893L,60894L, -60895L,60896L,60897L,60898L,60899L,60900L,60901L,60902L,60903L,60904L, -60905L,60906L,60907L,60908L,60909L,60910L,60911L,60912L,60913L,60914L, -60915L,60916L,60917L,60918L,60919L,60920L,60921L,60922L,60923L,60924L, -60925L,60926L,60927L,60928L,60929L,60930L,60931L,60932L,60933L,60934L, -60935L,60936L,60937L,60938L,60939L,60940L,60941L,60942L,60943L,60944L, -60945L,60946L,60947L,60948L,60949L,60950L,60951L,60952L,60953L,60954L, -60955L,60956L,60957L,60958L,60959L,60960L,60961L,60962L,60963L,60964L, -60965L,60966L,60967L,60968L,60969L,60970L,60971L,60972L,60973L,60974L, -60975L,60976L,60977L,60978L,60979L,60980L,60981L,60982L,60983L,60984L, -60985L,60986L,60987L,60988L,60989L,60990L,60991L,60992L,60993L,60994L, -60995L,60996L,60997L,60998L,60999L,61000L,61001L,61002L,61003L,61004L, -61005L,61006L,61007L,61008L,61009L,61010L,61011L,61012L,61013L,61014L, -61015L,61016L,61017L,61018L,61019L,61020L,61021L,61022L,61023L,61024L, -61025L,61026L,61027L,61028L,61029L,61030L,61031L,61032L,61033L,61034L, -61035L,61036L,61037L,61038L,61039L,61040L,61041L,61042L,61043L,61044L, -61045L,61046L,61047L,61048L,61049L,61050L,61051L,61052L,61053L,61054L, -61055L,61056L,61057L,61058L,61059L,61060L,61061L,61062L,61063L,61064L, -61065L,61066L,61067L,61068L,61069L,61070L,61071L,61072L,61073L,61074L, -61075L,61076L,61077L,61078L,61079L,61080L,61081L,61082L,61083L,61084L, -61085L,61086L,61087L,61088L,61089L,61090L,61091L,61092L,61093L,61094L, -61095L,61096L,61097L,61098L,61099L,61100L,61101L,61102L,61103L,61104L, -61105L,61106L,61107L,61108L,61109L,61110L,61111L,61112L,61113L,61114L, -61115L,61116L,61117L,61118L,61119L,61120L,61121L,61122L,61123L,61124L, -61125L,61126L,61127L,61128L,61129L,61130L,61131L,61132L,61133L,61134L, -61135L,61136L,61137L,61138L,61139L,61140L,61141L,61142L,61143L,61144L, -61145L,61146L,61147L,61148L,61149L,61150L,61151L,61152L,61153L,61154L, -61155L,61156L,61157L,61158L,61159L,61160L,61161L,61162L,61163L,61164L, -61165L,61166L,61167L,61168L,61169L,61170L,61171L,61172L,61173L,61174L, -61175L,61176L,61177L,61178L,61179L,61180L,61181L,61182L,61183L,61184L, -61185L,61186L,61187L,61188L,61189L,61190L,61191L,61192L,61193L,61194L, -61195L,61196L,61197L,61198L,61199L,61200L,61201L,61202L,61203L,61204L, -61205L,61206L,61207L,61208L,61209L,61210L,61211L,61212L,61213L,61214L, -61215L,61216L,61217L,61218L,61219L,61220L,61221L,61222L,61223L,61224L, -61225L,61226L,61227L,61228L,61229L,61230L,61231L,61232L,61233L,61234L, -61235L,61236L,61237L,61238L,61239L,61240L,61241L,61242L,61243L,61244L, -61245L,61246L,61247L,61248L,61249L,61250L,61251L,61252L,61253L,61254L, -61255L,61256L,61257L,61258L,61259L,61260L,61261L,61262L,61263L,61264L, -61265L,61266L,61267L,61268L,61269L,61270L,61271L,61272L,61273L,61274L, -61275L,61276L,61277L,61278L,61279L,61280L,61281L,61282L,61283L,61284L, -61285L,61286L,61287L,61288L,61289L,61290L,61291L,61292L,61293L,61294L, -61295L,61296L,61297L,61298L,61299L,61300L,61301L,61302L,61303L,61304L, -61305L,61306L,61307L,61308L,61309L,61310L,61311L,61312L,61313L,61314L, -61315L,61316L,61317L,61318L,61319L,61320L,61321L,61322L,61323L,61324L, -61325L,61326L,61327L,61328L,61329L,61330L,61331L,61332L,61333L,61334L, -61335L,61336L,61337L,61338L,61339L,61340L,61341L,61342L,61343L,61344L, -61345L,61346L,61347L,61348L,61349L,61350L,61351L,61352L,61353L,61354L, -61355L,61356L,61357L,61358L,61359L,61360L,61361L,61362L,61363L,61364L, -61365L,61366L,61367L,61368L,61369L,61370L,61371L,61372L,61373L,61374L, -61375L,61376L,61377L,61378L,61379L,61380L,61381L,61382L,61383L,61384L, -61385L,61386L,61387L,61388L,61389L,61390L,61391L,61392L,61393L,61394L, -61395L,61396L,61397L,61398L,61399L,61400L,61401L,61402L,61403L,61404L, -61405L,61406L,61407L,61408L,61409L,61410L,61411L,61412L,61413L,61414L, -61415L,61416L,61417L,61418L,61419L,61420L,61421L,61422L,61423L,61424L, -61425L,61426L,61427L,61428L,61429L,61430L,61431L,61432L,61433L,61434L, -61435L,61436L,61437L,61438L,61439L,61440L,61441L,61442L,61443L,61444L, -61445L,61446L,61447L,61448L,61449L,61450L,61451L,61452L,61453L,61454L, -61455L,61456L,61457L,61458L,61459L,61460L,61461L,61462L,61463L,61464L, -61465L,61466L,61467L,61468L,61469L,61470L,61471L,61472L,61473L,61474L, -61475L,61476L,61477L,61478L,61479L,61480L,61481L,61482L,61483L,61484L, -61485L,61486L,61487L,61488L,61489L,61490L,61491L,61492L,61493L,61494L, -61495L,61496L,61497L,61498L,61499L,61500L,61501L,61502L,61503L,61504L, -61505L,61506L,61507L,61508L,61509L,61510L,61511L,61512L,61513L,61514L, -61515L,61516L,61517L,61518L,61519L,61520L,61521L,61522L,61523L,61524L, -61525L,61526L,61527L,61528L,61529L,61530L,61531L,61532L,61533L,61534L, -61535L,61536L,61537L,61538L,61539L,61540L,61541L,61542L,61543L,61544L, -61545L,61546L,61547L,61548L,61549L,61550L,61551L,61552L,61553L,61554L, -61555L,61556L,61557L,61558L,61559L,61560L,61561L,61562L,61563L,61564L, -61565L,61566L,61567L,61568L,61569L,61570L,61571L,61572L,61573L,61574L, -61575L,61576L,61577L,61578L,61579L,61580L,61581L,61582L,61583L,61584L, -61585L,61586L,61587L,61588L,61589L,61590L,61591L,61592L,61593L,61594L, -61595L,61596L,61597L,61598L,61599L,61600L,61601L,61602L,61603L,61604L, -61605L,61606L,61607L,61608L,61609L,61610L,61611L,61612L,61613L,61614L, -61615L,61616L,61617L,61618L,61619L,61620L,61621L,61622L,61623L,61624L, -61625L,61626L,61627L,61628L,61629L,61630L,61631L,61632L,61633L,61634L, -61635L,61636L,61637L,61638L,61639L,61640L,61641L,61642L,61643L,61644L, -61645L,61646L,61647L,61648L,61649L,61650L,61651L,61652L,61653L,61654L, -61655L,61656L,61657L,61658L,61659L,61660L,61661L,61662L,61663L,61664L, -61665L,61666L,61667L,61668L,61669L,61670L,61671L,61672L,61673L,61674L, -61675L,61676L,61677L,61678L,61679L,61680L,61681L,61682L,61683L,61684L, -61685L,61686L,61687L,61688L,61689L,61690L,61691L,61692L,61693L,61694L, -61695L,61696L,61697L,61698L,61699L,61700L,61701L,61702L,61703L,61704L, -61705L,61706L,61707L,61708L,61709L,61710L,61711L,61712L,61713L,61714L, -61715L,61716L,61717L,61718L,61719L,61720L,61721L,61722L,61723L,61724L, -61725L,61726L,61727L,61728L,61729L,61730L,61731L,61732L,61733L,61734L, -61735L,61736L,61737L,61738L,61739L,61740L,61741L,61742L,61743L,61744L, -61745L,61746L,61747L,61748L,61749L,61750L,61751L,61752L,61753L,61754L, -61755L,61756L,61757L,61758L,61759L,61760L,61761L,61762L,61763L,61764L, -61765L,61766L,61767L,61768L,61769L,61770L,61771L,61772L,61773L,61774L, -61775L,61776L,61777L,61778L,61779L,61780L,61781L,61782L,61783L,61784L, -61785L,61786L,61787L,61788L,61789L,61790L,61791L,61792L,61793L,61794L, -61795L,61796L,61797L,61798L,61799L,61800L,61801L,61802L,61803L,61804L, -61805L,61806L,61807L,61808L,61809L,61810L,61811L,61812L,61813L,61814L, -61815L,61816L,61817L,61818L,61819L,61820L,61821L,61822L,61823L,61824L, -61825L,61826L,61827L,61828L,61829L,61830L,61831L,61832L,61833L,61834L, -61835L,61836L,61837L,61838L,61839L,61840L,61841L,61842L,61843L,61844L, -61845L,61846L,61847L,61848L,61849L,61850L,61851L,61852L,61853L,61854L, -61855L,61856L,61857L,61858L,61859L,61860L,61861L,61862L,61863L,61864L, -61865L,61866L,61867L,61868L,61869L,61870L,61871L,61872L,61873L,61874L, -61875L,61876L,61877L,61878L,61879L,61880L,61881L,61882L,61883L,61884L, -61885L,61886L,61887L,61888L,61889L,61890L,61891L,61892L,61893L,61894L, -61895L,61896L,61897L,61898L,61899L,61900L,61901L,61902L,61903L,61904L, -61905L,61906L,61907L,61908L,61909L,61910L,61911L,61912L,61913L,61914L, -61915L,61916L,61917L,61918L,61919L,61920L,61921L,61922L,61923L,61924L, -61925L,61926L,61927L,61928L,61929L,61930L,61931L,61932L,61933L,61934L, -61935L,61936L,61937L,61938L,61939L,61940L,61941L,61942L,61943L,61944L, -61945L,61946L,61947L,61948L,61949L,61950L,61951L,61952L,61953L,61954L, -61955L,61956L,61957L,61958L,61959L,61960L,61961L,61962L,61963L,61964L, -61965L,61966L,61967L,61968L,61969L,61970L,61971L,61972L,61973L,61974L, -61975L,61976L,61977L,61978L,61979L,61980L,61981L,61982L,61983L,61984L, -61985L,61986L,61987L,61988L,61989L,61990L,61991L,61992L,61993L,61994L, -61995L,61996L,61997L,61998L,61999L,62000L,62001L,62002L,62003L,62004L, -62005L,62006L,62007L,62008L,62009L,62010L,62011L,62012L,62013L,62014L, -62015L,62016L,62017L,62018L,62019L,62020L,62021L,62022L,62023L,62024L, -62025L,62026L,62027L,62028L,62029L,62030L,62031L,62032L,62033L,62034L, -62035L,62036L,62037L,62038L,62039L,62040L,62041L,62042L,62043L,62044L, -62045L,62046L,62047L,62048L,62049L,62050L,62051L,62052L,62053L,62054L, -62055L,62056L,62057L,62058L,62059L,62060L,62061L,62062L,62063L,62064L, -62065L,62066L,62067L,62068L,62069L,62070L,62071L,62072L,62073L,62074L, -62075L,62076L,62077L,62078L,62079L,62080L,62081L,62082L,62083L,62084L, -62085L,62086L,62087L,62088L,62089L,62090L,62091L,62092L,62093L,62094L, -62095L,62096L,62097L,62098L,62099L,62100L,62101L,62102L,62103L,62104L, -62105L,62106L,62107L,62108L,62109L,62110L,62111L,62112L,62113L,62114L, -62115L,62116L,62117L,62118L,62119L,62120L,62121L,62122L,62123L,62124L, -62125L,62126L,62127L,62128L,62129L,62130L,62131L,62132L,62133L,62134L, -62135L,62136L,62137L,62138L,62139L,62140L,62141L,62142L,62143L,62144L, -62145L,62146L,62147L,62148L,62149L,62150L,62151L,62152L,62153L,62154L, -62155L,62156L,62157L,62158L,62159L,62160L,62161L,62162L,62163L,62164L, -62165L,62166L,62167L,62168L,62169L,62170L,62171L,62172L,62173L,62174L, -62175L,62176L,62177L,62178L,62179L,62180L,62181L,62182L,62183L,62184L, -62185L,62186L,62187L,62188L,62189L,62190L,62191L,62192L,62193L,62194L, -62195L,62196L,62197L,62198L,62199L,62200L,62201L,62202L,62203L,62204L, -62205L,62206L,62207L,62208L,62209L,62210L,62211L,62212L,62213L,62214L, -62215L,62216L,62217L,62218L,62219L,62220L,62221L,62222L,62223L,62224L, -62225L,62226L,62227L,62228L,62229L,62230L,62231L,62232L,62233L,62234L, -62235L,62236L,62237L,62238L,62239L,62240L,62241L,62242L,62243L,62244L, -62245L,62246L,62247L,62248L,62249L,62250L,62251L,62252L,62253L,62254L, -62255L,62256L,62257L,62258L,62259L,62260L,62261L,62262L,62263L,62264L, -62265L,62266L,62267L,62268L,62269L,62270L,62271L,62272L,62273L,62274L, -62275L,62276L,62277L,62278L,62279L,62280L,62281L,62282L,62283L,62284L, -62285L,62286L,62287L,62288L,62289L,62290L,62291L,62292L,62293L,62294L, -62295L,62296L,62297L,62298L,62299L,62300L,62301L,62302L,62303L,62304L, -62305L,62306L,62307L,62308L,62309L,62310L,62311L,62312L,62313L,62314L, -62315L,62316L,62317L,62318L,62319L,62320L,62321L,62322L,62323L,62324L, -62325L,62326L,62327L,62328L,62329L,62330L,62331L,62332L,62333L,62334L, -62335L,62336L,62337L,62338L,62339L,62340L,62341L,62342L,62343L,62344L, -62345L,62346L,62347L,62348L,62349L,62350L,62351L,62352L,62353L,62354L, -62355L,62356L,62357L,62358L,62359L,62360L,62361L,62362L,62363L,62364L, -62365L,62366L,62367L,62368L,62369L,62370L,62371L,62372L,62373L,62374L, -62375L,62376L,62377L,62378L,62379L,62380L,62381L,62382L,62383L,62384L, -62385L,62386L,62387L,62388L,62389L,62390L,62391L,62392L,62393L,62394L, -62395L,62396L,62397L,62398L,62399L,62400L,62401L,62402L,62403L,62404L, -62405L,62406L,62407L,62408L,62409L,62410L,62411L,62412L,62413L,62414L, -62415L,62416L,62417L,62418L,62419L,62420L,62421L,62422L,62423L,62424L, -62425L,62426L,62427L,62428L,62429L,62430L,62431L,62432L,62433L,62434L, -62435L,62436L,62437L,62438L,62439L,62440L,62441L,62442L,62443L,62444L, -62445L,62446L,62447L,62448L,62449L,62450L,62451L,62452L,62453L,62454L, -62455L,62456L,62457L,62458L,62459L,62460L,62461L,62462L,62463L,62464L, -62465L,62466L,62467L,62468L,62469L,62470L,62471L,62472L,62473L,62474L, -62475L,62476L,62477L,62478L,62479L,62480L,62481L,62482L,62483L,62484L, -62485L,62486L,62487L,62488L,62489L,62490L,62491L,62492L,62493L,62494L, -62495L,62496L,62497L,62498L,62499L,62500L,62501L,62502L,62503L,62504L, -62505L,62506L,62507L,62508L,62509L,62510L,62511L,62512L,62513L,62514L, -62515L,62516L,62517L,62518L,62519L,62520L,62521L,62522L,62523L,62524L, -62525L,62526L,62527L,62528L,62529L,62530L,62531L,62532L,62533L,62534L, -62535L,62536L,62537L,62538L,62539L,62540L,62541L,62542L,62543L,62544L, -62545L,62546L,62547L,62548L,62549L,62550L,62551L,62552L,62553L,62554L, -62555L,62556L,62557L,62558L,62559L,62560L,62561L,62562L,62563L,62564L, -62565L,62566L,62567L,62568L,62569L,62570L,62571L,62572L,62573L,62574L, -62575L,62576L,62577L,62578L,62579L,62580L,62581L,62582L,62583L,62584L, -62585L,62586L,62587L,62588L,62589L,62590L,62591L,62592L,62593L,62594L, -62595L,62596L,62597L,62598L,62599L,62600L,62601L,62602L,62603L,62604L, -62605L,62606L,62607L,62608L,62609L,62610L,62611L,62612L,62613L,62614L, -62615L,62616L,62617L,62618L,62619L,62620L,62621L,62622L,62623L,62624L, -62625L,62626L,62627L,62628L,62629L,62630L,62631L,62632L,62633L,62634L, -62635L,62636L,62637L,62638L,62639L,62640L,62641L,62642L,62643L,62644L, -62645L,62646L,62647L,62648L,62649L,62650L,62651L,62652L,62653L,62654L, -62655L,62656L,62657L,62658L,62659L,62660L,62661L,62662L,62663L,62664L, -62665L,62666L,62667L,62668L,62669L,62670L,62671L,62672L,62673L,62674L, -62675L,62676L,62677L,62678L,62679L,62680L,62681L,62682L,62683L,62684L, -62685L,62686L,62687L,62688L,62689L,62690L,62691L,62692L,62693L,62694L, -62695L,62696L,62697L,62698L,62699L,62700L,62701L,62702L,62703L,62704L, -62705L,62706L,62707L,62708L,62709L,62710L,62711L,62712L,62713L,62714L, -62715L,62716L,62717L,62718L,62719L,62720L,62721L,62722L,62723L,62724L, -62725L,62726L,62727L,62728L,62729L,62730L,62731L,62732L,62733L,62734L, -62735L,62736L,62737L,62738L,62739L,62740L,62741L,62742L,62743L,62744L, -62745L,62746L,62747L,62748L,62749L,62750L,62751L,62752L,62753L,62754L, -62755L,62756L,62757L,62758L,62759L,62760L,62761L,62762L,62763L,62764L, -62765L,62766L,62767L,62768L,62769L,62770L,62771L,62772L,62773L,62774L, -62775L,62776L,62777L,62778L,62779L,62780L,62781L,62782L,62783L,62784L, -62785L,62786L,62787L,62788L,62789L,62790L,62791L,62792L,62793L,62794L, -62795L,62796L,62797L,62798L,62799L,62800L,62801L,62802L,62803L,62804L, -62805L,62806L,62807L,62808L,62809L,62810L,62811L,62812L,62813L,62814L, -62815L,62816L,62817L,62818L,62819L,62820L,62821L,62822L,62823L,62824L, -62825L,62826L,62827L,62828L,62829L,62830L,62831L,62832L,62833L,62834L, -62835L,62836L,62837L,62838L,62839L,62840L,62841L,62842L,62843L,62844L, -62845L,62846L,62847L,62848L,62849L,62850L,62851L,62852L,62853L,62854L, -62855L,62856L,62857L,62858L,62859L,62860L,62861L,62862L,62863L,62864L, -62865L,62866L,62867L,62868L,62869L,62870L,62871L,62872L,62873L,62874L, -62875L,62876L,62877L,62878L,62879L,62880L,62881L,62882L,62883L,62884L, -62885L,62886L,62887L,62888L,62889L,62890L,62891L,62892L,62893L,62894L, -62895L,62896L,62897L,62898L,62899L,62900L,62901L,62902L,62903L,62904L, -62905L,62906L,62907L,62908L,62909L,62910L,62911L,62912L,62913L,62914L, -62915L,62916L,62917L,62918L,62919L,62920L,62921L,62922L,62923L,62924L, -62925L,62926L,62927L,62928L,62929L,62930L,62931L,62932L,62933L,62934L, -62935L,62936L,62937L,62938L,62939L,62940L,62941L,62942L,62943L,62944L, -62945L,62946L,62947L,62948L,62949L,62950L,62951L,62952L,62953L,62954L, -62955L,62956L,62957L,62958L,62959L,62960L,62961L,62962L,62963L,62964L, -62965L,62966L,62967L,62968L,62969L,62970L,62971L,62972L,62973L,62974L, -62975L,62976L,62977L,62978L,62979L,62980L,62981L,62982L,62983L,62984L, -62985L,62986L,62987L,62988L,62989L,62990L,62991L,62992L,62993L,62994L, -62995L,62996L,62997L,62998L,62999L,63000L,63001L,63002L,63003L,63004L, -63005L,63006L,63007L,63008L,63009L,63010L,63011L,63012L,63013L,63014L, -63015L,63016L,63017L,63018L,63019L,63020L,63021L,63022L,63023L,63024L, -63025L,63026L,63027L,63028L,63029L,63030L,63031L,63032L,63033L,63034L, -63035L,63036L,63037L,63038L,63039L,63040L,63041L,63042L,63043L,63044L, -63045L,63046L,63047L,63048L,63049L,63050L,63051L,63052L,63053L,63054L, -63055L,63056L,63057L,63058L,63059L,63060L,63061L,63062L,63063L,63064L, -63065L,63066L,63067L,63068L,63069L,63070L,63071L,63072L,63073L,63074L, -63075L,63076L,63077L,63078L,63079L,63080L,63081L,63082L,63083L,63084L, -63085L,63086L,63087L,63088L,63089L,63090L,63091L,63092L,63093L,63094L, -63095L,63096L,63097L,63098L,63099L,63100L,63101L,63102L,63103L,63104L, -63105L,63106L,63107L,63108L,63109L,63110L,63111L,63112L,63113L,63114L, -63115L,63116L,63117L,63118L,63119L,63120L,63121L,63122L,63123L,63124L, -63125L,63126L,63127L,63128L,63129L,63130L,63131L,63132L,63133L,63134L, -63135L,63136L,63137L,63138L,63139L,63140L,63141L,63142L,63143L,63144L, -63145L,63146L,63147L,63148L,63149L,63150L,63151L,63152L,63153L,63154L, -63155L,63156L,63157L,63158L,63159L,63160L,63161L,63162L,63163L,63164L, -63165L,63166L,63167L,63168L,63169L,63170L,63171L,63172L,63173L,63174L, -63175L,63176L,63177L,63178L,63179L,63180L,63181L,63182L,63183L,63184L, -63185L,63186L,63187L,63188L,63189L,63190L,63191L,63192L,63193L,63194L, -63195L,63196L,63197L,63198L,63199L,63200L,63201L,63202L,63203L,63204L, -63205L,63206L,63207L,63208L,63209L,63210L,63211L,63212L,63213L,63214L, -63215L,63216L,63217L,63218L,63219L,63220L,63221L,63222L,63223L,63224L, -63225L,63226L,63227L,63228L,63229L,63230L,63231L,63232L,63233L,63234L, -63235L,63236L,63237L,63238L,63239L,63240L,63241L,63242L,63243L,63244L, -63245L,63246L,63247L,63248L,63249L,63250L,63251L,63252L,63253L,63254L, -63255L,63256L,63257L,63258L,63259L,63260L,63261L,63262L,63263L,63264L, -63265L,63266L,63267L,63268L,63269L,63270L,63271L,63272L,63273L,63274L, -63275L,63276L,63277L,63278L,63279L,63280L,63281L,63282L,63283L,63284L, -63285L,63286L,63287L,63288L,63289L,63290L,63291L,63292L,63293L,63294L, -63295L,63296L,63297L,63298L,63299L,63300L,63301L,63302L,63303L,63304L, -63305L,63306L,63307L,63308L,63309L,63310L,63311L,63312L,63313L,63314L, -63315L,63316L,63317L,63318L,63319L,63320L,63321L,63322L,63323L,63324L, -63325L,63326L,63327L,63328L,63329L,63330L,63331L,63332L,63333L,63334L, -63335L,63336L,63337L,63338L,63339L,63340L,63341L,63342L,63343L,63344L, -63345L,63346L,63347L,63348L,63349L,63350L,63351L,63352L,63353L,63354L, -63355L,63356L,63357L,63358L,63359L,63360L,63361L,63362L,63363L,63364L, -63365L,63366L,63367L,63368L,63369L,63370L,63371L,63372L,63373L,63374L, -63375L,63376L,63377L,63378L,63379L,63380L,63381L,63382L,63383L,63384L, -63385L,63386L,63387L,63388L,63389L,63390L,63391L,63392L,63393L,63394L, -63395L,63396L,63397L,63398L,63399L,63400L,63401L,63402L,63403L,63404L, -63405L,63406L,63407L,63408L,63409L,63410L,63411L,63412L,63413L,63414L, -63415L,63416L,63417L,63418L,63419L,63420L,63421L,63422L,63423L,63424L, -63425L,63426L,63427L,63428L,63429L,63430L,63431L,63432L,63433L,63434L, -63435L,63436L,63437L,63438L,63439L,63440L,63441L,63442L,63443L,63444L, -63445L,63446L,63447L,63448L,63449L,63450L,63451L,63452L,63453L,63454L, -63455L,63456L,63457L,63458L,63459L,63460L,63461L,63462L,63463L,63464L, -63465L,63466L,63467L,63468L,63469L,63470L,63471L,63472L,63473L,63474L, -63475L,63476L,63477L,63478L,63479L,63480L,63481L,63482L,63483L,63484L, -63485L,63486L,63487L,63488L,63489L,63490L,63491L,63492L,63493L,63494L, -63495L,63496L,63497L,63498L,63499L,63500L,63501L,63502L,63503L,63504L, -63505L,63506L,63507L,63508L,63509L,63510L,63511L,63512L,63513L,63514L, -63515L,63516L,63517L,63518L,63519L,63520L,63521L,63522L,63523L,63524L, -63525L,63526L,63527L,63528L,63529L,63530L,63531L,63532L,63533L,63534L, -63535L,63536L,63537L,63538L,63539L,63540L,63541L,63542L,63543L,63544L, -63545L,63546L,63547L,63548L,63549L,63550L,63551L,63552L,63553L,63554L, -63555L,63556L,63557L,63558L,63559L,63560L,63561L,63562L,63563L,63564L, -63565L,63566L,63567L,63568L,63569L,63570L,63571L,63572L,63573L,63574L, -63575L,63576L,63577L,63578L,63579L,63580L,63581L,63582L,63583L,63584L, -63585L,63586L,63587L,63588L,63589L,63590L,63591L,63592L,63593L,63594L, -63595L,63596L,63597L,63598L,63599L,63600L,63601L,63602L,63603L,63604L, -63605L,63606L,63607L,63608L,63609L,63610L,63611L,63612L,63613L,63614L, -63615L,63616L,63617L,63618L,63619L,63620L,63621L,63622L,63623L,63624L, -63625L,63626L,63627L,63628L,63629L,63630L,63631L,63632L,63633L,63634L, -63635L,63636L,63637L,63638L,63639L,63640L,63641L,63642L,63643L,63644L, -63645L,63646L,63647L,63648L,63649L,63650L,63651L,63652L,63653L,63654L, -63655L,63656L,63657L,63658L,63659L,63660L,63661L,63662L,63663L,63664L, -63665L,63666L,63667L,63668L,63669L,63670L,63671L,63672L,63673L,63674L, -63675L,63676L,63677L,63678L,63679L,63680L,63681L,63682L,63683L,63684L, -63685L,63686L,63687L,63688L,63689L,63690L,63691L,63692L,63693L,63694L, -63695L,63696L,63697L,63698L,63699L,63700L,63701L,63702L,63703L,63704L, -63705L,63706L,63707L,63708L,63709L,63710L,63711L,63712L,63713L,63714L, -63715L,63716L,63717L,63718L,63719L,63720L,63721L,63722L,63723L,63724L, -63725L,63726L,63727L,63728L,63729L,63730L,63731L,63732L,63733L,63734L, -63735L,63736L,63737L,63738L,63739L,63740L,63741L,63742L,63743L,63744L, -63745L,63746L,63747L,63748L,63749L,63750L,63751L,63752L,63753L,63754L, -63755L,63756L,63757L,63758L,63759L,63760L,63761L,63762L,63763L,63764L, -63765L,63766L,63767L,63768L,63769L,63770L,63771L,63772L,63773L,63774L, -63775L,63776L,63777L,63778L,63779L,63780L,63781L,63782L,63783L,63784L, -63785L,63786L,63787L,63788L,63789L,63790L,63791L,63792L,63793L,63794L, -63795L,63796L,63797L,63798L,63799L,63800L,63801L,63802L,63803L,63804L, -63805L,63806L,63807L,63808L,63809L,63810L,63811L,63812L,63813L,63814L, -63815L,63816L,63817L,63818L,63819L,63820L,63821L,63822L,63823L,63824L, -63825L,63826L,63827L,63828L,63829L,63830L,63831L,63832L,63833L,63834L, -63835L,63836L,63837L,63838L,63839L,63840L,63841L,63842L,63843L,63844L, -63845L,63846L,63847L,63848L,63849L,63850L,63851L,63852L,63853L,63854L, -63855L,63856L,63857L,63858L,63859L,63860L,63861L,63862L,63863L,63864L, -63865L,63866L,63867L,63868L,63869L,63870L,63871L,63872L,63873L,63874L, -63875L,63876L,63877L,63878L,63879L,63880L,63881L,63882L,63883L,63884L, -63885L,63886L,63887L,63888L,63889L,63890L,63891L,63892L,63893L,63894L, -63895L,63896L,63897L,63898L,63899L,63900L,63901L,63902L,63903L,63904L, -63905L,63906L,63907L,63908L,63909L,63910L,63911L,63912L,63913L,63914L, -63915L,63916L,63917L,63918L,63919L,63920L,63921L,63922L,63923L,63924L, -63925L,63926L,63927L,63928L,63929L,63930L,63931L,63932L,63933L,63934L, -63935L,63936L,63937L,63938L,63939L,63940L,63941L,63942L,63943L,63944L, -63945L,63946L,63947L,63948L,63949L,63950L,63951L,63952L,63953L,63954L, -63955L,63956L,63957L,63958L,63959L,63960L,63961L,63962L,63963L,63964L, -63965L,63966L,63967L,63968L,63969L,63970L,63971L,63972L,63973L,63974L, -63975L,63976L,63977L,63978L,63979L,63980L,63981L,63982L,63983L,63984L, -63985L,63986L,63987L,63988L,63989L,63990L,63991L,63992L,63993L,63994L, -63995L,63996L,63997L,63998L,63999L,64000L,64001L,64002L,64003L,64004L, -64005L,64006L,64007L,64008L,64009L,64010L,64011L,64012L,64013L,64014L, -64015L,64016L,64017L,64018L,64019L,64020L,64021L,64022L,64023L,64024L, -64025L,64026L,64027L,64028L,64029L,64030L,64031L,64032L,64033L,64034L, -64035L,64036L,64037L,64038L,64039L,64040L,64041L,64042L,64043L,64044L, -64045L,64046L,64047L,64048L,64049L,64050L,64051L,64052L,64053L,64054L, -64055L,64056L,64057L,64058L,64059L,64060L,64061L,64062L,64063L,64064L, -64065L,64066L,64067L,64068L,64069L,64070L,64071L,64072L,64073L,64074L, -64075L,64076L,64077L,64078L,64079L,64080L,64081L,64082L,64083L,64084L, -64085L,64086L,64087L,64088L,64089L,64090L,64091L,64092L,64093L,64094L, -64095L,64096L,64097L,64098L,64099L,64100L,64101L,64102L,64103L,64104L, -64105L,64106L,64107L,64108L,64109L,64110L,64111L,64112L,64113L,64114L, -64115L,64116L,64117L,64118L,64119L,64120L,64121L,64122L,64123L,64124L, -64125L,64126L,64127L,64128L,64129L,64130L,64131L,64132L,64133L,64134L, -64135L,64136L,64137L,64138L,64139L,64140L,64141L,64142L,64143L,64144L, -64145L,64146L,64147L,64148L,64149L,64150L,64151L,64152L,64153L,64154L, -64155L,64156L,64157L,64158L,64159L,64160L,64161L,64162L,64163L,64164L, -64165L,64166L,64167L,64168L,64169L,64170L,64171L,64172L,64173L,64174L, -64175L,64176L,64177L,64178L,64179L,64180L,64181L,64182L,64183L,64184L, -64185L,64186L,64187L,64188L,64189L,64190L,64191L,64192L,64193L,64194L, -64195L,64196L,64197L,64198L,64199L,64200L,64201L,64202L,64203L,64204L, -64205L,64206L,64207L,64208L,64209L,64210L,64211L,64212L,64213L,64214L, -64215L,64216L,64217L,64218L,64219L,64220L,64221L,64222L,64223L,64224L, -64225L,64226L,64227L,64228L,64229L,64230L,64231L,64232L,64233L,64234L, -64235L,64236L,64237L,64238L,64239L,64240L,64241L,64242L,64243L,64244L, -64245L,64246L,64247L,64248L,64249L,64250L,64251L,64252L,64253L,64254L, -64255L,64256L,64257L,64258L,64259L,64260L,64261L,64262L,64263L,64264L, -64265L,64266L,64267L,64268L,64269L,64270L,64271L,64272L,64273L,64274L, -64275L,64276L,64277L,64278L,64279L,64280L,64281L,64282L,64283L,64284L, -64285L,64286L,64287L,64288L,64289L,64290L,64291L,64292L,64293L,64294L, -64295L,64296L,64297L,64298L,64299L,64300L,64301L,64302L,64303L,64304L, -64305L,64306L,64307L,64308L,64309L,64310L,64311L,64312L,64313L,64314L, -64315L,64316L,64317L,64318L,64319L,64320L,64321L,64322L,64323L,64324L, -64325L,64326L,64327L,64328L,64329L,64330L,64331L,64332L,64333L,64334L, -64335L,64336L,64337L,64338L,64339L,64340L,64341L,64342L,64343L,64344L, -64345L,64346L,64347L,64348L,64349L,64350L,64351L,64352L,64353L,64354L, -64355L,64356L,64357L,64358L,64359L,64360L,64361L,64362L,64363L,64364L, -64365L,64366L,64367L,64368L,64369L,64370L,64371L,64372L,64373L,64374L, -64375L,64376L,64377L,64378L,64379L,64380L,64381L,64382L,64383L,64384L, -64385L,64386L,64387L,64388L,64389L,64390L,64391L,64392L,64393L,64394L, -64395L,64396L,64397L,64398L,64399L,64400L,64401L,64402L,64403L,64404L, -64405L,64406L,64407L,64408L,64409L,64410L,64411L,64412L,64413L,64414L, -64415L,64416L,64417L,64418L,64419L,64420L,64421L,64422L,64423L,64424L, -64425L,64426L,64427L,64428L,64429L,64430L,64431L,64432L,64433L,64434L, -64435L,64436L,64437L,64438L,64439L,64440L,64441L,64442L,64443L,64444L, -64445L,64446L,64447L,64448L,64449L,64450L,64451L,64452L,64453L,64454L, -64455L,64456L,64457L,64458L,64459L,64460L,64461L,64462L,64463L,64464L, -64465L,64466L,64467L,64468L,64469L,64470L,64471L,64472L,64473L,64474L, -64475L,64476L,64477L,64478L,64479L,64480L,64481L,64482L,64483L,64484L, -64485L,64486L,64487L,64488L,64489L,64490L,64491L,64492L,64493L,64494L, -64495L,64496L,64497L,64498L,64499L,64500L,64501L,64502L,64503L,64504L, -64505L,64506L,64507L,64508L,64509L,64510L,64511L,64512L,64513L,64514L, -64515L,64516L,64517L,64518L,64519L,64520L,64521L,64522L,64523L,64524L, -64525L,64526L,64527L,64528L,64529L,64530L,64531L,64532L,64533L,64534L, -64535L,64536L,64537L,64538L,64539L,64540L,64541L,64542L,64543L,64544L, -64545L,64546L,64547L,64548L,64549L,64550L,64551L,64552L,64553L,64554L, -64555L,64556L,64557L,64558L,64559L,64560L,64561L,64562L,64563L,64564L, -64565L,64566L,64567L,64568L,64569L,64570L,64571L,64572L,64573L,64574L, -64575L,64576L,64577L,64578L,64579L,64580L,64581L,64582L,64583L,64584L, -64585L,64586L,64587L,64588L,64589L,64590L,64591L,64592L,64593L,64594L, -64595L,64596L,64597L,64598L,64599L,64600L,64601L,64602L,64603L,64604L, -64605L,64606L,64607L,64608L,64609L,64610L,64611L,64612L,64613L,64614L, -64615L,64616L,64617L,64618L,64619L,64620L,64621L,64622L,64623L,64624L, -64625L,64626L,64627L,64628L,64629L,64630L,64631L,64632L,64633L,64634L, -64635L,64636L,64637L,64638L,64639L,64640L,64641L,64642L,64643L,64644L, -64645L,64646L,64647L,64648L,64649L,64650L,64651L,64652L,64653L,64654L, -64655L,64656L,64657L,64658L,64659L,64660L,64661L,64662L,64663L,64664L, -64665L,64666L,64667L,64668L,64669L,64670L,64671L,64672L,64673L,64674L, -64675L,64676L,64677L,64678L,64679L,64680L,64681L,64682L,64683L,64684L, -64685L,64686L,64687L,64688L,64689L,64690L,64691L,64692L,64693L,64694L, -64695L,64696L,64697L,64698L,64699L,64700L,64701L,64702L,64703L,64704L, -64705L,64706L,64707L,64708L,64709L,64710L,64711L,64712L,64713L,64714L, -64715L,64716L,64717L,64718L,64719L,64720L,64721L,64722L,64723L,64724L, -64725L,64726L,64727L,64728L,64729L,64730L,64731L,64732L,64733L,64734L, -64735L,64736L,64737L,64738L,64739L,64740L,64741L,64742L,64743L,64744L, -64745L,64746L,64747L,64748L,64749L,64750L,64751L,64752L,64753L,64754L, -64755L,64756L,64757L,64758L,64759L,64760L,64761L,64762L,64763L,64764L, -64765L,64766L,64767L,64768L,64769L,64770L,64771L,64772L,64773L,64774L, -64775L,64776L,64777L,64778L,64779L,64780L,64781L,64782L,64783L,64784L, -64785L,64786L,64787L,64788L,64789L,64790L,64791L,64792L,64793L,64794L, -64795L,64796L,64797L,64798L,64799L,64800L,64801L,64802L,64803L,64804L, -64805L,64806L,64807L,64808L,64809L,64810L,64811L,64812L,64813L,64814L, -64815L,64816L,64817L,64818L,64819L,64820L,64821L,64822L,64823L,64824L, -64825L,64826L,64827L,64828L,64829L,64830L,64831L,64832L,64833L,64834L, -64835L,64836L,64837L,64838L,64839L,64840L,64841L,64842L,64843L,64844L, -64845L,64846L,64847L,64848L,64849L,64850L,64851L,64852L,64853L,64854L, -64855L,64856L,64857L,64858L,64859L,64860L,64861L,64862L,64863L,64864L, -64865L,64866L,64867L,64868L,64869L,64870L,64871L,64872L,64873L,64874L, -64875L,64876L,64877L,64878L,64879L,64880L,64881L,64882L,64883L,64884L, -64885L,64886L,64887L,64888L,64889L,64890L,64891L,64892L,64893L,64894L, -64895L,64896L,64897L,64898L,64899L,64900L,64901L,64902L,64903L,64904L, -64905L,64906L,64907L,64908L,64909L,64910L,64911L,64912L,64913L,64914L, -64915L,64916L,64917L,64918L,64919L,64920L,64921L,64922L,64923L,64924L, -64925L,64926L,64927L,64928L,64929L,64930L,64931L,64932L,64933L,64934L, -64935L,64936L,64937L,64938L,64939L,64940L,64941L,64942L,64943L,64944L, -64945L,64946L,64947L,64948L,64949L,64950L,64951L,64952L,64953L,64954L, -64955L,64956L,64957L,64958L,64959L,64960L,64961L,64962L,64963L,64964L, -64965L,64966L,64967L,64968L,64969L,64970L,64971L,64972L,64973L,64974L, -64975L,64976L,64977L,64978L,64979L,64980L,64981L,64982L,64983L,64984L, -64985L,64986L,64987L,64988L,64989L,64990L,64991L,64992L,64993L,64994L, -64995L,64996L,64997L,64998L,64999L,65000L,65001L,65002L,65003L,65004L, -65005L,65006L,65007L,65008L,65009L,65010L,65011L,65012L,65013L,65014L, -65015L,65016L,65017L,65018L,65019L,65020L,65021L,65022L,65023L,65024L, -65025L,65026L,65027L,65028L,65029L,65030L,65031L,65032L,65033L,65034L, -65035L,65036L,65037L,65038L,65039L,65040L,65041L,65042L,65043L,65044L, -65045L,65046L,65047L,65048L,65049L,65050L,65051L,65052L,65053L,65054L, -65055L,65056L,65057L,65058L,65059L,65060L,65061L,65062L,65063L,65064L, -65065L,65066L,65067L,65068L,65069L,65070L,65071L,65072L,65073L,65074L, -65075L,65076L,65077L,65078L,65079L,65080L,65081L,65082L,65083L,65084L, -65085L,65086L,65087L,65088L,65089L,65090L,65091L,65092L,65093L,65094L, -65095L,65096L,65097L,65098L,65099L,65100L,65101L,65102L,65103L,65104L, -65105L,65106L,65107L,65108L,65109L,65110L,65111L,65112L,65113L,65114L, -65115L,65116L,65117L,65118L,65119L,65120L,65121L,65122L,65123L,65124L, -65125L,65126L,65127L,65128L,65129L,65130L,65131L,65132L,65133L,65134L, -65135L,65136L,65137L,65138L,65139L,65140L,65141L,65142L,65143L,65144L, -65145L,65146L,65147L,65148L,65149L,65150L,65151L,65152L,65153L,65154L, -65155L,65156L,65157L,65158L,65159L,65160L,65161L,65162L,65163L,65164L, -65165L,65166L,65167L,65168L,65169L,65170L,65171L,65172L,65173L,65174L, -65175L,65176L,65177L,65178L,65179L,65180L,65181L,65182L,65183L,65184L, -65185L,65186L,65187L,65188L,65189L,65190L,65191L,65192L,65193L,65194L, -65195L,65196L,65197L,65198L,65199L,65200L,65201L,65202L,65203L,65204L, -65205L,65206L,65207L,65208L,65209L,65210L,65211L,65212L,65213L,65214L, -65215L,65216L,65217L,65218L,65219L,65220L,65221L,65222L,65223L,65224L, -65225L,65226L,65227L,65228L,65229L,65230L,65231L,65232L,65233L,65234L, -65235L,65236L,65237L,65238L,65239L,65240L,65241L,65242L,65243L,65244L, -65245L,65246L,65247L,65248L,65249L,65250L,65251L,65252L,65253L,65254L, -65255L,65256L,65257L,65258L,65259L,65260L,65261L,65262L,65263L,65264L, -65265L,65266L,65267L,65268L,65269L,65270L,65271L,65272L,65273L,65274L, -65275L,65276L,65277L,65278L,65279L,65280L,65281L,65282L,65283L,65284L, -65285L,65286L,65287L,65288L,65289L,65290L,65291L,65292L,65293L,65294L, -65295L,65296L,65297L,65298L,65299L,65300L,65301L,65302L,65303L,65304L, -65305L,65306L,65307L,65308L,65309L,65310L,65311L,65312L,65313L,65314L, -65315L,65316L,65317L,65318L,65319L,65320L,65321L,65322L,65323L,65324L, -65325L,65326L,65327L,65328L,65329L,65330L,65331L,65332L,65333L,65334L, -65335L,65336L,65337L,65338L,65339L,65340L,65341L,65342L,65343L,65344L, -65313L,65314L,65315L,65316L,65317L,65318L,65319L,65320L,65321L,65322L, -65323L,65324L,65325L,65326L,65327L,65328L,65329L,65330L,65331L,65332L, -65333L,65334L,65335L,65336L,65337L,65338L,65371L,65372L,65373L,65374L, -65375L,65376L,65377L,65378L,65379L,65380L,65381L,65382L,65383L,65384L, -65385L,65386L,65387L,65388L,65389L,65390L,65391L,65392L,65393L,65394L, -65395L,65396L,65397L,65398L,65399L,65400L,65401L,65402L,65403L,65404L, -65405L,65406L,65407L,65408L,65409L,65410L,65411L,65412L,65413L,65414L, -65415L,65416L,65417L,65418L,65419L,65420L,65421L,65422L,65423L,65424L, -65425L,65426L,65427L,65428L,65429L,65430L,65431L,65432L,65433L,65434L, -65435L,65436L,65437L,65438L,65439L,65440L,65441L,65442L,65443L,65444L, -65445L,65446L,65447L,65448L,65449L,65450L,65451L,65452L,65453L,65454L, -65455L,65456L,65457L,65458L,65459L,65460L,65461L,65462L,65463L,65464L, -65465L,65466L,65467L,65468L,65469L,65470L,65471L,65472L,65473L,65474L, -65475L,65476L,65477L,65478L,65479L,65480L,65481L,65482L,65483L,65484L, -65485L,65486L,65487L,65488L,65489L,65490L,65491L,65492L,65493L,65494L, -65495L,65496L,65497L,65498L,65499L,65500L,65501L,65502L,65503L,65504L, -65505L,65506L,65507L,65508L,65509L,65510L,65511L,65512L,65513L,65514L, -65515L,65516L,65517L,65518L,65519L,65520L,65521L,65522L,65523L,65524L, -65525L,65526L,65527L,65528L,65529L,65530L,65531L,65532L,65533L,65534L, -65535L, -}; -#endif - -#if defined(DUK_USE_REGEXP_CANON_BITMAP) -/* - * Automatically generated by extract_caseconv.py, do not edit! - */ - -const duk_uint8_t duk_unicode_re_canon_bitmap[256] = { -23,0,224,19,1,228,255,255,255,255,255,255,255,255,255,255,63,254,255,127, -255,255,255,255,255,255,255,255,231,231,0,16,255,227,255,255,63,255,255, -255,255,255,255,255,1,252,255,255,255,255,255,255,255,255,255,255,255,255, -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -227,129,255,255,255,147,255,255,255,255,255,255,255,255,255,255,255,255, -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,251, -}; -#endif -/* - * Bitstream decoder. - */ - -/* #include duk_internal.h -> already included */ - -/* Decode 'bits' bits from the input stream (bits must be 1...24). - * When reading past bitstream end, zeroes are shifted in. The result - * is signed to match duk_bd_decode_flagged. - */ -DUK_INTERNAL duk_uint32_t duk_bd_decode(duk_bitdecoder_ctx *ctx, duk_small_int_t bits) { - duk_small_int_t shift; - duk_uint32_t mask; - duk_uint32_t tmp; - - /* Note: cannot read more than 24 bits without possibly shifting top bits out. - * Fixable, but adds complexity. - */ - DUK_ASSERT(bits >= 1 && bits <= 24); - - while (ctx->currbits < bits) { -#if 0 - DUK_DDD(DUK_DDDPRINT("decode_bits: shift more data (bits=%ld, currbits=%ld)", - (long) bits, (long) ctx->currbits)); -#endif - ctx->currval <<= 8; - if (ctx->offset < ctx->length) { - /* If ctx->offset >= ctx->length, we "shift zeroes in" - * instead of croaking. - */ - ctx->currval |= ctx->data[ctx->offset++]; - } - ctx->currbits += 8; - } -#if 0 - DUK_DDD(DUK_DDDPRINT("decode_bits: bits=%ld, currbits=%ld, currval=0x%08lx", - (long) bits, (long) ctx->currbits, (unsigned long) ctx->currval)); -#endif - - /* Extract 'top' bits of currval; note that the extracted bits do not need - * to be cleared, we just ignore them on next round. - */ - shift = ctx->currbits - bits; - mask = (((duk_uint32_t) 1U) << bits) - 1U; - tmp = (ctx->currval >> shift) & mask; - ctx->currbits = shift; /* remaining */ - -#if 0 - DUK_DDD(DUK_DDDPRINT("decode_bits: %ld bits -> 0x%08lx (%ld), currbits=%ld, currval=0x%08lx", - (long) bits, (unsigned long) tmp, (long) tmp, (long) ctx->currbits, (unsigned long) ctx->currval)); -#endif - - return tmp; -} - -DUK_INTERNAL duk_small_uint_t duk_bd_decode_flag(duk_bitdecoder_ctx *ctx) { - return (duk_small_uint_t) duk_bd_decode(ctx, 1); -} - -/* Decode a one-bit flag, and if set, decode a value of 'bits', otherwise return - * default value. - */ -DUK_INTERNAL duk_uint32_t duk_bd_decode_flagged(duk_bitdecoder_ctx *ctx, duk_small_int_t bits, duk_uint32_t def_value) { - if (duk_bd_decode_flag(ctx)) { - return duk_bd_decode(ctx, bits); - } else { - return def_value; - } -} - -/* Signed variant, allows negative marker value. */ -DUK_INTERNAL duk_int32_t duk_bd_decode_flagged_signed(duk_bitdecoder_ctx *ctx, duk_small_int_t bits, duk_int32_t def_value) { - return (duk_int32_t) duk_bd_decode_flagged(ctx, bits, (duk_uint32_t) def_value); -} - -/* Shared varint encoding. Match dukutil.py BitEncode.varuint(). */ -DUK_INTERNAL duk_uint32_t duk_bd_decode_varuint(duk_bitdecoder_ctx *ctx) { - duk_small_uint_t t; - - /* The bit encoding choices here are based on manual testing against - * the actual varuints generated by genbuiltins.py. - */ - switch (duk_bd_decode(ctx, 2)) { - case 0: - return 0; /* [0,0] */ - case 1: - return duk_bd_decode(ctx, 2) + 1; /* [1,4] */ - case 2: - return duk_bd_decode(ctx, 5) + 5; /* [5,36] */ - default: - t = duk_bd_decode(ctx, 7); - if (t == 0) { - return duk_bd_decode(ctx, 20); - } - return (t - 1) + 37; /* [37,163] */ - } -} - -/* Decode a bit packed string from a custom format used by genbuiltins.py. - * This function is here because it's used for both heap and thread inits. - * Caller must supply the output buffer whose size is NOT checked! - */ - -#define DUK__BITPACK_LETTER_LIMIT 26 -#define DUK__BITPACK_LOOKUP1 26 -#define DUK__BITPACK_LOOKUP2 27 -#define DUK__BITPACK_SWITCH1 28 -#define DUK__BITPACK_SWITCH 29 -#define DUK__BITPACK_UNUSED1 30 -#define DUK__BITPACK_EIGHTBIT 31 - -DUK_LOCAL const duk_uint8_t duk__bitpacked_lookup[16] = { DUK_ASC_0, DUK_ASC_1, DUK_ASC_2, DUK_ASC_3, - DUK_ASC_4, DUK_ASC_5, DUK_ASC_6, DUK_ASC_7, - DUK_ASC_8, DUK_ASC_9, DUK_ASC_UNDERSCORE, DUK_ASC_SPACE, - 0x82, 0x80, DUK_ASC_DOUBLEQUOTE, DUK_ASC_LCURLY }; - -DUK_INTERNAL duk_small_uint_t duk_bd_decode_bitpacked_string(duk_bitdecoder_ctx *bd, duk_uint8_t *out) { - duk_small_uint_t len; - duk_small_uint_t mode; - duk_small_uint_t t; - duk_small_uint_t i; - - len = duk_bd_decode(bd, 5); - if (len == 31) { - len = duk_bd_decode(bd, 8); /* Support up to 256 bytes; rare. */ - } - - mode = 32; /* 0 = uppercase, 32 = lowercase (= 'a' - 'A') */ - for (i = 0; i < len; i++) { - t = duk_bd_decode(bd, 5); - if (t < DUK__BITPACK_LETTER_LIMIT) { - t = t + DUK_ASC_UC_A + mode; - } else if (t == DUK__BITPACK_LOOKUP1) { - t = duk__bitpacked_lookup[duk_bd_decode(bd, 3)]; - } else if (t == DUK__BITPACK_LOOKUP2) { - t = duk__bitpacked_lookup[8 + duk_bd_decode(bd, 3)]; - } else if (t == DUK__BITPACK_SWITCH1) { - t = duk_bd_decode(bd, 5); - DUK_ASSERT_DISABLE(t >= 0); /* unsigned */ - DUK_ASSERT(t <= 25); - t = t + DUK_ASC_UC_A + (mode ^ 32); - } else if (t == DUK__BITPACK_SWITCH) { - mode = mode ^ 32; - t = duk_bd_decode(bd, 5); - DUK_ASSERT_DISABLE(t >= 0); - DUK_ASSERT(t <= 25); - t = t + DUK_ASC_UC_A + mode; - } else if (t == DUK__BITPACK_EIGHTBIT) { - t = duk_bd_decode(bd, 8); - } - out[i] = (duk_uint8_t) t; - } - - return len; -} - -/* automatic undefs */ -#undef DUK__BITPACK_EIGHTBIT -#undef DUK__BITPACK_LETTER_LIMIT -#undef DUK__BITPACK_LOOKUP1 -#undef DUK__BITPACK_LOOKUP2 -#undef DUK__BITPACK_SWITCH -#undef DUK__BITPACK_SWITCH1 -#undef DUK__BITPACK_UNUSED1 -/* - * Bitstream encoder. - */ - -/* #include duk_internal.h -> already included */ - -DUK_INTERNAL void duk_be_encode(duk_bitencoder_ctx *ctx, duk_uint32_t data, duk_small_int_t bits) { - duk_uint8_t tmp; - - DUK_ASSERT(ctx != NULL); - DUK_ASSERT(ctx->currbits < 8); - - /* This limitation would be fixable but adds unnecessary complexity. */ - DUK_ASSERT(bits >= 1 && bits <= 24); - - ctx->currval = (ctx->currval << bits) | data; - ctx->currbits += bits; - - while (ctx->currbits >= 8) { - if (ctx->offset < ctx->length) { - tmp = (duk_uint8_t) ((ctx->currval >> (ctx->currbits - 8)) & 0xff); - ctx->data[ctx->offset++] = tmp; - } else { - /* If buffer has been exhausted, truncate bitstream */ - ctx->truncated = 1; - } - - ctx->currbits -= 8; - } -} - -DUK_INTERNAL void duk_be_finish(duk_bitencoder_ctx *ctx) { - duk_small_int_t npad; - - DUK_ASSERT(ctx != NULL); - DUK_ASSERT(ctx->currbits < 8); - - npad = (duk_small_int_t) (8 - ctx->currbits); - if (npad > 0) { - duk_be_encode(ctx, 0, npad); - } - DUK_ASSERT(ctx->currbits == 0); -} -/* - * Fast buffer writer with slack management. - */ - -/* #include duk_internal.h -> already included */ - -/* XXX: Avoid duk_{memcmp,memmove}_unsafe() by imposing a minimum length of - * >0 for the underlying dynamic buffer. - */ - -/* - * Macro support functions (use only macros in calling code) - */ - -DUK_LOCAL void duk__bw_update_ptrs(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, duk_size_t curr_offset, duk_size_t new_length) { - duk_uint8_t *p; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(bw_ctx != NULL); - DUK_UNREF(thr); - - /* 'p' might be NULL when the underlying buffer is zero size. If so, - * the resulting pointers are not used unsafely. - */ - p = (duk_uint8_t *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, bw_ctx->buf); - DUK_ASSERT(p != NULL || (DUK_HBUFFER_DYNAMIC_GET_SIZE(bw_ctx->buf) == 0 && curr_offset == 0 && new_length == 0)); - bw_ctx->p = p + curr_offset; - bw_ctx->p_base = p; - bw_ctx->p_limit = p + new_length; -} - -DUK_INTERNAL void duk_bw_init(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, duk_hbuffer_dynamic *h_buf) { - DUK_ASSERT(thr != NULL); - DUK_ASSERT(bw_ctx != NULL); - DUK_ASSERT(h_buf != NULL); - - bw_ctx->buf = h_buf; - duk__bw_update_ptrs(thr, bw_ctx, 0, DUK_HBUFFER_DYNAMIC_GET_SIZE(h_buf)); -} - -DUK_INTERNAL void duk_bw_init_pushbuf(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, duk_size_t buf_size) { - DUK_ASSERT(thr != NULL); - DUK_ASSERT(bw_ctx != NULL); - - (void) duk_push_dynamic_buffer(thr, buf_size); - bw_ctx->buf = (duk_hbuffer_dynamic *) duk_known_hbuffer(thr, -1); - DUK_ASSERT(bw_ctx->buf != NULL); - duk__bw_update_ptrs(thr, bw_ctx, 0, buf_size); -} - -/* Resize target buffer for requested size. Called by the macro only when the - * fast path test (= there is space) fails. - */ -DUK_INTERNAL duk_uint8_t *duk_bw_resize(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, duk_size_t sz) { - duk_size_t curr_off; - duk_size_t add_sz; - duk_size_t new_sz; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(bw_ctx != NULL); - - /* We could do this operation without caller updating bw_ctx->ptr, - * but by writing it back here we can share code better. - */ - - curr_off = (duk_size_t) (bw_ctx->p - bw_ctx->p_base); - add_sz = (curr_off >> DUK_BW_SLACK_SHIFT) + DUK_BW_SLACK_ADD; - new_sz = curr_off + sz + add_sz; - if (DUK_UNLIKELY(new_sz < curr_off)) { - /* overflow */ - DUK_ERROR_RANGE(thr, DUK_STR_BUFFER_TOO_LONG); - DUK_WO_NORETURN(return NULL;); - } -#if 0 /* for manual torture testing: tight allocation, useful with valgrind */ - new_sz = curr_off + sz; -#endif - - /* This is important to ensure dynamic buffer data pointer is not - * NULL (which is possible if buffer size is zero), which in turn - * causes portability issues with e.g. memmove() and memcpy(). - */ - DUK_ASSERT(new_sz >= 1); - - DUK_DD(DUK_DDPRINT("resize bufferwriter from %ld to %ld (add_sz=%ld)", (long) curr_off, (long) new_sz, (long) add_sz)); - - duk_hbuffer_resize(thr, bw_ctx->buf, new_sz); - duk__bw_update_ptrs(thr, bw_ctx, curr_off, new_sz); - return bw_ctx->p; -} - -/* Make buffer compact, matching current written size. */ -DUK_INTERNAL void duk_bw_compact(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx) { - duk_size_t len; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(bw_ctx != NULL); - DUK_UNREF(thr); - - len = (duk_size_t) (bw_ctx->p - bw_ctx->p_base); - duk_hbuffer_resize(thr, bw_ctx->buf, len); - duk__bw_update_ptrs(thr, bw_ctx, len, len); -} - -DUK_INTERNAL void duk_bw_write_raw_slice(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t src_off, duk_size_t len) { - duk_uint8_t *p_base; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(bw != NULL); - DUK_ASSERT(src_off <= DUK_BW_GET_SIZE(thr, bw)); - DUK_ASSERT(len <= DUK_BW_GET_SIZE(thr, bw)); - DUK_ASSERT(src_off + len <= DUK_BW_GET_SIZE(thr, bw)); - DUK_UNREF(thr); - - p_base = bw->p_base; - duk_memcpy_unsafe((void *) bw->p, (const void *) (p_base + src_off), (size_t) len); - bw->p += len; -} - -DUK_INTERNAL void duk_bw_write_ensure_slice(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t src_off, duk_size_t len) { - DUK_ASSERT(thr != NULL); - DUK_ASSERT(bw != NULL); - DUK_ASSERT(src_off <= DUK_BW_GET_SIZE(thr, bw)); - DUK_ASSERT(len <= DUK_BW_GET_SIZE(thr, bw)); - DUK_ASSERT(src_off + len <= DUK_BW_GET_SIZE(thr, bw)); - - DUK_BW_ENSURE(thr, bw, len); - duk_bw_write_raw_slice(thr, bw, src_off, len); -} - -DUK_INTERNAL void duk_bw_insert_raw_bytes(duk_hthread *thr, - duk_bufwriter_ctx *bw, - duk_size_t dst_off, - const duk_uint8_t *buf, - duk_size_t len) { - duk_uint8_t *p_base; - duk_size_t buf_sz, move_sz; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(bw != NULL); - DUK_ASSERT(dst_off <= DUK_BW_GET_SIZE(thr, bw)); - DUK_ASSERT(buf != NULL); - DUK_UNREF(thr); - - p_base = bw->p_base; - buf_sz = (duk_size_t) (bw->p - p_base); /* constrained by maximum buffer size */ - move_sz = buf_sz - dst_off; - - DUK_ASSERT(p_base != NULL); /* buffer size is >= 1 */ - duk_memmove_unsafe((void *) (p_base + dst_off + len), (const void *) (p_base + dst_off), (size_t) move_sz); - duk_memcpy_unsafe((void *) (p_base + dst_off), (const void *) buf, (size_t) len); - bw->p += len; -} - -DUK_INTERNAL void duk_bw_insert_ensure_bytes(duk_hthread *thr, - duk_bufwriter_ctx *bw, - duk_size_t dst_off, - const duk_uint8_t *buf, - duk_size_t len) { - DUK_ASSERT(thr != NULL); - DUK_ASSERT(bw != NULL); - DUK_ASSERT(dst_off <= DUK_BW_GET_SIZE(thr, bw)); - DUK_ASSERT(buf != NULL); - - DUK_BW_ENSURE(thr, bw, len); - duk_bw_insert_raw_bytes(thr, bw, dst_off, buf, len); -} - -DUK_INTERNAL void duk_bw_insert_raw_slice(duk_hthread *thr, - duk_bufwriter_ctx *bw, - duk_size_t dst_off, - duk_size_t src_off, - duk_size_t len) { - duk_uint8_t *p_base; - duk_size_t buf_sz, move_sz; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(bw != NULL); - DUK_ASSERT(dst_off <= DUK_BW_GET_SIZE(thr, bw)); - DUK_ASSERT(src_off <= DUK_BW_GET_SIZE(thr, bw)); - DUK_ASSERT(len <= DUK_BW_GET_SIZE(thr, bw)); - DUK_ASSERT(src_off + len <= DUK_BW_GET_SIZE(thr, bw)); - DUK_UNREF(thr); - - p_base = bw->p_base; - - /* Don't support "straddled" source now. */ - DUK_ASSERT(dst_off <= src_off || dst_off >= src_off + len); - - if (dst_off <= src_off) { - /* Target is before source. Source offset is expressed as - * a "before change" offset. Account for the memmove. - */ - src_off += len; - } - - buf_sz = (duk_size_t) (bw->p - p_base); - move_sz = buf_sz - dst_off; - - DUK_ASSERT(p_base != NULL); /* buffer size is >= 1 */ - duk_memmove_unsafe((void *) (p_base + dst_off + len), (const void *) (p_base + dst_off), (size_t) move_sz); - duk_memcpy_unsafe((void *) (p_base + dst_off), (const void *) (p_base + src_off), (size_t) len); - bw->p += len; -} - -DUK_INTERNAL void duk_bw_insert_ensure_slice(duk_hthread *thr, - duk_bufwriter_ctx *bw, - duk_size_t dst_off, - duk_size_t src_off, - duk_size_t len) { - DUK_ASSERT(thr != NULL); - DUK_ASSERT(bw != NULL); - DUK_ASSERT(dst_off <= DUK_BW_GET_SIZE(thr, bw)); - DUK_ASSERT(src_off <= DUK_BW_GET_SIZE(thr, bw)); - DUK_ASSERT(len <= DUK_BW_GET_SIZE(thr, bw)); - DUK_ASSERT(src_off + len <= DUK_BW_GET_SIZE(thr, bw)); - - /* Don't support "straddled" source now. */ - DUK_ASSERT(dst_off <= src_off || dst_off >= src_off + len); - - DUK_BW_ENSURE(thr, bw, len); - duk_bw_insert_raw_slice(thr, bw, dst_off, src_off, len); -} - -DUK_INTERNAL duk_uint8_t *duk_bw_insert_raw_area(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t off, duk_size_t len) { - duk_uint8_t *p_base, *p_dst, *p_src; - duk_size_t buf_sz, move_sz; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(bw != NULL); - DUK_ASSERT(off <= DUK_BW_GET_SIZE(thr, bw)); - DUK_UNREF(thr); - - p_base = bw->p_base; - buf_sz = (duk_size_t) (bw->p - p_base); - move_sz = buf_sz - off; - p_dst = p_base + off + len; - p_src = p_base + off; - duk_memmove_unsafe((void *) p_dst, (const void *) p_src, (size_t) move_sz); - return p_src; /* point to start of 'reserved area' */ -} - -DUK_INTERNAL duk_uint8_t *duk_bw_insert_ensure_area(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t off, duk_size_t len) { - DUK_ASSERT(thr != NULL); - DUK_ASSERT(bw != NULL); - DUK_ASSERT(off <= DUK_BW_GET_SIZE(thr, bw)); - - DUK_BW_ENSURE(thr, bw, len); - return duk_bw_insert_raw_area(thr, bw, off, len); -} - -DUK_INTERNAL void duk_bw_remove_raw_slice(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t off, duk_size_t len) { - duk_size_t move_sz; - - duk_uint8_t *p_base; - duk_uint8_t *p_src; - duk_uint8_t *p_dst; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(bw != NULL); - DUK_ASSERT(off <= DUK_BW_GET_SIZE(thr, bw)); - DUK_ASSERT(len <= DUK_BW_GET_SIZE(thr, bw)); - DUK_ASSERT(off + len <= DUK_BW_GET_SIZE(thr, bw)); - DUK_UNREF(thr); - - p_base = bw->p_base; - p_dst = p_base + off; - p_src = p_dst + len; - move_sz = (duk_size_t) (bw->p - p_src); - duk_memmove_unsafe((void *) p_dst, (const void *) p_src, (size_t) move_sz); - bw->p -= len; -} - -/* - * Assertion helpers - */ - -#if defined(DUK_USE_ASSERTIONS) -DUK_INTERNAL void duk_bw_assert_valid(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx) { - DUK_UNREF(thr); - DUK_ASSERT(bw_ctx != NULL); - DUK_ASSERT(bw_ctx->buf != NULL); - DUK_ASSERT((DUK_HBUFFER_DYNAMIC_GET_SIZE(bw_ctx->buf) == 0) || - (bw_ctx->p != NULL && bw_ctx->p_base != NULL && bw_ctx->p_limit != NULL && bw_ctx->p_limit >= bw_ctx->p_base && - bw_ctx->p >= bw_ctx->p_base && bw_ctx->p <= bw_ctx->p_limit)); -} -#endif -/* - * Cast helpers. - * - * C99+ coercion is challenging portability-wise because out-of-range casts - * may invoke implementation defined or even undefined behavior. See e.g. - * http://blog.frama-c.com/index.php?post/2013/10/09/Overflow-float-integer. - * - * Provide explicit cast helpers which try to avoid implementation defined - * or undefined behavior. These helpers can then be simplified in the vast - * majority of cases where the implementation defined or undefined behavior - * is not problematic. - */ - -/* #include duk_internal.h -> already included */ - -/* Portable double-to-integer cast which avoids undefined behavior and avoids - * relying on fmin(), fmax(), or other intrinsics. Out-of-range results are - * not assumed by caller, but here value is clamped, NaN converts to minval. - */ -#define DUK__DOUBLE_INT_CAST1(tname, minval, maxval) \ - do { \ - if (DUK_LIKELY(x >= (duk_double_t) (minval))) { \ - DUK_ASSERT(!DUK_ISNAN(x)); \ - if (DUK_LIKELY(x <= (duk_double_t) (maxval))) { \ - return (tname) x; \ - } else { \ - return (tname) (maxval); \ - } \ - } else { \ - /* NaN or below minval. Since we don't care about the result \ - * for out-of-range values, just return the minimum value for \ - * both. \ - */ \ - return (tname) (minval); \ - } \ - } while (0) - -/* Rely on specific NaN behavior for duk_double_{fmin,fmax}(): if either - * argument is a NaN, return the second argument. This avoids a - * NaN-to-integer cast which is undefined behavior. - */ -#define DUK__DOUBLE_INT_CAST2(tname, minval, maxval) \ - do { \ - return (tname) duk_double_fmin(duk_double_fmax(x, (duk_double_t) (minval)), (duk_double_t) (maxval)); \ - } while (0) - -/* Another solution which doesn't need C99+ behavior for fmin() and fmax(). */ -#define DUK__DOUBLE_INT_CAST3(tname, minval, maxval) \ - do { \ - if (DUK_ISNAN(x)) { \ - /* 0 or any other value is fine. */ \ - return (tname) 0; \ - } else \ - return (tname) DUK_FMIN(DUK_FMAX(x, (duk_double_t) (minval)), (duk_double_t) (maxval)); \ - } \ - } \ - while (0) - -/* C99+ solution: relies on specific fmin() and fmax() behavior in C99: if - * one argument is NaN but the other isn't, the non-NaN argument is returned. - * Because the limits are non-NaN values, explicit NaN check is not needed. - * This may not work on all legacy platforms, and also doesn't seem to inline - * the fmin() and fmax() calls (unless one uses -ffast-math which we don't - * support). - */ -#define DUK__DOUBLE_INT_CAST4(tname, minval, maxval) \ - do { \ - return (tname) DUK_FMIN(DUK_FMAX(x, (duk_double_t) (minval)), (duk_double_t) (maxval)); \ - } while (0) - -DUK_INTERNAL duk_int_t duk_double_to_int_t(duk_double_t x) { -#if defined(DUK_USE_ALLOW_UNDEFINED_BEHAVIOR) - /* Real world solution: almost any practical platform will provide - * an integer value without any guarantees what it is (which is fine). - */ - return (duk_int_t) x; -#else - DUK__DOUBLE_INT_CAST1(duk_int_t, DUK_INT_MIN, DUK_INT_MAX); -#endif -} - -DUK_INTERNAL duk_uint_t duk_double_to_uint_t(duk_double_t x) { -#if defined(DUK_USE_ALLOW_UNDEFINED_BEHAVIOR) - return (duk_uint_t) x; -#else - DUK__DOUBLE_INT_CAST1(duk_uint_t, DUK_UINT_MIN, DUK_UINT_MAX); -#endif -} - -DUK_INTERNAL duk_int32_t duk_double_to_int32_t(duk_double_t x) { -#if defined(DUK_USE_ALLOW_UNDEFINED_BEHAVIOR) - return (duk_int32_t) x; -#else - DUK__DOUBLE_INT_CAST1(duk_int32_t, DUK_INT32_MIN, DUK_INT32_MAX); -#endif -} - -DUK_INTERNAL duk_uint32_t duk_double_to_uint32_t(duk_double_t x) { -#if defined(DUK_USE_ALLOW_UNDEFINED_BEHAVIOR) - return (duk_uint32_t) x; -#else - DUK__DOUBLE_INT_CAST1(duk_uint32_t, DUK_UINT32_MIN, DUK_UINT32_MAX); -#endif -} - -/* Largest IEEE double that doesn't round to infinity in the default rounding - * mode. The exact midpoint between (1 - 2^(-24)) * 2^128 and 2^128 rounds to - * infinity, at least on x64. This number is one double unit below that - * midpoint. See misc/float_cast.c. - */ -#define DUK__FLOAT_ROUND_LIMIT 340282356779733623858607532500980858880.0 - -/* Maximum IEEE float. Double-to-float conversion above this would be out of - * range and thus technically undefined behavior. - */ -#define DUK__FLOAT_MAX 340282346638528859811704183484516925440.0 - -DUK_INTERNAL duk_float_t duk_double_to_float_t(duk_double_t x) { - /* Even a double-to-float cast is technically undefined behavior if - * the double is out-of-range. C99 Section 6.3.1.5: - * - * If the value being converted is in the range of values that can - * be represented but cannot be represented exactly, the result is - * either the nearest higher or nearest lower representable value, - * chosen in an implementation-defined manner. If the value being - * converted is outside the range of values that can be represented, - * the behavior is undefined. - */ -#if defined(DUK_USE_ALLOW_UNDEFINED_BEHAVIOR) - return (duk_float_t) x; -#else - duk_double_t t; - - t = DUK_FABS(x); - DUK_ASSERT((DUK_ISNAN(x) && DUK_ISNAN(t)) || (!DUK_ISNAN(x) && !DUK_ISNAN(t))); - - if (DUK_LIKELY(t <= DUK__FLOAT_MAX)) { - /* Standard in-range case, try to get here with a minimum - * number of checks and branches. - */ - DUK_ASSERT(!DUK_ISNAN(x)); - return (duk_float_t) x; - } else if (t <= DUK__FLOAT_ROUND_LIMIT) { - /* Out-of-range, but rounds to min/max float. */ - DUK_ASSERT(!DUK_ISNAN(x)); - if (x < 0.0) { - return (duk_float_t) -DUK__FLOAT_MAX; - } else { - return (duk_float_t) DUK__FLOAT_MAX; - } - } else if (DUK_ISNAN(x)) { - /* Assumes double NaN -> float NaN considered "in range". */ - DUK_ASSERT(DUK_ISNAN(x)); - return (duk_float_t) x; - } else { - /* Out-of-range, rounds to +/- Infinity. */ - if (x < 0.0) { - return (duk_float_t) -DUK_DOUBLE_INFINITY; - } else { - return (duk_float_t) DUK_DOUBLE_INFINITY; - } - } -#endif -} - -/* automatic undefs */ -#undef DUK__DOUBLE_INT_CAST1 -#undef DUK__DOUBLE_INT_CAST2 -#undef DUK__DOUBLE_INT_CAST3 -#undef DUK__DOUBLE_INT_CAST4 -#undef DUK__FLOAT_MAX -#undef DUK__FLOAT_ROUND_LIMIT -/* - * IEEE double helpers. - */ - -/* #include duk_internal.h -> already included */ - -DUK_INTERNAL duk_bool_t duk_double_is_anyinf(duk_double_t x) { - duk_double_union du; - du.d = x; - return DUK_DBLUNION_IS_ANYINF(&du); -} - -DUK_INTERNAL duk_bool_t duk_double_is_posinf(duk_double_t x) { - duk_double_union du; - du.d = x; - return DUK_DBLUNION_IS_POSINF(&du); -} - -DUK_INTERNAL duk_bool_t duk_double_is_neginf(duk_double_t x) { - duk_double_union du; - du.d = x; - return DUK_DBLUNION_IS_NEGINF(&du); -} - -DUK_INTERNAL duk_bool_t duk_double_is_nan(duk_double_t x) { - duk_double_union du; - du.d = x; - /* Assumes we're dealing with a Duktape internal NaN which is - * NaN normalized if duk_tval requires it. - */ - DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du)); - return DUK_DBLUNION_IS_NAN(&du); -} - -DUK_INTERNAL duk_bool_t duk_double_is_nan_or_zero(duk_double_t x) { - duk_double_union du; - du.d = x; - /* Assumes we're dealing with a Duktape internal NaN which is - * NaN normalized if duk_tval requires it. - */ - DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du)); - return DUK_DBLUNION_IS_NAN(&du) || DUK_DBLUNION_IS_ANYZERO(&du); -} - -DUK_INTERNAL duk_bool_t duk_double_is_nan_or_inf(duk_double_t x) { - duk_double_union du; - du.d = x; - /* If exponent is 0x7FF the argument is either a NaN or an - * infinity. We don't need to check any other fields. - */ -#if defined(DUK_USE_64BIT_OPS) -#if defined(DUK_USE_DOUBLE_ME) - return (du.ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x000000007ff00000)) == DUK_U64_CONSTANT(0x000000007ff00000); -#else - return (du.ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x7ff0000000000000)) == DUK_U64_CONSTANT(0x7ff0000000000000); -#endif -#else - return (du.ui[DUK_DBL_IDX_UI0] & 0x7ff00000UL) == 0x7ff00000UL; -#endif -} - -DUK_INTERNAL duk_bool_t duk_double_is_nan_zero_inf(duk_double_t x) { - duk_double_union du; -#if defined(DUK_USE_64BIT_OPS) - duk_uint64_t t; -#else - duk_uint32_t t; -#endif - du.d = x; -#if defined(DUK_USE_64BIT_OPS) -#if defined(DUK_USE_DOUBLE_ME) - t = du.ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x000000007ff00000); - if (t == DUK_U64_CONSTANT(0x0000000000000000)) { - t = du.ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x0000000080000000); - return t == 0; - } - if (t == DUK_U64_CONSTANT(0x000000007ff00000)) { - return 1; - } -#else - t = du.ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x7ff0000000000000); - if (t == DUK_U64_CONSTANT(0x0000000000000000)) { - t = du.ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x8000000000000000); - return t == 0; - } - if (t == DUK_U64_CONSTANT(0x7ff0000000000000)) { - return 1; - } -#endif -#else - t = du.ui[DUK_DBL_IDX_UI0] & 0x7ff00000UL; - if (t == 0x00000000UL) { - return DUK_DBLUNION_IS_ANYZERO(&du); - } - if (t == 0x7ff00000UL) { - return 1; - } -#endif - return 0; -} - -DUK_INTERNAL duk_small_uint_t duk_double_signbit(duk_double_t x) { - duk_double_union du; - du.d = x; - return (duk_small_uint_t) DUK_DBLUNION_GET_SIGNBIT(&du); -} - -DUK_INTERNAL duk_double_t duk_double_trunc_towards_zero(duk_double_t x) { - /* XXX: optimize */ - duk_small_uint_t s = duk_double_signbit(x); - x = DUK_FLOOR(DUK_FABS(x)); /* truncate towards zero */ - if (s) { - x = -x; - } - return x; -} - -DUK_INTERNAL duk_bool_t duk_double_same_sign(duk_double_t x, duk_double_t y) { - duk_double_union du1; - duk_double_union du2; - du1.d = x; - du2.d = y; - - return (((du1.ui[DUK_DBL_IDX_UI0] ^ du2.ui[DUK_DBL_IDX_UI0]) & 0x80000000UL) == 0); -} - -DUK_INTERNAL duk_double_t duk_double_fmin(duk_double_t x, duk_double_t y) { - /* Doesn't replicate fmin() behavior exactly: for fmin() if one - * argument is a NaN, the other argument should be returned. - * Duktape doesn't rely on this behavior so the replacement can - * be simplified. - */ - return (x < y ? x : y); -} - -DUK_INTERNAL duk_double_t duk_double_fmax(duk_double_t x, duk_double_t y) { - /* Doesn't replicate fmax() behavior exactly: for fmax() if one - * argument is a NaN, the other argument should be returned. - * Duktape doesn't rely on this behavior so the replacement can - * be simplified. - */ - return (x > y ? x : y); -} - -DUK_INTERNAL duk_bool_t duk_double_is_finite(duk_double_t x) { - return !duk_double_is_nan_or_inf(x); -} - -DUK_INTERNAL duk_bool_t duk_double_is_integer(duk_double_t x) { - if (duk_double_is_nan_or_inf(x)) { - return 0; - } else { - return duk_double_equals(duk_js_tointeger_number(x), x); - } -} - -DUK_INTERNAL duk_bool_t duk_double_is_safe_integer(duk_double_t x) { - /* >>> 2**53-1 - * 9007199254740991 - */ - return duk_double_is_integer(x) && DUK_FABS(x) <= 9007199254740991.0; -} - -/* Check whether a duk_double_t is a whole number in the 32-bit range (reject - * negative zero), and if so, return a duk_int32_t. - * For compiler use: don't allow negative zero as it will cause trouble with - * LDINT+LDINTX, positive zero is OK. - */ -DUK_INTERNAL duk_bool_t duk_is_whole_get_int32_nonegzero(duk_double_t x, duk_int32_t *ival) { - duk_int32_t t; - - t = duk_double_to_int32_t(x); - if (!duk_double_equals((duk_double_t) t, x)) { - return 0; - } - if (t == 0) { - duk_double_union du; - du.d = x; - if (DUK_DBLUNION_HAS_SIGNBIT(&du)) { - return 0; - } - } - *ival = t; - return 1; -} - -/* Check whether a duk_double_t is a whole number in the 32-bit range, and if - * so, return a duk_int32_t. - */ -DUK_INTERNAL duk_bool_t duk_is_whole_get_int32(duk_double_t x, duk_int32_t *ival) { - duk_int32_t t; - - t = duk_double_to_int32_t(x); - if (!duk_double_equals((duk_double_t) t, x)) { - return 0; - } - *ival = t; - return 1; -} - -/* Division: division by zero is undefined behavior (and may in fact trap) - * so it needs special handling for portability. - */ - -DUK_INTERNAL DUK_INLINE duk_double_t duk_double_div(duk_double_t x, duk_double_t y) { -#if !defined(DUK_USE_ALLOW_UNDEFINED_BEHAVIOR) - if (DUK_UNLIKELY(duk_double_equals(y, 0.0) != 0)) { - /* In C99+ division by zero is undefined behavior so - * avoid it entirely. Hopefully the compiler is - * smart enough to avoid emitting any actual code - * because almost all practical platforms behave as - * expected. - */ - if (x > 0.0) { - if (DUK_SIGNBIT(y)) { - return -DUK_DOUBLE_INFINITY; - } else { - return DUK_DOUBLE_INFINITY; - } - } else if (x < 0.0) { - if (DUK_SIGNBIT(y)) { - return DUK_DOUBLE_INFINITY; - } else { - return -DUK_DOUBLE_INFINITY; - } - } else { - /* +/- 0, NaN */ - return DUK_DOUBLE_NAN; - } - } -#endif - - return x / y; -} - -/* Double and float byteorder changes. */ - -DUK_INTERNAL DUK_INLINE void duk_dblunion_host_to_little(duk_double_union *u) { -#if defined(DUK_USE_DOUBLE_LE) - /* HGFEDCBA -> HGFEDCBA */ - DUK_UNREF(u); -#elif defined(DUK_USE_DOUBLE_ME) - duk_uint32_t a, b; - - /* DCBAHGFE -> HGFEDCBA */ - a = u->ui[0]; - b = u->ui[1]; - u->ui[0] = b; - u->ui[1] = a; -#elif defined(DUK_USE_DOUBLE_BE) - /* ABCDEFGH -> HGFEDCBA */ -#if defined(DUK_USE_64BIT_OPS) - u->ull[0] = DUK_BSWAP64(u->ull[0]); -#else - duk_uint32_t a, b; - - a = u->ui[0]; - b = u->ui[1]; - u->ui[0] = DUK_BSWAP32(b); - u->ui[1] = DUK_BSWAP32(a); -#endif -#else -#error internal error -#endif -} - -DUK_INTERNAL DUK_INLINE void duk_dblunion_little_to_host(duk_double_union *u) { - duk_dblunion_host_to_little(u); -} - -DUK_INTERNAL DUK_INLINE void duk_dblunion_host_to_big(duk_double_union *u) { -#if defined(DUK_USE_DOUBLE_LE) - /* HGFEDCBA -> ABCDEFGH */ -#if defined(DUK_USE_64BIT_OPS) - u->ull[0] = DUK_BSWAP64(u->ull[0]); -#else - duk_uint32_t a, b; - - a = u->ui[0]; - b = u->ui[1]; - u->ui[0] = DUK_BSWAP32(b); - u->ui[1] = DUK_BSWAP32(a); -#endif -#elif defined(DUK_USE_DOUBLE_ME) - duk_uint32_t a, b; - - /* DCBAHGFE -> ABCDEFGH */ - a = u->ui[0]; - b = u->ui[1]; - u->ui[0] = DUK_BSWAP32(a); - u->ui[1] = DUK_BSWAP32(b); -#elif defined(DUK_USE_DOUBLE_BE) - /* ABCDEFGH -> ABCDEFGH */ - DUK_UNREF(u); -#else -#error internal error -#endif -} - -DUK_INTERNAL DUK_INLINE void duk_dblunion_big_to_host(duk_double_union *u) { - duk_dblunion_host_to_big(u); -} - -DUK_INTERNAL DUK_INLINE void duk_fltunion_host_to_big(duk_float_union *u) { -#if defined(DUK_USE_DOUBLE_LE) || defined(DUK_USE_DOUBLE_ME) - /* DCBA -> ABCD */ - u->ui[0] = DUK_BSWAP32(u->ui[0]); -#elif defined(DUK_USE_DOUBLE_BE) - /* ABCD -> ABCD */ - DUK_UNREF(u); -#else -#error internal error -#endif -} - -DUK_INTERNAL DUK_INLINE void duk_fltunion_big_to_host(duk_float_union *u) { - duk_fltunion_host_to_big(u); -} - -/* Comparison: ensures comparison operates on exactly correct types, avoiding - * some floating point comparison pitfalls (e.g. atan2() assertions failed on - * -m32 with direct comparison, even with explicit casts). - */ -#if defined(DUK_USE_GCC_PRAGMAS) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" -#elif defined(DUK_USE_CLANG_PRAGMAS) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wfloat-equal" -#endif - -DUK_INTERNAL DUK_ALWAYS_INLINE duk_bool_t duk_double_equals(duk_double_t x, duk_double_t y) { - return x == y; -} - -DUK_INTERNAL DUK_ALWAYS_INLINE duk_bool_t duk_float_equals(duk_float_t x, duk_float_t y) { - return x == y; -} -#if defined(DUK_USE_GCC_PRAGMAS) -#pragma GCC diagnostic pop -#elif defined(DUK_USE_CLANG_PRAGMAS) -#pragma clang diagnostic pop -#endif -/* - * Hash function duk_util_hashbytes(). - * - * Currently, 32-bit MurmurHash2. - * - * Don't rely on specific hash values; hash function may be endianness - * dependent, for instance. - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_STRHASH_DENSE) -/* 'magic' constants for Murmurhash2 */ -#define DUK__MAGIC_M ((duk_uint32_t) 0x5bd1e995UL) -#define DUK__MAGIC_R 24 - -DUK_INTERNAL duk_uint32_t duk_util_hashbytes(const duk_uint8_t *data, duk_size_t len, duk_uint32_t seed) { - duk_uint32_t h = seed ^ ((duk_uint32_t) len); - - while (len >= 4) { - /* Portability workaround is required for platforms without - * unaligned access. The replacement code emulates little - * endian access even on big endian architectures, which is - * OK as long as it is consistent for a build. - */ -#if defined(DUK_USE_HASHBYTES_UNALIGNED_U32_ACCESS) - duk_uint32_t k = *((const duk_uint32_t *) (const void *) data); -#else - duk_uint32_t k = ((duk_uint32_t) data[0]) | (((duk_uint32_t) data[1]) << 8) | (((duk_uint32_t) data[2]) << 16) | - (((duk_uint32_t) data[3]) << 24); -#endif - - k *= DUK__MAGIC_M; - k ^= k >> DUK__MAGIC_R; - k *= DUK__MAGIC_M; - h *= DUK__MAGIC_M; - h ^= k; - data += 4; - len -= 4; - } - - switch (len) { - case 3: - h ^= data[2] << 16; - case 2: - h ^= data[1] << 8; - case 1: - h ^= data[0]; - h *= DUK__MAGIC_M; - } - - h ^= h >> 13; - h *= DUK__MAGIC_M; - h ^= h >> 15; - - return h; -} -#endif /* DUK_USE_STRHASH_DENSE */ - -/* automatic undefs */ -#undef DUK__MAGIC_M -#undef DUK__MAGIC_R -/* - * Memory utils. - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_ALLOW_UNDEFINED_BEHAVIOR) -DUK_INTERNAL DUK_INLINE duk_small_int_t duk_memcmp_unsafe(const void *s1, const void *s2, duk_size_t len) { - DUK_ASSERT(s1 != NULL || len == 0U); - DUK_ASSERT(s2 != NULL || len == 0U); - return DUK_MEMCMP(s1, s2, (size_t) len); -} - -DUK_INTERNAL DUK_INLINE duk_small_int_t duk_memcmp(const void *s1, const void *s2, duk_size_t len) { - DUK_ASSERT(s1 != NULL); - DUK_ASSERT(s2 != NULL); - return DUK_MEMCMP(s1, s2, (size_t) len); -} -#else /* DUK_USE_ALLOW_UNDEFINED_BEHAVIOR */ -DUK_INTERNAL DUK_INLINE duk_small_int_t duk_memcmp_unsafe(const void *s1, const void *s2, duk_size_t len) { - DUK_ASSERT(s1 != NULL || len == 0U); - DUK_ASSERT(s2 != NULL || len == 0U); - if (DUK_UNLIKELY(len == 0U)) { - return 0; - } - DUK_ASSERT(s1 != NULL); - DUK_ASSERT(s2 != NULL); - return duk_memcmp(s1, s2, len); -} - -DUK_INTERNAL DUK_INLINE duk_small_int_t duk_memcmp(const void *s1, const void *s2, duk_size_t len) { - DUK_ASSERT(s1 != NULL); - DUK_ASSERT(s2 != NULL); - return DUK_MEMCMP(s1, s2, (size_t) len); -} -#endif /* DUK_USE_ALLOW_UNDEFINED_BEHAVIOR */ -/* - * A tiny random number generator used for Math.random() and other internals. - * - * Default algorithm is xoroshiro128+: http://xoroshiro.di.unimi.it/xoroshiro128plus.c - * with SplitMix64 seed preparation: http://xorshift.di.unimi.it/splitmix64.c. - * - * Low memory targets and targets without 64-bit types use a slightly smaller - * (but slower) algorithm by Adi Shamir: - * http://www.woodmann.com/forum/archive/index.php/t-3100.html. - * - */ - -/* #include duk_internal.h -> already included */ - -#if !defined(DUK_USE_GET_RANDOM_DOUBLE) - -#if defined(DUK_USE_PREFER_SIZE) || !defined(DUK_USE_64BIT_OPS) -#define DUK__RANDOM_SHAMIR3OP -#else -#define DUK__RANDOM_XOROSHIRO128PLUS -#endif - -#if defined(DUK__RANDOM_SHAMIR3OP) -#define DUK__UPDATE_RND(rnd) \ - do { \ - (rnd) += ((rnd) * (rnd)) | 0x05UL; \ - (rnd) = ((rnd) &0xffffffffUL); /* if duk_uint32_t is exactly 32 bits, this is a NOP */ \ - } while (0) - -#define DUK__RND_BIT(rnd) ((rnd) >> 31) /* only use the highest bit */ - -DUK_INTERNAL void duk_util_tinyrandom_prepare_seed(duk_hthread *thr) { - DUK_UNREF(thr); /* Nothing now. */ -} - -DUK_INTERNAL duk_double_t duk_util_tinyrandom_get_double(duk_hthread *thr) { - duk_double_t t; - duk_small_int_t n; - duk_uint32_t rnd; - - rnd = thr->heap->rnd_state; - - n = 53; /* enough to cover the whole mantissa */ - t = 0.0; - - do { - DUK__UPDATE_RND(rnd); - t += DUK__RND_BIT(rnd); - t /= 2.0; - } while (--n); - - thr->heap->rnd_state = rnd; - - DUK_ASSERT(t >= (duk_double_t) 0.0); - DUK_ASSERT(t < (duk_double_t) 1.0); - - return t; -} -#endif /* DUK__RANDOM_SHAMIR3OP */ - -#if defined(DUK__RANDOM_XOROSHIRO128PLUS) -DUK_LOCAL DUK_ALWAYS_INLINE duk_uint64_t duk__rnd_splitmix64(duk_uint64_t *x) { - duk_uint64_t z; - z = (*x += DUK_U64_CONSTANT(0x9E3779B97F4A7C15)); - z = (z ^ (z >> 30U)) * DUK_U64_CONSTANT(0xBF58476D1CE4E5B9); - z = (z ^ (z >> 27U)) * DUK_U64_CONSTANT(0x94D049BB133111EB); - return z ^ (z >> 31U); -} - -DUK_LOCAL DUK_ALWAYS_INLINE duk_uint64_t duk__rnd_rotl(const duk_uint64_t x, duk_small_uint_t k) { - return (x << k) | (x >> (64U - k)); -} - -DUK_LOCAL DUK_ALWAYS_INLINE duk_uint64_t duk__xoroshiro128plus(duk_uint64_t *s) { - duk_uint64_t s0; - duk_uint64_t s1; - duk_uint64_t res; - - s0 = s[0]; - s1 = s[1]; - res = s0 + s1; - s1 ^= s0; - s[0] = duk__rnd_rotl(s0, 55) ^ s1 ^ (s1 << 14U); - s[1] = duk__rnd_rotl(s1, 36); - - return res; -} - -DUK_INTERNAL void duk_util_tinyrandom_prepare_seed(duk_hthread *thr) { - duk_small_uint_t i; - duk_uint64_t x; - - /* Mix both halves of the initial seed with SplitMix64. The intent - * is to ensure that very similar raw seeds (which is usually the case - * because current seed is Date.now()) result in different xoroshiro128+ - * seeds. - */ - x = thr->heap->rnd_state[0]; /* Only [0] is used as input here. */ - for (i = 0; i < 64; i++) { - thr->heap->rnd_state[i & 0x01] = duk__rnd_splitmix64(&x); /* Keep last 2 values. */ - } -} - -DUK_INTERNAL duk_double_t duk_util_tinyrandom_get_double(duk_hthread *thr) { - duk_uint64_t v; - duk_double_union du; - - /* For big and little endian the integer and IEEE double byte order - * is the same so a direct assignment works. For mixed endian the - * 32-bit parts must be swapped. - */ - v = (DUK_U64_CONSTANT(0x3ff) << 52U) | (duk__xoroshiro128plus((duk_uint64_t *) thr->heap->rnd_state) >> 12U); - du.ull[0] = v; -#if defined(DUK_USE_DOUBLE_ME) - do { - duk_uint32_t tmp; - tmp = du.ui[0]; - du.ui[0] = du.ui[1]; - du.ui[1] = tmp; - } while (0); -#endif - return du.d - 1.0; -} -#endif /* DUK__RANDOM_XOROSHIRO128PLUS */ - -#endif /* !DUK_USE_GET_RANDOM_DOUBLE */ - -/* automatic undefs */ -#undef DUK__RANDOM_SHAMIR3OP -#undef DUK__RANDOM_XOROSHIRO128PLUS -#undef DUK__RND_BIT -#undef DUK__UPDATE_RND diff --git a/src/thirdparty/duktape/duktape.h b/src/thirdparty/duktape/duktape.h deleted file mode 100644 index d3cba119e3..0000000000 --- a/src/thirdparty/duktape/duktape.h +++ /dev/null @@ -1,1456 +0,0 @@ -/* - * Duktape public API for Duktape 2.7.0. - * - * See the API reference for documentation on call semantics. The exposed, - * supported API is between the "BEGIN PUBLIC API" and "END PUBLIC API" - * comments. Other parts of the header are Duktape internal and related to - * e.g. platform/compiler/feature detection. - * - * Git commit external (external). - * Git branch external. - * - * See Duktape AUTHORS.rst and LICENSE.txt for copyright and - * licensing information. - */ - -/* LICENSE.txt */ -/* - * =============== - * Duktape license - * =============== - * - * (http://opensource.org/licenses/MIT) - * - * Copyright (c) 2013-present by Duktape authors (see AUTHORS.rst) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* AUTHORS.rst */ -/* - * =============== - * Duktape authors - * =============== - * - * Copyright - * ========= - * - * Duktape copyrights are held by its authors. Each author has a copyright - * to their contribution, and agrees to irrevocably license the contribution - * under the Duktape ``LICENSE.txt``. - * - * Authors - * ======= - * - * Please include an e-mail address, a link to your GitHub profile, or something - * similar to allow your contribution to be identified accurately. - * - * The following people have contributed code, website contents, or Wiki contents, - * and agreed to irrevocably license their contributions under the Duktape - * ``LICENSE.txt`` (in order of appearance): - * - * * Sami Vaarala - * * Niki Dobrev - * * Andreas \u00d6man - * * L\u00e1szl\u00f3 Lang\u00f3 - * * Legimet - * * Karl Skomski - * * Bruce Pascoe - * * Ren\u00e9 Hollander - * * Julien Hamaide (https://github.com/crazyjul) - * * Sebastian G\u00f6tte (https://github.com/jaseg) - * * Tomasz Magulski (https://github.com/magul) - * * \D. Bohdan (https://github.com/dbohdan) - * * Ond\u0159ej Jirman (https://github.com/megous) - * * Sa\u00fal Ibarra Corretg\u00e9 - * * Jeremy HU - * * Ole Andr\u00e9 Vadla Ravn\u00e5s (https://github.com/oleavr) - * * Harold Brenes (https://github.com/harold-b) - * * Oliver Crow (https://github.com/ocrow) - * * Jakub Ch\u0142api\u0144ski (https://github.com/jchlapinski) - * * Brett Vickers (https://github.com/beevik) - * * Dominik Okwieka (https://github.com/okitec) - * * Remko Tron\u00e7on (https://el-tramo.be) - * * Romero Malaquias (rbsm@ic.ufal.br) - * * Michael Drake - * * Steven Don (https://github.com/shdon) - * * Simon Stone (https://github.com/sstone1) - * * \J. McC. (https://github.com/jmhmccr) - * * Jakub Nowakowski (https://github.com/jimvonmoon) - * * Tommy Nguyen (https://github.com/tn0502) - * * Fabrice Fontaine (https://github.com/ffontaine) - * * Christopher Hiller (https://github.com/boneskull) - * * Gonzalo Diethelm (https://github.com/gonzus) - * * Michal Kasperek (https://github.com/michalkas) - * * Andrew Janke (https://github.com/apjanke) - * * Steve Fan (https://github.com/stevefan1999) - * * Edward Betts (https://github.com/edwardbetts) - * * Ozhan Duz (https://github.com/webfolderio) - * * Akos Kiss (https://github.com/akosthekiss) - * * TheBrokenRail (https://github.com/TheBrokenRail) - * * Jesse Doyle (https://github.com/jessedoyle) - * * Gero Kuehn (https://github.com/dc6jgk) - * * James Swift (https://github.com/phraemer) - * * Luis de Bethencourt (https://github.com/luisbg) - * * Ian Whyman (https://github.com/v00d00) - * * Rick Sayre (https://github.com/whorfin) - * * Craig Leres (https://github.com/leres) - * * Maurici Abad (https://github.com/mauriciabad) - * * Nancy Li (https://github.com/NancyLi1013) - * * William Parks (https://github.com/WilliamParks) - * * Sam Hellawell (https://github.com/samhellawell) - * * Vladislavs Sokurenko (https://github.com/sokurenko) - * - * Other contributions - * =================== - * - * The following people have contributed something other than code (e.g. reported - * bugs, provided ideas, etc; roughly in order of appearance): - * - * * Greg Burns - * * Anthony Rabine - * * Carlos Costa - * * Aur\u00e9lien Bouilland - * * Preet Desai (Pris Matic) - * * judofyr (http://www.reddit.com/user/judofyr) - * * Jason Woofenden - * * Micha\u0142 Przyby\u015b - * * Anthony Howe - * * Conrad Pankoff - * * Jim Schimpf - * * Rajaran Gaunker (https://github.com/zimbabao) - * * Andreas \u00d6man - * * Doug Sanden - * * Josh Engebretson (https://github.com/JoshEngebretson) - * * Remo Eichenberger (https://github.com/remoe) - * * Mamod Mehyar (https://github.com/mamod) - * * David Demelier (https://github.com/markand) - * * Tim Caswell (https://github.com/creationix) - * * Mitchell Blank Jr (https://github.com/mitchblank) - * * https://github.com/yushli - * * Seo Sanghyeon (https://github.com/sanxiyn) - * * Han ChoongWoo (https://github.com/tunz) - * * Joshua Peek (https://github.com/josh) - * * Bruce E. Pascoe (https://github.com/fatcerberus) - * * https://github.com/Kelledin - * * https://github.com/sstruchtrup - * * Michael Drake (https://github.com/tlsa) - * * https://github.com/chris-y - * * Laurent Zubiaur (https://github.com/lzubiaur) - * * Neil Kolban (https://github.com/nkolban) - * * Wilhelm Wanecek (https://github.com/wanecek) - * * Andrew Janke (https://github.com/apjanke) - * * Unamer (https://github.com/unamer) - * * Karl Dahlke (eklhad@gmail.com) - * - * If you are accidentally missing from this list, send me an e-mail - * (``sami.vaarala@iki.fi``) and I'll fix the omission. - */ - -#if !defined(DUKTAPE_H_INCLUDED) -#define DUKTAPE_H_INCLUDED - -#define DUK_SINGLE_FILE - -/* - * BEGIN PUBLIC API - */ - -/* - * Version and Git commit identification - */ - -/* Duktape version, (major * 10000) + (minor * 100) + patch. Allows C code - * to #if (DUK_VERSION >= NNN) against Duktape API version. The same value - * is also available to ECMAScript code in Duktape.version. Unofficial - * development snapshots have 99 for patch level (e.g. 0.10.99 would be a - * development version after 0.10.0 but before the next official release). - */ -#define DUK_VERSION 20700L - -/* Git commit, describe, and branch for Duktape build. Useful for - * non-official snapshot builds so that application code can easily log - * which Duktape snapshot was used. Not available in the ECMAScript - * environment. - */ -#define DUK_GIT_COMMIT "external" -#define DUK_GIT_DESCRIBE "external" -#define DUK_GIT_BRANCH "external" - -/* External duk_config.h provides platform/compiler/OS dependent - * typedefs and macros, and DUK_USE_xxx config options so that - * the rest of Duktape doesn't need to do any feature detection. - * DUK_VERSION is defined before including so that configuration - * snippets can react to it. - */ -#include "duk_config.h" - -/* - * Avoid C++ name mangling - */ - -#if defined(__cplusplus) -extern "C" { -#endif - -/* - * Some defines forwarded from feature detection - */ - -#undef DUK_API_VARIADIC_MACROS -#if defined(DUK_USE_VARIADIC_MACROS) -#define DUK_API_VARIADIC_MACROS -#endif - -#define DUK_API_NORETURN(decl) DUK_NORETURN(decl) - -/* - * Public API specific typedefs - * - * Many types are wrapped by Duktape for portability to rare platforms - * where e.g. 'int' is a 16-bit type. See practical typing discussion - * in Duktape web documentation. - */ - -struct duk_thread_state; -struct duk_memory_functions; -struct duk_function_list_entry; -struct duk_number_list_entry; -struct duk_time_components; - -/* duk_context is now defined in duk_config.h because it may also be - * referenced there by prototypes. - */ -typedef struct duk_thread_state duk_thread_state; -typedef struct duk_memory_functions duk_memory_functions; -typedef struct duk_function_list_entry duk_function_list_entry; -typedef struct duk_number_list_entry duk_number_list_entry; -typedef struct duk_time_components duk_time_components; - -typedef duk_ret_t (*duk_c_function)(duk_context *ctx); -typedef void *(*duk_alloc_function) (void *udata, duk_size_t size); -typedef void *(*duk_realloc_function) (void *udata, void *ptr, duk_size_t size); -typedef void (*duk_free_function) (void *udata, void *ptr); -typedef void (*duk_fatal_function) (void *udata, const char *msg); -typedef void (*duk_decode_char_function) (void *udata, duk_codepoint_t codepoint); -typedef duk_codepoint_t (*duk_map_char_function) (void *udata, duk_codepoint_t codepoint); -typedef duk_ret_t (*duk_safe_call_function) (duk_context *ctx, void *udata); -typedef duk_size_t (*duk_debug_read_function) (void *udata, char *buffer, duk_size_t length); -typedef duk_size_t (*duk_debug_write_function) (void *udata, const char *buffer, duk_size_t length); -typedef duk_size_t (*duk_debug_peek_function) (void *udata); -typedef void (*duk_debug_read_flush_function) (void *udata); -typedef void (*duk_debug_write_flush_function) (void *udata); -typedef duk_idx_t (*duk_debug_request_function) (duk_context *ctx, void *udata, duk_idx_t nvalues); -typedef void (*duk_debug_detached_function) (duk_context *ctx, void *udata); - -struct duk_thread_state { - /* XXX: Enough space to hold internal suspend/resume structure. - * This is rather awkward and to be fixed when the internal - * structure is visible for the public API header. - */ - char data[128]; -}; - -struct duk_memory_functions { - duk_alloc_function alloc_func; - duk_realloc_function realloc_func; - duk_free_function free_func; - void *udata; -}; - -struct duk_function_list_entry { - const char *key; - duk_c_function value; - duk_idx_t nargs; -}; - -struct duk_number_list_entry { - const char *key; - duk_double_t value; -}; - -struct duk_time_components { - duk_double_t year; /* year, e.g. 2016, ECMAScript year range */ - duk_double_t month; /* month: 1-12 */ - duk_double_t day; /* day: 1-31 */ - duk_double_t hours; /* hour: 0-59 */ - duk_double_t minutes; /* minute: 0-59 */ - duk_double_t seconds; /* second: 0-59 (in POSIX time no leap second) */ - duk_double_t milliseconds; /* may contain sub-millisecond fractions */ - duk_double_t weekday; /* weekday: 0-6, 0=Sunday, 1=Monday, ..., 6=Saturday */ -}; - -/* - * Constants - */ - -/* Duktape debug protocol version used by this build. */ -#define DUK_DEBUG_PROTOCOL_VERSION 2 - -/* Used to represent invalid index; if caller uses this without checking, - * this index will map to a non-existent stack entry. Also used in some - * API calls as a marker to denote "no value". - */ -#define DUK_INVALID_INDEX DUK_IDX_MIN - -/* Indicates that a native function does not have a fixed number of args, - * and the argument stack should not be capped/extended at all. - */ -#define DUK_VARARGS ((duk_int_t) (-1)) - -/* Number of value stack entries (in addition to actual call arguments) - * guaranteed to be allocated on entry to a Duktape/C function. - */ -#define DUK_API_ENTRY_STACK 64U - -/* Value types, used by e.g. duk_get_type() */ -#define DUK_TYPE_MIN 0U -#define DUK_TYPE_NONE 0U /* no value, e.g. invalid index */ -#define DUK_TYPE_UNDEFINED 1U /* ECMAScript undefined */ -#define DUK_TYPE_NULL 2U /* ECMAScript null */ -#define DUK_TYPE_BOOLEAN 3U /* ECMAScript boolean: 0 or 1 */ -#define DUK_TYPE_NUMBER 4U /* ECMAScript number: double */ -#define DUK_TYPE_STRING 5U /* ECMAScript string: CESU-8 / extended UTF-8 encoded */ -#define DUK_TYPE_OBJECT 6U /* ECMAScript object: includes objects, arrays, functions, threads */ -#define DUK_TYPE_BUFFER 7U /* fixed or dynamic, garbage collected byte buffer */ -#define DUK_TYPE_POINTER 8U /* raw void pointer */ -#define DUK_TYPE_LIGHTFUNC 9U /* lightweight function pointer */ -#define DUK_TYPE_MAX 9U - -/* Value mask types, used by e.g. duk_get_type_mask() */ -#define DUK_TYPE_MASK_NONE (1U << DUK_TYPE_NONE) -#define DUK_TYPE_MASK_UNDEFINED (1U << DUK_TYPE_UNDEFINED) -#define DUK_TYPE_MASK_NULL (1U << DUK_TYPE_NULL) -#define DUK_TYPE_MASK_BOOLEAN (1U << DUK_TYPE_BOOLEAN) -#define DUK_TYPE_MASK_NUMBER (1U << DUK_TYPE_NUMBER) -#define DUK_TYPE_MASK_STRING (1U << DUK_TYPE_STRING) -#define DUK_TYPE_MASK_OBJECT (1U << DUK_TYPE_OBJECT) -#define DUK_TYPE_MASK_BUFFER (1U << DUK_TYPE_BUFFER) -#define DUK_TYPE_MASK_POINTER (1U << DUK_TYPE_POINTER) -#define DUK_TYPE_MASK_LIGHTFUNC (1U << DUK_TYPE_LIGHTFUNC) -#define DUK_TYPE_MASK_THROW (1U << 10) /* internal flag value: throw if mask doesn't match */ -#define DUK_TYPE_MASK_PROMOTE (1U << 11) /* internal flag value: promote to object if mask matches */ - -/* Coercion hints */ -#define DUK_HINT_NONE 0 /* prefer number, unless input is a Date, in which - * case prefer string (E5 Section 8.12.8) - */ -#define DUK_HINT_STRING 1 /* prefer string */ -#define DUK_HINT_NUMBER 2 /* prefer number */ - -/* Enumeration flags for duk_enum() */ -#define DUK_ENUM_INCLUDE_NONENUMERABLE (1U << 0) /* enumerate non-numerable properties in addition to enumerable */ -#define DUK_ENUM_INCLUDE_HIDDEN (1U << 1) /* enumerate hidden symbols too (in Duktape 1.x called internal properties) */ -#define DUK_ENUM_INCLUDE_SYMBOLS (1U << 2) /* enumerate symbols */ -#define DUK_ENUM_EXCLUDE_STRINGS (1U << 3) /* exclude strings */ -#define DUK_ENUM_OWN_PROPERTIES_ONLY (1U << 4) /* don't walk prototype chain, only check own properties */ -#define DUK_ENUM_ARRAY_INDICES_ONLY (1U << 5) /* only enumerate array indices */ -/* XXX: misleading name */ -#define DUK_ENUM_SORT_ARRAY_INDICES (1U << 6) /* sort array indices (applied to full enumeration result, including inherited array indices); XXX: misleading name */ -#define DUK_ENUM_NO_PROXY_BEHAVIOR (1U << 7) /* enumerate a proxy object itself without invoking proxy behavior */ - -/* Compilation flags for duk_compile() and duk_eval() */ -/* DUK_COMPILE_xxx bits 0-2 are reserved for an internal 'nargs' argument. - */ -#define DUK_COMPILE_EVAL (1U << 3) /* compile eval code (instead of global code) */ -#define DUK_COMPILE_FUNCTION (1U << 4) /* compile function code (instead of global code) */ -#define DUK_COMPILE_STRICT (1U << 5) /* use strict (outer) context for global, eval, or function code */ -#define DUK_COMPILE_SHEBANG (1U << 6) /* allow shebang ('#! ...') comment on first line of source */ -#define DUK_COMPILE_SAFE (1U << 7) /* (internal) catch compilation errors */ -#define DUK_COMPILE_NORESULT (1U << 8) /* (internal) omit eval result */ -#define DUK_COMPILE_NOSOURCE (1U << 9) /* (internal) no source string on stack */ -#define DUK_COMPILE_STRLEN (1U << 10) /* (internal) take strlen() of src_buffer (avoids double evaluation in macro) */ -#define DUK_COMPILE_NOFILENAME (1U << 11) /* (internal) no filename on stack */ -#define DUK_COMPILE_FUNCEXPR (1U << 12) /* (internal) source is a function expression (used for Function constructor) */ - -/* Flags for duk_def_prop() and its variants; base flags + a lot of convenience shorthands */ -#define DUK_DEFPROP_WRITABLE (1U << 0) /* set writable (effective if DUK_DEFPROP_HAVE_WRITABLE set) */ -#define DUK_DEFPROP_ENUMERABLE (1U << 1) /* set enumerable (effective if DUK_DEFPROP_HAVE_ENUMERABLE set) */ -#define DUK_DEFPROP_CONFIGURABLE (1U << 2) /* set configurable (effective if DUK_DEFPROP_HAVE_CONFIGURABLE set) */ -#define DUK_DEFPROP_HAVE_WRITABLE (1U << 3) /* set/clear writable */ -#define DUK_DEFPROP_HAVE_ENUMERABLE (1U << 4) /* set/clear enumerable */ -#define DUK_DEFPROP_HAVE_CONFIGURABLE (1U << 5) /* set/clear configurable */ -#define DUK_DEFPROP_HAVE_VALUE (1U << 6) /* set value (given on value stack) */ -#define DUK_DEFPROP_HAVE_GETTER (1U << 7) /* set getter (given on value stack) */ -#define DUK_DEFPROP_HAVE_SETTER (1U << 8) /* set setter (given on value stack) */ -#define DUK_DEFPROP_FORCE (1U << 9) /* force change if possible, may still fail for e.g. virtual properties */ -#define DUK_DEFPROP_SET_WRITABLE (DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_WRITABLE) -#define DUK_DEFPROP_CLEAR_WRITABLE DUK_DEFPROP_HAVE_WRITABLE -#define DUK_DEFPROP_SET_ENUMERABLE (DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_ENUMERABLE) -#define DUK_DEFPROP_CLEAR_ENUMERABLE DUK_DEFPROP_HAVE_ENUMERABLE -#define DUK_DEFPROP_SET_CONFIGURABLE (DUK_DEFPROP_HAVE_CONFIGURABLE | DUK_DEFPROP_CONFIGURABLE) -#define DUK_DEFPROP_CLEAR_CONFIGURABLE DUK_DEFPROP_HAVE_CONFIGURABLE -#define DUK_DEFPROP_W DUK_DEFPROP_WRITABLE -#define DUK_DEFPROP_E DUK_DEFPROP_ENUMERABLE -#define DUK_DEFPROP_C DUK_DEFPROP_CONFIGURABLE -#define DUK_DEFPROP_WE (DUK_DEFPROP_WRITABLE | DUK_DEFPROP_ENUMERABLE) -#define DUK_DEFPROP_WC (DUK_DEFPROP_WRITABLE | DUK_DEFPROP_CONFIGURABLE) -#define DUK_DEFPROP_EC (DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_CONFIGURABLE) -#define DUK_DEFPROP_WEC (DUK_DEFPROP_WRITABLE | DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_CONFIGURABLE) -#define DUK_DEFPROP_HAVE_W DUK_DEFPROP_HAVE_WRITABLE -#define DUK_DEFPROP_HAVE_E DUK_DEFPROP_HAVE_ENUMERABLE -#define DUK_DEFPROP_HAVE_C DUK_DEFPROP_HAVE_CONFIGURABLE -#define DUK_DEFPROP_HAVE_WE (DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_HAVE_ENUMERABLE) -#define DUK_DEFPROP_HAVE_WC (DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_HAVE_CONFIGURABLE) -#define DUK_DEFPROP_HAVE_EC (DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_HAVE_CONFIGURABLE) -#define DUK_DEFPROP_HAVE_WEC (DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_HAVE_CONFIGURABLE) -#define DUK_DEFPROP_SET_W DUK_DEFPROP_SET_WRITABLE -#define DUK_DEFPROP_SET_E DUK_DEFPROP_SET_ENUMERABLE -#define DUK_DEFPROP_SET_C DUK_DEFPROP_SET_CONFIGURABLE -#define DUK_DEFPROP_SET_WE (DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_ENUMERABLE) -#define DUK_DEFPROP_SET_WC (DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_CONFIGURABLE) -#define DUK_DEFPROP_SET_EC (DUK_DEFPROP_SET_ENUMERABLE | DUK_DEFPROP_SET_CONFIGURABLE) -#define DUK_DEFPROP_SET_WEC (DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_ENUMERABLE | DUK_DEFPROP_SET_CONFIGURABLE) -#define DUK_DEFPROP_CLEAR_W DUK_DEFPROP_CLEAR_WRITABLE -#define DUK_DEFPROP_CLEAR_E DUK_DEFPROP_CLEAR_ENUMERABLE -#define DUK_DEFPROP_CLEAR_C DUK_DEFPROP_CLEAR_CONFIGURABLE -#define DUK_DEFPROP_CLEAR_WE (DUK_DEFPROP_CLEAR_WRITABLE | DUK_DEFPROP_CLEAR_ENUMERABLE) -#define DUK_DEFPROP_CLEAR_WC (DUK_DEFPROP_CLEAR_WRITABLE | DUK_DEFPROP_CLEAR_CONFIGURABLE) -#define DUK_DEFPROP_CLEAR_EC (DUK_DEFPROP_CLEAR_ENUMERABLE | DUK_DEFPROP_CLEAR_CONFIGURABLE) -#define DUK_DEFPROP_CLEAR_WEC (DUK_DEFPROP_CLEAR_WRITABLE | DUK_DEFPROP_CLEAR_ENUMERABLE | DUK_DEFPROP_CLEAR_CONFIGURABLE) -#define DUK_DEFPROP_ATTR_W (DUK_DEFPROP_HAVE_WEC | DUK_DEFPROP_W) -#define DUK_DEFPROP_ATTR_E (DUK_DEFPROP_HAVE_WEC | DUK_DEFPROP_E) -#define DUK_DEFPROP_ATTR_C (DUK_DEFPROP_HAVE_WEC | DUK_DEFPROP_C) -#define DUK_DEFPROP_ATTR_WE (DUK_DEFPROP_HAVE_WEC | DUK_DEFPROP_WE) -#define DUK_DEFPROP_ATTR_WC (DUK_DEFPROP_HAVE_WEC | DUK_DEFPROP_WC) -#define DUK_DEFPROP_ATTR_EC (DUK_DEFPROP_HAVE_WEC | DUK_DEFPROP_EC) -#define DUK_DEFPROP_ATTR_WEC (DUK_DEFPROP_HAVE_WEC | DUK_DEFPROP_WEC) - -/* Flags for duk_push_thread_raw() */ -#define DUK_THREAD_NEW_GLOBAL_ENV (1U << 0) /* create a new global environment */ - -/* Flags for duk_gc() */ -#define DUK_GC_COMPACT (1U << 0) /* compact heap objects */ - -/* Error codes (must be 8 bits at most, see duk_error.h) */ -#define DUK_ERR_NONE 0 /* no error (e.g. from duk_get_error_code()) */ -#define DUK_ERR_ERROR 1 /* Error */ -#define DUK_ERR_EVAL_ERROR 2 /* EvalError */ -#define DUK_ERR_RANGE_ERROR 3 /* RangeError */ -#define DUK_ERR_REFERENCE_ERROR 4 /* ReferenceError */ -#define DUK_ERR_SYNTAX_ERROR 5 /* SyntaxError */ -#define DUK_ERR_TYPE_ERROR 6 /* TypeError */ -#define DUK_ERR_URI_ERROR 7 /* URIError */ - -/* Return codes for C functions (shortcut for throwing an error) */ -#define DUK_RET_ERROR (-DUK_ERR_ERROR) -#define DUK_RET_EVAL_ERROR (-DUK_ERR_EVAL_ERROR) -#define DUK_RET_RANGE_ERROR (-DUK_ERR_RANGE_ERROR) -#define DUK_RET_REFERENCE_ERROR (-DUK_ERR_REFERENCE_ERROR) -#define DUK_RET_SYNTAX_ERROR (-DUK_ERR_SYNTAX_ERROR) -#define DUK_RET_TYPE_ERROR (-DUK_ERR_TYPE_ERROR) -#define DUK_RET_URI_ERROR (-DUK_ERR_URI_ERROR) - -/* Return codes for protected calls (duk_safe_call(), duk_pcall()) */ -#define DUK_EXEC_SUCCESS 0 -#define DUK_EXEC_ERROR 1 - -/* Debug levels for DUK_USE_DEBUG_WRITE(). */ -#define DUK_LEVEL_DEBUG 0 -#define DUK_LEVEL_DDEBUG 1 -#define DUK_LEVEL_DDDEBUG 2 - -/* - * Macros to create Symbols as C statically constructed strings. - * - * Call e.g. as DUK_HIDDEN_SYMBOL("myProperty") <=> ("\xFF" "myProperty"). - * - * Local symbols have a unique suffix, caller should take care to avoid - * conflicting with the Duktape internal representation by e.g. prepending - * a '!' character: DUK_LOCAL_SYMBOL("myLocal", "!123"). - * - * Note that these can only be used for string constants, not dynamically - * created strings. - * - * You shouldn't normally use DUK_INTERNAL_SYMBOL() at all. It is reserved - * for Duktape internal symbols only. There are no versioning guarantees - * for internal symbols. - */ - -#define DUK_HIDDEN_SYMBOL(x) ("\xFF" x) -#define DUK_GLOBAL_SYMBOL(x) ("\x80" x) -#define DUK_LOCAL_SYMBOL(x,uniq) ("\x81" x "\xff" uniq) -#define DUK_WELLKNOWN_SYMBOL(x) ("\x81" x "\xff") -#define DUK_INTERNAL_SYMBOL(x) ("\x82" x) - -/* - * If no variadic macros, __FILE__ and __LINE__ are passed through globals - * which is ugly and not thread safe. - */ - -#if !defined(DUK_API_VARIADIC_MACROS) -DUK_EXTERNAL_DECL const char *duk_api_global_filename; -DUK_EXTERNAL_DECL duk_int_t duk_api_global_line; -#endif - -/* - * Context management - */ - -DUK_EXTERNAL_DECL -duk_context *duk_create_heap(duk_alloc_function alloc_func, - duk_realloc_function realloc_func, - duk_free_function free_func, - void *heap_udata, - duk_fatal_function fatal_handler); -DUK_EXTERNAL_DECL void duk_destroy_heap(duk_context *ctx); - -DUK_EXTERNAL_DECL void duk_suspend(duk_context *ctx, duk_thread_state *state); -DUK_EXTERNAL_DECL void duk_resume(duk_context *ctx, const duk_thread_state *state); - -#define duk_create_heap_default() \ - duk_create_heap(NULL, NULL, NULL, NULL, NULL) - -/* - * Memory management - * - * Raw functions have no side effects (cannot trigger GC). - */ - -DUK_EXTERNAL_DECL void *duk_alloc_raw(duk_context *ctx, duk_size_t size); -DUK_EXTERNAL_DECL void duk_free_raw(duk_context *ctx, void *ptr); -DUK_EXTERNAL_DECL void *duk_realloc_raw(duk_context *ctx, void *ptr, duk_size_t size); -DUK_EXTERNAL_DECL void *duk_alloc(duk_context *ctx, duk_size_t size); -DUK_EXTERNAL_DECL void duk_free(duk_context *ctx, void *ptr); -DUK_EXTERNAL_DECL void *duk_realloc(duk_context *ctx, void *ptr, duk_size_t size); -DUK_EXTERNAL_DECL void duk_get_memory_functions(duk_context *ctx, duk_memory_functions *out_funcs); -DUK_EXTERNAL_DECL void duk_gc(duk_context *ctx, duk_uint_t flags); - -/* - * Error handling - */ - -DUK_API_NORETURN(DUK_EXTERNAL_DECL void duk_throw_raw(duk_context *ctx)); -#define duk_throw(ctx) \ - (duk_throw_raw((ctx)), (duk_ret_t) 0) -DUK_API_NORETURN(DUK_EXTERNAL_DECL void duk_fatal_raw(duk_context *ctx, const char *err_msg)); -#define duk_fatal(ctx,err_msg) \ - (duk_fatal_raw((ctx), (err_msg)), (duk_ret_t) 0) -DUK_API_NORETURN(DUK_EXTERNAL_DECL void duk_error_raw(duk_context *ctx, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, ...)); - -#if defined(DUK_API_VARIADIC_MACROS) -#define duk_error(ctx,err_code,...) \ - (duk_error_raw((ctx), (duk_errcode_t) (err_code), (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), __VA_ARGS__), (duk_ret_t) 0) -#define duk_generic_error(ctx,...) \ - (duk_error_raw((ctx), (duk_errcode_t) DUK_ERR_ERROR, (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), __VA_ARGS__), (duk_ret_t) 0) -#define duk_eval_error(ctx,...) \ - (duk_error_raw((ctx), (duk_errcode_t) DUK_ERR_EVAL_ERROR, (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), __VA_ARGS__), (duk_ret_t) 0) -#define duk_range_error(ctx,...) \ - (duk_error_raw((ctx), (duk_errcode_t) DUK_ERR_RANGE_ERROR, (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), __VA_ARGS__), (duk_ret_t) 0) -#define duk_reference_error(ctx,...) \ - (duk_error_raw((ctx), (duk_errcode_t) DUK_ERR_REFERENCE_ERROR, (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), __VA_ARGS__), (duk_ret_t) 0) -#define duk_syntax_error(ctx,...) \ - (duk_error_raw((ctx), (duk_errcode_t) DUK_ERR_SYNTAX_ERROR, (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), __VA_ARGS__), (duk_ret_t) 0) -#define duk_type_error(ctx,...) \ - (duk_error_raw((ctx), (duk_errcode_t) DUK_ERR_TYPE_ERROR, (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), __VA_ARGS__), (duk_ret_t) 0) -#define duk_uri_error(ctx,...) \ - (duk_error_raw((ctx), (duk_errcode_t) DUK_ERR_URI_ERROR, (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), __VA_ARGS__), (duk_ret_t) 0) -#else /* DUK_API_VARIADIC_MACROS */ -/* For legacy compilers without variadic macros a macro hack is used to allow - * variable arguments. While the macro allows "return duk_error(...)", it - * will fail with e.g. "(void) duk_error(...)". The calls are noreturn but - * with a return value to allow the "return duk_error(...)" idiom. This may - * cause some compiler warnings, but without noreturn the generated code is - * often worse. The same approach as with variadic macros (using - * "(duk_error(...), 0)") won't work due to the macro hack structure. - */ -DUK_API_NORETURN(DUK_EXTERNAL_DECL duk_ret_t duk_error_stash(duk_context *ctx, duk_errcode_t err_code, const char *fmt, ...)); -DUK_API_NORETURN(DUK_EXTERNAL_DECL duk_ret_t duk_generic_error_stash(duk_context *ctx, const char *fmt, ...)); -DUK_API_NORETURN(DUK_EXTERNAL_DECL duk_ret_t duk_eval_error_stash(duk_context *ctx, const char *fmt, ...)); -DUK_API_NORETURN(DUK_EXTERNAL_DECL duk_ret_t duk_range_error_stash(duk_context *ctx, const char *fmt, ...)); -DUK_API_NORETURN(DUK_EXTERNAL_DECL duk_ret_t duk_reference_error_stash(duk_context *ctx, const char *fmt, ...)); -DUK_API_NORETURN(DUK_EXTERNAL_DECL duk_ret_t duk_syntax_error_stash(duk_context *ctx, const char *fmt, ...)); -DUK_API_NORETURN(DUK_EXTERNAL_DECL duk_ret_t duk_type_error_stash(duk_context *ctx, const char *fmt, ...)); -DUK_API_NORETURN(DUK_EXTERNAL_DECL duk_ret_t duk_uri_error_stash(duk_context *ctx, const char *fmt, ...)); -#define duk_error \ - (duk_api_global_filename = (const char *) (DUK_FILE_MACRO), \ - duk_api_global_line = (duk_int_t) (DUK_LINE_MACRO), \ - duk_error_stash) /* last value is func pointer, arguments follow in parens */ -#define duk_generic_error \ - (duk_api_global_filename = (const char *) (DUK_FILE_MACRO), \ - duk_api_global_line = (duk_int_t) (DUK_LINE_MACRO), \ - duk_generic_error_stash) -#define duk_eval_error \ - (duk_api_global_filename = (const char *) (DUK_FILE_MACRO), \ - duk_api_global_line = (duk_int_t) (DUK_LINE_MACRO), \ - duk_eval_error_stash) -#define duk_range_error \ - (duk_api_global_filename = (const char *) (DUK_FILE_MACRO), \ - duk_api_global_line = (duk_int_t) (DUK_LINE_MACRO), \ - duk_range_error_stash) -#define duk_reference_error \ - (duk_api_global_filename = (const char *) (DUK_FILE_MACRO), \ - duk_api_global_line = (duk_int_t) (DUK_LINE_MACRO), \ - duk_reference_error_stash) -#define duk_syntax_error \ - (duk_api_global_filename = (const char *) (DUK_FILE_MACRO), \ - duk_api_global_line = (duk_int_t) (DUK_LINE_MACRO), \ - duk_syntax_error_stash) -#define duk_type_error \ - (duk_api_global_filename = (const char *) (DUK_FILE_MACRO), \ - duk_api_global_line = (duk_int_t) (DUK_LINE_MACRO), \ - duk_type_error_stash) -#define duk_uri_error \ - (duk_api_global_filename = (const char *) (DUK_FILE_MACRO), \ - duk_api_global_line = (duk_int_t) (DUK_LINE_MACRO), \ - duk_uri_error_stash) -#endif /* DUK_API_VARIADIC_MACROS */ - -DUK_API_NORETURN(DUK_EXTERNAL_DECL void duk_error_va_raw(duk_context *ctx, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, va_list ap)); - -#define duk_error_va(ctx,err_code,fmt,ap) \ - (duk_error_va_raw((ctx), (duk_errcode_t) (err_code), (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), (fmt), (ap)), (duk_ret_t) 0) -#define duk_generic_error_va(ctx,fmt,ap) \ - (duk_error_va_raw((ctx), (duk_errcode_t) DUK_ERR_ERROR, (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), (fmt), (ap)), (duk_ret_t) 0) -#define duk_eval_error_va(ctx,fmt,ap) \ - (duk_error_va_raw((ctx), (duk_errcode_t) DUK_ERR_EVAL_ERROR, (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), (fmt), (ap)), (duk_ret_t) 0) -#define duk_range_error_va(ctx,fmt,ap) \ - (duk_error_va_raw((ctx), (duk_errcode_t) DUK_ERR_RANGE_ERROR, (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), (fmt), (ap)), (duk_ret_t) 0) -#define duk_reference_error_va(ctx,fmt,ap) \ - (duk_error_va_raw((ctx), (duk_errcode_t) DUK_ERR_REFERENCE_ERROR, (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), (fmt), (ap)), (duk_ret_t) 0) -#define duk_syntax_error_va(ctx,fmt,ap) \ - (duk_error_va_raw((ctx), (duk_errcode_t) DUK_ERR_SYNTAX_ERROR, (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), (fmt), (ap)), (duk_ret_t) 0) -#define duk_type_error_va(ctx,fmt,ap) \ - (duk_error_va_raw((ctx), (duk_errcode_t) DUK_ERR_TYPE_ERROR, (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), (fmt), (ap)), (duk_ret_t) 0) -#define duk_uri_error_va(ctx,fmt,ap) \ - (duk_error_va_raw((ctx), (duk_errcode_t) DUK_ERR_URI_ERROR, (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), (fmt), (ap)), (duk_ret_t) 0) - -/* - * Other state related functions - */ - -DUK_EXTERNAL_DECL duk_bool_t duk_is_strict_call(duk_context *ctx); -DUK_EXTERNAL_DECL duk_bool_t duk_is_constructor_call(duk_context *ctx); - -/* - * Stack management - */ - -DUK_EXTERNAL_DECL duk_idx_t duk_normalize_index(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_idx_t duk_require_normalize_index(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_bool_t duk_is_valid_index(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL void duk_require_valid_index(duk_context *ctx, duk_idx_t idx); - -DUK_EXTERNAL_DECL duk_idx_t duk_get_top(duk_context *ctx); -DUK_EXTERNAL_DECL void duk_set_top(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_idx_t duk_get_top_index(duk_context *ctx); -DUK_EXTERNAL_DECL duk_idx_t duk_require_top_index(duk_context *ctx); - -/* Although extra/top could be an unsigned type here, using a signed type - * makes the API more robust to calling code calculation errors or corner - * cases (where caller might occasionally come up with negative values). - * Negative values are treated as zero, which is better than casting them - * to a large unsigned number. (This principle is used elsewhere in the - * API too.) - */ -DUK_EXTERNAL_DECL duk_bool_t duk_check_stack(duk_context *ctx, duk_idx_t extra); -DUK_EXTERNAL_DECL void duk_require_stack(duk_context *ctx, duk_idx_t extra); -DUK_EXTERNAL_DECL duk_bool_t duk_check_stack_top(duk_context *ctx, duk_idx_t top); -DUK_EXTERNAL_DECL void duk_require_stack_top(duk_context *ctx, duk_idx_t top); - -/* - * Stack manipulation (other than push/pop) - */ - -DUK_EXTERNAL_DECL void duk_swap(duk_context *ctx, duk_idx_t idx1, duk_idx_t idx2); -DUK_EXTERNAL_DECL void duk_swap_top(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL void duk_dup(duk_context *ctx, duk_idx_t from_idx); -DUK_EXTERNAL_DECL void duk_dup_top(duk_context *ctx); -DUK_EXTERNAL_DECL void duk_insert(duk_context *ctx, duk_idx_t to_idx); -DUK_EXTERNAL_DECL void duk_pull(duk_context *ctx, duk_idx_t from_idx); -DUK_EXTERNAL_DECL void duk_replace(duk_context *ctx, duk_idx_t to_idx); -DUK_EXTERNAL_DECL void duk_copy(duk_context *ctx, duk_idx_t from_idx, duk_idx_t to_idx); -DUK_EXTERNAL_DECL void duk_remove(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL void duk_xcopymove_raw(duk_context *to_ctx, duk_context *from_ctx, duk_idx_t count, duk_bool_t is_copy); - -#define duk_xmove_top(to_ctx,from_ctx,count) \ - duk_xcopymove_raw((to_ctx), (from_ctx), (count), 0 /*is_copy*/) -#define duk_xcopy_top(to_ctx,from_ctx,count) \ - duk_xcopymove_raw((to_ctx), (from_ctx), (count), 1 /*is_copy*/) - -/* - * Push operations - * - * Push functions return the absolute (relative to bottom of frame) - * position of the pushed value for convenience. - * - * Note: duk_dup() is technically a push. - */ - -DUK_EXTERNAL_DECL void duk_push_undefined(duk_context *ctx); -DUK_EXTERNAL_DECL void duk_push_null(duk_context *ctx); -DUK_EXTERNAL_DECL void duk_push_boolean(duk_context *ctx, duk_bool_t val); -DUK_EXTERNAL_DECL void duk_push_true(duk_context *ctx); -DUK_EXTERNAL_DECL void duk_push_false(duk_context *ctx); -DUK_EXTERNAL_DECL void duk_push_number(duk_context *ctx, duk_double_t val); -DUK_EXTERNAL_DECL void duk_push_nan(duk_context *ctx); -DUK_EXTERNAL_DECL void duk_push_int(duk_context *ctx, duk_int_t val); -DUK_EXTERNAL_DECL void duk_push_uint(duk_context *ctx, duk_uint_t val); -DUK_EXTERNAL_DECL const char *duk_push_string(duk_context *ctx, const char *str); -DUK_EXTERNAL_DECL const char *duk_push_lstring(duk_context *ctx, const char *str, duk_size_t len); -DUK_EXTERNAL_DECL void duk_push_pointer(duk_context *ctx, void *p); -DUK_EXTERNAL_DECL const char *duk_push_sprintf(duk_context *ctx, const char *fmt, ...); -DUK_EXTERNAL_DECL const char *duk_push_vsprintf(duk_context *ctx, const char *fmt, va_list ap); - -/* duk_push_literal() may evaluate its argument (a C string literal) more than - * once on purpose. When speed is preferred, sizeof() avoids an unnecessary - * strlen() at runtime. Sizeof("foo") == 4, so subtract 1. The argument - * must be non-NULL and should not contain internal NUL characters as the - * behavior will then depend on config options. - */ -#if defined(DUK_USE_PREFER_SIZE) -#define duk_push_literal(ctx,cstring) duk_push_string((ctx), (cstring)) -#else -DUK_EXTERNAL_DECL const char *duk_push_literal_raw(duk_context *ctx, const char *str, duk_size_t len); -#define duk_push_literal(ctx,cstring) duk_push_literal_raw((ctx), (cstring), sizeof((cstring)) - 1U) -#endif - -DUK_EXTERNAL_DECL void duk_push_this(duk_context *ctx); -DUK_EXTERNAL_DECL void duk_push_new_target(duk_context *ctx); -DUK_EXTERNAL_DECL void duk_push_current_function(duk_context *ctx); -DUK_EXTERNAL_DECL void duk_push_current_thread(duk_context *ctx); -DUK_EXTERNAL_DECL void duk_push_global_object(duk_context *ctx); -DUK_EXTERNAL_DECL void duk_push_heap_stash(duk_context *ctx); -DUK_EXTERNAL_DECL void duk_push_global_stash(duk_context *ctx); -DUK_EXTERNAL_DECL void duk_push_thread_stash(duk_context *ctx, duk_context *target_ctx); - -DUK_EXTERNAL_DECL duk_idx_t duk_push_object(duk_context *ctx); -DUK_EXTERNAL_DECL duk_idx_t duk_push_bare_object(duk_context *ctx); -DUK_EXTERNAL_DECL duk_idx_t duk_push_array(duk_context *ctx); -DUK_EXTERNAL_DECL duk_idx_t duk_push_bare_array(duk_context *ctx); -DUK_EXTERNAL_DECL duk_idx_t duk_push_c_function(duk_context *ctx, duk_c_function func, duk_idx_t nargs); -DUK_EXTERNAL_DECL duk_idx_t duk_push_c_lightfunc(duk_context *ctx, duk_c_function func, duk_idx_t nargs, duk_idx_t length, duk_int_t magic); -DUK_EXTERNAL_DECL duk_idx_t duk_push_thread_raw(duk_context *ctx, duk_uint_t flags); -DUK_EXTERNAL_DECL duk_idx_t duk_push_proxy(duk_context *ctx, duk_uint_t proxy_flags); - -#define duk_push_thread(ctx) \ - duk_push_thread_raw((ctx), 0 /*flags*/) - -#define duk_push_thread_new_globalenv(ctx) \ - duk_push_thread_raw((ctx), DUK_THREAD_NEW_GLOBAL_ENV /*flags*/) - -DUK_EXTERNAL_DECL duk_idx_t duk_push_error_object_raw(duk_context *ctx, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, ...); - -#if defined(DUK_API_VARIADIC_MACROS) -#define duk_push_error_object(ctx,err_code,...) \ - duk_push_error_object_raw((ctx), (err_code), (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), __VA_ARGS__) -#else -DUK_EXTERNAL_DECL duk_idx_t duk_push_error_object_stash(duk_context *ctx, duk_errcode_t err_code, const char *fmt, ...); -/* Note: parentheses are required so that the comma expression works in assignments. */ -#define duk_push_error_object \ - (duk_api_global_filename = (const char *) (DUK_FILE_MACRO), \ - duk_api_global_line = (duk_int_t) (DUK_LINE_MACRO), \ - duk_push_error_object_stash) /* last value is func pointer, arguments follow in parens */ -#endif - -DUK_EXTERNAL_DECL duk_idx_t duk_push_error_object_va_raw(duk_context *ctx, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, va_list ap); -#define duk_push_error_object_va(ctx,err_code,fmt,ap) \ - duk_push_error_object_va_raw((ctx), (err_code), (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), (fmt), (ap)) - -#define DUK_BUF_FLAG_DYNAMIC (1 << 0) /* internal flag: dynamic buffer */ -#define DUK_BUF_FLAG_EXTERNAL (1 << 1) /* internal flag: external buffer */ -#define DUK_BUF_FLAG_NOZERO (1 << 2) /* internal flag: don't zero allocated buffer */ - -DUK_EXTERNAL_DECL void *duk_push_buffer_raw(duk_context *ctx, duk_size_t size, duk_small_uint_t flags); - -#define duk_push_buffer(ctx,size,dynamic) \ - duk_push_buffer_raw((ctx), (size), (dynamic) ? DUK_BUF_FLAG_DYNAMIC : 0) -#define duk_push_fixed_buffer(ctx,size) \ - duk_push_buffer_raw((ctx), (size), 0 /*flags*/) -#define duk_push_dynamic_buffer(ctx,size) \ - duk_push_buffer_raw((ctx), (size), DUK_BUF_FLAG_DYNAMIC /*flags*/) -#define duk_push_external_buffer(ctx) \ - ((void) duk_push_buffer_raw((ctx), 0, DUK_BUF_FLAG_DYNAMIC | DUK_BUF_FLAG_EXTERNAL)) - -#define DUK_BUFOBJ_ARRAYBUFFER 0 -#define DUK_BUFOBJ_NODEJS_BUFFER 1 -#define DUK_BUFOBJ_DATAVIEW 2 -#define DUK_BUFOBJ_INT8ARRAY 3 -#define DUK_BUFOBJ_UINT8ARRAY 4 -#define DUK_BUFOBJ_UINT8CLAMPEDARRAY 5 -#define DUK_BUFOBJ_INT16ARRAY 6 -#define DUK_BUFOBJ_UINT16ARRAY 7 -#define DUK_BUFOBJ_INT32ARRAY 8 -#define DUK_BUFOBJ_UINT32ARRAY 9 -#define DUK_BUFOBJ_FLOAT32ARRAY 10 -#define DUK_BUFOBJ_FLOAT64ARRAY 11 - -DUK_EXTERNAL_DECL void duk_push_buffer_object(duk_context *ctx, duk_idx_t idx_buffer, duk_size_t byte_offset, duk_size_t byte_length, duk_uint_t flags); - -DUK_EXTERNAL_DECL duk_idx_t duk_push_heapptr(duk_context *ctx, void *ptr); - -/* - * Pop operations - */ - -DUK_EXTERNAL_DECL void duk_pop(duk_context *ctx); -DUK_EXTERNAL_DECL void duk_pop_n(duk_context *ctx, duk_idx_t count); -DUK_EXTERNAL_DECL void duk_pop_2(duk_context *ctx); -DUK_EXTERNAL_DECL void duk_pop_3(duk_context *ctx); - -/* - * Type checks - * - * duk_is_none(), which would indicate whether index it outside of stack, - * is not needed; duk_is_valid_index() gives the same information. - */ - -DUK_EXTERNAL_DECL duk_int_t duk_get_type(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_bool_t duk_check_type(duk_context *ctx, duk_idx_t idx, duk_int_t type); -DUK_EXTERNAL_DECL duk_uint_t duk_get_type_mask(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_bool_t duk_check_type_mask(duk_context *ctx, duk_idx_t idx, duk_uint_t mask); - -DUK_EXTERNAL_DECL duk_bool_t duk_is_undefined(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_bool_t duk_is_null(duk_context *ctx, duk_idx_t idx); -#define duk_is_null_or_undefined(ctx, idx) \ - ((duk_get_type_mask((ctx), (idx)) & (DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_UNDEFINED)) ? 1 : 0) - -DUK_EXTERNAL_DECL duk_bool_t duk_is_boolean(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_bool_t duk_is_number(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_bool_t duk_is_nan(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_bool_t duk_is_string(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_bool_t duk_is_object(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_bool_t duk_is_buffer(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_bool_t duk_is_buffer_data(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_bool_t duk_is_pointer(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_bool_t duk_is_lightfunc(duk_context *ctx, duk_idx_t idx); - -DUK_EXTERNAL_DECL duk_bool_t duk_is_symbol(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_bool_t duk_is_array(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_bool_t duk_is_function(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_bool_t duk_is_c_function(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_bool_t duk_is_ecmascript_function(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_bool_t duk_is_bound_function(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_bool_t duk_is_thread(duk_context *ctx, duk_idx_t idx); - -#define duk_is_callable(ctx,idx) \ - duk_is_function((ctx), (idx)) -DUK_EXTERNAL_DECL duk_bool_t duk_is_constructable(duk_context *ctx, duk_idx_t idx); - -DUK_EXTERNAL_DECL duk_bool_t duk_is_dynamic_buffer(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_bool_t duk_is_fixed_buffer(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_bool_t duk_is_external_buffer(duk_context *ctx, duk_idx_t idx); - -/* Buffers and lightfuncs are not considered primitive because they mimic - * objects and e.g. duk_to_primitive() will coerce them instead of returning - * them as is. Symbols are represented as strings internally. - */ -#define duk_is_primitive(ctx,idx) \ - duk_check_type_mask((ctx), (idx), DUK_TYPE_MASK_UNDEFINED | \ - DUK_TYPE_MASK_NULL | \ - DUK_TYPE_MASK_BOOLEAN | \ - DUK_TYPE_MASK_NUMBER | \ - DUK_TYPE_MASK_STRING | \ - DUK_TYPE_MASK_POINTER) - -/* Symbols are object coercible, covered by DUK_TYPE_MASK_STRING. */ -#define duk_is_object_coercible(ctx,idx) \ - duk_check_type_mask((ctx), (idx), DUK_TYPE_MASK_BOOLEAN | \ - DUK_TYPE_MASK_NUMBER | \ - DUK_TYPE_MASK_STRING | \ - DUK_TYPE_MASK_OBJECT | \ - DUK_TYPE_MASK_BUFFER | \ - DUK_TYPE_MASK_POINTER | \ - DUK_TYPE_MASK_LIGHTFUNC) - -DUK_EXTERNAL_DECL duk_errcode_t duk_get_error_code(duk_context *ctx, duk_idx_t idx); -#define duk_is_error(ctx,idx) \ - (duk_get_error_code((ctx), (idx)) != 0) -#define duk_is_eval_error(ctx,idx) \ - (duk_get_error_code((ctx), (idx)) == DUK_ERR_EVAL_ERROR) -#define duk_is_range_error(ctx,idx) \ - (duk_get_error_code((ctx), (idx)) == DUK_ERR_RANGE_ERROR) -#define duk_is_reference_error(ctx,idx) \ - (duk_get_error_code((ctx), (idx)) == DUK_ERR_REFERENCE_ERROR) -#define duk_is_syntax_error(ctx,idx) \ - (duk_get_error_code((ctx), (idx)) == DUK_ERR_SYNTAX_ERROR) -#define duk_is_type_error(ctx,idx) \ - (duk_get_error_code((ctx), (idx)) == DUK_ERR_TYPE_ERROR) -#define duk_is_uri_error(ctx,idx) \ - (duk_get_error_code((ctx), (idx)) == DUK_ERR_URI_ERROR) - -/* - * Get operations: no coercion, returns default value for invalid - * indices and invalid value types. - * - * duk_get_undefined() and duk_get_null() would be pointless and - * are not included. - */ - -DUK_EXTERNAL_DECL duk_bool_t duk_get_boolean(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_double_t duk_get_number(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_int_t duk_get_int(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_uint_t duk_get_uint(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL const char *duk_get_string(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL const char *duk_get_lstring(duk_context *ctx, duk_idx_t idx, duk_size_t *out_len); -DUK_EXTERNAL_DECL void *duk_get_buffer(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size); -DUK_EXTERNAL_DECL void *duk_get_buffer_data(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size); -DUK_EXTERNAL_DECL void *duk_get_pointer(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_c_function duk_get_c_function(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_context *duk_get_context(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL void *duk_get_heapptr(duk_context *ctx, duk_idx_t idx); - -/* - * Get-with-explicit default operations: like get operations but with an - * explicit default value. - */ - -DUK_EXTERNAL_DECL duk_bool_t duk_get_boolean_default(duk_context *ctx, duk_idx_t idx, duk_bool_t def_value); -DUK_EXTERNAL_DECL duk_double_t duk_get_number_default(duk_context *ctx, duk_idx_t idx, duk_double_t def_value); -DUK_EXTERNAL_DECL duk_int_t duk_get_int_default(duk_context *ctx, duk_idx_t idx, duk_int_t def_value); -DUK_EXTERNAL_DECL duk_uint_t duk_get_uint_default(duk_context *ctx, duk_idx_t idx, duk_uint_t def_value); -DUK_EXTERNAL_DECL const char *duk_get_string_default(duk_context *ctx, duk_idx_t idx, const char *def_value); -DUK_EXTERNAL_DECL const char *duk_get_lstring_default(duk_context *ctx, duk_idx_t idx, duk_size_t *out_len, const char *def_ptr, duk_size_t def_len); -DUK_EXTERNAL_DECL void *duk_get_buffer_default(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_len); -DUK_EXTERNAL_DECL void *duk_get_buffer_data_default(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_len); -DUK_EXTERNAL_DECL void *duk_get_pointer_default(duk_context *ctx, duk_idx_t idx, void *def_value); -DUK_EXTERNAL_DECL duk_c_function duk_get_c_function_default(duk_context *ctx, duk_idx_t idx, duk_c_function def_value); -DUK_EXTERNAL_DECL duk_context *duk_get_context_default(duk_context *ctx, duk_idx_t idx, duk_context *def_value); -DUK_EXTERNAL_DECL void *duk_get_heapptr_default(duk_context *ctx, duk_idx_t idx, void *def_value); - -/* - * Opt operations: like require operations but with an explicit default value - * when value is undefined or index is invalid, null and non-matching types - * cause a TypeError. - */ - -DUK_EXTERNAL_DECL duk_bool_t duk_opt_boolean(duk_context *ctx, duk_idx_t idx, duk_bool_t def_value); -DUK_EXTERNAL_DECL duk_double_t duk_opt_number(duk_context *ctx, duk_idx_t idx, duk_double_t def_value); -DUK_EXTERNAL_DECL duk_int_t duk_opt_int(duk_context *ctx, duk_idx_t idx, duk_int_t def_value); -DUK_EXTERNAL_DECL duk_uint_t duk_opt_uint(duk_context *ctx, duk_idx_t idx, duk_uint_t def_value); -DUK_EXTERNAL_DECL const char *duk_opt_string(duk_context *ctx, duk_idx_t idx, const char *def_ptr); -DUK_EXTERNAL_DECL const char *duk_opt_lstring(duk_context *ctx, duk_idx_t idx, duk_size_t *out_len, const char *def_ptr, duk_size_t def_len); -DUK_EXTERNAL_DECL void *duk_opt_buffer(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size); -DUK_EXTERNAL_DECL void *duk_opt_buffer_data(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size); -DUK_EXTERNAL_DECL void *duk_opt_pointer(duk_context *ctx, duk_idx_t idx, void *def_value); -DUK_EXTERNAL_DECL duk_c_function duk_opt_c_function(duk_context *ctx, duk_idx_t idx, duk_c_function def_value); -DUK_EXTERNAL_DECL duk_context *duk_opt_context(duk_context *ctx, duk_idx_t idx, duk_context *def_value); -DUK_EXTERNAL_DECL void *duk_opt_heapptr(duk_context *ctx, duk_idx_t idx, void *def_value); - -/* - * Require operations: no coercion, throw error if index or type - * is incorrect. No defaulting. - */ - -#define duk_require_type_mask(ctx,idx,mask) \ - ((void) duk_check_type_mask((ctx), (idx), (mask) | DUK_TYPE_MASK_THROW)) - -DUK_EXTERNAL_DECL void duk_require_undefined(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL void duk_require_null(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_bool_t duk_require_boolean(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_double_t duk_require_number(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_int_t duk_require_int(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_uint_t duk_require_uint(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL const char *duk_require_string(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL const char *duk_require_lstring(duk_context *ctx, duk_idx_t idx, duk_size_t *out_len); -DUK_EXTERNAL_DECL void duk_require_object(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL void *duk_require_buffer(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size); -DUK_EXTERNAL_DECL void *duk_require_buffer_data(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size); -DUK_EXTERNAL_DECL void *duk_require_pointer(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_c_function duk_require_c_function(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_context *duk_require_context(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL void duk_require_function(duk_context *ctx, duk_idx_t idx); -#define duk_require_callable(ctx,idx) \ - duk_require_function((ctx), (idx)) -DUK_EXTERNAL_DECL void duk_require_constructor_call(duk_context *ctx); -DUK_EXTERNAL_DECL void duk_require_constructable(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL void *duk_require_heapptr(duk_context *ctx, duk_idx_t idx); - -/* Symbols are object coercible and covered by DUK_TYPE_MASK_STRING. */ -#define duk_require_object_coercible(ctx,idx) \ - ((void) duk_check_type_mask((ctx), (idx), DUK_TYPE_MASK_BOOLEAN | \ - DUK_TYPE_MASK_NUMBER | \ - DUK_TYPE_MASK_STRING | \ - DUK_TYPE_MASK_OBJECT | \ - DUK_TYPE_MASK_BUFFER | \ - DUK_TYPE_MASK_POINTER | \ - DUK_TYPE_MASK_LIGHTFUNC | \ - DUK_TYPE_MASK_THROW)) - -/* - * Coercion operations: in-place coercion, return coerced value where - * applicable. If index is invalid, throw error. Some coercions may - * throw an expected error (e.g. from a toString() or valueOf() call) - * or an internal error (e.g. from out of memory). - */ - -DUK_EXTERNAL_DECL void duk_to_undefined(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL void duk_to_null(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_bool_t duk_to_boolean(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_double_t duk_to_number(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_int_t duk_to_int(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_uint_t duk_to_uint(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_int32_t duk_to_int32(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_uint32_t duk_to_uint32(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_uint16_t duk_to_uint16(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL const char *duk_to_string(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL const char *duk_to_lstring(duk_context *ctx, duk_idx_t idx, duk_size_t *out_len); -DUK_EXTERNAL_DECL void *duk_to_buffer_raw(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size, duk_uint_t flags); -DUK_EXTERNAL_DECL void *duk_to_pointer(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL void duk_to_object(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL void duk_to_primitive(duk_context *ctx, duk_idx_t idx, duk_int_t hint); - -#define DUK_BUF_MODE_FIXED 0 /* internal: request fixed buffer result */ -#define DUK_BUF_MODE_DYNAMIC 1 /* internal: request dynamic buffer result */ -#define DUK_BUF_MODE_DONTCARE 2 /* internal: don't care about fixed/dynamic nature */ - -#define duk_to_buffer(ctx,idx,out_size) \ - duk_to_buffer_raw((ctx), (idx), (out_size), DUK_BUF_MODE_DONTCARE) -#define duk_to_fixed_buffer(ctx,idx,out_size) \ - duk_to_buffer_raw((ctx), (idx), (out_size), DUK_BUF_MODE_FIXED) -#define duk_to_dynamic_buffer(ctx,idx,out_size) \ - duk_to_buffer_raw((ctx), (idx), (out_size), DUK_BUF_MODE_DYNAMIC) - -/* safe variants of a few coercion operations */ -DUK_EXTERNAL_DECL const char *duk_safe_to_lstring(duk_context *ctx, duk_idx_t idx, duk_size_t *out_len); -DUK_EXTERNAL_DECL const char *duk_to_stacktrace(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL const char *duk_safe_to_stacktrace(duk_context *ctx, duk_idx_t idx); -#define duk_safe_to_string(ctx,idx) \ - duk_safe_to_lstring((ctx), (idx), NULL) - -/* - * Value length - */ - -DUK_EXTERNAL_DECL duk_size_t duk_get_length(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL void duk_set_length(duk_context *ctx, duk_idx_t idx, duk_size_t len); -#if 0 -/* duk_require_length()? */ -/* duk_opt_length()? */ -#endif - -/* - * Misc conversion - */ - -DUK_EXTERNAL_DECL const char *duk_base64_encode(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL void duk_base64_decode(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL const char *duk_hex_encode(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL void duk_hex_decode(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL const char *duk_json_encode(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL void duk_json_decode(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL void duk_cbor_encode(duk_context *ctx, duk_idx_t idx, duk_uint_t encode_flags); -DUK_EXTERNAL_DECL void duk_cbor_decode(duk_context *ctx, duk_idx_t idx, duk_uint_t decode_flags); - -DUK_EXTERNAL_DECL const char *duk_buffer_to_string(duk_context *ctx, duk_idx_t idx); - -/* - * Buffer - */ - -DUK_EXTERNAL_DECL void *duk_resize_buffer(duk_context *ctx, duk_idx_t idx, duk_size_t new_size); -DUK_EXTERNAL_DECL void *duk_steal_buffer(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size); -DUK_EXTERNAL_DECL void duk_config_buffer(duk_context *ctx, duk_idx_t idx, void *ptr, duk_size_t len); - -/* - * Property access - * - * The basic function assumes key is on stack. The _(l)string variant takes - * a C string as a property name; the _literal variant takes a C literal. - * The _index variant takes an array index as a property name (e.g. 123 is - * equivalent to the key "123"). The _heapptr variant takes a raw, borrowed - * heap pointer. - */ - -DUK_EXTERNAL_DECL duk_bool_t duk_get_prop(duk_context *ctx, duk_idx_t obj_idx); -DUK_EXTERNAL_DECL duk_bool_t duk_get_prop_string(duk_context *ctx, duk_idx_t obj_idx, const char *key); -DUK_EXTERNAL_DECL duk_bool_t duk_get_prop_lstring(duk_context *ctx, duk_idx_t obj_idx, const char *key, duk_size_t key_len); -#if defined(DUK_USE_PREFER_SIZE) -#define duk_get_prop_literal(ctx,obj_idx,key) duk_get_prop_string((ctx), (obj_idx), (key)) -#else -DUK_EXTERNAL_DECL duk_bool_t duk_get_prop_literal_raw(duk_context *ctx, duk_idx_t obj_idx, const char *key, duk_size_t key_len); -#define duk_get_prop_literal(ctx,obj_idx,key) duk_get_prop_literal_raw((ctx), (obj_idx), (key), sizeof((key)) - 1U) -#endif -DUK_EXTERNAL_DECL duk_bool_t duk_get_prop_index(duk_context *ctx, duk_idx_t obj_idx, duk_uarridx_t arr_idx); -DUK_EXTERNAL_DECL duk_bool_t duk_get_prop_heapptr(duk_context *ctx, duk_idx_t obj_idx, void *ptr); -DUK_EXTERNAL_DECL duk_bool_t duk_put_prop(duk_context *ctx, duk_idx_t obj_idx); -DUK_EXTERNAL_DECL duk_bool_t duk_put_prop_string(duk_context *ctx, duk_idx_t obj_idx, const char *key); -DUK_EXTERNAL_DECL duk_bool_t duk_put_prop_lstring(duk_context *ctx, duk_idx_t obj_idx, const char *key, duk_size_t key_len); -#if defined(DUK_USE_PREFER_SIZE) -#define duk_put_prop_literal(ctx,obj_idx,key) duk_put_prop_string((ctx), (obj_idx), (key)) -#else -DUK_EXTERNAL_DECL duk_bool_t duk_put_prop_literal_raw(duk_context *ctx, duk_idx_t obj_idx, const char *key, duk_size_t key_len); -#define duk_put_prop_literal(ctx,obj_idx,key) duk_put_prop_literal_raw((ctx), (obj_idx), (key), sizeof((key)) - 1U) -#endif -DUK_EXTERNAL_DECL duk_bool_t duk_put_prop_index(duk_context *ctx, duk_idx_t obj_idx, duk_uarridx_t arr_idx); -DUK_EXTERNAL_DECL duk_bool_t duk_put_prop_heapptr(duk_context *ctx, duk_idx_t obj_idx, void *ptr); -DUK_EXTERNAL_DECL duk_bool_t duk_del_prop(duk_context *ctx, duk_idx_t obj_idx); -DUK_EXTERNAL_DECL duk_bool_t duk_del_prop_string(duk_context *ctx, duk_idx_t obj_idx, const char *key); -DUK_EXTERNAL_DECL duk_bool_t duk_del_prop_lstring(duk_context *ctx, duk_idx_t obj_idx, const char *key, duk_size_t key_len); -#if defined(DUK_USE_PREFER_SIZE) -#define duk_del_prop_literal(ctx,obj_idx,key) duk_del_prop_string((ctx), (obj_idx), (key)) -#else -DUK_EXTERNAL_DECL duk_bool_t duk_del_prop_literal_raw(duk_context *ctx, duk_idx_t obj_idx, const char *key, duk_size_t key_len); -#define duk_del_prop_literal(ctx,obj_idx,key) duk_del_prop_literal_raw((ctx), (obj_idx), (key), sizeof((key)) - 1U) -#endif -DUK_EXTERNAL_DECL duk_bool_t duk_del_prop_index(duk_context *ctx, duk_idx_t obj_idx, duk_uarridx_t arr_idx); -DUK_EXTERNAL_DECL duk_bool_t duk_del_prop_heapptr(duk_context *ctx, duk_idx_t obj_idx, void *ptr); -DUK_EXTERNAL_DECL duk_bool_t duk_has_prop(duk_context *ctx, duk_idx_t obj_idx); -DUK_EXTERNAL_DECL duk_bool_t duk_has_prop_string(duk_context *ctx, duk_idx_t obj_idx, const char *key); -DUK_EXTERNAL_DECL duk_bool_t duk_has_prop_lstring(duk_context *ctx, duk_idx_t obj_idx, const char *key, duk_size_t key_len); -#if defined(DUK_USE_PREFER_SIZE) -#define duk_has_prop_literal(ctx,obj_idx,key) duk_has_prop_string((ctx), (obj_idx), (key)) -#else -DUK_EXTERNAL_DECL duk_bool_t duk_has_prop_literal_raw(duk_context *ctx, duk_idx_t obj_idx, const char *key, duk_size_t key_len); -#define duk_has_prop_literal(ctx,obj_idx,key) duk_has_prop_literal_raw((ctx), (obj_idx), (key), sizeof((key)) - 1U) -#endif -DUK_EXTERNAL_DECL duk_bool_t duk_has_prop_index(duk_context *ctx, duk_idx_t obj_idx, duk_uarridx_t arr_idx); -DUK_EXTERNAL_DECL duk_bool_t duk_has_prop_heapptr(duk_context *ctx, duk_idx_t obj_idx, void *ptr); - -DUK_EXTERNAL_DECL void duk_get_prop_desc(duk_context *ctx, duk_idx_t obj_idx, duk_uint_t flags); -DUK_EXTERNAL_DECL void duk_def_prop(duk_context *ctx, duk_idx_t obj_idx, duk_uint_t flags); - -DUK_EXTERNAL_DECL duk_bool_t duk_get_global_string(duk_context *ctx, const char *key); -DUK_EXTERNAL_DECL duk_bool_t duk_get_global_lstring(duk_context *ctx, const char *key, duk_size_t key_len); -#if defined(DUK_USE_PREFER_SIZE) -#define duk_get_global_literal(ctx,key) duk_get_global_string((ctx), (key)) -#else -DUK_EXTERNAL_DECL duk_bool_t duk_get_global_literal_raw(duk_context *ctx, const char *key, duk_size_t key_len); -#define duk_get_global_literal(ctx,key) duk_get_global_literal_raw((ctx), (key), sizeof((key)) - 1U) -#endif -DUK_EXTERNAL_DECL duk_bool_t duk_get_global_heapptr(duk_context *ctx, void *ptr); -DUK_EXTERNAL_DECL duk_bool_t duk_put_global_string(duk_context *ctx, const char *key); -DUK_EXTERNAL_DECL duk_bool_t duk_put_global_lstring(duk_context *ctx, const char *key, duk_size_t key_len); -#if defined(DUK_USE_PREFER_SIZE) -#define duk_put_global_literal(ctx,key) duk_put_global_string((ctx), (key)) -#else -DUK_EXTERNAL_DECL duk_bool_t duk_put_global_literal_raw(duk_context *ctx, const char *key, duk_size_t key_len); -#define duk_put_global_literal(ctx,key) duk_put_global_literal_raw((ctx), (key), sizeof((key)) - 1U) -#endif -DUK_EXTERNAL_DECL duk_bool_t duk_put_global_heapptr(duk_context *ctx, void *ptr); - -/* - * Inspection - */ - -DUK_EXTERNAL_DECL void duk_inspect_value(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL void duk_inspect_callstack_entry(duk_context *ctx, duk_int_t level); - -/* - * Object prototype - */ - -DUK_EXTERNAL_DECL void duk_get_prototype(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL void duk_set_prototype(duk_context *ctx, duk_idx_t idx); - -/* - * Object finalizer - */ - -DUK_EXTERNAL_DECL void duk_get_finalizer(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL void duk_set_finalizer(duk_context *ctx, duk_idx_t idx); - -/* - * Global object - */ - -DUK_EXTERNAL_DECL void duk_set_global_object(duk_context *ctx); - -/* - * Duktape/C function magic value - */ - -DUK_EXTERNAL_DECL duk_int_t duk_get_magic(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL void duk_set_magic(duk_context *ctx, duk_idx_t idx, duk_int_t magic); -DUK_EXTERNAL_DECL duk_int_t duk_get_current_magic(duk_context *ctx); - -/* - * Module helpers: put multiple function or constant properties - */ - -DUK_EXTERNAL_DECL void duk_put_function_list(duk_context *ctx, duk_idx_t obj_idx, const duk_function_list_entry *funcs); -DUK_EXTERNAL_DECL void duk_put_number_list(duk_context *ctx, duk_idx_t obj_idx, const duk_number_list_entry *numbers); - -/* - * Object operations - */ - -DUK_EXTERNAL_DECL void duk_compact(duk_context *ctx, duk_idx_t obj_idx); -DUK_EXTERNAL_DECL void duk_enum(duk_context *ctx, duk_idx_t obj_idx, duk_uint_t enum_flags); -DUK_EXTERNAL_DECL duk_bool_t duk_next(duk_context *ctx, duk_idx_t enum_idx, duk_bool_t get_value); -DUK_EXTERNAL_DECL void duk_seal(duk_context *ctx, duk_idx_t obj_idx); -DUK_EXTERNAL_DECL void duk_freeze(duk_context *ctx, duk_idx_t obj_idx); - -/* - * String manipulation - */ - -DUK_EXTERNAL_DECL void duk_concat(duk_context *ctx, duk_idx_t count); -DUK_EXTERNAL_DECL void duk_join(duk_context *ctx, duk_idx_t count); -DUK_EXTERNAL_DECL void duk_decode_string(duk_context *ctx, duk_idx_t idx, duk_decode_char_function callback, void *udata); -DUK_EXTERNAL_DECL void duk_map_string(duk_context *ctx, duk_idx_t idx, duk_map_char_function callback, void *udata); -DUK_EXTERNAL_DECL void duk_substring(duk_context *ctx, duk_idx_t idx, duk_size_t start_char_offset, duk_size_t end_char_offset); -DUK_EXTERNAL_DECL void duk_trim(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_codepoint_t duk_char_code_at(duk_context *ctx, duk_idx_t idx, duk_size_t char_offset); - -/* - * ECMAScript operators - */ - -DUK_EXTERNAL_DECL duk_bool_t duk_equals(duk_context *ctx, duk_idx_t idx1, duk_idx_t idx2); -DUK_EXTERNAL_DECL duk_bool_t duk_strict_equals(duk_context *ctx, duk_idx_t idx1, duk_idx_t idx2); -DUK_EXTERNAL_DECL duk_bool_t duk_samevalue(duk_context *ctx, duk_idx_t idx1, duk_idx_t idx2); -DUK_EXTERNAL_DECL duk_bool_t duk_instanceof(duk_context *ctx, duk_idx_t idx1, duk_idx_t idx2); - -/* - * Random - */ - -DUK_EXTERNAL_DECL duk_double_t duk_random(duk_context *ctx); - -/* - * Function (method) calls - */ - -DUK_EXTERNAL_DECL void duk_call(duk_context *ctx, duk_idx_t nargs); -DUK_EXTERNAL_DECL void duk_call_method(duk_context *ctx, duk_idx_t nargs); -DUK_EXTERNAL_DECL void duk_call_prop(duk_context *ctx, duk_idx_t obj_idx, duk_idx_t nargs); -DUK_EXTERNAL_DECL duk_int_t duk_pcall(duk_context *ctx, duk_idx_t nargs); -DUK_EXTERNAL_DECL duk_int_t duk_pcall_method(duk_context *ctx, duk_idx_t nargs); -DUK_EXTERNAL_DECL duk_int_t duk_pcall_prop(duk_context *ctx, duk_idx_t obj_idx, duk_idx_t nargs); -DUK_EXTERNAL_DECL void duk_new(duk_context *ctx, duk_idx_t nargs); -DUK_EXTERNAL_DECL duk_int_t duk_pnew(duk_context *ctx, duk_idx_t nargs); -DUK_EXTERNAL_DECL duk_int_t duk_safe_call(duk_context *ctx, duk_safe_call_function func, void *udata, duk_idx_t nargs, duk_idx_t nrets); - -/* - * Thread management - */ - -/* There are currently no native functions to yield/resume, due to the internal - * limitations on coroutine handling. These will be added later. - */ - -/* - * Compilation and evaluation - */ - -DUK_EXTERNAL_DECL duk_int_t duk_eval_raw(duk_context *ctx, const char *src_buffer, duk_size_t src_length, duk_uint_t flags); -DUK_EXTERNAL_DECL duk_int_t duk_compile_raw(duk_context *ctx, const char *src_buffer, duk_size_t src_length, duk_uint_t flags); - -/* plain */ -#define duk_eval(ctx) \ - ((void) duk_eval_raw((ctx), NULL, 0, 1 /*args*/ | DUK_COMPILE_EVAL | DUK_COMPILE_NOFILENAME)) - -#define duk_eval_noresult(ctx) \ - ((void) duk_eval_raw((ctx), NULL, 0, 1 /*args*/ | DUK_COMPILE_EVAL | DUK_COMPILE_NORESULT | DUK_COMPILE_NOFILENAME)) - -#define duk_peval(ctx) \ - (duk_eval_raw((ctx), NULL, 0, 1 /*args*/ | DUK_COMPILE_EVAL | DUK_COMPILE_SAFE | DUK_COMPILE_NOFILENAME)) - -#define duk_peval_noresult(ctx) \ - (duk_eval_raw((ctx), NULL, 0, 1 /*args*/ | DUK_COMPILE_EVAL | DUK_COMPILE_SAFE | DUK_COMPILE_NORESULT | DUK_COMPILE_NOFILENAME)) - -#define duk_compile(ctx,flags) \ - ((void) duk_compile_raw((ctx), NULL, 0, 2 /*args*/ | (flags))) - -#define duk_pcompile(ctx,flags) \ - (duk_compile_raw((ctx), NULL, 0, 2 /*args*/ | (flags) | DUK_COMPILE_SAFE)) - -/* string */ -#define duk_eval_string(ctx,src) \ - ((void) duk_eval_raw((ctx), (src), 0, 0 /*args*/ | DUK_COMPILE_EVAL | DUK_COMPILE_NOSOURCE | DUK_COMPILE_STRLEN | DUK_COMPILE_NOFILENAME)) - -#define duk_eval_string_noresult(ctx,src) \ - ((void) duk_eval_raw((ctx), (src), 0, 0 /*args*/ | DUK_COMPILE_EVAL | DUK_COMPILE_NOSOURCE | DUK_COMPILE_STRLEN | DUK_COMPILE_NORESULT | DUK_COMPILE_NOFILENAME)) - -#define duk_peval_string(ctx,src) \ - (duk_eval_raw((ctx), (src), 0, 0 /*args*/ | DUK_COMPILE_EVAL | DUK_COMPILE_SAFE | DUK_COMPILE_NOSOURCE | DUK_COMPILE_STRLEN | DUK_COMPILE_NOFILENAME)) - -#define duk_peval_string_noresult(ctx,src) \ - (duk_eval_raw((ctx), (src), 0, 0 /*args*/ | DUK_COMPILE_EVAL | DUK_COMPILE_SAFE | DUK_COMPILE_NOSOURCE | DUK_COMPILE_STRLEN | DUK_COMPILE_NORESULT | DUK_COMPILE_NOFILENAME)) - -#define duk_compile_string(ctx,flags,src) \ - ((void) duk_compile_raw((ctx), (src), 0, 0 /*args*/ | (flags) | DUK_COMPILE_NOSOURCE | DUK_COMPILE_STRLEN | DUK_COMPILE_NOFILENAME)) - -#define duk_compile_string_filename(ctx,flags,src) \ - ((void) duk_compile_raw((ctx), (src), 0, 1 /*args*/ | (flags) | DUK_COMPILE_NOSOURCE | DUK_COMPILE_STRLEN)) - -#define duk_pcompile_string(ctx,flags,src) \ - (duk_compile_raw((ctx), (src), 0, 0 /*args*/ | (flags) | DUK_COMPILE_SAFE | DUK_COMPILE_NOSOURCE | DUK_COMPILE_STRLEN | DUK_COMPILE_NOFILENAME)) - -#define duk_pcompile_string_filename(ctx,flags,src) \ - (duk_compile_raw((ctx), (src), 0, 1 /*args*/ | (flags) | DUK_COMPILE_SAFE | DUK_COMPILE_NOSOURCE | DUK_COMPILE_STRLEN)) - -/* lstring */ -#define duk_eval_lstring(ctx,buf,len) \ - ((void) duk_eval_raw((ctx), buf, len, 0 /*args*/ | DUK_COMPILE_EVAL | DUK_COMPILE_NOSOURCE | DUK_COMPILE_NOFILENAME)) - -#define duk_eval_lstring_noresult(ctx,buf,len) \ - ((void) duk_eval_raw((ctx), buf, len, 0 /*args*/ | DUK_COMPILE_EVAL | DUK_COMPILE_NOSOURCE | DUK_COMPILE_NORESULT | DUK_COMPILE_NOFILENAME)) - -#define duk_peval_lstring(ctx,buf,len) \ - (duk_eval_raw((ctx), buf, len, 0 /*args*/ | DUK_COMPILE_EVAL | DUK_COMPILE_NOSOURCE | DUK_COMPILE_SAFE | DUK_COMPILE_NOFILENAME)) - -#define duk_peval_lstring_noresult(ctx,buf,len) \ - (duk_eval_raw((ctx), buf, len, 0 /*args*/ | DUK_COMPILE_EVAL | DUK_COMPILE_SAFE | DUK_COMPILE_NOSOURCE | DUK_COMPILE_NORESULT | DUK_COMPILE_NOFILENAME)) - -#define duk_compile_lstring(ctx,flags,buf,len) \ - ((void) duk_compile_raw((ctx), buf, len, 0 /*args*/ | (flags) | DUK_COMPILE_NOSOURCE | DUK_COMPILE_NOFILENAME)) - -#define duk_compile_lstring_filename(ctx,flags,buf,len) \ - ((void) duk_compile_raw((ctx), buf, len, 1 /*args*/ | (flags) | DUK_COMPILE_NOSOURCE)) - -#define duk_pcompile_lstring(ctx,flags,buf,len) \ - (duk_compile_raw((ctx), buf, len, 0 /*args*/ | (flags) | DUK_COMPILE_SAFE | DUK_COMPILE_NOSOURCE | DUK_COMPILE_NOFILENAME)) - -#define duk_pcompile_lstring_filename(ctx,flags,buf,len) \ - (duk_compile_raw((ctx), buf, len, 1 /*args*/ | (flags) | DUK_COMPILE_SAFE | DUK_COMPILE_NOSOURCE)) - -/* - * Bytecode load/dump - */ - -DUK_EXTERNAL_DECL void duk_dump_function(duk_context *ctx); -DUK_EXTERNAL_DECL void duk_load_function(duk_context *ctx); - -/* - * Debugging - */ - -DUK_EXTERNAL_DECL void duk_push_context_dump(duk_context *ctx); - -/* - * Debugger (debug protocol) - */ - -DUK_EXTERNAL_DECL void duk_debugger_attach(duk_context *ctx, - duk_debug_read_function read_cb, - duk_debug_write_function write_cb, - duk_debug_peek_function peek_cb, - duk_debug_read_flush_function read_flush_cb, - duk_debug_write_flush_function write_flush_cb, - duk_debug_request_function request_cb, - duk_debug_detached_function detached_cb, - void *udata); -DUK_EXTERNAL_DECL void duk_debugger_detach(duk_context *ctx); -DUK_EXTERNAL_DECL void duk_debugger_cooperate(duk_context *ctx); -DUK_EXTERNAL_DECL duk_bool_t duk_debugger_notify(duk_context *ctx, duk_idx_t nvalues); -DUK_EXTERNAL_DECL void duk_debugger_pause(duk_context *ctx); - -/* - * Time handling - */ - -DUK_EXTERNAL_DECL duk_double_t duk_get_now(duk_context *ctx); -DUK_EXTERNAL_DECL void duk_time_to_components(duk_context *ctx, duk_double_t timeval, duk_time_components *comp); -DUK_EXTERNAL_DECL duk_double_t duk_components_to_time(duk_context *ctx, duk_time_components *comp); - -/* - * Date provider related constants - * - * NOTE: These are "semi public" - you should only use these if you write - * your own platform specific Date provider, see doc/datetime.rst. - */ - -/* Millisecond count constants. */ -#define DUK_DATE_MSEC_SECOND 1000L -#define DUK_DATE_MSEC_MINUTE (60L * 1000L) -#define DUK_DATE_MSEC_HOUR (60L * 60L * 1000L) -#define DUK_DATE_MSEC_DAY (24L * 60L * 60L * 1000L) - -/* ECMAScript date range is 100 million days from Epoch: - * > 100e6 * 24 * 60 * 60 * 1000 // 100M days in millisecs - * 8640000000000000 - * (= 8.64e15) - */ -#define DUK_DATE_MSEC_100M_DAYS (8.64e15) -#define DUK_DATE_MSEC_100M_DAYS_LEEWAY (8.64e15 + 24 * 3600e3) - -/* ECMAScript year range: - * > new Date(100e6 * 24 * 3600e3).toISOString() - * '+275760-09-13T00:00:00.000Z' - * > new Date(-100e6 * 24 * 3600e3).toISOString() - * '-271821-04-20T00:00:00.000Z' - */ -#define DUK_DATE_MIN_ECMA_YEAR (-271821L) -#define DUK_DATE_MAX_ECMA_YEAR 275760L - -/* Part indices for internal breakdowns. Part order from DUK_DATE_IDX_YEAR - * to DUK_DATE_IDX_MILLISECOND matches argument ordering of ECMAScript API - * calls (like Date constructor call). Some functions in duk_bi_date.c - * depend on the specific ordering, so change with care. 16 bits are not - * enough for all parts (year, specifically). - * - * Must be in-sync with genbuiltins.py. - */ -#define DUK_DATE_IDX_YEAR 0 /* year */ -#define DUK_DATE_IDX_MONTH 1 /* month: 0 to 11 */ -#define DUK_DATE_IDX_DAY 2 /* day within month: 0 to 30 */ -#define DUK_DATE_IDX_HOUR 3 -#define DUK_DATE_IDX_MINUTE 4 -#define DUK_DATE_IDX_SECOND 5 -#define DUK_DATE_IDX_MILLISECOND 6 -#define DUK_DATE_IDX_WEEKDAY 7 /* weekday: 0 to 6, 0=sunday, 1=monday, etc */ -#define DUK_DATE_IDX_NUM_PARTS 8 - -/* Internal API call flags, used for various functions in duk_bi_date.c. - * Certain flags are used by only certain functions, but since the flags - * don't overlap, a single flags value can be passed around to multiple - * functions. - * - * The unused top bits of the flags field are also used to pass values - * to helpers (duk__get_part_helper() and duk__set_part_helper()). - * - * Must be in-sync with genbuiltins.py. - */ - -/* NOTE: when writing a Date provider you only need a few specific - * flags from here, the rest are internal. Avoid using anything you - * don't need. - */ - -#define DUK_DATE_FLAG_NAN_TO_ZERO (1 << 0) /* timeval breakdown: internal time value NaN -> zero */ -#define DUK_DATE_FLAG_NAN_TO_RANGE_ERROR (1 << 1) /* timeval breakdown: internal time value NaN -> RangeError (toISOString) */ -#define DUK_DATE_FLAG_ONEBASED (1 << 2) /* timeval breakdown: convert month and day-of-month parts to one-based (default is zero-based) */ -#define DUK_DATE_FLAG_EQUIVYEAR (1 << 3) /* timeval breakdown: replace year with equivalent year in the [1971,2037] range for DST calculations */ -#define DUK_DATE_FLAG_LOCALTIME (1 << 4) /* convert time value to local time */ -#define DUK_DATE_FLAG_SUB1900 (1 << 5) /* getter: subtract 1900 from year when getting year part */ -#define DUK_DATE_FLAG_TOSTRING_DATE (1 << 6) /* include date part in string conversion result */ -#define DUK_DATE_FLAG_TOSTRING_TIME (1 << 7) /* include time part in string conversion result */ -#define DUK_DATE_FLAG_TOSTRING_LOCALE (1 << 8) /* use locale specific formatting if available */ -#define DUK_DATE_FLAG_TIMESETTER (1 << 9) /* setter: call is a time setter (affects hour, min, sec, ms); otherwise date setter (affects year, month, day-in-month) */ -#define DUK_DATE_FLAG_YEAR_FIXUP (1 << 10) /* setter: perform 2-digit year fixup (00...99 -> 1900...1999) */ -#define DUK_DATE_FLAG_SEP_T (1 << 11) /* string conversion: use 'T' instead of ' ' as a separator */ -#define DUK_DATE_FLAG_VALUE_SHIFT 12 /* additional values begin at bit 12 */ - -/* - * ROM pointer compression - */ - -/* Support array for ROM pointer compression. Only declared when ROM - * pointer compression is active. - */ -#if defined(DUK_USE_ROM_OBJECTS) && defined(DUK_USE_HEAPPTR16) -DUK_EXTERNAL_DECL const void * const duk_rom_compressed_pointers[]; -#endif - -/* - * C++ name mangling - */ - -#if defined(__cplusplus) -/* end 'extern "C"' wrapper */ -} -#endif - -/* - * END PUBLIC API - */ - -#endif /* DUKTAPE_H_INCLUDED */ diff --git a/src/thirdparty/quickjs-ng/quickjs-amalgam.c b/src/thirdparty/quickjs-ng/quickjs-amalgam.c new file mode 100644 index 0000000000..6f9fdd51f3 --- /dev/null +++ b/src/thirdparty/quickjs-ng/quickjs-amalgam.c @@ -0,0 +1,82922 @@ +#if defined(QJS_BUILD_LIBC) && defined(__linux__) && !defined(_GNU_SOURCE) +#define _GNU_SOURCE +#endif +/* + * QuickJS C atomics definitions + * + * Copyright (c) 2023 Marcin Kolny + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#if (defined(__GNUC__) || defined(__GNUG__)) && !defined(__clang__) + // Use GCC builtins for version < 4.9 +# if((__GNUC__ << 16) + __GNUC_MINOR__ < ((4) << 16) + 9) +# define GCC_BUILTIN_ATOMICS +# endif +#endif + +#ifdef GCC_BUILTIN_ATOMICS +#define atomic_fetch_add(obj, arg) \ + __atomic_fetch_add(obj, arg, __ATOMIC_SEQ_CST) +#define atomic_compare_exchange_strong(obj, expected, desired) \ + __atomic_compare_exchange_n(obj, expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) +#define atomic_exchange(obj, desired) \ + __atomic_exchange_n (obj, desired, __ATOMIC_SEQ_CST) +#define atomic_load(obj) \ + __atomic_load_n(obj, __ATOMIC_SEQ_CST) +#define atomic_store(obj, desired) \ + __atomic_store_n(obj, desired, __ATOMIC_SEQ_CST) +#define atomic_fetch_or(obj, arg) \ + __atomic_fetch_or(obj, arg, __ATOMIC_SEQ_CST) +#define atomic_fetch_xor(obj, arg) \ + __atomic_fetch_xor(obj, arg, __ATOMIC_SEQ_CST) +#define atomic_fetch_and(obj, arg) \ + __atomic_fetch_and(obj, arg, __ATOMIC_SEQ_CST) +#define atomic_fetch_sub(obj, arg) \ + __atomic_fetch_sub(obj, arg, __ATOMIC_SEQ_CST) +#define _Atomic +#else +#include +#endif +/* + * C utilities + * + * Copyright (c) 2017 Fabrice Bellard + * Copyright (c) 2018 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef CUTILS_H +#define CUTILS_H + +#include +#include +#include +#include +#if !defined(_MSC_VER) +#include +#endif +#if defined(__APPLE__) +#include +#endif +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(_MSC_VER) +#include +#define alloca _alloca +#define ssize_t ptrdiff_t +#endif +#if defined(__APPLE__) +#include +#elif defined(__linux__) || defined(__ANDROID__) || defined(__CYGWIN__) || defined(__GLIBC__) +#include +#elif defined(__FreeBSD__) +#include +#elif defined(_WIN32) +#include +#include +#include // _beginthread +#endif +#if !defined(_WIN32) && !defined(EMSCRIPTEN) && !defined(__wasi__) && !defined(__DJGPP) +#include +#include +#endif +#if !defined(_WIN32) +#include +#include +#endif + +#if defined(__sun) +#undef __maybe_unused +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +# define likely(x) (x) +# define unlikely(x) (x) +# define no_inline __declspec(noinline) +# define __maybe_unused +# define __attribute__(x) +# define __attribute(x) +#else +# define likely(x) __builtin_expect(!!(x), 1) +# define unlikely(x) __builtin_expect(!!(x), 0) +# define no_inline __attribute__((noinline)) +# define __maybe_unused __attribute__((unused)) +#endif + +#ifndef offsetof +#define offsetof(type, field) ((size_t) &((type *)0)->field) +#endif +#ifndef countof +#define countof(x) (sizeof(x) / sizeof((x)[0])) +#ifndef endof +#define endof(x) ((x) + countof(x)) +#endif +#endif +#ifndef container_of +/* return the pointer of type 'type *' containing 'ptr' as field 'member' */ +#define container_of(ptr, type, member) ((type *)((uint8_t *)(ptr) - offsetof(type, member))) +#endif + +#if defined(_MSC_VER) || defined(__cplusplus) +#define minimum_length(n) n +#else +#define minimum_length(n) static n +#endif + +/* Borrowed from Folly */ +#ifndef JS_PRINTF_FORMAT +#ifdef _MSC_VER +#include +#define JS_PRINTF_FORMAT _Printf_format_string_ +#define JS_PRINTF_FORMAT_ATTR(format_param, dots_param) +#else +#define JS_PRINTF_FORMAT +#if !defined(__clang__) && defined(__GNUC__) +#define JS_PRINTF_FORMAT_ATTR(format_param, dots_param) \ + __attribute__((format(gnu_printf, format_param, dots_param))) +#else +#define JS_PRINTF_FORMAT_ATTR(format_param, dots_param) \ + __attribute__((format(printf, format_param, dots_param))) +#endif +#endif +#endif + +#if defined(PATH_MAX) +# define JS__PATH_MAX PATH_MAX +#elif defined(_WIN32) +# define JS__PATH_MAX 32767 +#else +# define JS__PATH_MAX 8192 +#endif + +static inline void js__pstrcpy(char *buf, int buf_size, const char *str); +static inline char *js__pstrcat(char *buf, int buf_size, const char *s); +static inline int js__strstart(const char *str, const char *val, const char **ptr); +static inline int js__has_suffix(const char *str, const char *suffix); + +static inline uint8_t is_be(void) { + union { + uint16_t a; + uint8_t b; + } u = { 0x100 }; + return u.b; +} + +static inline int max_int(int a, int b) +{ + if (a > b) + return a; + else + return b; +} + +static inline int min_int(int a, int b) +{ + if (a < b) + return a; + else + return b; +} + +static inline uint32_t max_uint32(uint32_t a, uint32_t b) +{ + if (a > b) + return a; + else + return b; +} + +static inline uint32_t min_uint32(uint32_t a, uint32_t b) +{ + if (a < b) + return a; + else + return b; +} + +static inline int64_t max_int64(int64_t a, int64_t b) +{ + if (a > b) + return a; + else + return b; +} + +static inline int64_t min_int64(int64_t a, int64_t b) +{ + if (a < b) + return a; + else + return b; +} + +/* WARNING: undefined if a = 0 */ +static inline int clz32(unsigned int a) +{ +#if defined(_MSC_VER) && !defined(__clang__) + unsigned long index; + _BitScanReverse(&index, a); + return 31 - index; +#else + return __builtin_clz(a); +#endif +} + +/* WARNING: undefined if a = 0 */ +static inline int clz64(uint64_t a) +{ +#if defined(_MSC_VER) && !defined(__clang__) +#if INTPTR_MAX == INT64_MAX + unsigned long index; + _BitScanReverse64(&index, a); + return 63 - index; +#else + if (a >> 32) + return clz32((unsigned)(a >> 32)); + else + return clz32((unsigned)a) + 32; +#endif +#else + return __builtin_clzll(a); +#endif +} + +/* WARNING: undefined if a = 0 */ +static inline int ctz32(unsigned int a) +{ +#if defined(_MSC_VER) && !defined(__clang__) + unsigned long index; + _BitScanForward(&index, a); + return index; +#else + return __builtin_ctz(a); +#endif +} + +/* WARNING: undefined if a = 0 */ +static inline int ctz64(uint64_t a) +{ +#if defined(_MSC_VER) && !defined(__clang__) + unsigned long index; + _BitScanForward64(&index, a); + return index; +#else + return __builtin_ctzll(a); +#endif +} + +static inline uint64_t get_u64(const uint8_t *tab) +{ + uint64_t v; + memcpy(&v, tab, sizeof(v)); + return v; +} + +static inline int64_t get_i64(const uint8_t *tab) +{ + int64_t v; + memcpy(&v, tab, sizeof(v)); + return v; +} + +static inline void put_u64(uint8_t *tab, uint64_t val) +{ + memcpy(tab, &val, sizeof(val)); +} + +static inline uint32_t get_u32(const uint8_t *tab) +{ + uint32_t v; + memcpy(&v, tab, sizeof(v)); + return v; +} + +static inline int32_t get_i32(const uint8_t *tab) +{ + int32_t v; + memcpy(&v, tab, sizeof(v)); + return v; +} + +static inline void put_u32(uint8_t *tab, uint32_t val) +{ + memcpy(tab, &val, sizeof(val)); +} + +static inline uint32_t get_u16(const uint8_t *tab) +{ + uint16_t v; + memcpy(&v, tab, sizeof(v)); + return v; +} + +static inline int32_t get_i16(const uint8_t *tab) +{ + int16_t v; + memcpy(&v, tab, sizeof(v)); + return v; +} + +static inline void put_u16(uint8_t *tab, uint16_t val) +{ + memcpy(tab, &val, sizeof(val)); +} + +static inline uint32_t get_u8(const uint8_t *tab) +{ + return *tab; +} + +static inline int32_t get_i8(const uint8_t *tab) +{ + return (int8_t)*tab; +} + +static inline void put_u8(uint8_t *tab, uint8_t val) +{ + *tab = val; +} + +#ifndef bswap16 +static inline uint16_t bswap16(uint16_t x) +{ + return (x >> 8) | (x << 8); +} +#endif + +#ifndef bswap32 +static inline uint32_t bswap32(uint32_t v) +{ + return ((v & 0xff000000) >> 24) | ((v & 0x00ff0000) >> 8) | + ((v & 0x0000ff00) << 8) | ((v & 0x000000ff) << 24); +} +#endif + +#ifndef bswap64 +static inline uint64_t bswap64(uint64_t v) +{ + return ((v & ((uint64_t)0xff << (7 * 8))) >> (7 * 8)) | + ((v & ((uint64_t)0xff << (6 * 8))) >> (5 * 8)) | + ((v & ((uint64_t)0xff << (5 * 8))) >> (3 * 8)) | + ((v & ((uint64_t)0xff << (4 * 8))) >> (1 * 8)) | + ((v & ((uint64_t)0xff << (3 * 8))) << (1 * 8)) | + ((v & ((uint64_t)0xff << (2 * 8))) << (3 * 8)) | + ((v & ((uint64_t)0xff << (1 * 8))) << (5 * 8)) | + ((v & ((uint64_t)0xff << (0 * 8))) << (7 * 8)); +} +#endif + +static inline void inplace_bswap16(uint8_t *tab) { + put_u16(tab, bswap16(get_u16(tab))); +} + +static inline void inplace_bswap32(uint8_t *tab) { + put_u32(tab, bswap32(get_u32(tab))); +} + +static inline double fromfp16(uint16_t v) { + double d, s; + int e; + if ((v & 0x7C00) == 0x7C00) { + d = (v & 0x3FF) ? NAN : INFINITY; + } else { + d = (v & 0x3FF) / 1024.; + e = (v & 0x7C00) >> 10; + if (e == 0) { + e = -14; + } else { + d += 1; + e -= 15; + } + d = scalbn(d, e); + } + s = (v & 0x8000) ? -1.0 : 1.0; + return d * s; +} + +static inline uint16_t tofp16(double d) { + uint16_t f, s; + double t; + int e; + s = 0; + if (copysign(1, d) < 0) { // preserve sign when |d| is negative zero + d = -d; + s = 0x8000; + } + if (isinf(d)) + return s | 0x7C00; + if (isnan(d)) + return s | 0x7C01; + if (d == 0) + return s | 0; + d = 2 * frexp(d, &e); + e--; + if (e > 15) + return s | 0x7C00; // out of range, return +/-infinity + if (e < -25) { + d = 0; + e = 0; + } else if (e < -14) { + d = scalbn(d, e + 14); + e = 0; + } else { + d -= 1; + e += 15; + } + d *= 1024.; + f = (uint16_t)d; + t = d - f; + if (t < 0.5) + goto done; + if (t == 0.5) + if ((f & 1) == 0) + goto done; + // adjust for rounding + if (++f == 1024) { + f = 0; + if (++e == 31) + return s | 0x7C00; // out of range, return +/-infinity + } +done: + return s | (e << 10) | f; +} + +static inline int isfp16nan(uint16_t v) { + return (v & 0x7FFF) > 0x7C00; +} + +static inline int isfp16zero(uint16_t v) { + return (v & 0x7FFF) == 0; +} + +/* XXX: should take an extra argument to pass slack information to the caller */ +typedef void *DynBufReallocFunc(void *opaque, void *ptr, size_t size); + +typedef struct DynBuf { + uint8_t *buf; + size_t size; + size_t allocated_size; + bool error; /* true if a memory allocation error occurred */ + DynBufReallocFunc *realloc_func; + void *opaque; /* for realloc_func */ +} DynBuf; + +static inline void dbuf_init(DynBuf *s); +static inline void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func); +static inline int dbuf_claim(DynBuf *s, size_t len); +static inline int dbuf_put(DynBuf *s, const void *data, size_t len); +static inline int dbuf_put_self(DynBuf *s, size_t offset, size_t len); +static inline int __dbuf_putc(DynBuf *s, uint8_t c); +static inline int __dbuf_put_u16(DynBuf *s, uint16_t val); +static inline int __dbuf_put_u32(DynBuf *s, uint32_t val); +static inline int __dbuf_put_u64(DynBuf *s, uint64_t val); +static inline int dbuf_putstr(DynBuf *s, const char *str); +static inline int dbuf_putc(DynBuf *s, uint8_t val) +{ + if (unlikely((s->allocated_size - s->size) < 1)) + return __dbuf_putc(s, val); + s->buf[s->size++] = val; + return 0; +} +static inline int dbuf_put_u16(DynBuf *s, uint16_t val) +{ + if (unlikely((s->allocated_size - s->size) < 2)) + return __dbuf_put_u16(s, val); + put_u16(s->buf + s->size, val); + s->size += 2; + return 0; +} +static inline int dbuf_put_u32(DynBuf *s, uint32_t val) +{ + if (unlikely((s->allocated_size - s->size) < 4)) + return __dbuf_put_u32(s, val); + put_u32(s->buf + s->size, val); + s->size += 4; + return 0; +} +static inline int dbuf_put_u64(DynBuf *s, uint64_t val) +{ + if (unlikely((s->allocated_size - s->size) < 8)) + return __dbuf_put_u64(s, val); + put_u64(s->buf + s->size, val); + s->size += 8; + return 0; +} +static inline int JS_PRINTF_FORMAT_ATTR(2, 3) dbuf_printf(DynBuf *s, JS_PRINTF_FORMAT const char *fmt, ...); +static inline void dbuf_free(DynBuf *s); +static inline bool dbuf_error(DynBuf *s) { + return s->error; +} +static inline void dbuf_set_error(DynBuf *s) +{ + s->error = true; +} + +/*---- UTF-8 and UTF-16 handling ----*/ + +#define UTF8_CHAR_LEN_MAX 4 + +enum { + UTF8_PLAIN_ASCII = 0, // 7-bit ASCII plain text + UTF8_NON_ASCII = 1, // has non ASCII code points (8-bit or more) + UTF8_HAS_16BIT = 2, // has 16-bit code points + UTF8_HAS_NON_BMP1 = 4, // has non-BMP1 code points, needs UTF-16 surrogate pairs + UTF8_HAS_ERRORS = 8, // has encoding errors +}; +static inline int utf8_scan(const char *buf, size_t len, size_t *plen); +static inline size_t utf8_encode_len(uint32_t c); +static inline size_t utf8_encode(uint8_t buf[minimum_length(UTF8_CHAR_LEN_MAX)], uint32_t c); +static inline uint32_t utf8_decode_len(const uint8_t *p, size_t max_len, const uint8_t **pp); +static inline uint32_t utf8_decode(const uint8_t *p, const uint8_t **pp); +static inline size_t utf8_decode_buf8(uint8_t *dest, size_t dest_len, const char *src, size_t src_len); +static inline size_t utf8_decode_buf16(uint16_t *dest, size_t dest_len, const char *src, size_t src_len); +static inline size_t utf8_encode_buf8(char *dest, size_t dest_len, const uint8_t *src, size_t src_len); +static inline size_t utf8_encode_buf16(char *dest, size_t dest_len, const uint16_t *src, size_t src_len); + +static inline bool is_surrogate(uint32_t c) +{ + return (c >> 11) == (0xD800 >> 11); // 0xD800-0xDFFF +} + +static inline bool is_hi_surrogate(uint32_t c) +{ + return (c >> 10) == (0xD800 >> 10); // 0xD800-0xDBFF +} + +static inline bool is_lo_surrogate(uint32_t c) +{ + return (c >> 10) == (0xDC00 >> 10); // 0xDC00-0xDFFF +} + +static inline uint32_t get_hi_surrogate(uint32_t c) +{ + return (c >> 10) - (0x10000 >> 10) + 0xD800; +} + +static inline uint32_t get_lo_surrogate(uint32_t c) +{ + return (c & 0x3FF) | 0xDC00; +} + +static inline uint32_t from_surrogate(uint32_t hi, uint32_t lo) +{ + return 65536 + 1024 * (hi & 1023) + (lo & 1023); +} + +static inline int from_hex(int c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + else if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + else + return -1; +} + +static inline uint8_t is_upper_ascii(uint8_t c) { + return c >= 'A' && c <= 'Z'; +} + +static inline uint8_t to_upper_ascii(uint8_t c) { + return c >= 'a' && c <= 'z' ? c - 'a' + 'A' : c; +} + +static inline void rqsort(void *base, size_t nmemb, size_t size, + int (*cmp)(const void *, const void *, void *), + void *arg); + +static inline uint64_t float64_as_uint64(double d) +{ + union { + double d; + uint64_t u64; + } u; + u.d = d; + return u.u64; +} + +static inline double uint64_as_float64(uint64_t u64) +{ + union { + double d; + uint64_t u64; + } u; + u.u64 = u64; + return u.d; +} + +static inline int64_t js__gettimeofday_us(void); +static inline uint64_t js__hrtime_ns(void); + +static inline size_t js__malloc_usable_size(const void *ptr) +{ +#if defined(__APPLE__) + return malloc_size(ptr); +#elif defined(_WIN32) + return _msize((void *)ptr); +#elif defined(__linux__) || defined(__ANDROID__) || defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__GLIBC__) + return malloc_usable_size((void *)ptr); +#else + return 0; +#endif +} + +static inline int js_exepath(char* buffer, size_t* size); + +/* Cross-platform threading APIs. */ + +#if defined(EMSCRIPTEN) || defined(__wasi__) || defined(__DJGPP) + +#define JS_HAVE_THREADS 0 + +#else + +#define JS_HAVE_THREADS 1 + +#if defined(_WIN32) +#define JS_ONCE_INIT INIT_ONCE_STATIC_INIT +typedef INIT_ONCE js_once_t; +typedef CRITICAL_SECTION js_mutex_t; +typedef CONDITION_VARIABLE js_cond_t; +typedef HANDLE js_thread_t; +#else +#define JS_ONCE_INIT PTHREAD_ONCE_INIT +typedef pthread_once_t js_once_t; +typedef pthread_mutex_t js_mutex_t; +typedef pthread_cond_t js_cond_t; +typedef pthread_t js_thread_t; +#endif + +static inline void js_once(js_once_t *guard, void (*callback)(void)); + +static inline void js_mutex_init(js_mutex_t *mutex); +static inline void js_mutex_destroy(js_mutex_t *mutex); +static inline void js_mutex_lock(js_mutex_t *mutex); +static inline void js_mutex_unlock(js_mutex_t *mutex); + +static inline void js_cond_init(js_cond_t *cond); +static inline void js_cond_destroy(js_cond_t *cond); +static inline void js_cond_signal(js_cond_t *cond); +static inline void js_cond_broadcast(js_cond_t *cond); +static inline void js_cond_wait(js_cond_t *cond, js_mutex_t *mutex); +static inline int js_cond_timedwait(js_cond_t *cond, js_mutex_t *mutex, uint64_t timeout); + +enum { + JS_THREAD_CREATE_DETACHED = 1, +}; + +// creates threads with 2 MB stacks (glibc default) +static inline int js_thread_create(js_thread_t *thrd, void (*start)(void *), void *arg, + int flags); +static inline int js_thread_join(js_thread_t thrd); + +#endif /* !defined(EMSCRIPTEN) && !defined(__wasi__) */ + +// JS requires strict rounding behavior. Turn on 64-bits double precision +// and disable x87 80-bits extended precision for intermediate floating-point +// results. 0x300 is extended precision, 0x200 is double precision. +// Note that `*&cw` in the asm constraints looks redundant but isn't. +#if defined(__i386__) && !defined(_MSC_VER) +#define JS_X87_FPCW_SAVE_AND_ADJUST(cw) \ + unsigned short cw; \ + __asm__ __volatile__("fnstcw %0" : "=m"(*&cw)); \ + do { \ + unsigned short t = 0x200 | (cw & ~0x300); \ + __asm__ __volatile__("fldcw %0" : /*empty*/ : "m"(*&t)); \ + } while (0) +#define JS_X87_FPCW_RESTORE(cw) \ + __asm__ __volatile__("fldcw %0" : /*empty*/ : "m"(*&cw)) +#else +#define JS_X87_FPCW_SAVE_AND_ADJUST(cw) +#define JS_X87_FPCW_RESTORE(cw) +#endif + +#undef NANOSEC +#define NANOSEC ((uint64_t) 1e9) + +static inline void js__pstrcpy(char *buf, int buf_size, const char *str) +{ + int c; + char *q = buf; + + if (buf_size <= 0) + return; + + for(;;) { + c = *str++; + if (c == 0 || q >= buf + buf_size - 1) + break; + *q++ = c; + } + *q = '\0'; +} + +/* strcat and truncate. */ +static inline char *js__pstrcat(char *buf, int buf_size, const char *s) +{ + int len; + len = strlen(buf); + if (len < buf_size) + js__pstrcpy(buf + len, buf_size - len, s); + return buf; +} + +static inline int js__strstart(const char *str, const char *val, const char **ptr) +{ + const char *p, *q; + p = str; + q = val; + while (*q != '\0') { + if (*p != *q) + return 0; + p++; + q++; + } + if (ptr) + *ptr = p; + return 1; +} + +static inline int js__has_suffix(const char *str, const char *suffix) +{ + size_t len = strlen(str); + size_t slen = strlen(suffix); + return (len >= slen && !memcmp(str + len - slen, suffix, slen)); +} + +/* Dynamic buffer package */ + +static void *dbuf_default_realloc(void *opaque, void *ptr, size_t size) +{ + if (unlikely(size == 0)) { + free(ptr); + return NULL; + } + return realloc(ptr, size); +} + +static inline void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func) +{ + memset(s, 0, sizeof(*s)); + if (!realloc_func) + realloc_func = dbuf_default_realloc; + s->opaque = opaque; + s->realloc_func = realloc_func; +} + +static inline void dbuf_init(DynBuf *s) +{ + dbuf_init2(s, NULL, NULL); +} + +/* Try to allocate 'len' more bytes. return < 0 if error */ +static inline int dbuf_claim(DynBuf *s, size_t len) +{ + size_t new_size, size, new_allocated_size; + uint8_t *new_buf; + new_size = s->size + len; + if (new_size < len) + return -1; /* overflow */ + if (new_size > s->allocated_size) { + if (s->error) + return -1; + size = s->allocated_size + (s->allocated_size / 2); + if (size < new_size || size < s->allocated_size) /* overflow test */ + new_allocated_size = new_size; + else + new_allocated_size = size; + new_buf = s->realloc_func(s->opaque, s->buf, new_allocated_size); + if (!new_buf) { + s->error = true; + return -1; + } + s->buf = new_buf; + s->allocated_size = new_allocated_size; + } + return 0; +} + +static inline int dbuf_put(DynBuf *s, const void *data, size_t len) +{ + if (unlikely((s->size + len) > s->allocated_size)) { + if (dbuf_claim(s, len)) + return -1; + } + if (len > 0) { + memcpy(s->buf + s->size, data, len); + s->size += len; + } + return 0; +} + +static inline int dbuf_put_self(DynBuf *s, size_t offset, size_t len) +{ + if (unlikely((s->size + len) > s->allocated_size)) { + if (dbuf_claim(s, len)) + return -1; + } + if (len > 0) { + memcpy(s->buf + s->size, s->buf + offset, len); + s->size += len; + } + return 0; +} + +static inline int __dbuf_putc(DynBuf *s, uint8_t c) +{ + return dbuf_put(s, &c, 1); +} + +static inline int __dbuf_put_u16(DynBuf *s, uint16_t val) +{ + return dbuf_put(s, (uint8_t *)&val, 2); +} + +static inline int __dbuf_put_u32(DynBuf *s, uint32_t val) +{ + return dbuf_put(s, (uint8_t *)&val, 4); +} + +static inline int __dbuf_put_u64(DynBuf *s, uint64_t val) +{ + return dbuf_put(s, (uint8_t *)&val, 8); +} + +static inline int dbuf_putstr(DynBuf *s, const char *str) +{ + return dbuf_put(s, (const uint8_t *)str, strlen(str)); +} + +static inline int JS_PRINTF_FORMAT_ATTR(2, 3) dbuf_printf(DynBuf *s, JS_PRINTF_FORMAT const char *fmt, ...) +{ + va_list ap; + char buf[128]; + int len; + + va_start(ap, fmt); + len = vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + if (len < (int)sizeof(buf)) { + /* fast case */ + return dbuf_put(s, (uint8_t *)buf, len); + } else { + if (dbuf_claim(s, len + 1)) + return -1; + va_start(ap, fmt); + vsnprintf((char *)(s->buf + s->size), s->allocated_size - s->size, + fmt, ap); + va_end(ap); + s->size += len; + } + return 0; +} + +static inline void dbuf_free(DynBuf *s) +{ + /* we test s->buf as a fail safe to avoid crashing if dbuf_free() + is called twice */ + if (s->buf) { + s->realloc_func(s->opaque, s->buf, 0); + } + memset(s, 0, sizeof(*s)); +} + +/*--- UTF-8 utility functions --*/ + +/* Note: only encode valid codepoints (0x0000..0x10FFFF). + At most UTF8_CHAR_LEN_MAX bytes are output. */ + +/* Compute the number of bytes of the UTF-8 encoding for a codepoint + `c` is a code-point. + Returns the number of bytes. If a codepoint is beyond 0x10FFFF the + return value is 3 as the codepoint would be encoded as 0xFFFD. + */ +static inline size_t utf8_encode_len(uint32_t c) +{ + if (c < 0x80) + return 1; + if (c < 0x800) + return 2; + if (c < 0x10000) + return 3; + if (c < 0x110000) + return 4; + return 3; +} + +/* Encode a codepoint in UTF-8 + `buf` points to an array of at least `UTF8_CHAR_LEN_MAX` bytes + `c` is a code-point. + Returns the number of bytes. If a codepoint is beyond 0x10FFFF the + return value is 3 and the codepoint is encoded as 0xFFFD. + No null byte is stored after the encoded bytes. + Return value is in range 1..4 + */ +static inline size_t utf8_encode(uint8_t buf[minimum_length(UTF8_CHAR_LEN_MAX)], uint32_t c) +{ + if (c < 0x80) { + buf[0] = c; + return 1; + } + if (c < 0x800) { + buf[0] = (c >> 6) | 0xC0; + buf[1] = (c & 0x3F) | 0x80; + return 2; + } + if (c < 0x10000) { + buf[0] = (c >> 12) | 0xE0; + buf[1] = ((c >> 6) & 0x3F) | 0x80; + buf[2] = (c & 0x3F) | 0x80; + return 3; + } + if (c < 0x110000) { + buf[0] = (c >> 18) | 0xF0; + buf[1] = ((c >> 12) & 0x3F) | 0x80; + buf[2] = ((c >> 6) & 0x3F) | 0x80; + buf[3] = (c & 0x3F) | 0x80; + return 4; + } + buf[0] = (0xFFFD >> 12) | 0xE0; + buf[1] = ((0xFFFD >> 6) & 0x3F) | 0x80; + buf[2] = (0xFFFD & 0x3F) | 0x80; + return 3; +} + +/* Decode a single code point from a UTF-8 encoded array of bytes + `p` is a valid pointer to an array of bytes + `pp` is a valid pointer to a `const uint8_t *` to store a pointer + to the byte following the current sequence. + Return the code point at `p`, in the range `0..0x10FFFF` + Return 0xFFFD on error. Only a single byte is consumed in this case + The maximum length for a UTF-8 byte sequence is 4 bytes. + This implements the algorithm specified in whatwg.org, except it accepts + UTF-8 encoded surrogates as JavaScript allows them in strings. + The source string is assumed to have at least UTF8_CHAR_LEN_MAX bytes + or be null terminated. + If `p[0]` is '\0', the return value is `0` and the byte is consumed. + cf: https://encoding.spec.whatwg.org/#utf-8-encoder + */ +static inline uint32_t utf8_decode(const uint8_t *p, const uint8_t **pp) +{ + uint32_t c; + uint8_t lower, upper; + + c = *p++; + if (c < 0x80) { + *pp = p; + return c; + } + switch(c) { + case 0xC2: case 0xC3: + case 0xC4: case 0xC5: case 0xC6: case 0xC7: + case 0xC8: case 0xC9: case 0xCA: case 0xCB: + case 0xCC: case 0xCD: case 0xCE: case 0xCF: + case 0xD0: case 0xD1: case 0xD2: case 0xD3: + case 0xD4: case 0xD5: case 0xD6: case 0xD7: + case 0xD8: case 0xD9: case 0xDA: case 0xDB: + case 0xDC: case 0xDD: case 0xDE: case 0xDF: + if (*p >= 0x80 && *p <= 0xBF) { + *pp = p + 1; + return ((c - 0xC0) << 6) + (*p - 0x80); + } + // otherwise encoding error + break; + case 0xE0: + lower = 0xA0; /* reject invalid encoding */ + goto need2; + case 0xE1: case 0xE2: case 0xE3: + case 0xE4: case 0xE5: case 0xE6: case 0xE7: + case 0xE8: case 0xE9: case 0xEA: case 0xEB: + case 0xEC: case 0xED: case 0xEE: case 0xEF: + lower = 0x80; + need2: + if (*p >= lower && *p <= 0xBF && p[1] >= 0x80 && p[1] <= 0xBF) { + *pp = p + 2; + return ((c - 0xE0) << 12) + ((*p - 0x80) << 6) + (p[1] - 0x80); + } + // otherwise encoding error + break; + case 0xF0: + lower = 0x90; /* reject invalid encoding */ + upper = 0xBF; + goto need3; + case 0xF4: + lower = 0x80; + upper = 0x8F; /* reject values above 0x10FFFF */ + goto need3; + case 0xF1: case 0xF2: case 0xF3: + lower = 0x80; + upper = 0xBF; + need3: + if (*p >= lower && *p <= upper && p[1] >= 0x80 && p[1] <= 0xBF + && p[2] >= 0x80 && p[2] <= 0xBF) { + *pp = p + 3; + return ((c - 0xF0) << 18) + ((*p - 0x80) << 12) + + ((p[1] - 0x80) << 6) + (p[2] - 0x80); + } + // otherwise encoding error + break; + default: + // invalid lead byte + break; + } + *pp = p; + return 0xFFFD; +} + +static inline uint32_t utf8_decode_len(const uint8_t *p, size_t max_len, const uint8_t **pp) { + switch (max_len) { + case 0: + *pp = p; + return 0xFFFD; + case 1: + if (*p < 0x80) + goto good; + break; + case 2: + if (*p < 0xE0) + goto good; + break; + case 3: + if (*p < 0xF0) + goto good; + break; + default: + good: + return utf8_decode(p, pp); + } + *pp = p + 1; + return 0xFFFD; +} + +/* Scan a UTF-8 encoded buffer for content type + `buf` is a valid pointer to a UTF-8 encoded string + `len` is the number of bytes to scan + `plen` points to a `size_t` variable to receive the number of units + Return value is a mask of bits. + - `UTF8_PLAIN_ASCII`: return value for 7-bit ASCII plain text + - `UTF8_NON_ASCII`: bit for non ASCII code points (8-bit or more) + - `UTF8_HAS_16BIT`: bit for 16-bit code points + - `UTF8_HAS_NON_BMP1`: bit for non-BMP1 code points, needs UTF-16 surrogate pairs + - `UTF8_HAS_ERRORS`: bit for encoding errors + */ +static inline int utf8_scan(const char *buf, size_t buf_len, size_t *plen) +{ + const uint8_t *p, *p_end, *p_next; + size_t i, len; + int kind; + uint8_t cbits; + + kind = UTF8_PLAIN_ASCII; + cbits = 0; + len = buf_len; + // TODO: handle more than 1 byte at a time + for (i = 0; i < buf_len; i++) + cbits |= buf[i]; + if (cbits >= 0x80) { + p = (const uint8_t *)buf; + p_end = p + buf_len; + kind = UTF8_NON_ASCII; + len = 0; + while (p < p_end) { + len++; + if (*p++ >= 0x80) { + /* parse UTF-8 sequence, check for encoding error */ + uint32_t c = utf8_decode_len(p - 1, p_end - (p - 1), &p_next); + if (p_next == p) + kind |= UTF8_HAS_ERRORS; + p = p_next; + if (c > 0xFF) { + kind |= UTF8_HAS_16BIT; + if (c > 0xFFFF) { + len++; + kind |= UTF8_HAS_NON_BMP1; + } + } + } + } + } + *plen = len; + return kind; +} + +/* Decode a string encoded in UTF-8 into an array of bytes + `src` points to the source string. It is assumed to be correctly encoded + and only contains code points below 0x800 + `src_len` is the length of the source string + `dest` points to the destination array, it can be null if `dest_len` is `0` + `dest_len` is the length of the destination array. A null + terminator is stored at the end of the array unless `dest_len` is `0`. + */ +static inline size_t utf8_decode_buf8(uint8_t *dest, size_t dest_len, const char *src, size_t src_len) +{ + const uint8_t *p, *p_end; + size_t i; + + p = (const uint8_t *)src; + p_end = p + src_len; + for (i = 0; p < p_end; i++) { + uint32_t c = *p++; + if (c >= 0xC0) + c = (c << 6) + *p++ - ((0xC0 << 6) + 0x80); + if (i < dest_len) + dest[i] = c; + } + if (i < dest_len) + dest[i] = '\0'; + else if (dest_len > 0) + dest[dest_len - 1] = '\0'; + return i; +} + +/* Decode a string encoded in UTF-8 into an array of 16-bit words + `src` points to the source string. It is assumed to be correctly encoded. + `src_len` is the length of the source string + `dest` points to the destination array, it can be null if `dest_len` is `0` + `dest_len` is the length of the destination array. No null terminator is + stored at the end of the array. + */ +static inline size_t utf8_decode_buf16(uint16_t *dest, size_t dest_len, const char *src, size_t src_len) +{ + const uint8_t *p, *p_end; + size_t i; + + p = (const uint8_t *)src; + p_end = p + src_len; + for (i = 0; p < p_end; i++) { + uint32_t c = *p++; + if (c >= 0x80) { + /* parse utf-8 sequence */ + c = utf8_decode_len(p - 1, p_end - (p - 1), &p); + /* encoding errors are converted as 0xFFFD and use a single byte */ + if (c > 0xFFFF) { + if (i < dest_len) + dest[i] = get_hi_surrogate(c); + i++; + c = get_lo_surrogate(c); + } + } + if (i < dest_len) + dest[i] = c; + } + return i; +} + +/* Encode a buffer of 8-bit bytes as a UTF-8 encoded string + `src` points to the source buffer. + `src_len` is the length of the source buffer + `dest` points to the destination array, it can be null if `dest_len` is `0` + `dest_len` is the length in bytes of the destination array. A null + terminator is stored at the end of the array unless `dest_len` is `0`. + */ +static inline size_t utf8_encode_buf8(char *dest, size_t dest_len, const uint8_t *src, size_t src_len) +{ + size_t i, j; + uint32_t c; + + for (i = j = 0; i < src_len; i++) { + c = src[i]; + if (c < 0x80) { + if (j + 1 >= dest_len) + goto overflow; + dest[j++] = c; + } else { + if (j + 2 >= dest_len) + goto overflow; + dest[j++] = (c >> 6) | 0xC0; + dest[j++] = (c & 0x3F) | 0x80; + } + } + if (j < dest_len) + dest[j] = '\0'; + return j; + +overflow: + if (j < dest_len) + dest[j] = '\0'; + while (i < src_len) + j += 1 + (src[i++] >= 0x80); + return j; +} + +/* Encode a buffer of 16-bit code points as a UTF-8 encoded string + `src` points to the source buffer. + `src_len` is the length of the source buffer + `dest` points to the destination array, it can be null if `dest_len` is `0` + `dest_len` is the length in bytes of the destination array. A null + terminator is stored at the end of the array unless `dest_len` is `0`. + */ +static inline size_t utf8_encode_buf16(char *dest, size_t dest_len, const uint16_t *src, size_t src_len) +{ + size_t i, j; + uint32_t c; + + for (i = j = 0; i < src_len;) { + c = src[i++]; + if (c < 0x80) { + if (j + 1 >= dest_len) + goto overflow; + dest[j++] = c; + } else { + if (is_hi_surrogate(c) && i < src_len && is_lo_surrogate(src[i])) + c = from_surrogate(c, src[i++]); + if (j + utf8_encode_len(c) >= dest_len) + goto overflow; + j += utf8_encode((uint8_t *)dest + j, c); + } + } + if (j < dest_len) + dest[j] = '\0'; + return j; + +overflow: + i -= 1 + (c > 0xFFFF); + if (j < dest_len) + dest[j] = '\0'; + while (i < src_len) { + c = src[i++]; + if (c < 0x80) { + j++; + } else { + if (is_hi_surrogate(c) && i < src_len && is_lo_surrogate(src[i])) + c = from_surrogate(c, src[i++]); + j += utf8_encode_len(c); + } + } + return j; +} + +/*---- sorting with opaque argument ----*/ + +typedef void (*exchange_f)(void *a, void *b, size_t size); +typedef int (*cmp_f)(const void *, const void *, void *opaque); + +static void exchange_bytes(void *a, void *b, size_t size) { + uint8_t *ap = (uint8_t *)a; + uint8_t *bp = (uint8_t *)b; + + while (size-- != 0) { + uint8_t t = *ap; + *ap++ = *bp; + *bp++ = t; + } +} + +static void exchange_one_byte(void *a, void *b, size_t size) { + uint8_t *ap = (uint8_t *)a; + uint8_t *bp = (uint8_t *)b; + uint8_t t = *ap; + *ap = *bp; + *bp = t; +} + +static void exchange_int16s(void *a, void *b, size_t size) { + uint16_t *ap = (uint16_t *)a; + uint16_t *bp = (uint16_t *)b; + + for (size /= sizeof(uint16_t); size-- != 0;) { + uint16_t t = *ap; + *ap++ = *bp; + *bp++ = t; + } +} + +static void exchange_one_int16(void *a, void *b, size_t size) { + uint16_t *ap = (uint16_t *)a; + uint16_t *bp = (uint16_t *)b; + uint16_t t = *ap; + *ap = *bp; + *bp = t; +} + +static void exchange_int32s(void *a, void *b, size_t size) { + uint32_t *ap = (uint32_t *)a; + uint32_t *bp = (uint32_t *)b; + + for (size /= sizeof(uint32_t); size-- != 0;) { + uint32_t t = *ap; + *ap++ = *bp; + *bp++ = t; + } +} + +static void exchange_one_int32(void *a, void *b, size_t size) { + uint32_t *ap = (uint32_t *)a; + uint32_t *bp = (uint32_t *)b; + uint32_t t = *ap; + *ap = *bp; + *bp = t; +} + +static void exchange_int64s(void *a, void *b, size_t size) { + uint64_t *ap = (uint64_t *)a; + uint64_t *bp = (uint64_t *)b; + + for (size /= sizeof(uint64_t); size-- != 0;) { + uint64_t t = *ap; + *ap++ = *bp; + *bp++ = t; + } +} + +static void exchange_one_int64(void *a, void *b, size_t size) { + uint64_t *ap = (uint64_t *)a; + uint64_t *bp = (uint64_t *)b; + uint64_t t = *ap; + *ap = *bp; + *bp = t; +} + +static void exchange_int128s(void *a, void *b, size_t size) { + uint64_t *ap = (uint64_t *)a; + uint64_t *bp = (uint64_t *)b; + + for (size /= sizeof(uint64_t) * 2; size-- != 0; ap += 2, bp += 2) { + uint64_t t = ap[0]; + uint64_t u = ap[1]; + ap[0] = bp[0]; + ap[1] = bp[1]; + bp[0] = t; + bp[1] = u; + } +} + +static void exchange_one_int128(void *a, void *b, size_t size) { + uint64_t *ap = (uint64_t *)a; + uint64_t *bp = (uint64_t *)b; + uint64_t t = ap[0]; + uint64_t u = ap[1]; + ap[0] = bp[0]; + ap[1] = bp[1]; + bp[0] = t; + bp[1] = u; +} + +static inline exchange_f exchange_func(const void *base, size_t size) { + switch (((uintptr_t)base | (uintptr_t)size) & 15) { + case 0: + if (size == sizeof(uint64_t) * 2) + return exchange_one_int128; + else + return exchange_int128s; + case 8: + if (size == sizeof(uint64_t)) + return exchange_one_int64; + else + return exchange_int64s; + case 4: + case 12: + if (size == sizeof(uint32_t)) + return exchange_one_int32; + else + return exchange_int32s; + case 2: + case 6: + case 10: + case 14: + if (size == sizeof(uint16_t)) + return exchange_one_int16; + else + return exchange_int16s; + default: + if (size == 1) + return exchange_one_byte; + else + return exchange_bytes; + } +} + +static void heapsortx(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque) +{ + uint8_t *basep = (uint8_t *)base; + size_t i, n, c, r; + exchange_f swap = exchange_func(base, size); + + if (nmemb > 1) { + i = (nmemb / 2) * size; + n = nmemb * size; + + while (i > 0) { + i -= size; + for (r = i; (c = r * 2 + size) < n; r = c) { + if (c < n - size && cmp(basep + c, basep + c + size, opaque) <= 0) + c += size; + if (cmp(basep + r, basep + c, opaque) > 0) + break; + swap(basep + r, basep + c, size); + } + } + for (i = n - size; i > 0; i -= size) { + swap(basep, basep + i, size); + + for (r = 0; (c = r * 2 + size) < i; r = c) { + if (c < i - size && cmp(basep + c, basep + c + size, opaque) <= 0) + c += size; + if (cmp(basep + r, basep + c, opaque) > 0) + break; + swap(basep + r, basep + c, size); + } + } + } +} + +static inline void *med3(void *a, void *b, void *c, cmp_f cmp, void *opaque) +{ + return cmp(a, b, opaque) < 0 ? + (cmp(b, c, opaque) < 0 ? b : (cmp(a, c, opaque) < 0 ? c : a )) : + (cmp(b, c, opaque) > 0 ? b : (cmp(a, c, opaque) < 0 ? a : c )); +} + +/* pointer based version with local stack and insertion sort threshhold */ +static inline void rqsort(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque) +{ + struct { uint8_t *base; size_t count; int depth; } stack[50], *sp = stack; + uint8_t *ptr, *pi, *pj, *plt, *pgt, *top, *m; + size_t m4, i, lt, gt, span, span2; + int c, depth; + exchange_f swap = exchange_func(base, size); + exchange_f swap_block = exchange_func(base, size | 128); + + if (nmemb < 2 || size <= 0) + return; + + sp->base = (uint8_t *)base; + sp->count = nmemb; + sp->depth = 0; + sp++; + + while (sp > stack) { + sp--; + ptr = sp->base; + nmemb = sp->count; + depth = sp->depth; + + while (nmemb > 6) { + if (++depth > 50) { + /* depth check to ensure worst case logarithmic time */ + heapsortx(ptr, nmemb, size, cmp, opaque); + nmemb = 0; + break; + } + /* select median of 3 from 1/4, 1/2, 3/4 positions */ + /* should use median of 5 or 9? */ + m4 = (nmemb >> 2) * size; + m = med3(ptr + m4, ptr + 2 * m4, ptr + 3 * m4, cmp, opaque); + swap(ptr, m, size); /* move the pivot to the start or the array */ + i = lt = 1; + pi = plt = ptr + size; + gt = nmemb; + pj = pgt = top = ptr + nmemb * size; + for (;;) { + while (pi < pj && (c = cmp(ptr, pi, opaque)) >= 0) { + if (c == 0) { + swap(plt, pi, size); + lt++; + plt += size; + } + i++; + pi += size; + } + while (pi < (pj -= size) && (c = cmp(ptr, pj, opaque)) <= 0) { + if (c == 0) { + gt--; + pgt -= size; + swap(pgt, pj, size); + } + } + if (pi >= pj) + break; + swap(pi, pj, size); + i++; + pi += size; + } + /* array has 4 parts: + * from 0 to lt excluded: elements identical to pivot + * from lt to pi excluded: elements smaller than pivot + * from pi to gt excluded: elements greater than pivot + * from gt to n excluded: elements identical to pivot + */ + /* move elements identical to pivot in the middle of the array: */ + /* swap values in ranges [0..lt[ and [i-lt..i[ + swapping the smallest span between lt and i-lt is sufficient + */ + span = plt - ptr; + span2 = pi - plt; + lt = i - lt; + if (span > span2) + span = span2; + swap_block(ptr, pi - span, span); + /* swap values in ranges [gt..top[ and [i..top-(top-gt)[ + swapping the smallest span between top-gt and gt-i is sufficient + */ + span = top - pgt; + span2 = pgt - pi; + pgt = top - span2; + gt = nmemb - (gt - i); + if (span > span2) + span = span2; + swap_block(pi, top - span, span); + + /* now array has 3 parts: + * from 0 to lt excluded: elements smaller than pivot + * from lt to gt excluded: elements identical to pivot + * from gt to n excluded: elements greater than pivot + */ + /* stack the larger segment and keep processing the smaller one + to minimize stack use for pathological distributions */ + if (lt > nmemb - gt) { + sp->base = ptr; + sp->count = lt; + sp->depth = depth; + sp++; + ptr = pgt; + nmemb -= gt; + } else { + sp->base = pgt; + sp->count = nmemb - gt; + sp->depth = depth; + sp++; + nmemb = lt; + } + } + /* Use insertion sort for small fragments */ + for (pi = ptr + size, top = ptr + nmemb * size; pi < top; pi += size) { + for (pj = pi; pj > ptr && cmp(pj - size, pj, opaque) > 0; pj -= size) + swap(pj, pj - size, size); + } + } +} + +/*---- Portable time functions ----*/ + +#ifdef _WIN32 + // From: https://stackoverflow.com/a/26085827 +static int gettimeofday_msvc(struct timeval *tp) +{ + static const uint64_t EPOCH = ((uint64_t)116444736000000000ULL); + + SYSTEMTIME system_time; + FILETIME file_time; + uint64_t time; + + GetSystemTime(&system_time); + SystemTimeToFileTime(&system_time, &file_time); + time = ((uint64_t)file_time.dwLowDateTime); + time += ((uint64_t)file_time.dwHighDateTime) << 32; + + tp->tv_sec = (long)((time - EPOCH) / 10000000L); + tp->tv_usec = (long)(system_time.wMilliseconds * 1000); + + return 0; +} + +static inline uint64_t js__hrtime_ns(void) { + LARGE_INTEGER counter, frequency; + double scaled_freq; + double result; + + if (!QueryPerformanceFrequency(&frequency)) + abort(); + assert(frequency.QuadPart != 0); + + if (!QueryPerformanceCounter(&counter)) + abort(); + assert(counter.QuadPart != 0); + + /* Because we have no guarantee about the order of magnitude of the + * performance counter interval, integer math could cause this computation + * to overflow. Therefore we resort to floating point math. + */ + scaled_freq = (double) frequency.QuadPart / NANOSEC; + result = (double) counter.QuadPart / scaled_freq; + return (uint64_t) result; +} +#else +static inline uint64_t js__hrtime_ns(void) { +#ifdef __DJGPP + struct timeval tv; + if (gettimeofday(&tv, NULL)) + abort(); + return tv.tv_sec * NANOSEC + tv.tv_usec * 1000; +#else + struct timespec t; + + if (clock_gettime(CLOCK_MONOTONIC, &t)) + abort(); + + return t.tv_sec * NANOSEC + t.tv_nsec; +#endif +} +#endif + +static inline int64_t js__gettimeofday_us(void) { + struct timeval tv; +#ifdef _WIN32 + gettimeofday_msvc(&tv); +#else + gettimeofday(&tv, NULL); +#endif + return ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec; +} + +#if defined(_WIN32) +static inline int js_exepath(char *buffer, size_t *size_ptr) { + int utf8_len, utf16_buffer_len, utf16_len; + WCHAR* utf16_buffer; + + if (buffer == NULL || size_ptr == NULL || *size_ptr == 0) + return -1; + + if (*size_ptr > 32768) { + /* Windows paths can never be longer than this. */ + utf16_buffer_len = 32768; + } else { + utf16_buffer_len = (int)*size_ptr; + } + + utf16_buffer = malloc(sizeof(WCHAR) * utf16_buffer_len); + if (!utf16_buffer) + return -1; + + /* Get the path as UTF-16. */ + utf16_len = GetModuleFileNameW(NULL, utf16_buffer, utf16_buffer_len); + if (utf16_len <= 0) + goto error; + + /* Convert to UTF-8 */ + utf8_len = WideCharToMultiByte(CP_UTF8, + 0, + utf16_buffer, + -1, + buffer, + (int)*size_ptr, + NULL, + NULL); + if (utf8_len == 0) + goto error; + + free(utf16_buffer); + + /* utf8_len *does* include the terminating null at this point, but the + * returned size shouldn't. */ + *size_ptr = utf8_len - 1; + return 0; + +error: + free(utf16_buffer); + return -1; +} +#elif defined(__APPLE__) +static inline int js_exepath(char *buffer, size_t *size) { + /* realpath(exepath) may be > PATH_MAX so double it to be on the safe side. */ + char abspath[PATH_MAX * 2 + 1]; + char exepath[PATH_MAX + 1]; + uint32_t exepath_size; + size_t abspath_size; + + if (buffer == NULL || size == NULL || *size == 0) + return -1; + + exepath_size = sizeof(exepath); + if (_NSGetExecutablePath(exepath, &exepath_size)) + return -1; + + if (realpath(exepath, abspath) != abspath) + return -1; + + abspath_size = strlen(abspath); + if (abspath_size == 0) + return -1; + + *size -= 1; + if (*size > abspath_size) + *size = abspath_size; + + memcpy(buffer, abspath, *size); + buffer[*size] = '\0'; + + return 0; +} +#elif defined(__linux__) || defined(__GNU__) +static inline int js_exepath(char *buffer, size_t *size) { + ssize_t n; + + if (buffer == NULL || size == NULL || *size == 0) + return -1; + + n = *size - 1; + if (n > 0) + n = readlink("/proc/self/exe", buffer, n); + + if (n == -1) + return n; + + buffer[n] = '\0'; + *size = n; + + return 0; +} +#else +static inline int js_exepath(char* buffer, size_t* size_ptr) { + return -1; +} +#endif + +/*--- Cross-platform threading APIs. ----*/ + +#if JS_HAVE_THREADS +#if defined(_WIN32) +typedef void (*js__once_cb)(void); + +typedef struct { + js__once_cb callback; +} js__once_data_t; + +static int WINAPI js__once_inner(INIT_ONCE *once, void *param, void **context) { + js__once_data_t *data = param; + + data->callback(); + + return 1; +} + +static inline void js_once(js_once_t *guard, js__once_cb callback) { + js__once_data_t data = { .callback = callback }; + InitOnceExecuteOnce(guard, js__once_inner, (void*) &data, NULL); +} + +static inline void js_mutex_init(js_mutex_t *mutex) { + InitializeCriticalSection(mutex); +} + +static inline void js_mutex_destroy(js_mutex_t *mutex) { + DeleteCriticalSection(mutex); +} + +static inline void js_mutex_lock(js_mutex_t *mutex) { + EnterCriticalSection(mutex); +} + +static inline void js_mutex_unlock(js_mutex_t *mutex) { + LeaveCriticalSection(mutex); +} + +static inline void js_cond_init(js_cond_t *cond) { + InitializeConditionVariable(cond); +} + +static inline void js_cond_destroy(js_cond_t *cond) { + /* nothing to do */ + (void) cond; +} + +static inline void js_cond_signal(js_cond_t *cond) { + WakeConditionVariable(cond); +} + +static inline void js_cond_broadcast(js_cond_t *cond) { + WakeAllConditionVariable(cond); +} + +static inline void js_cond_wait(js_cond_t *cond, js_mutex_t *mutex) { + if (!SleepConditionVariableCS(cond, mutex, INFINITE)) + abort(); +} + +static inline int js_cond_timedwait(js_cond_t *cond, js_mutex_t *mutex, uint64_t timeout) { + if (SleepConditionVariableCS(cond, mutex, (DWORD)(timeout / 1e6))) + return 0; + if (GetLastError() != ERROR_TIMEOUT) + abort(); + return -1; +} + +static inline int js_thread_create(js_thread_t *thrd, void (*start)(void *), void *arg, + int flags) +{ + HANDLE h, cp; + + *thrd = INVALID_HANDLE_VALUE; + if (flags & ~JS_THREAD_CREATE_DETACHED) + return -1; + h = (HANDLE)_beginthread(start, /*stacksize*/2<<20, arg); + if (!h) + return -1; + if (flags & JS_THREAD_CREATE_DETACHED) + return 0; + // _endthread() automatically closes the handle but we want to wait on + // it so make a copy. Race-y for very short-lived threads. Can be solved + // by switching to _beginthreadex(CREATE_SUSPENDED) but means changing + // |start| from __cdecl to __stdcall. + cp = GetCurrentProcess(); + if (DuplicateHandle(cp, h, cp, thrd, 0, FALSE, DUPLICATE_SAME_ACCESS)) + return 0; + return -1; +} + +static inline int js_thread_join(js_thread_t thrd) +{ + if (WaitForSingleObject(thrd, INFINITE)) + return -1; + CloseHandle(thrd); + return 0; +} + +#else /* !defined(_WIN32) */ + +static inline void js_once(js_once_t *guard, void (*callback)(void)) { + if (pthread_once(guard, callback)) + abort(); +} + +static inline void js_mutex_init(js_mutex_t *mutex) { + if (pthread_mutex_init(mutex, NULL)) + abort(); +} + +static inline void js_mutex_destroy(js_mutex_t *mutex) { + if (pthread_mutex_destroy(mutex)) + abort(); +} + +static inline void js_mutex_lock(js_mutex_t *mutex) { + if (pthread_mutex_lock(mutex)) + abort(); +} + +static inline void js_mutex_unlock(js_mutex_t *mutex) { + if (pthread_mutex_unlock(mutex)) + abort(); +} + +static inline void js_cond_init(js_cond_t *cond) { +#if defined(__APPLE__) && defined(__MACH__) + if (pthread_cond_init(cond, NULL)) + abort(); +#else + pthread_condattr_t attr; + + if (pthread_condattr_init(&attr)) + abort(); + + if (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC)) + abort(); + + if (pthread_cond_init(cond, &attr)) + abort(); + + if (pthread_condattr_destroy(&attr)) + abort(); +#endif +} + +static inline void js_cond_destroy(js_cond_t *cond) { +#if defined(__APPLE__) && defined(__MACH__) + /* It has been reported that destroying condition variables that have been + * signalled but not waited on can sometimes result in application crashes. + * See https://codereview.chromium.org/1323293005. + */ + pthread_mutex_t mutex; + struct timespec ts; + int err; + + if (pthread_mutex_init(&mutex, NULL)) + abort(); + + if (pthread_mutex_lock(&mutex)) + abort(); + + ts.tv_sec = 0; + ts.tv_nsec = 1; + + err = pthread_cond_timedwait_relative_np(cond, &mutex, &ts); + if (err != 0 && err != ETIMEDOUT) + abort(); + + if (pthread_mutex_unlock(&mutex)) + abort(); + + if (pthread_mutex_destroy(&mutex)) + abort(); +#endif /* defined(__APPLE__) && defined(__MACH__) */ + + if (pthread_cond_destroy(cond)) + abort(); +} + +static inline void js_cond_signal(js_cond_t *cond) { + if (pthread_cond_signal(cond)) + abort(); +} + +static inline void js_cond_broadcast(js_cond_t *cond) { + if (pthread_cond_broadcast(cond)) + abort(); +} + +static inline void js_cond_wait(js_cond_t *cond, js_mutex_t *mutex) { +#if defined(__APPLE__) && defined(__MACH__) + int r; + + errno = 0; + r = pthread_cond_wait(cond, mutex); + + /* Workaround for a bug in OS X at least up to 13.6 + * See https://github.com/libuv/libuv/issues/4165 + */ + if (r == EINVAL && errno == EBUSY) + return; + if (r) + abort(); +#else + if (pthread_cond_wait(cond, mutex)) + abort(); +#endif +} + +static inline int js_cond_timedwait(js_cond_t *cond, js_mutex_t *mutex, uint64_t timeout) { + int r; + struct timespec ts; + +#if !defined(__APPLE__) + timeout += js__hrtime_ns(); +#endif + + ts.tv_sec = timeout / NANOSEC; + ts.tv_nsec = timeout % NANOSEC; +#if defined(__APPLE__) && defined(__MACH__) + r = pthread_cond_timedwait_relative_np(cond, mutex, &ts); +#else + r = pthread_cond_timedwait(cond, mutex, &ts); +#endif + + if (r == 0) + return 0; + + if (r == ETIMEDOUT) + return -1; + + abort(); + + /* Pacify some compilers. */ + return -1; +} + +static inline int js_thread_create(js_thread_t *thrd, void (*start)(void *), void *arg, + int flags) +{ + union { + void (*x)(void *); + void *(*f)(void *); + } u = {start}; + pthread_attr_t attr; + int ret; + + if (flags & ~JS_THREAD_CREATE_DETACHED) + return -1; + if (pthread_attr_init(&attr)) + return -1; + ret = -1; + if (pthread_attr_setstacksize(&attr, 2<<20)) + goto fail; + if (flags & JS_THREAD_CREATE_DETACHED) + if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) + goto fail; + if (pthread_create(thrd, &attr, u.f, arg)) + goto fail; + ret = 0; +fail: + pthread_attr_destroy(&attr); + return ret; +} + +static inline int js_thread_join(js_thread_t thrd) +{ + if (pthread_join(thrd, NULL)) + return -1; + return 0; +} + +#endif /* !defined(_WIN32) */ +#endif /* JS_HAVE_THREADS */ + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif /* CUTILS_H */ +/* + * Tiny float64 printing and parsing library + * + * Copyright (c) 2024 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef DTOA_H +#define DTOA_H + +//#define JS_DTOA_DUMP_STATS + +/* maximum number of digits for fixed and frac formats */ +#define JS_DTOA_MAX_DIGITS 101 + +/* radix != 10 is only supported with flags = JS_DTOA_FORMAT_FREE */ +/* use as many digits as necessary */ +#define JS_DTOA_FORMAT_FREE (0 << 0) +/* use n_digits significant digits (1 <= n_digits <= JS_DTOA_MAX_DIGITS) */ +#define JS_DTOA_FORMAT_FIXED (1 << 0) +/* force fractional format: [-]dd.dd with n_digits fractional digits. + 0 <= n_digits <= JS_DTOA_MAX_DIGITS */ +#define JS_DTOA_FORMAT_FRAC (2 << 0) +#define JS_DTOA_FORMAT_MASK (3 << 0) + +/* select exponential notation either in fixed or free format */ +#define JS_DTOA_EXP_AUTO (0 << 2) +#define JS_DTOA_EXP_ENABLED (1 << 2) +#define JS_DTOA_EXP_DISABLED (2 << 2) +#define JS_DTOA_EXP_MASK (3 << 2) + +#define JS_DTOA_MINUS_ZERO (1 << 4) /* show the minus sign for -0 */ + +/* only accepts integers (no dot, no exponent) */ +#define JS_ATOD_INT_ONLY (1 << 0) +/* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */ +#define JS_ATOD_ACCEPT_BIN_OCT (1 << 1) +/* accept O prefix as octal if radix == 0 and properly formed (Annex B) */ +#define JS_ATOD_ACCEPT_LEGACY_OCTAL (1 << 2) +/* accept _ between digits as a digit separator */ +#define JS_ATOD_ACCEPT_UNDERSCORES (1 << 3) + +typedef struct { + uint64_t mem[37]; +} JSDTOATempMem; + +typedef struct { + uint64_t mem[27]; +} JSATODTempMem; + +/* return a maximum bound of the string length */ +int js_dtoa_max_len(double d, int radix, int n_digits, int flags); +/* return the string length */ +int js_dtoa(char *buf, double d, int radix, int n_digits, int flags, + JSDTOATempMem *tmp_mem); +double js_atod(const char *str, const char **pnext, int radix, int flags, + JSATODTempMem *tmp_mem); + +#ifdef JS_DTOA_DUMP_STATS +void js_dtoa_dump_stats(void); +#endif + +/* additional exported functions */ +size_t u32toa(char *buf, uint32_t n); +size_t i32toa(char *buf, int32_t n); +size_t u64toa(char *buf, uint64_t n); +size_t i64toa(char *buf, int64_t n); +size_t u64toa_radix(char *buf, uint64_t n, unsigned int radix); +size_t i64toa_radix(char *buf, int64_t n, unsigned int radix); + +#endif /* DTOA_H */ +/* + * Linux klist like system + * + * Copyright (c) 2016-2017 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef LIST_H +#define LIST_H + +#ifndef NULL +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +struct list_head { + struct list_head *prev; + struct list_head *next; +}; + +#define LIST_HEAD_INIT(el) { &(el), &(el) } + +/* return the pointer of type 'type *' containing 'el' as field 'member' */ +#define list_entry(el, type, member) container_of(el, type, member) + +static inline void init_list_head(struct list_head *head) +{ + head->prev = head; + head->next = head; +} + +/* insert 'el' between 'prev' and 'next' */ +static inline void __list_add(struct list_head *el, + struct list_head *prev, struct list_head *next) +{ + prev->next = el; + el->prev = prev; + el->next = next; + next->prev = el; +} + +/* add 'el' at the head of the list 'head' (= after element head) */ +static inline void list_add(struct list_head *el, struct list_head *head) +{ + __list_add(el, head, head->next); +} + +/* add 'el' at the end of the list 'head' (= before element head) */ +static inline void list_add_tail(struct list_head *el, struct list_head *head) +{ + __list_add(el, head->prev, head); +} + +static inline void list_del(struct list_head *el) +{ + struct list_head *prev, *next; + prev = el->prev; + next = el->next; + prev->next = next; + next->prev = prev; + el->prev = NULL; /* fail safe */ + el->next = NULL; /* fail safe */ +} + +static inline int list_empty(struct list_head *el) +{ + return el->next == el; +} + +#define list_for_each(el, head) \ + for(el = (head)->next; el != (head); el = el->next) + +#define list_for_each_safe(el, el1, head) \ + for(el = (head)->next, el1 = el->next; el != (head); \ + el = el1, el1 = el->next) + +#define list_for_each_prev(el, head) \ + for(el = (head)->prev; el != (head); el = el->prev) + +#define list_for_each_prev_safe(el, el1, head) \ + for(el = (head)->prev, el1 = el->prev; el != (head); \ + el = el1, el1 = el->prev) + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif /* LIST_H */ +/* + * Unicode utilities + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef LIBUNICODE_H +#define LIBUNICODE_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define LRE_CC_RES_LEN_MAX 3 + +typedef enum { + UNICODE_NFC, + UNICODE_NFD, + UNICODE_NFKC, + UNICODE_NFKD, +} UnicodeNormalizationEnum; + +int lre_case_conv(uint32_t *res, uint32_t c, int conv_type); +int lre_canonicalize(uint32_t c, bool is_unicode); +bool lre_is_cased(uint32_t c); +bool lre_is_case_ignorable(uint32_t c); + +/* char ranges */ + +typedef struct { + int len; /* in points, always even */ + int size; + uint32_t *points; /* points sorted by increasing value */ + void *mem_opaque; + void *(*realloc_func)(void *opaque, void *ptr, size_t size); +} CharRange; + +typedef enum { + CR_OP_UNION, + CR_OP_INTER, + CR_OP_XOR, +} CharRangeOpEnum; + +void cr_init(CharRange *cr, void *mem_opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size)); +void cr_free(CharRange *cr); +int cr_realloc(CharRange *cr, int size); +int cr_copy(CharRange *cr, const CharRange *cr1); + +static inline int cr_add_point(CharRange *cr, uint32_t v) +{ + if (cr->len >= cr->size) { + if (cr_realloc(cr, cr->len + 1)) + return -1; + } + cr->points[cr->len++] = v; + return 0; +} + +static inline int cr_add_interval(CharRange *cr, uint32_t c1, uint32_t c2) +{ + if ((cr->len + 2) > cr->size) { + if (cr_realloc(cr, cr->len + 2)) + return -1; + } + cr->points[cr->len++] = c1; + cr->points[cr->len++] = c2; + return 0; +} + +int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len); + +static inline int cr_union_interval(CharRange *cr, uint32_t c1, uint32_t c2) +{ + uint32_t b_pt[2]; + b_pt[0] = c1; + b_pt[1] = c2 + 1; + return cr_union1(cr, b_pt, 2); +} + +int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len, + const uint32_t *b_pt, int b_len, int op); + +int cr_invert(CharRange *cr); +int cr_regexp_canonicalize(CharRange *cr, bool is_unicode); + +bool lre_is_id_start(uint32_t c); +bool lre_is_id_continue(uint32_t c); +bool lre_is_white_space(uint32_t c); + +int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len, + UnicodeNormalizationEnum n_type, + void *opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size)); + +/* Unicode character range functions */ + +int unicode_script(CharRange *cr, + const char *script_name, bool is_ext); +int unicode_general_category(CharRange *cr, const char *gc_name); +int unicode_prop(CharRange *cr, const char *prop_name); + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif /* LIBUNICODE_H */ +/* + * Regular Expression Engine + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef LIBREGEXP_H +#define LIBREGEXP_H + +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + +#define LRE_FLAG_GLOBAL (1 << 0) +#define LRE_FLAG_IGNORECASE (1 << 1) +#define LRE_FLAG_MULTILINE (1 << 2) +#define LRE_FLAG_DOTALL (1 << 3) +#define LRE_FLAG_UNICODE (1 << 4) +#define LRE_FLAG_STICKY (1 << 5) +#define LRE_FLAG_INDICES (1 << 6) /* Unused by libregexp, just recorded. */ +#define LRE_FLAG_NAMED_GROUPS (1 << 7) /* named groups are present in the regexp */ +#define LRE_FLAG_UNICODE_SETS (1 << 8) + +#define LRE_RET_MEMORY_ERROR (-1) +#define LRE_RET_TIMEOUT (-2) + +uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size, + const char *buf, size_t buf_len, int re_flags, + void *opaque); +int lre_get_capture_count(const uint8_t *bc_buf); +int lre_get_flags(const uint8_t *bc_buf); +const char *lre_get_groupnames(const uint8_t *bc_buf); +int lre_exec(uint8_t **capture, + const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen, + int cbuf_type, void *opaque); + +int lre_parse_escape(const uint8_t **pp, int allow_utf16); +bool lre_is_space(int c); + +void lre_byte_swap(uint8_t *buf, size_t len, bool is_byte_swapped); + +/* must be provided by the user */ +bool lre_check_stack_overflow(void *opaque, size_t alloca_size); +/* must be provided by the user, return non zero if time out */ +int lre_check_timeout(void *opaque); +void *lre_realloc(void *opaque, void *ptr, size_t size); + +/* JS identifier test */ +extern uint32_t const lre_id_start_table_ascii[4]; +extern uint32_t const lre_id_continue_table_ascii[4]; + +static inline int lre_js_is_ident_first(int c) +{ + if ((uint32_t)c < 128) { + return (lre_id_start_table_ascii[c >> 5] >> (c & 31)) & 1; + } else { + return lre_is_id_start(c); + } +} + +static inline int lre_js_is_ident_next(int c) +{ + if ((uint32_t)c < 128) { + return (lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1; + } else { + /* ZWNJ and ZWJ are accepted in identifiers */ + return lre_is_id_continue(c) || c == 0x200C || c == 0x200D; + } +} + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif /* LIBREGEXP_H */ +/* Compressed unicode tables */ +/* Automatically generated file - do not edit */ + +#include + +static const uint32_t case_conv_table1[378] = { + 0x00209a30, 0x00309a00, 0x005a8173, 0x00601730, + 0x006c0730, 0x006f81b3, 0x00701700, 0x007c0700, + 0x007f8100, 0x00803040, 0x009801c3, 0x00988190, + 0x00990640, 0x009c9040, 0x00a481b4, 0x00a52e40, + 0x00bc0130, 0x00bc8640, 0x00bf8170, 0x00c00100, + 0x00c08130, 0x00c10440, 0x00c30130, 0x00c38240, + 0x00c48230, 0x00c58240, 0x00c70130, 0x00c78130, + 0x00c80130, 0x00c88240, 0x00c98130, 0x00ca0130, + 0x00ca8100, 0x00cb0130, 0x00cb8130, 0x00cc0240, + 0x00cd0100, 0x00cd8101, 0x00ce0130, 0x00ce8130, + 0x00cf0100, 0x00cf8130, 0x00d00640, 0x00d30130, + 0x00d38240, 0x00d48130, 0x00d60240, 0x00d70130, + 0x00d78240, 0x00d88230, 0x00d98440, 0x00db8130, + 0x00dc0240, 0x00de0240, 0x00df8100, 0x00e20350, + 0x00e38350, 0x00e50350, 0x00e69040, 0x00ee8100, + 0x00ef1240, 0x00f801b4, 0x00f88350, 0x00fa0240, + 0x00fb0130, 0x00fb8130, 0x00fc2840, 0x01100130, + 0x01111240, 0x011d0131, 0x011d8240, 0x011e8130, + 0x011f0131, 0x011f8201, 0x01208240, 0x01218130, + 0x01220130, 0x01228130, 0x01230a40, 0x01280101, + 0x01288101, 0x01290101, 0x01298100, 0x012a0100, + 0x012b0200, 0x012c8100, 0x012d8100, 0x012e0101, + 0x01300100, 0x01308101, 0x01318100, 0x01320101, + 0x01328101, 0x01330101, 0x01340100, 0x01348100, + 0x01350101, 0x01358101, 0x01360101, 0x01378100, + 0x01388101, 0x01390100, 0x013a8100, 0x013e8101, + 0x01400100, 0x01410101, 0x01418100, 0x01438101, + 0x01440100, 0x01448100, 0x01450200, 0x01460100, + 0x01490100, 0x014e8101, 0x014f0101, 0x01a28173, + 0x01b80440, 0x01bb0240, 0x01bd8300, 0x01bf8130, + 0x01c30130, 0x01c40330, 0x01c60130, 0x01c70230, + 0x01c801d0, 0x01c89130, 0x01d18930, 0x01d60100, + 0x01d68300, 0x01d801d3, 0x01d89100, 0x01e10173, + 0x01e18900, 0x01e60100, 0x01e68200, 0x01e78130, + 0x01e80173, 0x01e88173, 0x01ea8173, 0x01eb0173, + 0x01eb8100, 0x01ec1840, 0x01f80173, 0x01f88173, + 0x01f90100, 0x01f98100, 0x01fa01a0, 0x01fa8173, + 0x01fb8240, 0x01fc8130, 0x01fd0240, 0x01fe8330, + 0x02001030, 0x02082030, 0x02182000, 0x02281000, + 0x02302240, 0x02453640, 0x02600130, 0x02608e40, + 0x02678100, 0x02686040, 0x0298a630, 0x02b0a600, + 0x02c381b5, 0x08502631, 0x08638131, 0x08668131, + 0x08682b00, 0x087e8300, 0x09d05011, 0x09f80610, + 0x09fc0620, 0x0e400174, 0x0e408174, 0x0e410174, + 0x0e418174, 0x0e420174, 0x0e428174, 0x0e430174, + 0x0e438180, 0x0e440180, 0x0e448240, 0x0e482b30, + 0x0e5e8330, 0x0ebc8101, 0x0ebe8101, 0x0ec70101, + 0x0f007e40, 0x0f3f1840, 0x0f4b01b5, 0x0f4b81b6, + 0x0f4c01b6, 0x0f4c81b6, 0x0f4d01b7, 0x0f4d8180, + 0x0f4f0130, 0x0f506040, 0x0f800800, 0x0f840830, + 0x0f880600, 0x0f8c0630, 0x0f900800, 0x0f940830, + 0x0f980800, 0x0f9c0830, 0x0fa00600, 0x0fa40630, + 0x0fa801b0, 0x0fa88100, 0x0fa901d3, 0x0fa98100, + 0x0faa01d3, 0x0faa8100, 0x0fab01d3, 0x0fab8100, + 0x0fac8130, 0x0fad8130, 0x0fae8130, 0x0faf8130, + 0x0fb00800, 0x0fb40830, 0x0fb80200, 0x0fb90400, + 0x0fbb0201, 0x0fbc0201, 0x0fbd0201, 0x0fbe0201, + 0x0fc008b7, 0x0fc40867, 0x0fc808b8, 0x0fcc0868, + 0x0fd008b8, 0x0fd40868, 0x0fd80200, 0x0fd901b9, + 0x0fd981b1, 0x0fda01b9, 0x0fdb01b1, 0x0fdb81d7, + 0x0fdc0230, 0x0fdd0230, 0x0fde0161, 0x0fdf0173, + 0x0fe101b9, 0x0fe181b2, 0x0fe201ba, 0x0fe301b2, + 0x0fe381d8, 0x0fe40430, 0x0fe60162, 0x0fe80201, + 0x0fe901d0, 0x0fe981d0, 0x0feb01b0, 0x0feb81d0, + 0x0fec0230, 0x0fed0230, 0x0ff00201, 0x0ff101d3, + 0x0ff181d3, 0x0ff201ba, 0x0ff28101, 0x0ff301b0, + 0x0ff381d3, 0x0ff40231, 0x0ff50230, 0x0ff60131, + 0x0ff901ba, 0x0ff981b2, 0x0ffa01bb, 0x0ffb01b2, + 0x0ffb81d9, 0x0ffc0230, 0x0ffd0230, 0x0ffe0162, + 0x109301a0, 0x109501a0, 0x109581a0, 0x10990131, + 0x10a70101, 0x10b01031, 0x10b81001, 0x10c18240, + 0x125b1a31, 0x12681a01, 0x16003031, 0x16183001, + 0x16300240, 0x16310130, 0x16318130, 0x16320130, + 0x16328100, 0x16330100, 0x16338640, 0x16368130, + 0x16370130, 0x16378130, 0x16380130, 0x16390240, + 0x163a8240, 0x163f0230, 0x16406440, 0x16758440, + 0x16790240, 0x16802600, 0x16938100, 0x16968100, + 0x53202e40, 0x53401c40, 0x53910e40, 0x53993e40, + 0x53bc8440, 0x53be8130, 0x53bf0a40, 0x53c58240, + 0x53c68130, 0x53c80440, 0x53ca0101, 0x53cb1440, + 0x53d50130, 0x53d58130, 0x53d60130, 0x53d68130, + 0x53d70130, 0x53d80130, 0x53d88130, 0x53d90130, + 0x53d98131, 0x53da1040, 0x53e20131, 0x53e28130, + 0x53e30130, 0x53e38440, 0x53e58130, 0x53e61040, + 0x53ee0130, 0x53fa8240, 0x55a98101, 0x55b85020, + 0x7d8001b2, 0x7d8081b2, 0x7d8101b2, 0x7d8181da, + 0x7d8201da, 0x7d8281b3, 0x7d8301b3, 0x7d8981bb, + 0x7d8a01bb, 0x7d8a81bb, 0x7d8b01bc, 0x7d8b81bb, + 0x7f909a31, 0x7fa09a01, 0x82002831, 0x82142801, + 0x82582431, 0x826c2401, 0x82b80b31, 0x82be0f31, + 0x82c60731, 0x82ca0231, 0x82cb8b01, 0x82d18f01, + 0x82d98701, 0x82dd8201, 0x86403331, 0x86603301, + 0x86a81631, 0x86b81601, 0x8c502031, 0x8c602001, + 0xb7202031, 0xb7302001, 0xb7501931, 0xb75d9901, + 0xf4802231, 0xf4912201, +}; + +static const uint8_t case_conv_table2[378] = { + 0x01, 0x00, 0x9c, 0x06, 0x07, 0x4d, 0x03, 0x04, + 0x10, 0x00, 0x8f, 0x0b, 0x00, 0x00, 0x11, 0x00, + 0x08, 0x00, 0x53, 0x4b, 0x52, 0x00, 0x53, 0x00, + 0x54, 0x00, 0x3b, 0x55, 0x56, 0x00, 0x58, 0x5a, + 0x40, 0x5f, 0x5e, 0x00, 0x47, 0x50, 0x63, 0x65, + 0x43, 0x66, 0x00, 0x68, 0x00, 0x6a, 0x00, 0x6c, + 0x00, 0x6e, 0x00, 0x70, 0x00, 0x00, 0x41, 0x00, + 0x00, 0x00, 0x00, 0x1a, 0x00, 0x93, 0x00, 0x00, + 0x20, 0x36, 0x00, 0x28, 0x00, 0x24, 0x00, 0x24, + 0x25, 0x2d, 0x00, 0x13, 0x6d, 0x6f, 0x00, 0x29, + 0x27, 0x2a, 0x14, 0x16, 0x18, 0x1b, 0x1c, 0x41, + 0x1e, 0x42, 0x1f, 0x4e, 0x3c, 0x40, 0x22, 0x21, + 0x44, 0x21, 0x43, 0x26, 0x28, 0x27, 0x29, 0x23, + 0x2b, 0x4b, 0x2d, 0x46, 0x2f, 0x4c, 0x31, 0x4d, + 0x33, 0x47, 0x45, 0x99, 0x00, 0x00, 0x97, 0x91, + 0x7f, 0x80, 0x85, 0x86, 0x12, 0x82, 0x84, 0x78, + 0x79, 0x12, 0x7d, 0xa3, 0x7e, 0x7a, 0x7b, 0x8c, + 0x92, 0x98, 0xa6, 0xa0, 0x87, 0x00, 0x9a, 0xa1, + 0x95, 0x77, 0x33, 0x95, 0x00, 0x90, 0x00, 0x76, + 0x9b, 0x9a, 0x99, 0x98, 0x00, 0x00, 0xa0, 0x00, + 0x9e, 0x00, 0xa3, 0xa2, 0x15, 0x31, 0x32, 0x33, + 0xb7, 0xb8, 0x53, 0xac, 0xab, 0x12, 0x14, 0x1e, + 0x21, 0x22, 0x22, 0x2a, 0x34, 0x35, 0x00, 0xa8, + 0xa9, 0x39, 0x22, 0x4c, 0x00, 0x00, 0x97, 0x01, + 0x5a, 0xda, 0x1d, 0x36, 0x05, 0x00, 0xc7, 0xc6, + 0xc9, 0xc8, 0xcb, 0xca, 0xcd, 0xcc, 0xcf, 0xce, + 0xc4, 0xd8, 0x45, 0xd9, 0x42, 0xda, 0x46, 0xdb, + 0xd1, 0xd3, 0xd5, 0xd7, 0xdd, 0xdc, 0xf1, 0xf9, + 0x01, 0x11, 0x0a, 0x12, 0x80, 0x9f, 0x00, 0x21, + 0x80, 0xa3, 0xf0, 0x00, 0xc0, 0x40, 0xc6, 0x60, + 0xea, 0xde, 0xe6, 0x99, 0xc0, 0x00, 0x00, 0x06, + 0x60, 0xdf, 0x29, 0x00, 0x15, 0x12, 0x06, 0x16, + 0xfb, 0xe0, 0x09, 0x15, 0x12, 0x84, 0x0b, 0xc6, + 0x16, 0x02, 0xe2, 0x06, 0xc0, 0x40, 0x00, 0x46, + 0x60, 0xe1, 0xe3, 0x6d, 0x37, 0x38, 0x39, 0x18, + 0x17, 0x1a, 0x19, 0x00, 0x1d, 0x1c, 0x1f, 0x1e, + 0x00, 0x61, 0xba, 0x67, 0x45, 0x48, 0x00, 0x50, + 0x64, 0x4f, 0x51, 0x00, 0x00, 0x49, 0x00, 0x00, + 0x00, 0xa5, 0xa6, 0xa7, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xb9, 0x00, 0x00, 0x5c, 0x00, 0x4a, 0x00, + 0x5d, 0x57, 0x59, 0x62, 0x60, 0x72, 0x6b, 0x71, + 0x52, 0x00, 0x3e, 0x69, 0xbb, 0x00, 0x5b, 0x00, + 0x25, 0x00, 0x48, 0xaa, 0x8a, 0x8b, 0x8c, 0xab, + 0xac, 0x58, 0x58, 0xaf, 0x94, 0xb0, 0x6f, 0xb2, + 0x61, 0x60, 0x63, 0x62, 0x65, 0x64, 0x6a, 0x6b, + 0x6c, 0x6d, 0x66, 0x67, 0x68, 0x69, 0x6f, 0x6e, + 0x71, 0x70, 0x73, 0x72, 0x75, 0x74, 0x77, 0x76, + 0x79, 0x78, +}; + +static const uint16_t case_conv_ext[58] = { + 0x0399, 0x0308, 0x0301, 0x03a5, 0x0313, 0x0300, 0x0342, 0x0391, + 0x0397, 0x03a9, 0x0046, 0x0049, 0x004c, 0x0053, 0x0069, 0x0307, + 0x02bc, 0x004e, 0x004a, 0x030c, 0x0535, 0x0552, 0x0048, 0x0331, + 0x0054, 0x0057, 0x030a, 0x0059, 0x0041, 0x02be, 0x1f08, 0x1f80, + 0x1f28, 0x1f90, 0x1f68, 0x1fa0, 0x1fba, 0x0386, 0x1fb3, 0x1fca, + 0x0389, 0x1fc3, 0x03a1, 0x1ffa, 0x038f, 0x1ff3, 0x0544, 0x0546, + 0x053b, 0x054e, 0x053d, 0x03b8, 0x0462, 0xa64a, 0x1e60, 0x03c9, + 0x006b, 0x00e5, +}; + +static const uint8_t unicode_prop_Cased1_table[190] = { + 0x40, 0xa9, 0x80, 0x8e, 0x80, 0xfc, 0x80, 0xd3, + 0x80, 0x9b, 0x81, 0x8d, 0x02, 0x80, 0xe1, 0x80, + 0x91, 0x85, 0x9a, 0x01, 0x00, 0x01, 0x11, 0x03, + 0x04, 0x08, 0x01, 0x08, 0x30, 0x08, 0x01, 0x15, + 0x20, 0x01, 0x31, 0x99, 0x31, 0x9d, 0x84, 0x40, + 0x94, 0x80, 0xd6, 0x82, 0xa6, 0x80, 0x41, 0x62, + 0x80, 0xa6, 0x80, 0x4b, 0x72, 0x80, 0x4c, 0x02, + 0xf8, 0x02, 0x80, 0x8f, 0x80, 0xb0, 0x40, 0xdb, + 0x08, 0x80, 0x41, 0xd0, 0x80, 0x8c, 0x80, 0x8f, + 0x8c, 0xe4, 0x03, 0x01, 0x89, 0x00, 0x14, 0x28, + 0x10, 0x11, 0x02, 0x01, 0x18, 0x0b, 0x24, 0x4b, + 0x26, 0x01, 0x01, 0x86, 0xe5, 0x80, 0x60, 0x79, + 0xb6, 0x81, 0x40, 0x91, 0x81, 0xbd, 0x88, 0x94, + 0x05, 0x80, 0x98, 0x80, 0xc0, 0x1a, 0x82, 0x43, + 0x34, 0xa2, 0x06, 0x80, 0x8d, 0x60, 0x5c, 0x15, + 0x01, 0x10, 0xa9, 0x80, 0x88, 0x60, 0xcc, 0x44, + 0xd4, 0x80, 0xc6, 0x01, 0x08, 0x09, 0x0b, 0x80, + 0x8b, 0x00, 0x06, 0x80, 0xc0, 0x03, 0x0f, 0x06, + 0x80, 0x9b, 0x03, 0x04, 0x00, 0x16, 0x80, 0x41, + 0x53, 0x81, 0x98, 0x80, 0x98, 0x80, 0x9e, 0x80, + 0x98, 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, 0x80, + 0x98, 0x80, 0x9e, 0x80, 0x98, 0x07, 0x47, 0x33, + 0x89, 0x80, 0x93, 0x2d, 0x41, 0x04, 0xbd, 0x50, + 0xc1, 0x99, 0x85, 0x99, 0x85, 0x99, +}; + +static const uint8_t unicode_prop_Cased1_index[18] = { + 0xb9, 0x02, 0x80, 0xa0, 0x1e, 0x40, 0x9e, 0xa6, + 0x40, 0x55, 0xd4, 0x21, 0x15, 0xd7, 0x21, 0x8a, + 0xf1, 0x01, +}; + +static const uint8_t unicode_prop_Case_Ignorable_table[785] = { + 0xa6, 0x05, 0x80, 0x8a, 0x80, 0xa2, 0x00, 0x80, + 0xc6, 0x03, 0x00, 0x03, 0x01, 0x81, 0x41, 0xf6, + 0x40, 0xbf, 0x19, 0x18, 0x88, 0x08, 0x80, 0x40, + 0xfa, 0x86, 0x40, 0xce, 0x04, 0x80, 0xb0, 0xac, + 0x00, 0x01, 0x01, 0x00, 0xab, 0x80, 0x8a, 0x85, + 0x89, 0x8a, 0x00, 0xa2, 0x80, 0x89, 0x94, 0x8f, + 0x80, 0xe4, 0x38, 0x89, 0x03, 0xa0, 0x00, 0x80, + 0x9d, 0x9a, 0xda, 0x8a, 0xb9, 0x8a, 0x18, 0x08, + 0x97, 0x97, 0xaa, 0x82, 0xab, 0x06, 0x0c, 0x88, + 0xa8, 0xb9, 0xb6, 0x00, 0x03, 0x3b, 0x02, 0x86, + 0x89, 0x81, 0x8c, 0x80, 0x8e, 0x80, 0xb9, 0x03, + 0x1f, 0x80, 0x93, 0x81, 0x99, 0x01, 0x81, 0xb8, + 0x03, 0x0b, 0x09, 0x12, 0x80, 0x9d, 0x0a, 0x80, + 0x8a, 0x81, 0xb8, 0x03, 0x20, 0x0b, 0x80, 0x93, + 0x81, 0x95, 0x28, 0x80, 0xb9, 0x01, 0x00, 0x1f, + 0x06, 0x81, 0x8a, 0x81, 0x9d, 0x80, 0xbc, 0x80, + 0x8b, 0x80, 0xb1, 0x02, 0x80, 0xb6, 0x00, 0x14, + 0x10, 0x1e, 0x81, 0x8a, 0x81, 0x9c, 0x80, 0xb9, + 0x01, 0x05, 0x04, 0x81, 0x93, 0x81, 0x9b, 0x81, + 0xb8, 0x0b, 0x1f, 0x80, 0x93, 0x81, 0x9c, 0x80, + 0xc7, 0x06, 0x10, 0x80, 0xd9, 0x01, 0x86, 0x8a, + 0x88, 0xe1, 0x01, 0x88, 0x88, 0x00, 0x86, 0xc8, + 0x81, 0x9a, 0x00, 0x00, 0x80, 0xb6, 0x8d, 0x04, + 0x01, 0x84, 0x8a, 0x80, 0xa3, 0x88, 0x80, 0xe5, + 0x18, 0x28, 0x09, 0x81, 0x98, 0x0b, 0x82, 0x8f, + 0x83, 0x8c, 0x01, 0x0d, 0x80, 0x8e, 0x80, 0xdd, + 0x80, 0x42, 0x5f, 0x82, 0x43, 0xb1, 0x82, 0x9c, + 0x81, 0x9d, 0x81, 0x9d, 0x81, 0xbf, 0x08, 0x37, + 0x01, 0x8a, 0x10, 0x20, 0xac, 0x84, 0xb2, 0x80, + 0xc0, 0x81, 0xa1, 0x80, 0xf5, 0x13, 0x81, 0x88, + 0x05, 0x82, 0x40, 0xda, 0x09, 0x80, 0xb9, 0x00, + 0x30, 0x00, 0x01, 0x3d, 0x89, 0x08, 0xa6, 0x07, + 0xad, 0x81, 0x8b, 0x93, 0x83, 0xaf, 0x00, 0x20, + 0x04, 0x80, 0xa7, 0x88, 0x8b, 0x81, 0x9f, 0x19, + 0x08, 0x82, 0xb7, 0x00, 0x0a, 0x00, 0x82, 0xb9, + 0x39, 0x81, 0xbf, 0x85, 0xd1, 0x10, 0x8c, 0x06, + 0x18, 0x28, 0x11, 0xb1, 0xbe, 0x8c, 0x80, 0xa1, + 0xe4, 0x41, 0xbc, 0x00, 0x82, 0x8a, 0x82, 0x8c, + 0x82, 0x8c, 0x82, 0x8c, 0x81, 0x8b, 0x27, 0x81, + 0x89, 0x01, 0x01, 0x84, 0xb0, 0x20, 0x89, 0x00, + 0x8c, 0x80, 0x8f, 0x8c, 0xb2, 0xa0, 0x4b, 0x8a, + 0x81, 0xf0, 0x82, 0xfc, 0x80, 0x8e, 0x80, 0xdf, + 0x9f, 0xae, 0x80, 0x41, 0xd4, 0x80, 0xa3, 0x1a, + 0x24, 0x80, 0xdc, 0x85, 0xdc, 0x82, 0x60, 0x6f, + 0x15, 0x80, 0x44, 0xe1, 0x85, 0x41, 0x0d, 0x80, + 0xe1, 0x18, 0x89, 0x00, 0x9b, 0x83, 0xcf, 0x81, + 0x8d, 0xa1, 0xcd, 0x80, 0x96, 0x82, 0xe5, 0x1a, + 0x0f, 0x02, 0x03, 0x80, 0x98, 0x0c, 0x80, 0x40, + 0x96, 0x81, 0x99, 0x91, 0x8c, 0x80, 0xa5, 0x87, + 0x98, 0x8a, 0xad, 0x82, 0xaf, 0x01, 0x19, 0x81, + 0x90, 0x80, 0x94, 0x81, 0xc1, 0x29, 0x09, 0x81, + 0x8b, 0x07, 0x80, 0xa2, 0x80, 0x8a, 0x80, 0xb2, + 0x00, 0x11, 0x0c, 0x08, 0x80, 0x9a, 0x80, 0x8d, + 0x0c, 0x08, 0x80, 0xe3, 0x84, 0x88, 0x82, 0xf8, + 0x01, 0x03, 0x80, 0x60, 0x4f, 0x2f, 0x80, 0x40, + 0x92, 0x90, 0x42, 0x3c, 0x8f, 0x10, 0x8b, 0x8f, + 0xa1, 0x01, 0x80, 0x40, 0xa8, 0x06, 0x05, 0x80, + 0x8a, 0x80, 0xa2, 0x00, 0x80, 0xae, 0x80, 0xac, + 0x81, 0xc2, 0x80, 0x94, 0x82, 0x42, 0x00, 0x80, + 0x40, 0xe1, 0x80, 0x40, 0x94, 0x84, 0x44, 0x04, + 0x28, 0xa9, 0x80, 0x88, 0x42, 0x45, 0x10, 0x0c, + 0x83, 0xa7, 0x13, 0x80, 0x40, 0xa4, 0x81, 0x42, + 0x3c, 0x83, 0xa5, 0x80, 0x99, 0x20, 0x80, 0x41, + 0x3a, 0x81, 0x97, 0x80, 0xb3, 0x85, 0xc5, 0x8a, + 0xb0, 0x83, 0xfa, 0x80, 0xb5, 0x8e, 0xa8, 0x01, + 0x81, 0x89, 0x82, 0xb0, 0x19, 0x09, 0x03, 0x80, + 0x89, 0x80, 0xb1, 0x82, 0xa3, 0x20, 0x87, 0xbd, + 0x80, 0x8b, 0x81, 0xb3, 0x88, 0x89, 0x19, 0x80, + 0xde, 0x11, 0x00, 0x0d, 0x01, 0x80, 0x40, 0x9c, + 0x02, 0x87, 0x94, 0x81, 0xb8, 0x0a, 0x80, 0xa4, + 0x32, 0x84, 0xc5, 0x85, 0x8c, 0x00, 0x00, 0x80, + 0x8d, 0x81, 0xd4, 0x39, 0x10, 0x80, 0x96, 0x80, + 0xd3, 0x28, 0x03, 0x08, 0x81, 0x40, 0xed, 0x1d, + 0x08, 0x81, 0x9a, 0x81, 0xd4, 0x39, 0x00, 0x81, + 0xe9, 0x00, 0x01, 0x28, 0x80, 0xe4, 0x00, 0x01, + 0x18, 0x84, 0x41, 0x02, 0x88, 0x01, 0x40, 0xff, + 0x08, 0x03, 0x80, 0x40, 0x8f, 0x19, 0x0b, 0x80, + 0x9f, 0x89, 0xa7, 0x29, 0x1f, 0x80, 0x88, 0x29, + 0x82, 0xad, 0x8c, 0x01, 0x40, 0xc5, 0x00, 0x10, + 0x80, 0x40, 0xc8, 0x30, 0x28, 0x80, 0xd1, 0x95, + 0x0e, 0x01, 0x01, 0xf9, 0x2a, 0x00, 0x08, 0x30, + 0x80, 0xc7, 0x0a, 0x00, 0x80, 0xc0, 0x80, 0x41, + 0x18, 0x81, 0x8a, 0x81, 0xb3, 0x24, 0x00, 0x80, + 0x96, 0x80, 0x54, 0xd4, 0x90, 0x85, 0x8e, 0x60, + 0x2c, 0xc7, 0x8b, 0x12, 0x49, 0xbf, 0x84, 0xba, + 0x86, 0x88, 0x83, 0x41, 0xfb, 0x82, 0xa7, 0x81, + 0x41, 0xe1, 0x80, 0xbe, 0x90, 0xbf, 0x08, 0x81, + 0x8c, 0x81, 0x60, 0x3f, 0xfb, 0x18, 0x30, 0x81, + 0x4c, 0x9d, 0x08, 0x83, 0x52, 0x5b, 0xad, 0x81, + 0x96, 0x42, 0x1f, 0x82, 0x88, 0x8f, 0x0e, 0x9d, + 0x83, 0x40, 0x93, 0x82, 0x47, 0xba, 0xb6, 0x83, + 0xb1, 0x38, 0x8d, 0x80, 0x95, 0x20, 0x8e, 0x45, + 0x4f, 0x30, 0x90, 0x0e, 0x01, 0x04, 0x84, 0xbd, + 0xa0, 0x80, 0x40, 0x9f, 0x8d, 0x41, 0x6f, 0x80, + 0xbc, 0x83, 0x41, 0xfa, 0x84, 0x40, 0xfd, 0x81, + 0x40, 0xf2, 0x01, 0x06, 0x0c, 0x80, 0x88, 0x80, + 0x41, 0xcf, 0x86, 0xec, 0x87, 0x4a, 0xae, 0x84, + 0x6c, 0x0c, 0x00, 0x80, 0x9d, 0xdf, 0xff, 0x40, + 0xef, +}; + +static const uint8_t unicode_prop_Case_Ignorable_index[75] = { + 0xbe, 0x05, 0x00, 0xfe, 0x07, 0x00, 0x52, 0x0a, + 0xa0, 0xc1, 0x0b, 0x00, 0x82, 0x0d, 0x00, 0x3f, + 0x10, 0x80, 0xd4, 0x17, 0x40, 0xde, 0x1a, 0x20, + 0xe9, 0x1c, 0x00, 0x72, 0x20, 0x00, 0x16, 0xa0, + 0x40, 0xc6, 0xa8, 0x40, 0xc2, 0xaa, 0xa0, 0x30, + 0xfe, 0x00, 0xb1, 0x07, 0x41, 0x51, 0x0f, 0x01, + 0xd0, 0x11, 0x01, 0x5f, 0x14, 0x01, 0x44, 0x19, + 0x61, 0xa8, 0x1c, 0x01, 0x2a, 0x61, 0x61, 0xff, + 0xaf, 0x01, 0x19, 0xe0, 0x61, 0x00, 0xe7, 0x01, + 0xf0, 0x01, 0x0e, +}; + +static const uint8_t unicode_prop_ID_Start_table[1146] = { + 0xc0, 0x99, 0x85, 0x99, 0xae, 0x80, 0x89, 0x03, + 0x04, 0x96, 0x80, 0x9e, 0x80, 0x41, 0xc9, 0x83, + 0x8b, 0x8d, 0x26, 0x00, 0x80, 0x40, 0x80, 0x20, + 0x09, 0x18, 0x05, 0x00, 0x10, 0x00, 0x93, 0x80, + 0xd2, 0x80, 0x40, 0x8a, 0x87, 0x40, 0xa5, 0x80, + 0xa5, 0x08, 0x85, 0xa8, 0xc6, 0x9a, 0x1b, 0xac, + 0xaa, 0xa2, 0x08, 0xe2, 0x00, 0x8e, 0x0e, 0x81, + 0x89, 0x11, 0x80, 0x8f, 0x00, 0x9d, 0x9c, 0xd8, + 0x8a, 0x80, 0x97, 0xa0, 0x88, 0x0b, 0x04, 0x95, + 0x18, 0x88, 0x02, 0x80, 0x96, 0x98, 0x86, 0x8a, + 0x84, 0x97, 0x06, 0x8f, 0xa9, 0xb9, 0xb5, 0x10, + 0x91, 0x06, 0x89, 0x8e, 0x8f, 0x1f, 0x09, 0x81, + 0x95, 0x06, 0x00, 0x13, 0x10, 0x8f, 0x80, 0x8c, + 0x08, 0x82, 0x8d, 0x81, 0x89, 0x07, 0x2b, 0x09, + 0x95, 0x06, 0x01, 0x01, 0x01, 0x9e, 0x18, 0x80, + 0x92, 0x82, 0x8f, 0x88, 0x02, 0x80, 0x95, 0x06, + 0x01, 0x04, 0x10, 0x91, 0x80, 0x8e, 0x81, 0x96, + 0x80, 0x8a, 0x39, 0x09, 0x95, 0x06, 0x01, 0x04, + 0x10, 0x9d, 0x08, 0x82, 0x8e, 0x80, 0x90, 0x00, + 0x2a, 0x10, 0x1a, 0x08, 0x00, 0x0a, 0x0a, 0x12, + 0x8b, 0x95, 0x80, 0xb3, 0x38, 0x10, 0x96, 0x80, + 0x8f, 0x10, 0x99, 0x10, 0x09, 0x81, 0x9d, 0x03, + 0x38, 0x10, 0x96, 0x80, 0x89, 0x04, 0x10, 0x9d, + 0x10, 0x81, 0x8e, 0x81, 0x90, 0x88, 0x02, 0x80, + 0xa8, 0x08, 0x8f, 0x04, 0x17, 0x82, 0x97, 0x2c, + 0x91, 0x82, 0x97, 0x80, 0x88, 0x00, 0x0e, 0xb9, + 0xaf, 0x01, 0x8b, 0x86, 0xb9, 0x08, 0x00, 0x20, + 0x97, 0x00, 0x80, 0x89, 0x01, 0x88, 0x01, 0x20, + 0x80, 0x94, 0x83, 0x9f, 0x80, 0xbe, 0x38, 0xa3, + 0x9a, 0x84, 0xf2, 0xaa, 0x93, 0x80, 0x8f, 0x2b, + 0x1a, 0x02, 0x0e, 0x13, 0x8c, 0x8b, 0x80, 0x90, + 0xa5, 0x00, 0x20, 0x81, 0xaa, 0x80, 0x41, 0x4c, + 0x03, 0x0e, 0x00, 0x03, 0x81, 0xa8, 0x03, 0x81, + 0xa0, 0x03, 0x0e, 0x00, 0x03, 0x81, 0x8e, 0x80, + 0xb8, 0x03, 0x81, 0xc2, 0xa4, 0x8f, 0x8f, 0xd5, + 0x0d, 0x82, 0x42, 0x6b, 0x81, 0x90, 0x80, 0x99, + 0x84, 0xca, 0x82, 0x8a, 0x86, 0x91, 0x8c, 0x92, + 0x8d, 0x91, 0x8d, 0x8c, 0x02, 0x8e, 0xb3, 0xa2, + 0x03, 0x80, 0xc2, 0xd8, 0x86, 0xa8, 0x00, 0x84, + 0xc5, 0x89, 0x9e, 0xb0, 0x9d, 0x0c, 0x8a, 0xab, + 0x83, 0x99, 0xb5, 0x96, 0x88, 0xb4, 0xd1, 0x80, + 0xdc, 0xae, 0x90, 0x87, 0xb5, 0x9d, 0x8c, 0x81, + 0x89, 0xab, 0x99, 0xa3, 0xa8, 0x82, 0x89, 0xa3, + 0x81, 0x8a, 0x84, 0xaa, 0x0a, 0xa8, 0x18, 0x28, + 0x0a, 0x04, 0x40, 0xbf, 0xbf, 0x41, 0x15, 0x0d, + 0x81, 0xa5, 0x0d, 0x0f, 0x00, 0x00, 0x00, 0x80, + 0x9e, 0x81, 0xb4, 0x06, 0x00, 0x12, 0x06, 0x13, + 0x0d, 0x83, 0x8c, 0x22, 0x06, 0xf3, 0x80, 0x8c, + 0x80, 0x8f, 0x8c, 0xe4, 0x03, 0x01, 0x89, 0x00, + 0x0d, 0x28, 0x00, 0x00, 0x80, 0x8f, 0x0b, 0x24, + 0x18, 0x90, 0xa8, 0x4a, 0x76, 0x40, 0xe4, 0x2b, + 0x11, 0x8b, 0xa5, 0x00, 0x20, 0x81, 0xb7, 0x30, + 0x8f, 0x96, 0x88, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x86, 0x42, 0x25, 0x82, 0x98, 0x88, + 0x34, 0x0c, 0x83, 0xd5, 0x1c, 0x80, 0xd9, 0x03, + 0x84, 0xaa, 0x80, 0xdd, 0x90, 0x9f, 0xaf, 0x8f, + 0x41, 0xff, 0x59, 0xbf, 0xbf, 0x60, 0x56, 0x8c, + 0xc2, 0xad, 0x81, 0x41, 0x0c, 0x82, 0x8f, 0x89, + 0x81, 0x93, 0xae, 0x8f, 0x9e, 0x81, 0xcf, 0xa6, + 0x88, 0x81, 0xe6, 0x81, 0xd1, 0x93, 0x90, 0x02, + 0x03, 0x80, 0x96, 0x9c, 0xb3, 0x8d, 0xb1, 0xbd, + 0x2a, 0x00, 0x81, 0x8a, 0x9b, 0x89, 0x96, 0x98, + 0x9c, 0x86, 0xae, 0x9b, 0x80, 0x8f, 0x20, 0x89, + 0x89, 0x20, 0xa8, 0x96, 0x10, 0x87, 0x93, 0x96, + 0x10, 0x82, 0xb1, 0x00, 0x11, 0x0c, 0x08, 0x00, + 0x97, 0x11, 0x8a, 0x32, 0x8b, 0x29, 0x29, 0x85, + 0x88, 0x30, 0x30, 0xaa, 0x80, 0x8d, 0x85, 0xf2, + 0x9c, 0x60, 0x2b, 0xa3, 0x8b, 0x96, 0x83, 0xb0, + 0x60, 0x21, 0x03, 0x41, 0x6d, 0x81, 0xe9, 0xa5, + 0x86, 0x8b, 0x24, 0x00, 0x89, 0x80, 0x8c, 0x04, + 0x00, 0x01, 0x01, 0x80, 0xeb, 0xa0, 0x41, 0x6a, + 0x91, 0xbf, 0x81, 0xb5, 0xa7, 0x8b, 0xf3, 0x20, + 0x40, 0x86, 0xa3, 0x99, 0x85, 0x99, 0x8a, 0xd8, + 0x15, 0x0d, 0x0d, 0x0a, 0xa2, 0x8b, 0x80, 0x99, + 0x80, 0x92, 0x01, 0x80, 0x8e, 0x81, 0x8d, 0xa1, + 0xfa, 0xc4, 0xb4, 0x41, 0x0a, 0x9c, 0x82, 0xb0, + 0xae, 0x9f, 0x8c, 0x9d, 0x84, 0xa5, 0x89, 0x9d, + 0x81, 0xa3, 0x1f, 0x04, 0xa9, 0x40, 0x9d, 0x91, + 0xa3, 0x83, 0xa3, 0x83, 0xa7, 0x87, 0xb3, 0x8b, + 0x8a, 0x80, 0x8e, 0x06, 0x01, 0x80, 0x8a, 0x80, + 0x8e, 0x06, 0x01, 0x82, 0xb3, 0x8b, 0x41, 0x36, + 0x88, 0x95, 0x89, 0x87, 0x97, 0x28, 0xa9, 0x80, + 0x88, 0xc4, 0x29, 0x00, 0xab, 0x01, 0x10, 0x81, + 0x96, 0x89, 0x96, 0x88, 0x9e, 0xc0, 0x92, 0x01, + 0x89, 0x95, 0x89, 0x99, 0x85, 0x99, 0xa5, 0xb7, + 0x29, 0xbf, 0x80, 0x8e, 0x18, 0x10, 0x9c, 0xa9, + 0x9c, 0x82, 0x9c, 0xa2, 0x38, 0x9b, 0x9a, 0xb5, + 0x89, 0x95, 0x89, 0x92, 0x8c, 0x91, 0xed, 0xc8, + 0xb6, 0xb2, 0x8c, 0xb2, 0x8c, 0xa3, 0xa5, 0x9b, + 0x88, 0x96, 0x40, 0xf9, 0xa9, 0x29, 0x8f, 0x85, + 0xb7, 0x9c, 0x89, 0x07, 0x95, 0xa9, 0x91, 0xad, + 0x94, 0x9a, 0x96, 0x8b, 0xb4, 0xb8, 0x09, 0x80, + 0x8c, 0xac, 0x9f, 0x98, 0x99, 0xa3, 0x9c, 0x01, + 0x07, 0xa2, 0x10, 0x8b, 0xaf, 0x8d, 0x83, 0x94, + 0x00, 0x80, 0xa2, 0x91, 0x80, 0x98, 0x92, 0x81, + 0xbe, 0x30, 0x00, 0x18, 0x8e, 0x80, 0x89, 0x86, + 0xae, 0xa5, 0x39, 0x09, 0x95, 0x06, 0x01, 0x04, + 0x10, 0x91, 0x80, 0x8b, 0x84, 0x9d, 0x89, 0x00, + 0x08, 0x80, 0xa5, 0x00, 0x98, 0x00, 0x80, 0xab, + 0xb4, 0x91, 0x83, 0x93, 0x82, 0x9d, 0xaf, 0x93, + 0x08, 0x80, 0x40, 0xb7, 0xae, 0xa8, 0x83, 0xa3, + 0xaf, 0x93, 0x80, 0xba, 0xaa, 0x8c, 0x80, 0xc6, + 0x9a, 0xa4, 0x86, 0x40, 0xb8, 0xab, 0xf3, 0xbf, + 0x9e, 0x39, 0x01, 0x38, 0x08, 0x97, 0x8e, 0x00, + 0x80, 0xdd, 0x39, 0xa6, 0x8f, 0x00, 0x80, 0x9b, + 0x80, 0x89, 0xa7, 0x30, 0x94, 0x80, 0x8a, 0xad, + 0x92, 0x80, 0x91, 0xc8, 0x40, 0xc6, 0xa0, 0x9e, + 0x88, 0x80, 0xa4, 0x90, 0x80, 0xb0, 0x9d, 0xef, + 0x30, 0x08, 0xa5, 0x94, 0x80, 0x98, 0x28, 0x08, + 0x9f, 0x8d, 0x80, 0x96, 0xab, 0x41, 0x03, 0x92, + 0x8e, 0x00, 0x8c, 0x80, 0xa1, 0xfb, 0x80, 0xce, + 0x43, 0x99, 0xe5, 0xee, 0x90, 0x40, 0xc3, 0x4a, + 0x4b, 0xe0, 0x8e, 0x44, 0x2f, 0x90, 0x85, 0x98, + 0x4f, 0x9a, 0x84, 0x42, 0x46, 0x5a, 0xb8, 0x9d, + 0x46, 0xe1, 0x42, 0x38, 0x86, 0x9e, 0x90, 0xce, + 0x90, 0x9d, 0x91, 0xaf, 0x8f, 0x83, 0x9e, 0x94, + 0x84, 0x92, 0x41, 0xaf, 0xac, 0x40, 0xd2, 0xbf, + 0x9f, 0x98, 0x81, 0x98, 0xab, 0xca, 0x20, 0xc1, + 0x8c, 0xbf, 0x08, 0x80, 0x8d, 0x84, 0x88, 0x5c, + 0xd5, 0xa8, 0x9f, 0xe0, 0xf2, 0x60, 0x21, 0xfc, + 0x18, 0x30, 0x08, 0x41, 0x22, 0x8e, 0x80, 0x9c, + 0x11, 0x80, 0x8d, 0x1f, 0x41, 0x8b, 0x49, 0x03, + 0xea, 0x84, 0x8c, 0x82, 0x88, 0x86, 0x89, 0x57, + 0x65, 0xd4, 0x80, 0xc6, 0x01, 0x08, 0x09, 0x0b, + 0x80, 0x8b, 0x00, 0x06, 0x80, 0xc0, 0x03, 0x0f, + 0x06, 0x80, 0x9b, 0x03, 0x04, 0x00, 0x16, 0x80, + 0x41, 0x53, 0x81, 0x98, 0x80, 0x98, 0x80, 0x9e, + 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, + 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x07, 0x47, + 0x33, 0x9e, 0x2d, 0x41, 0x04, 0xbd, 0x40, 0x91, + 0xac, 0x89, 0x86, 0x8f, 0x80, 0x41, 0x40, 0x9d, + 0x91, 0xab, 0x41, 0xe3, 0x9b, 0x40, 0xe3, 0x9d, + 0x08, 0x40, 0xce, 0x9e, 0x02, 0x01, 0x06, 0x0c, + 0x88, 0x81, 0x40, 0xdf, 0x30, 0x18, 0x08, 0x8e, + 0x80, 0x40, 0xc4, 0xba, 0xc3, 0x30, 0x44, 0xb3, + 0x18, 0x9a, 0x01, 0x00, 0x08, 0x80, 0x89, 0x03, + 0x00, 0x00, 0x28, 0x18, 0x00, 0x00, 0x02, 0x01, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x0b, 0x06, 0x03, 0x03, 0x00, 0x80, 0x89, 0x80, + 0x90, 0x22, 0x04, 0x80, 0x90, 0x51, 0x43, 0x60, + 0xa6, 0xdf, 0x9f, 0x51, 0x1d, 0x81, 0x56, 0x8d, + 0x81, 0x5d, 0x30, 0x8e, 0x42, 0x6d, 0x49, 0xa1, + 0x42, 0x1d, 0x45, 0xe1, 0x53, 0x4a, 0x84, 0x60, + 0x21, 0x29, +}; + +static const uint8_t unicode_prop_ID_Start_index[108] = { + 0xf6, 0x03, 0x20, 0xa6, 0x07, 0x00, 0xa9, 0x09, + 0x20, 0xb1, 0x0a, 0x00, 0xba, 0x0b, 0x20, 0x3b, + 0x0d, 0x20, 0xc7, 0x0e, 0x20, 0x49, 0x12, 0x00, + 0x9b, 0x16, 0x00, 0xac, 0x19, 0x00, 0xc0, 0x1d, + 0x80, 0x80, 0x20, 0x20, 0x70, 0x2d, 0x00, 0x00, + 0x32, 0x00, 0x06, 0xa8, 0x00, 0x77, 0xaa, 0x00, + 0xfc, 0xd7, 0x00, 0xfd, 0xfe, 0x40, 0xd1, 0x02, + 0x01, 0xb2, 0x05, 0x21, 0xf6, 0x08, 0x01, 0x49, + 0x0c, 0x01, 0x76, 0x10, 0x01, 0xdf, 0x12, 0x21, + 0xc8, 0x14, 0x41, 0x42, 0x19, 0x21, 0x31, 0x1d, + 0x61, 0xf1, 0x2f, 0x41, 0x78, 0x6b, 0x01, 0x23, + 0xb1, 0xa1, 0xad, 0xd4, 0x01, 0x6f, 0xd7, 0x01, + 0xee, 0xe5, 0x01, 0x38, 0xee, 0x01, 0xe0, 0xa6, + 0x42, 0x7a, 0x34, 0x03, +}; + +static const uint8_t unicode_prop_ID_Continue1_table[708] = { + 0xaf, 0x89, 0xa4, 0x80, 0xd6, 0x80, 0x42, 0x47, + 0xef, 0x96, 0x80, 0x40, 0xfa, 0x84, 0x41, 0x08, + 0xac, 0x00, 0x01, 0x01, 0x00, 0xc7, 0x8a, 0xaf, + 0x9e, 0x28, 0xe4, 0x31, 0x29, 0x08, 0x19, 0x89, + 0x96, 0x80, 0x9d, 0x9a, 0xda, 0x8a, 0x8e, 0x89, + 0xa0, 0x88, 0x88, 0x80, 0x97, 0x18, 0x88, 0x02, + 0x04, 0xaa, 0x82, 0xba, 0x88, 0xa9, 0x97, 0x80, + 0xa0, 0xb5, 0x10, 0x91, 0x06, 0x89, 0x09, 0x89, + 0x90, 0x82, 0xb7, 0x00, 0x31, 0x09, 0x82, 0x88, + 0x80, 0x89, 0x09, 0x89, 0x8d, 0x01, 0x82, 0xb7, + 0x00, 0x23, 0x09, 0x12, 0x80, 0x93, 0x8b, 0x10, + 0x8a, 0x82, 0xb7, 0x00, 0x38, 0x10, 0x82, 0x93, + 0x09, 0x89, 0x89, 0x28, 0x82, 0xb7, 0x00, 0x31, + 0x09, 0x16, 0x82, 0x89, 0x09, 0x89, 0x91, 0x80, + 0xba, 0x22, 0x10, 0x83, 0x88, 0x80, 0x8d, 0x89, + 0x8f, 0x84, 0xb6, 0x00, 0x30, 0x10, 0x1e, 0x81, + 0x8a, 0x09, 0x89, 0x90, 0x82, 0xb7, 0x00, 0x30, + 0x10, 0x1e, 0x81, 0x8a, 0x09, 0x89, 0x10, 0x8b, + 0x83, 0xb6, 0x08, 0x30, 0x10, 0x83, 0x88, 0x80, + 0x89, 0x09, 0x89, 0x90, 0x82, 0xc5, 0x03, 0x28, + 0x00, 0x3d, 0x89, 0x09, 0xbc, 0x01, 0x86, 0x8b, + 0x38, 0x89, 0xd6, 0x01, 0x88, 0x8a, 0x30, 0x89, + 0xbd, 0x0d, 0x89, 0x8a, 0x00, 0x00, 0x03, 0x81, + 0xb0, 0x93, 0x01, 0x84, 0x8a, 0x80, 0xa3, 0x88, + 0x80, 0xe3, 0x93, 0x80, 0x89, 0x8b, 0x1b, 0x10, + 0x11, 0x32, 0x83, 0x8c, 0x8b, 0x80, 0x8e, 0x42, + 0xbe, 0x82, 0x88, 0x88, 0x43, 0x9f, 0x83, 0x9b, + 0x82, 0x9c, 0x81, 0x9d, 0x81, 0xbf, 0x9f, 0x88, + 0x01, 0x89, 0xa0, 0x10, 0x8a, 0x40, 0x8e, 0x80, + 0xf5, 0x8b, 0x83, 0x8b, 0x89, 0x89, 0xff, 0x8a, + 0xbb, 0x84, 0xb8, 0x89, 0x80, 0x9c, 0x81, 0x8a, + 0x85, 0x89, 0x95, 0x8d, 0x80, 0x9e, 0x81, 0x8b, + 0x93, 0x84, 0xae, 0x90, 0x8a, 0x89, 0x90, 0x88, + 0x8b, 0x82, 0x9d, 0x8c, 0x81, 0x89, 0xab, 0x8d, + 0xaf, 0x93, 0x87, 0x89, 0x85, 0x89, 0xf5, 0x10, + 0x94, 0x18, 0x28, 0x0a, 0x40, 0xc5, 0xbf, 0x42, + 0x0b, 0x81, 0xb0, 0x81, 0x92, 0x80, 0xfa, 0x8c, + 0x18, 0x82, 0x8b, 0x4b, 0xfd, 0x82, 0x40, 0x8c, + 0x80, 0xdf, 0x9f, 0x42, 0x29, 0x85, 0xe8, 0x81, + 0xdf, 0x80, 0x60, 0x75, 0x23, 0x89, 0xc4, 0x03, + 0x89, 0x9f, 0x81, 0xcf, 0x81, 0x41, 0x0f, 0x02, + 0x03, 0x80, 0x96, 0x23, 0x80, 0xd2, 0x81, 0xb1, + 0x91, 0x89, 0x89, 0x85, 0x91, 0x8c, 0x8a, 0x9b, + 0x87, 0x98, 0x8c, 0xab, 0x83, 0xae, 0x8d, 0x8e, + 0x89, 0x8a, 0x80, 0x89, 0x89, 0xae, 0x8d, 0x8b, + 0x07, 0x09, 0x89, 0xa0, 0x82, 0xb1, 0x00, 0x11, + 0x0c, 0x08, 0x80, 0xa8, 0x24, 0x81, 0x40, 0xeb, + 0x38, 0x09, 0x89, 0x60, 0x4f, 0x23, 0x80, 0x42, + 0xe0, 0x8f, 0x8f, 0x8f, 0x11, 0x97, 0x82, 0x40, + 0xbf, 0x89, 0xa4, 0x80, 0xa4, 0x80, 0x42, 0x96, + 0x80, 0x40, 0xe1, 0x80, 0x40, 0x94, 0x84, 0x41, + 0x24, 0x89, 0x45, 0x56, 0x10, 0x0c, 0x83, 0xa7, + 0x13, 0x80, 0x40, 0xa4, 0x81, 0x42, 0x3c, 0x1f, + 0x89, 0x85, 0x89, 0x9e, 0x84, 0x41, 0x3c, 0x81, + 0xcc, 0x85, 0xc5, 0x8a, 0xb0, 0x83, 0xf9, 0x82, + 0xb4, 0x8e, 0x9e, 0x8a, 0x09, 0x89, 0x83, 0xac, + 0x8a, 0x30, 0xac, 0x89, 0x2a, 0xa3, 0x8d, 0x80, + 0x89, 0x21, 0xab, 0x80, 0x8b, 0x82, 0xaf, 0x8d, + 0x3b, 0x80, 0x8b, 0xd1, 0x8b, 0x28, 0x08, 0x40, + 0x9c, 0x8b, 0x84, 0x89, 0x2b, 0xb6, 0x08, 0x31, + 0x09, 0x82, 0x88, 0x80, 0x89, 0x09, 0x32, 0x84, + 0xc2, 0x88, 0x00, 0x08, 0x03, 0x04, 0x00, 0x8d, + 0x81, 0xd1, 0x91, 0x88, 0x89, 0x18, 0xd0, 0x93, + 0x8b, 0x89, 0x40, 0xd4, 0x31, 0x88, 0x9a, 0x81, + 0xd1, 0x90, 0x8e, 0x89, 0xd0, 0x8c, 0x87, 0x89, + 0x85, 0x93, 0xb8, 0x8e, 0x83, 0x89, 0x40, 0xf1, + 0x8e, 0x40, 0xa4, 0x89, 0xc5, 0x28, 0x09, 0x18, + 0x00, 0x81, 0x8b, 0x89, 0xf6, 0x31, 0x32, 0x80, + 0x9b, 0x89, 0xa7, 0x30, 0x1f, 0x80, 0x88, 0x8a, + 0xad, 0x8f, 0x40, 0xc5, 0x87, 0x40, 0x87, 0x89, + 0xb4, 0x38, 0x87, 0x8f, 0x89, 0xb7, 0x95, 0x80, + 0x8d, 0xf9, 0x2a, 0x00, 0x08, 0x30, 0x07, 0x89, + 0xaf, 0x20, 0x08, 0x27, 0x89, 0xb5, 0x89, 0x41, + 0x08, 0x83, 0x88, 0x08, 0x80, 0xaf, 0x32, 0x84, + 0x8c, 0x8a, 0x54, 0xe4, 0x05, 0x8e, 0x60, 0x2c, + 0xc7, 0x9b, 0x49, 0x25, 0x89, 0xd5, 0x89, 0xa5, + 0x84, 0xba, 0x86, 0x98, 0x89, 0x42, 0x15, 0x89, + 0x41, 0xd4, 0x00, 0xb6, 0x33, 0xd0, 0x80, 0x8a, + 0x81, 0x60, 0x4c, 0xaa, 0x81, 0x50, 0x50, 0x89, + 0x42, 0x05, 0xad, 0x81, 0x96, 0x42, 0x1d, 0x22, + 0x2f, 0x39, 0x86, 0x9d, 0x83, 0x40, 0x93, 0x82, + 0x45, 0x88, 0xb1, 0x41, 0xff, 0xb6, 0x83, 0xb1, + 0x38, 0x8d, 0x80, 0x95, 0x20, 0x8e, 0x45, 0x4f, + 0x30, 0x90, 0x0e, 0x01, 0x04, 0xe3, 0x80, 0x40, + 0x9f, 0x86, 0x88, 0x89, 0x41, 0x63, 0x80, 0xbc, + 0x8d, 0x41, 0xf1, 0x8d, 0x40, 0xf3, 0x08, 0x89, + 0x40, 0xe7, 0x01, 0x06, 0x0c, 0x80, 0x41, 0xd9, + 0x86, 0xec, 0x34, 0x89, 0x52, 0x95, 0x89, 0x6c, + 0x05, 0x05, 0x40, 0xef, +}; + +static const uint8_t unicode_prop_ID_Continue1_index[66] = { + 0xfa, 0x06, 0x00, 0x70, 0x09, 0x00, 0xf0, 0x0a, + 0x40, 0x57, 0x0c, 0x00, 0xf0, 0x0d, 0x60, 0xc7, + 0x0f, 0x20, 0xea, 0x17, 0x40, 0xec, 0x1a, 0x00, + 0x0e, 0x20, 0x40, 0x7e, 0xa6, 0x20, 0xda, 0xa9, + 0x20, 0x10, 0xfe, 0x40, 0x40, 0x0a, 0x41, 0xbb, + 0x10, 0x21, 0x4e, 0x13, 0x41, 0xde, 0x15, 0x01, + 0xe5, 0x19, 0x01, 0x5a, 0x1d, 0x01, 0xf5, 0x6a, + 0x21, 0x8c, 0xd1, 0x61, 0x37, 0xe1, 0x41, 0xf0, + 0x01, 0x0e, +}; + +static const uint8_t unicode_prop_White_Space_table[22] = { + 0x88, 0x84, 0x91, 0x80, 0xe3, 0x80, 0x99, 0x80, + 0x55, 0xde, 0x80, 0x49, 0x7e, 0x8a, 0x9c, 0x0c, + 0x80, 0xae, 0x80, 0x4f, 0x9f, 0x80, +}; + +static const uint8_t unicode_prop_White_Space_index[3] = { + 0x01, 0x30, 0x00, +}; + +static const uint8_t unicode_cc_table[937] = { + 0xb2, 0xcf, 0xd4, 0x00, 0xe8, 0x03, 0xdc, 0x00, + 0xe8, 0x00, 0xd8, 0x04, 0xdc, 0x01, 0xca, 0x03, + 0xdc, 0x01, 0xca, 0x0a, 0xdc, 0x04, 0x01, 0x03, + 0xdc, 0xc7, 0x00, 0xf0, 0xc0, 0x02, 0xdc, 0xc2, + 0x01, 0xdc, 0x80, 0xc2, 0x03, 0xdc, 0xc0, 0x00, + 0xe8, 0x01, 0xdc, 0xc0, 0x41, 0xe9, 0x00, 0xea, + 0x41, 0xe9, 0x00, 0xea, 0x00, 0xe9, 0xcc, 0xb0, + 0xe2, 0xc4, 0xb0, 0xd8, 0x00, 0xdc, 0xc3, 0x00, + 0xdc, 0xc2, 0x00, 0xde, 0x00, 0xdc, 0xc5, 0x05, + 0xdc, 0xc1, 0x00, 0xdc, 0xc1, 0x00, 0xde, 0x00, + 0xe4, 0xc0, 0x49, 0x0a, 0x43, 0x13, 0x80, 0x00, + 0x17, 0x80, 0x41, 0x18, 0x80, 0xc0, 0x00, 0xdc, + 0x80, 0x00, 0x12, 0xb0, 0x17, 0xc7, 0x42, 0x1e, + 0xaf, 0x47, 0x1b, 0xc1, 0x01, 0xdc, 0xc4, 0x00, + 0xdc, 0xc1, 0x00, 0xdc, 0x8f, 0x00, 0x23, 0xb0, + 0x34, 0xc6, 0x81, 0xc3, 0x00, 0xdc, 0xc0, 0x81, + 0xc1, 0x80, 0x00, 0xdc, 0xc1, 0x00, 0xdc, 0xa2, + 0x00, 0x24, 0x9d, 0xc0, 0x00, 0xdc, 0xc1, 0x00, + 0xdc, 0xc1, 0x02, 0xdc, 0xc0, 0x01, 0xdc, 0xc0, + 0x00, 0xdc, 0xc2, 0x00, 0xdc, 0xc0, 0x00, 0xdc, + 0xc0, 0x00, 0xdc, 0xc0, 0x00, 0xdc, 0xc1, 0xb0, + 0x6f, 0xc6, 0x00, 0xdc, 0xc0, 0x88, 0x00, 0xdc, + 0x97, 0xc3, 0x80, 0xc8, 0x80, 0xc2, 0x80, 0xc4, + 0xaa, 0x02, 0xdc, 0xb0, 0x0a, 0xc1, 0x02, 0xdc, + 0xc3, 0xa9, 0xc4, 0x04, 0xdc, 0xcd, 0x80, 0x00, + 0xdc, 0xc1, 0x00, 0xdc, 0xc1, 0x00, 0xdc, 0xc2, + 0x02, 0xdc, 0x42, 0x1b, 0xc2, 0x00, 0xdc, 0xc1, + 0x01, 0xdc, 0xc4, 0xb0, 0x0b, 0x00, 0x07, 0x8f, + 0x00, 0x09, 0x82, 0xc0, 0x00, 0xdc, 0xc1, 0xb0, + 0x36, 0x00, 0x07, 0x8f, 0x00, 0x09, 0xaf, 0xc0, + 0xb0, 0x0c, 0x00, 0x07, 0x8f, 0x00, 0x09, 0xb0, + 0x3d, 0x00, 0x07, 0x8f, 0x00, 0x09, 0xb0, 0x3d, + 0x00, 0x07, 0x8f, 0x00, 0x09, 0xb0, 0x4e, 0x00, + 0x09, 0xb0, 0x3d, 0x00, 0x07, 0x8f, 0x00, 0x09, + 0x86, 0x00, 0x54, 0x00, 0x5b, 0xb0, 0x34, 0x00, + 0x07, 0x8f, 0x00, 0x09, 0xb0, 0x3c, 0x01, 0x09, + 0x8f, 0x00, 0x09, 0xb0, 0x4b, 0x00, 0x09, 0xb0, + 0x3c, 0x01, 0x67, 0x00, 0x09, 0x8c, 0x03, 0x6b, + 0xb0, 0x3b, 0x01, 0x76, 0x00, 0x09, 0x8c, 0x03, + 0x7a, 0xb0, 0x1b, 0x01, 0xdc, 0x9a, 0x00, 0xdc, + 0x80, 0x00, 0xdc, 0x80, 0x00, 0xd8, 0xb0, 0x06, + 0x41, 0x81, 0x80, 0x00, 0x84, 0x84, 0x03, 0x82, + 0x81, 0x00, 0x82, 0x80, 0xc1, 0x00, 0x09, 0x80, + 0xc1, 0xb0, 0x0d, 0x00, 0xdc, 0xb0, 0x3f, 0x00, + 0x07, 0x80, 0x01, 0x09, 0xb0, 0x21, 0x00, 0xdc, + 0xb2, 0x9e, 0xc2, 0xb3, 0x83, 0x01, 0x09, 0x9d, + 0x00, 0x09, 0xb0, 0x6c, 0x00, 0x09, 0x89, 0xc0, + 0xb0, 0x9a, 0x00, 0xe4, 0xb0, 0x5e, 0x00, 0xde, + 0xc0, 0x00, 0xdc, 0xb0, 0xaa, 0xc0, 0x00, 0xdc, + 0xb0, 0x16, 0x00, 0x09, 0x93, 0xc7, 0x81, 0x00, + 0xdc, 0xaf, 0xc4, 0x05, 0xdc, 0xc1, 0x00, 0xdc, + 0x80, 0x01, 0xdc, 0xc1, 0x01, 0xdc, 0xc4, 0x00, + 0xdc, 0xd1, 0x00, 0xdc, 0x81, 0xc5, 0x00, 0xdc, + 0xc3, 0x00, 0xea, 0xb0, 0x17, 0x00, 0x07, 0x8e, + 0x00, 0x09, 0xa5, 0xc0, 0x00, 0xdc, 0xc6, 0xb0, + 0x05, 0x01, 0x09, 0xb0, 0x09, 0x00, 0x07, 0x8a, + 0x01, 0x09, 0xb0, 0x12, 0x00, 0x07, 0xb0, 0x67, + 0xc2, 0x41, 0x00, 0x04, 0xdc, 0xc1, 0x03, 0xdc, + 0xc0, 0x41, 0x00, 0x05, 0x01, 0x83, 0x00, 0xdc, + 0x85, 0xc0, 0x82, 0xc1, 0xb0, 0x95, 0xc1, 0x00, + 0xdc, 0xc6, 0x00, 0xdc, 0xc1, 0x00, 0xea, 0x00, + 0xd6, 0x00, 0xdc, 0x00, 0xca, 0xe4, 0x00, 0xe8, + 0x01, 0xe4, 0x00, 0xdc, 0x00, 0xda, 0xc0, 0x00, + 0xe9, 0x00, 0xdc, 0xc0, 0x00, 0xdc, 0xb2, 0x9f, + 0xc1, 0x01, 0x01, 0xc3, 0x02, 0x01, 0xc1, 0x83, + 0xc0, 0x82, 0x01, 0x01, 0xc0, 0x00, 0xdc, 0xc0, + 0x01, 0x01, 0x03, 0xdc, 0xc0, 0xb8, 0x03, 0xcd, + 0xc2, 0xb0, 0x5c, 0x00, 0x09, 0xb0, 0x2f, 0xdf, + 0xb1, 0xf9, 0x00, 0xda, 0x00, 0xe4, 0x00, 0xe8, + 0x00, 0xde, 0x01, 0xe0, 0xb0, 0x38, 0x01, 0x08, + 0xb8, 0x6d, 0xa3, 0xc0, 0x83, 0xc9, 0x9f, 0xc1, + 0xb0, 0x1f, 0xc1, 0xb0, 0xe3, 0x00, 0x09, 0xa4, + 0x00, 0x09, 0xb0, 0x66, 0x00, 0x09, 0x9a, 0xd1, + 0xb0, 0x08, 0x02, 0xdc, 0xa4, 0x00, 0x09, 0xb0, + 0x2e, 0x00, 0x07, 0x8b, 0x00, 0x09, 0xb0, 0xbe, + 0xc0, 0x80, 0xc1, 0x00, 0xdc, 0x81, 0xc1, 0x84, + 0xc1, 0x80, 0xc0, 0xb0, 0x03, 0x00, 0x09, 0xb0, + 0xc5, 0x00, 0x09, 0xb8, 0x46, 0xff, 0x00, 0x1a, + 0xb2, 0xd0, 0xc6, 0x06, 0xdc, 0xc1, 0xb3, 0x9c, + 0x00, 0xdc, 0xb0, 0xb1, 0x00, 0xdc, 0xb0, 0x64, + 0xc4, 0xb6, 0x61, 0x00, 0xdc, 0x80, 0xc0, 0xa7, + 0xc0, 0x00, 0x01, 0x00, 0xdc, 0x83, 0x00, 0x09, + 0xb0, 0x74, 0xc0, 0x00, 0xdc, 0xb2, 0x0c, 0xc3, + 0xb0, 0x10, 0xc4, 0xb1, 0x0c, 0xc1, 0xb0, 0x1c, + 0x01, 0xdc, 0x80, 0x02, 0xdc, 0xb0, 0x15, 0x01, + 0xdc, 0xc2, 0x00, 0xdc, 0xc0, 0x03, 0xdc, 0xb0, + 0x00, 0xc0, 0x00, 0xdc, 0xc0, 0x00, 0xdc, 0xb0, + 0x8f, 0x00, 0x09, 0xa8, 0x00, 0x09, 0x8d, 0x00, + 0x09, 0xb0, 0x08, 0x00, 0x09, 0x00, 0x07, 0xb0, + 0x14, 0xc2, 0xaf, 0x01, 0x09, 0xb0, 0x0d, 0x00, + 0x07, 0xb0, 0x1b, 0x00, 0x09, 0x88, 0x00, 0x07, + 0xb0, 0x39, 0x00, 0x09, 0x00, 0x07, 0xb0, 0x81, + 0x00, 0x07, 0x00, 0x09, 0xb0, 0x1f, 0x01, 0x07, + 0x8f, 0x00, 0x09, 0x97, 0xc6, 0x82, 0xc4, 0xb0, + 0x28, 0x02, 0x09, 0xb0, 0x40, 0x00, 0x09, 0x82, + 0x00, 0x07, 0x96, 0xc0, 0xb0, 0x32, 0x00, 0x09, + 0x00, 0x07, 0xb0, 0xca, 0x00, 0x09, 0x00, 0x07, + 0xb0, 0x4d, 0x00, 0x09, 0xb0, 0x45, 0x00, 0x09, + 0x00, 0x07, 0xb0, 0x42, 0x00, 0x09, 0xb0, 0xdc, + 0x00, 0x09, 0x00, 0x07, 0xb0, 0xd1, 0x01, 0x09, + 0x83, 0x00, 0x07, 0xb0, 0x6b, 0x00, 0x09, 0xb0, + 0x22, 0x00, 0x09, 0x91, 0x00, 0x09, 0xb0, 0x20, + 0x00, 0x09, 0xb1, 0x74, 0x00, 0x09, 0xb0, 0xd1, + 0x00, 0x07, 0x80, 0x01, 0x09, 0xb0, 0x20, 0x00, + 0x09, 0xb1, 0x78, 0x01, 0x09, 0xb8, 0x39, 0xbb, + 0x00, 0x09, 0xb8, 0x01, 0x8f, 0x04, 0x01, 0xb0, + 0x0a, 0xc6, 0xb4, 0x88, 0x01, 0x06, 0xb8, 0x44, + 0x7b, 0x00, 0x01, 0xb8, 0x0c, 0x95, 0x01, 0xd8, + 0x02, 0x01, 0x82, 0x00, 0xe2, 0x04, 0xd8, 0x87, + 0x07, 0xdc, 0x81, 0xc4, 0x01, 0xdc, 0x9d, 0xc3, + 0xb0, 0x63, 0xc2, 0xb8, 0x05, 0x8a, 0xc6, 0x80, + 0xd0, 0x81, 0xc6, 0x80, 0xc1, 0x80, 0xc4, 0xb0, + 0x33, 0xc0, 0xb0, 0x6f, 0xc6, 0xb1, 0x46, 0xc0, + 0xb0, 0x0c, 0xc3, 0xb1, 0xcb, 0x01, 0xe8, 0x00, + 0xdc, 0xc0, 0xb0, 0xcd, 0xc0, 0x00, 0xdc, 0xb0, + 0xc2, 0xc0, 0x81, 0xc0, 0x86, 0xc1, 0x84, 0xc0, + 0xb1, 0xa9, 0x06, 0xdc, 0xb0, 0x3c, 0xc5, 0x00, + 0x07, +}; + +static const uint8_t unicode_cc_index[90] = { + 0x4d, 0x03, 0x00, 0x97, 0x05, 0x20, 0xc6, 0x05, + 0x00, 0xe7, 0x06, 0x00, 0x45, 0x07, 0x00, 0x9c, + 0x08, 0x00, 0x4d, 0x09, 0x00, 0x3c, 0x0b, 0x00, + 0x3d, 0x0d, 0x00, 0x36, 0x0f, 0x00, 0x38, 0x10, + 0x20, 0x3a, 0x19, 0x00, 0xcb, 0x1a, 0x20, 0xf2, + 0x1b, 0x00, 0xc3, 0x1d, 0x20, 0xd0, 0x20, 0x00, + 0x00, 0x2e, 0x00, 0x2c, 0xa8, 0x00, 0xbe, 0xaa, + 0x00, 0x76, 0x03, 0x01, 0xfa, 0x0e, 0x01, 0x80, + 0x10, 0x21, 0xe9, 0x12, 0x01, 0xc3, 0x14, 0x01, + 0x3f, 0x19, 0x01, 0x98, 0x1d, 0x21, 0x67, 0xd1, + 0x01, 0x8f, 0xe0, 0x21, 0xf6, 0xe6, 0x01, 0x4b, + 0xe9, 0x01, +}; + +static const uint32_t unicode_decomp_table1[709] = { + 0x00280081, 0x002a0097, 0x002a8081, 0x002bc097, + 0x002c8115, 0x002d0097, 0x002d4081, 0x002e0097, + 0x002e4115, 0x002f0199, 0x00302016, 0x00400842, + 0x00448a42, 0x004a0442, 0x004c0096, 0x004c8117, + 0x004d0242, 0x004e4342, 0x004fc12f, 0x0050c342, + 0x005240bf, 0x00530342, 0x00550942, 0x005a0842, + 0x005e0096, 0x005e4342, 0x005fc081, 0x00680142, + 0x006bc142, 0x00710185, 0x0071c317, 0x00734844, + 0x00778344, 0x00798342, 0x007b02be, 0x007c4197, + 0x007d0142, 0x007e0444, 0x00800e42, 0x00878142, + 0x00898744, 0x00ac0483, 0x00b60317, 0x00b80283, + 0x00d00214, 0x00d10096, 0x00dd0080, 0x00de8097, + 0x00df8080, 0x00e10097, 0x00e1413e, 0x00e1c080, + 0x00e204be, 0x00ea83ae, 0x00f282ae, 0x00f401ad, + 0x00f4c12e, 0x00f54103, 0x00fc0303, 0x00fe4081, + 0x0100023e, 0x0101c0be, 0x010301be, 0x010640be, + 0x010e40be, 0x0114023e, 0x0115c0be, 0x011701be, + 0x011d8144, 0x01304144, 0x01340244, 0x01358144, + 0x01368344, 0x01388344, 0x013a8644, 0x013e0144, + 0x0161c085, 0x018882ae, 0x019d422f, 0x01b00184, + 0x01b4c084, 0x024a4084, 0x024c4084, 0x024d0084, + 0x0256042e, 0x0272c12e, 0x02770120, 0x0277c084, + 0x028cc084, 0x028d8084, 0x029641ae, 0x02978084, + 0x02d20084, 0x02d2c12e, 0x02d70120, 0x02e50084, + 0x02f281ae, 0x03120084, 0x03300084, 0x0331c122, + 0x0332812e, 0x035281ae, 0x03768084, 0x037701ae, + 0x038cc085, 0x03acc085, 0x03b7012f, 0x03c30081, + 0x03d0c084, 0x03d34084, 0x03d48084, 0x03d5c084, + 0x03d70084, 0x03da4084, 0x03dcc084, 0x03dd412e, + 0x03ddc085, 0x03de0084, 0x03de4085, 0x03e04084, + 0x03e4c084, 0x03e74084, 0x03e88084, 0x03e9c084, + 0x03eb0084, 0x03ee4084, 0x04098084, 0x043f0081, + 0x06c18484, 0x06c48084, 0x06cec184, 0x06d00120, + 0x06d0c084, 0x074b0383, 0x074cc41f, 0x074f1783, + 0x075e0081, 0x0766d283, 0x07801d44, 0x078e8942, + 0x07931844, 0x079f0d42, 0x07a58216, 0x07a68085, + 0x07a6c0be, 0x07a80d44, 0x07aea044, 0x07c00122, + 0x07c08344, 0x07c20122, 0x07c28344, 0x07c40122, + 0x07c48244, 0x07c60122, 0x07c68244, 0x07c8113e, + 0x07d08244, 0x07d20122, 0x07d28244, 0x07d40122, + 0x07d48344, 0x07d64c3e, 0x07dc4080, 0x07dc80be, + 0x07dcc080, 0x07dd00be, 0x07dd4080, 0x07dd80be, + 0x07ddc080, 0x07de00be, 0x07de4080, 0x07de80be, + 0x07dec080, 0x07df00be, 0x07df4080, 0x07e00820, + 0x07e40820, 0x07e80820, 0x07ec05be, 0x07eec080, + 0x07ef00be, 0x07ef4097, 0x07ef8080, 0x07efc117, + 0x07f0443e, 0x07f24080, 0x07f280be, 0x07f2c080, + 0x07f303be, 0x07f4c080, 0x07f582ae, 0x07f6c080, + 0x07f7433e, 0x07f8c080, 0x07f903ae, 0x07fac080, + 0x07fb013e, 0x07fb8102, 0x07fc83be, 0x07fe4080, + 0x07fe80be, 0x07fec080, 0x07ff00be, 0x07ff4080, + 0x07ff8097, 0x0800011e, 0x08008495, 0x08044081, + 0x0805c097, 0x08090081, 0x08094097, 0x08098099, + 0x080bc081, 0x080cc085, 0x080d00b1, 0x080d8085, + 0x080dc0b1, 0x080f0197, 0x0811c197, 0x0815c0b3, + 0x0817c081, 0x081c0595, 0x081ec081, 0x081f0215, + 0x0820051f, 0x08228583, 0x08254415, 0x082a0097, + 0x08400119, 0x08408081, 0x0840c0bf, 0x08414119, + 0x0841c081, 0x084240bf, 0x0842852d, 0x08454081, + 0x08458097, 0x08464295, 0x08480097, 0x08484099, + 0x08488097, 0x08490081, 0x08498080, 0x084a0081, + 0x084a8102, 0x084b0495, 0x084d421f, 0x084e4081, + 0x084ec099, 0x084f0283, 0x08514295, 0x08540119, + 0x0854809b, 0x0854c619, 0x0857c097, 0x08580081, + 0x08584097, 0x08588099, 0x0858c097, 0x08590081, + 0x08594097, 0x08598099, 0x0859c09b, 0x085a0097, + 0x085a4081, 0x085a8097, 0x085ac099, 0x085b0295, + 0x085c4097, 0x085c8099, 0x085cc097, 0x085d0081, + 0x085d4097, 0x085d8099, 0x085dc09b, 0x085e0097, + 0x085e4081, 0x085e8097, 0x085ec099, 0x085f0215, + 0x08624099, 0x0866813e, 0x086b80be, 0x087341be, + 0x088100be, 0x088240be, 0x088300be, 0x088901be, + 0x088b0085, 0x088b40b1, 0x088bc085, 0x088c00b1, + 0x089040be, 0x089100be, 0x0891c1be, 0x089801be, + 0x089b42be, 0x089d0144, 0x089e0144, 0x08a00144, + 0x08a10144, 0x08a20144, 0x08ab023e, 0x08b80244, + 0x08ba8220, 0x08ca411e, 0x0918049f, 0x091a4523, + 0x091cc097, 0x091d04a5, 0x091f452b, 0x0921c09b, + 0x092204a1, 0x09244525, 0x0926c099, 0x09270d25, + 0x092d8d1f, 0x09340d1f, 0x093a8081, 0x0a8300b3, + 0x0a9d0099, 0x0a9d4097, 0x0a9d8099, 0x0ab700be, + 0x0b1f0115, 0x0b5bc081, 0x0ba7c081, 0x0bbcc081, + 0x0bc004ad, 0x0bc244ad, 0x0bc484ad, 0x0bc6f383, + 0x0be0852d, 0x0be31d03, 0x0bf1882d, 0x0c000081, + 0x0c0d8283, 0x0c130b84, 0x0c194284, 0x0c1c0122, + 0x0c1cc122, 0x0c1d8122, 0x0c1e4122, 0x0c1f0122, + 0x0c250084, 0x0c26c123, 0x0c278084, 0x0c27c085, + 0x0c2b0b84, 0x0c314284, 0x0c340122, 0x0c34c122, + 0x0c358122, 0x0c364122, 0x0c370122, 0x0c3d0084, + 0x0c3dc220, 0x0c3f8084, 0x0c3fc085, 0x0c4c4a2d, + 0x0c51451f, 0x0c53ca9f, 0x0c5915ad, 0x0c648703, + 0x0c800741, 0x0c838089, 0x0c83c129, 0x0c8441a9, + 0x0c850089, 0x0c854129, 0x0c85c2a9, 0x0c870089, + 0x0c87408f, 0x0c87808d, 0x0c881241, 0x0c910203, + 0x0c940099, 0x0c9444a3, 0x0c968323, 0x0c98072d, + 0x0c9b84af, 0x0c9dc2a1, 0x0c9f00b5, 0x0c9f40b3, + 0x0c9f8085, 0x0ca01883, 0x0cac4223, 0x0cad4523, + 0x0cafc097, 0x0cb004a1, 0x0cb241a5, 0x0cb30097, + 0x0cb34099, 0x0cb38097, 0x0cb3c099, 0x0cb417ad, + 0x0cbfc085, 0x0cc001b3, 0x0cc0c0b1, 0x0cc100b3, + 0x0cc14131, 0x0cc1c0b5, 0x0cc200b3, 0x0cc241b1, + 0x0cc30133, 0x0cc38131, 0x0cc40085, 0x0cc440b1, + 0x0cc48133, 0x0cc50085, 0x0cc540b5, 0x0cc580b7, + 0x0cc5c0b5, 0x0cc600b1, 0x0cc64135, 0x0cc6c0b3, + 0x0cc701b1, 0x0cc7c0b3, 0x0cc800b5, 0x0cc840b3, + 0x0cc881b1, 0x0cc9422f, 0x0cca4131, 0x0ccac0b5, + 0x0ccb00b1, 0x0ccb40b3, 0x0ccb80b5, 0x0ccbc0b1, + 0x0ccc012f, 0x0ccc80b5, 0x0cccc0b3, 0x0ccd00b5, + 0x0ccd40b1, 0x0ccd80b5, 0x0ccdc085, 0x0cce02b1, + 0x0ccf40b3, 0x0ccf80b1, 0x0ccfc085, 0x0cd001b1, + 0x0cd0c0b3, 0x0cd101b1, 0x0cd1c0b5, 0x0cd200b3, + 0x0cd24085, 0x0cd280b5, 0x0cd2c085, 0x0cd30133, + 0x0cd381b1, 0x0cd440b3, 0x0cd48085, 0x0cd4c0b1, + 0x0cd500b3, 0x0cd54085, 0x0cd580b5, 0x0cd5c0b1, + 0x0cd60521, 0x0cd88525, 0x0cdb02a5, 0x0cdc4099, + 0x0cdc8117, 0x0cdd0099, 0x0cdd4197, 0x0cde0127, + 0x0cde8285, 0x0cdfc089, 0x0ce0043f, 0x0ce20099, + 0x0ce2409b, 0x0ce283bf, 0x0ce44219, 0x0ce54205, + 0x0ce6433f, 0x0ce7c131, 0x0ce84085, 0x0ce881b1, + 0x0ce94085, 0x0ce98107, 0x0cea0089, 0x0cea4097, + 0x0cea8219, 0x0ceb809d, 0x0cebc08d, 0x0cec083f, + 0x0cf00105, 0x0cf0809b, 0x0cf0c197, 0x0cf1809b, + 0x0cf1c099, 0x0cf20517, 0x0cf48099, 0x0cf4c117, + 0x0cf54119, 0x0cf5c097, 0x0cf6009b, 0x0cf64099, + 0x0cf68217, 0x0cf78119, 0x0cf804a1, 0x0cfa4525, + 0x0cfcc525, 0x0cff4125, 0x0cffc099, 0x29a70103, + 0x29dc0081, 0x29fc4215, 0x29fe0103, 0x2ad70203, + 0x2ada4081, 0x3e401482, 0x3e4a7f82, 0x3e6a3f82, + 0x3e8aa102, 0x3e9b0110, 0x3e9c2f82, 0x3eb3c590, + 0x3ec00197, 0x3ec0c119, 0x3ec1413f, 0x3ec4c2af, + 0x3ec74184, 0x3ec804ad, 0x3eca4081, 0x3eca8304, + 0x3ecc03a0, 0x3ece02a0, 0x3ecf8084, 0x3ed00120, + 0x3ed0c120, 0x3ed184ae, 0x3ed3c085, 0x3ed4312d, + 0x3ef4cbad, 0x3efa892f, 0x3eff022d, 0x3f002f2f, + 0x3f1782a5, 0x3f18c0b1, 0x3f1907af, 0x3f1cffaf, + 0x3f3c81a5, 0x3f3d64af, 0x3f542031, 0x3f649b31, + 0x3f7c0131, 0x3f7c83b3, 0x3f7e40b1, 0x3f7e80bd, + 0x3f7ec0bb, 0x3f7f00b3, 0x3f840503, 0x3f8c01ad, + 0x3f8cc315, 0x3f8e462d, 0x3f91cc03, 0x3f97c695, + 0x3f9c01af, 0x3f9d0085, 0x3f9d852f, 0x3fa03aad, + 0x3fbd442f, 0x3fc06f1f, 0x3fd7c11f, 0x3fd85fad, + 0x3fe80081, 0x3fe84f1f, 0x3ff0831f, 0x3ff2831f, + 0x3ff4831f, 0x3ff6819f, 0x3ff80783, 0x41724092, + 0x41790092, 0x41e04d83, 0x41e70f91, 0x44268192, + 0x442ac092, 0x444b8112, 0x44d2c112, 0x44e0c192, + 0x44e38092, 0x44e44092, 0x44f14212, 0x452ec212, + 0x456e8112, 0x464e0092, 0x58484412, 0x5b5a0192, + 0x73358d1f, 0x733c051f, 0x74578392, 0x746ec312, + 0x75000d1f, 0x75068d1f, 0x750d0d1f, 0x7513839f, + 0x7515891f, 0x751a0d1f, 0x75208d1f, 0x75271015, + 0x752f439f, 0x7531459f, 0x75340d1f, 0x753a8d1f, + 0x75410395, 0x7543441f, 0x7545839f, 0x75478d1f, + 0x754e0795, 0x7552839f, 0x75548d1f, 0x755b0d1f, + 0x75618d1f, 0x75680d1f, 0x756e8d1f, 0x75750d1f, + 0x757b8d1f, 0x75820d1f, 0x75888d1f, 0x758f0d1f, + 0x75958d1f, 0x759c0d1f, 0x75a28d1f, 0x75a90103, + 0x75aa089f, 0x75ae4081, 0x75ae839f, 0x75b04081, + 0x75b08c9f, 0x75b6c081, 0x75b7032d, 0x75b8889f, + 0x75bcc081, 0x75bd039f, 0x75bec081, 0x75bf0c9f, + 0x75c54081, 0x75c5832d, 0x75c7089f, 0x75cb4081, + 0x75cb839f, 0x75cd4081, 0x75cd8c9f, 0x75d3c081, + 0x75d4032d, 0x75d5889f, 0x75d9c081, 0x75da039f, + 0x75dbc081, 0x75dc0c9f, 0x75e24081, 0x75e2832d, + 0x75e4089f, 0x75e84081, 0x75e8839f, 0x75ea4081, + 0x75ea8c9f, 0x75f0c081, 0x75f1042d, 0x75f3851f, + 0x75f6051f, 0x75f8851f, 0x75fb051f, 0x75fd851f, + 0x780c049f, 0x780e419f, 0x780f059f, 0x7811c203, + 0x7812d0ad, 0x781b0103, 0x7b80022d, 0x7b814dad, + 0x7b884203, 0x7b89c081, 0x7b8a452d, 0x7b8d0403, + 0x7b908081, 0x7b91dc03, 0x7ba0052d, 0x7ba2c8ad, + 0x7ba84483, 0x7baac8ad, 0x7c400097, 0x7c404521, + 0x7c440d25, 0x7c4a8087, 0x7c4ac115, 0x7c4b4117, + 0x7c4c0d1f, 0x7c528217, 0x7c538099, 0x7c53c097, + 0x7c5a8197, 0x7c640097, 0x7c80012f, 0x7c808081, + 0x7c841603, 0x7c9004c1, 0x7c940103, 0x7efc051f, + 0xbe0001ac, 0xbe00d110, 0xbe0947ac, 0xbe0d3910, + 0xbe29872c, 0xbe2d022c, 0xbe2e3790, 0xbe49ff90, + 0xbe69bc10, +}; + +static const uint16_t unicode_decomp_table2[709] = { + 0x0020, 0x0000, 0x0061, 0x0002, 0x0004, 0x0006, 0x03bc, 0x0008, + 0x000a, 0x000c, 0x0015, 0x0095, 0x00a5, 0x00b9, 0x00c1, 0x00c3, + 0x00c7, 0x00cb, 0x00d1, 0x00d7, 0x00dd, 0x00e0, 0x00e6, 0x00f8, + 0x0108, 0x010a, 0x0073, 0x0110, 0x0112, 0x0114, 0x0120, 0x012c, + 0x0144, 0x014d, 0x0153, 0x0162, 0x0168, 0x016a, 0x0176, 0x0192, + 0x0194, 0x01a9, 0x01bb, 0x01c7, 0x01d1, 0x01d5, 0x02b9, 0x01d7, + 0x003b, 0x01d9, 0x01db, 0x00b7, 0x01e1, 0x01fc, 0x020c, 0x0218, + 0x021d, 0x0223, 0x0227, 0x03a3, 0x0233, 0x023f, 0x0242, 0x024b, + 0x024e, 0x0251, 0x025d, 0x0260, 0x0269, 0x026c, 0x026f, 0x0275, + 0x0278, 0x0281, 0x028a, 0x029c, 0x029f, 0x02a3, 0x02af, 0x02b9, + 0x02c5, 0x02c9, 0x02cd, 0x02d1, 0x02d5, 0x02e7, 0x02ed, 0x02f1, + 0x02f5, 0x02f9, 0x02fd, 0x0305, 0x0309, 0x030d, 0x0313, 0x0317, + 0x031b, 0x0323, 0x0327, 0x032b, 0x032f, 0x0335, 0x033d, 0x0341, + 0x0349, 0x034d, 0x0351, 0x0f0b, 0x0357, 0x035b, 0x035f, 0x0363, + 0x0367, 0x036b, 0x036f, 0x0373, 0x0379, 0x037d, 0x0381, 0x0385, + 0x0389, 0x038d, 0x0391, 0x0395, 0x0399, 0x039d, 0x03a1, 0x10dc, + 0x03a5, 0x03c9, 0x03cd, 0x03d9, 0x03dd, 0x03e1, 0x03ef, 0x03f1, + 0x043d, 0x044f, 0x0499, 0x04f0, 0x0502, 0x054a, 0x0564, 0x056c, + 0x0570, 0x0573, 0x059a, 0x05fa, 0x05fe, 0x0607, 0x060b, 0x0614, + 0x0618, 0x061e, 0x0622, 0x0628, 0x068e, 0x0694, 0x0698, 0x069e, + 0x06a2, 0x06ab, 0x03ac, 0x06f3, 0x03ad, 0x06f6, 0x03ae, 0x06f9, + 0x03af, 0x06fc, 0x03cc, 0x06ff, 0x03cd, 0x0702, 0x03ce, 0x0705, + 0x0709, 0x070d, 0x0711, 0x0386, 0x0732, 0x0735, 0x03b9, 0x0737, + 0x073b, 0x0388, 0x0753, 0x0389, 0x0756, 0x0390, 0x076b, 0x038a, + 0x0777, 0x03b0, 0x0789, 0x038e, 0x0799, 0x079f, 0x07a3, 0x038c, + 0x07b8, 0x038f, 0x07bb, 0x00b4, 0x07be, 0x07c0, 0x07c2, 0x2010, + 0x07cb, 0x002e, 0x07cd, 0x07cf, 0x0020, 0x07d2, 0x07d6, 0x07db, + 0x07df, 0x07e4, 0x07ea, 0x07f0, 0x0020, 0x07f6, 0x2212, 0x0801, + 0x0805, 0x0807, 0x081d, 0x0825, 0x0827, 0x0043, 0x082d, 0x0830, + 0x0190, 0x0836, 0x0839, 0x004e, 0x0845, 0x0847, 0x084c, 0x084e, + 0x0851, 0x005a, 0x03a9, 0x005a, 0x0853, 0x0857, 0x0860, 0x0069, + 0x0862, 0x0865, 0x086f, 0x0874, 0x087a, 0x087e, 0x08a2, 0x0049, + 0x08a4, 0x08a6, 0x08a9, 0x0056, 0x08ab, 0x08ad, 0x08b0, 0x08b4, + 0x0058, 0x08b6, 0x08b8, 0x08bb, 0x08c0, 0x08c2, 0x08c5, 0x0076, + 0x08c7, 0x08c9, 0x08cc, 0x08d0, 0x0078, 0x08d2, 0x08d4, 0x08d7, + 0x08db, 0x08de, 0x08e4, 0x08e7, 0x08f0, 0x08f3, 0x08f6, 0x08f9, + 0x0902, 0x0906, 0x090b, 0x090f, 0x0914, 0x0917, 0x091a, 0x0923, + 0x092c, 0x093b, 0x093e, 0x0941, 0x0944, 0x0947, 0x094a, 0x0956, + 0x095c, 0x0960, 0x0962, 0x0964, 0x0968, 0x096a, 0x0970, 0x0978, + 0x097c, 0x0980, 0x0986, 0x0989, 0x098f, 0x0991, 0x0030, 0x0993, + 0x0999, 0x099c, 0x099e, 0x09a1, 0x09a4, 0x2d61, 0x6bcd, 0x9f9f, + 0x09a6, 0x09b1, 0x09bc, 0x09c7, 0x0a95, 0x0aa1, 0x0b15, 0x0020, + 0x0b27, 0x0b31, 0x0b8d, 0x0ba1, 0x0ba5, 0x0ba9, 0x0bad, 0x0bb1, + 0x0bb5, 0x0bb9, 0x0bbd, 0x0bc1, 0x0bc5, 0x0c21, 0x0c35, 0x0c39, + 0x0c3d, 0x0c41, 0x0c45, 0x0c49, 0x0c4d, 0x0c51, 0x0c55, 0x0c59, + 0x0c6f, 0x0c71, 0x0c73, 0x0ca0, 0x0cbc, 0x0cdc, 0x0ce4, 0x0cec, + 0x0cf4, 0x0cfc, 0x0d04, 0x0d0c, 0x0d14, 0x0d22, 0x0d2e, 0x0d7a, + 0x0d82, 0x0d85, 0x0d89, 0x0d8d, 0x0d9d, 0x0db1, 0x0db5, 0x0dbc, + 0x0dc2, 0x0dc6, 0x0e28, 0x0e2c, 0x0e30, 0x0e32, 0x0e36, 0x0e3c, + 0x0e3e, 0x0e41, 0x0e43, 0x0e46, 0x0e77, 0x0e7b, 0x0e89, 0x0e8e, + 0x0e94, 0x0e9c, 0x0ea3, 0x0ea9, 0x0eb4, 0x0ebe, 0x0ec6, 0x0eca, + 0x0ecf, 0x0ed9, 0x0edd, 0x0ee4, 0x0eec, 0x0ef3, 0x0ef8, 0x0f04, + 0x0f0a, 0x0f15, 0x0f1b, 0x0f22, 0x0f28, 0x0f33, 0x0f3d, 0x0f45, + 0x0f4c, 0x0f51, 0x0f57, 0x0f5e, 0x0f63, 0x0f69, 0x0f70, 0x0f76, + 0x0f7d, 0x0f82, 0x0f89, 0x0f8d, 0x0f9e, 0x0fa4, 0x0fa9, 0x0fad, + 0x0fb8, 0x0fbe, 0x0fc9, 0x0fd0, 0x0fd6, 0x0fda, 0x0fe1, 0x0fe5, + 0x0fef, 0x0ffa, 0x1000, 0x1004, 0x1009, 0x100f, 0x1013, 0x101a, + 0x101f, 0x1023, 0x1029, 0x102f, 0x1032, 0x1036, 0x1039, 0x103f, + 0x1045, 0x1059, 0x1061, 0x1079, 0x107c, 0x1080, 0x1095, 0x10a1, + 0x10b1, 0x10c3, 0x10cb, 0x10cf, 0x10da, 0x10de, 0x10ea, 0x10f2, + 0x10f4, 0x1100, 0x1105, 0x1111, 0x1141, 0x1149, 0x114d, 0x1153, + 0x1157, 0x115a, 0x116e, 0x1171, 0x1175, 0x117b, 0x117d, 0x1181, + 0x1184, 0x118c, 0x1192, 0x1196, 0x119c, 0x11a2, 0x11a8, 0x11ab, + 0xa76f, 0x11af, 0x11b3, 0x11b7, 0x028d, 0x11bf, 0x1211, 0x130f, + 0x140d, 0x1491, 0x1496, 0x1554, 0x156d, 0x1573, 0x1579, 0x157f, + 0x158b, 0x1597, 0x002b, 0x15a2, 0x15ba, 0x15be, 0x15c2, 0x15c6, + 0x15ca, 0x15ce, 0x15e2, 0x15e6, 0x164a, 0x1663, 0x1689, 0x168f, + 0x174d, 0x1753, 0x1758, 0x1778, 0x1878, 0x187e, 0x1912, 0x19d4, + 0x1a78, 0x1a80, 0x1a9e, 0x1aa3, 0x1ab7, 0x1ac1, 0x1ac7, 0x1adb, + 0x1ae0, 0x1ae6, 0x1af4, 0x1b24, 0x1b31, 0x1b39, 0x1b3d, 0x1b53, + 0x1bca, 0x1bdc, 0x1bde, 0x1be0, 0x3164, 0x1c21, 0x1c23, 0x1c25, + 0x1c27, 0x1c29, 0x1c2b, 0x1c49, 0x1c4e, 0x1c53, 0x1c89, 0x1ccf, + 0x1cdd, 0x1ce2, 0x1ceb, 0x1cf4, 0x1d02, 0x1d07, 0x1d0c, 0x1d1e, + 0x1d30, 0x1d39, 0x1d3e, 0x1d62, 0x1d70, 0x1d72, 0x1d74, 0x1d94, + 0x1daf, 0x1db1, 0x1db3, 0x1db5, 0x1db7, 0x1db9, 0x1dbb, 0x1dbd, + 0x1ddd, 0x1ddf, 0x1de1, 0x1de3, 0x1de5, 0x1dec, 0x1dee, 0x1df0, + 0x1df2, 0x1e01, 0x1e03, 0x1e05, 0x1e07, 0x1e09, 0x1e0b, 0x1e0d, + 0x1e0f, 0x1e11, 0x1e13, 0x1e15, 0x1e17, 0x1e19, 0x1e1b, 0x1e1d, + 0x1e21, 0x03f4, 0x1e23, 0x2207, 0x1e25, 0x2202, 0x1e27, 0x1e2f, + 0x03f4, 0x1e31, 0x2207, 0x1e33, 0x2202, 0x1e35, 0x1e3d, 0x03f4, + 0x1e3f, 0x2207, 0x1e41, 0x2202, 0x1e43, 0x1e4b, 0x03f4, 0x1e4d, + 0x2207, 0x1e4f, 0x2202, 0x1e51, 0x1e59, 0x03f4, 0x1e5b, 0x2207, + 0x1e5d, 0x2202, 0x1e5f, 0x1e69, 0x1e6b, 0x1e6d, 0x1e6f, 0x1e71, + 0x1e73, 0x1e75, 0x1e77, 0x1e79, 0x1e81, 0x1ea4, 0x1ea8, 0x1eae, + 0x1ecb, 0x062d, 0x1ed3, 0x1edf, 0x062c, 0x1eef, 0x1f5f, 0x1f6b, + 0x1f7e, 0x1f90, 0x1fa3, 0x1fa5, 0x1fa9, 0x1faf, 0x1fb5, 0x1fb7, + 0x1fbb, 0x1fbd, 0x1fc5, 0x1fc8, 0x1fca, 0x1fd0, 0x1fd2, 0x30b5, + 0x1fd8, 0x2030, 0x2046, 0x204a, 0x204c, 0x2051, 0x209e, 0x20af, + 0x21b0, 0x21c0, 0x21c6, 0x22c0, 0x23de, +}; + +static const uint8_t unicode_decomp_data[9452] = { + 0x20, 0x88, 0x20, 0x84, 0x32, 0x33, 0x20, 0x81, + 0x20, 0xa7, 0x31, 0x6f, 0x31, 0xd0, 0x34, 0x31, + 0xd0, 0x32, 0x33, 0xd0, 0x34, 0x41, 0x80, 0x41, + 0x81, 0x41, 0x82, 0x41, 0x83, 0x41, 0x88, 0x41, + 0x8a, 0x00, 0x00, 0x43, 0xa7, 0x45, 0x80, 0x45, + 0x81, 0x45, 0x82, 0x45, 0x88, 0x49, 0x80, 0x49, + 0x81, 0x49, 0x82, 0x49, 0x88, 0x00, 0x00, 0x4e, + 0x83, 0x4f, 0x80, 0x4f, 0x81, 0x4f, 0x82, 0x4f, + 0x83, 0x4f, 0x88, 0x00, 0x00, 0x00, 0x00, 0x55, + 0x80, 0x55, 0x81, 0x55, 0x82, 0x55, 0x88, 0x59, + 0x81, 0x00, 0x00, 0x00, 0x00, 0x61, 0x80, 0x61, + 0x81, 0x61, 0x82, 0x61, 0x83, 0x61, 0x88, 0x61, + 0x8a, 0x00, 0x00, 0x63, 0xa7, 0x65, 0x80, 0x65, + 0x81, 0x65, 0x82, 0x65, 0x88, 0x69, 0x80, 0x69, + 0x81, 0x69, 0x82, 0x69, 0x88, 0x00, 0x00, 0x6e, + 0x83, 0x6f, 0x80, 0x6f, 0x81, 0x6f, 0x82, 0x6f, + 0x83, 0x6f, 0x88, 0x00, 0x00, 0x00, 0x00, 0x75, + 0x80, 0x75, 0x81, 0x75, 0x82, 0x75, 0x88, 0x79, + 0x81, 0x00, 0x00, 0x79, 0x88, 0x41, 0x84, 0x41, + 0x86, 0x41, 0xa8, 0x43, 0x81, 0x43, 0x82, 0x43, + 0x87, 0x43, 0x8c, 0x44, 0x8c, 0x45, 0x84, 0x45, + 0x86, 0x45, 0x87, 0x45, 0xa8, 0x45, 0x8c, 0x47, + 0x82, 0x47, 0x86, 0x47, 0x87, 0x47, 0xa7, 0x48, + 0x82, 0x49, 0x83, 0x49, 0x84, 0x49, 0x86, 0x49, + 0xa8, 0x49, 0x87, 0x49, 0x4a, 0x69, 0x6a, 0x4a, + 0x82, 0x4b, 0xa7, 0x4c, 0x81, 0x4c, 0xa7, 0x4c, + 0x8c, 0x4c, 0x00, 0x00, 0x6b, 0x20, 0x6b, 0x4e, + 0x81, 0x4e, 0xa7, 0x4e, 0x8c, 0xbc, 0x02, 0x6e, + 0x4f, 0x84, 0x4f, 0x86, 0x4f, 0x8b, 0x52, 0x81, + 0x52, 0xa7, 0x52, 0x8c, 0x53, 0x81, 0x53, 0x82, + 0x53, 0xa7, 0x53, 0x8c, 0x54, 0xa7, 0x54, 0x8c, + 0x55, 0x83, 0x55, 0x84, 0x55, 0x86, 0x55, 0x8a, + 0x55, 0x8b, 0x55, 0xa8, 0x57, 0x82, 0x59, 0x82, + 0x59, 0x88, 0x5a, 0x81, 0x5a, 0x87, 0x5a, 0x8c, + 0x4f, 0x9b, 0x55, 0x9b, 0x44, 0x00, 0x7d, 0x01, + 0x44, 0x00, 0x7e, 0x01, 0x64, 0x00, 0x7e, 0x01, + 0x4c, 0x4a, 0x4c, 0x6a, 0x6c, 0x6a, 0x4e, 0x4a, + 0x4e, 0x6a, 0x6e, 0x6a, 0x41, 0x00, 0x8c, 0x49, + 0x00, 0x8c, 0x4f, 0x00, 0x8c, 0x55, 0x00, 0x8c, + 0xdc, 0x00, 0x84, 0xdc, 0x00, 0x81, 0xdc, 0x00, + 0x8c, 0xdc, 0x00, 0x80, 0xc4, 0x00, 0x84, 0x26, + 0x02, 0x84, 0xc6, 0x00, 0x84, 0x47, 0x8c, 0x4b, + 0x8c, 0x4f, 0xa8, 0xea, 0x01, 0x84, 0xeb, 0x01, + 0x84, 0xb7, 0x01, 0x8c, 0x92, 0x02, 0x8c, 0x6a, + 0x00, 0x8c, 0x44, 0x5a, 0x44, 0x7a, 0x64, 0x7a, + 0x47, 0x81, 0x4e, 0x00, 0x80, 0xc5, 0x00, 0x81, + 0xc6, 0x00, 0x81, 0xd8, 0x00, 0x81, 0x41, 0x8f, + 0x41, 0x91, 0x45, 0x8f, 0x45, 0x91, 0x49, 0x8f, + 0x49, 0x91, 0x4f, 0x8f, 0x4f, 0x91, 0x52, 0x8f, + 0x52, 0x91, 0x55, 0x8f, 0x55, 0x91, 0x53, 0xa6, + 0x54, 0xa6, 0x48, 0x8c, 0x41, 0x00, 0x87, 0x45, + 0x00, 0xa7, 0xd6, 0x00, 0x84, 0xd5, 0x00, 0x84, + 0x4f, 0x00, 0x87, 0x2e, 0x02, 0x84, 0x59, 0x00, + 0x84, 0x68, 0x00, 0x66, 0x02, 0x6a, 0x00, 0x72, + 0x00, 0x79, 0x02, 0x7b, 0x02, 0x81, 0x02, 0x77, + 0x00, 0x79, 0x00, 0x20, 0x86, 0x20, 0x87, 0x20, + 0x8a, 0x20, 0xa8, 0x20, 0x83, 0x20, 0x8b, 0x63, + 0x02, 0x6c, 0x00, 0x73, 0x00, 0x78, 0x00, 0x95, + 0x02, 0x80, 0x81, 0x00, 0x93, 0x88, 0x81, 0x20, + 0xc5, 0x20, 0x81, 0xa8, 0x00, 0x81, 0x91, 0x03, + 0x81, 0x95, 0x03, 0x81, 0x97, 0x03, 0x81, 0x99, + 0x03, 0x81, 0x00, 0x00, 0x00, 0x9f, 0x03, 0x81, + 0x00, 0x00, 0x00, 0xa5, 0x03, 0x81, 0xa9, 0x03, + 0x81, 0xca, 0x03, 0x81, 0x01, 0x03, 0x98, 0x07, + 0xa4, 0x07, 0xb0, 0x00, 0xb4, 0x00, 0xb6, 0x00, + 0xb8, 0x00, 0xca, 0x00, 0x01, 0x03, 0xb8, 0x07, + 0xc4, 0x07, 0xbe, 0x00, 0xc4, 0x00, 0xc8, 0x00, + 0xa5, 0x03, 0x0d, 0x13, 0x00, 0x01, 0x03, 0xd1, + 0x00, 0xd1, 0x07, 0xc6, 0x03, 0xc0, 0x03, 0xba, + 0x03, 0xc1, 0x03, 0xc2, 0x03, 0x00, 0x00, 0x98, + 0x03, 0xb5, 0x03, 0x15, 0x04, 0x80, 0x15, 0x04, + 0x88, 0x00, 0x00, 0x00, 0x13, 0x04, 0x81, 0x06, + 0x04, 0x88, 0x1a, 0x04, 0x81, 0x18, 0x04, 0x80, + 0x23, 0x04, 0x86, 0x18, 0x04, 0x86, 0x38, 0x04, + 0x86, 0x35, 0x04, 0x80, 0x35, 0x04, 0x88, 0x00, + 0x00, 0x00, 0x33, 0x04, 0x81, 0x56, 0x04, 0x88, + 0x3a, 0x04, 0x81, 0x38, 0x04, 0x80, 0x43, 0x04, + 0x86, 0x74, 0x04, 0x8f, 0x16, 0x04, 0x86, 0x10, + 0x04, 0x86, 0x10, 0x04, 0x88, 0x15, 0x04, 0x86, + 0xd8, 0x04, 0x88, 0x16, 0x04, 0x88, 0x17, 0x04, + 0x88, 0x18, 0x04, 0x84, 0x18, 0x04, 0x88, 0x1e, + 0x04, 0x88, 0xe8, 0x04, 0x88, 0x2d, 0x04, 0x88, + 0x23, 0x04, 0x84, 0x23, 0x04, 0x88, 0x23, 0x04, + 0x8b, 0x27, 0x04, 0x88, 0x2b, 0x04, 0x88, 0x65, + 0x05, 0x82, 0x05, 0x27, 0x06, 0x00, 0x2c, 0x00, + 0x2d, 0x21, 0x2d, 0x00, 0x2e, 0x23, 0x2d, 0x27, + 0x06, 0x00, 0x4d, 0x21, 0x4d, 0xa0, 0x4d, 0x23, + 0x4d, 0xd5, 0x06, 0x54, 0x06, 0x00, 0x00, 0x00, + 0x00, 0xc1, 0x06, 0x54, 0x06, 0xd2, 0x06, 0x54, + 0x06, 0x28, 0x09, 0x3c, 0x09, 0x30, 0x09, 0x3c, + 0x09, 0x33, 0x09, 0x3c, 0x09, 0x15, 0x09, 0x00, + 0x27, 0x01, 0x27, 0x02, 0x27, 0x07, 0x27, 0x0c, + 0x27, 0x0d, 0x27, 0x16, 0x27, 0x1a, 0x27, 0xbe, + 0x09, 0x09, 0x00, 0x09, 0x19, 0xa1, 0x09, 0xbc, + 0x09, 0xaf, 0x09, 0xbc, 0x09, 0x32, 0x0a, 0x3c, + 0x0a, 0x38, 0x0a, 0x3c, 0x0a, 0x16, 0x0a, 0x00, + 0x26, 0x01, 0x26, 0x06, 0x26, 0x2b, 0x0a, 0x3c, + 0x0a, 0x47, 0x0b, 0x56, 0x0b, 0x3e, 0x0b, 0x09, + 0x00, 0x09, 0x19, 0x21, 0x0b, 0x3c, 0x0b, 0x92, + 0x0b, 0xd7, 0x0b, 0xbe, 0x0b, 0x08, 0x00, 0x09, + 0x00, 0x08, 0x19, 0x46, 0x0c, 0x56, 0x0c, 0xbf, + 0x0c, 0xd5, 0x0c, 0xc6, 0x0c, 0xd5, 0x0c, 0xc2, + 0x0c, 0x04, 0x00, 0x08, 0x13, 0x3e, 0x0d, 0x08, + 0x00, 0x09, 0x00, 0x08, 0x19, 0xd9, 0x0d, 0xca, + 0x0d, 0xca, 0x0d, 0x0f, 0x05, 0x12, 0x00, 0x0f, + 0x15, 0x4d, 0x0e, 0x32, 0x0e, 0xcd, 0x0e, 0xb2, + 0x0e, 0x99, 0x0e, 0x12, 0x00, 0x12, 0x08, 0x42, + 0x0f, 0xb7, 0x0f, 0x4c, 0x0f, 0xb7, 0x0f, 0x51, + 0x0f, 0xb7, 0x0f, 0x56, 0x0f, 0xb7, 0x0f, 0x5b, + 0x0f, 0xb7, 0x0f, 0x40, 0x0f, 0xb5, 0x0f, 0x71, + 0x0f, 0x72, 0x0f, 0x71, 0x0f, 0x00, 0x03, 0x41, + 0x0f, 0xb2, 0x0f, 0x81, 0x0f, 0xb3, 0x0f, 0x80, + 0x0f, 0xb3, 0x0f, 0x81, 0x0f, 0x71, 0x0f, 0x80, + 0x0f, 0x92, 0x0f, 0xb7, 0x0f, 0x9c, 0x0f, 0xb7, + 0x0f, 0xa1, 0x0f, 0xb7, 0x0f, 0xa6, 0x0f, 0xb7, + 0x0f, 0xab, 0x0f, 0xb7, 0x0f, 0x90, 0x0f, 0xb5, + 0x0f, 0x25, 0x10, 0x2e, 0x10, 0x05, 0x1b, 0x35, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x07, 0x1b, 0x35, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x09, 0x1b, 0x35, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x1b, 0x35, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x1b, 0x35, + 0x1b, 0x11, 0x1b, 0x35, 0x1b, 0x3a, 0x1b, 0x35, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x1b, 0x35, + 0x1b, 0x3e, 0x1b, 0x35, 0x1b, 0x42, 0x1b, 0x35, + 0x1b, 0x41, 0x00, 0xc6, 0x00, 0x42, 0x00, 0x00, + 0x00, 0x44, 0x00, 0x45, 0x00, 0x8e, 0x01, 0x47, + 0x00, 0x4f, 0x00, 0x22, 0x02, 0x50, 0x00, 0x52, + 0x00, 0x54, 0x00, 0x55, 0x00, 0x57, 0x00, 0x61, + 0x00, 0x50, 0x02, 0x51, 0x02, 0x02, 0x1d, 0x62, + 0x00, 0x64, 0x00, 0x65, 0x00, 0x59, 0x02, 0x5b, + 0x02, 0x5c, 0x02, 0x67, 0x00, 0x00, 0x00, 0x6b, + 0x00, 0x6d, 0x00, 0x4b, 0x01, 0x6f, 0x00, 0x54, + 0x02, 0x16, 0x1d, 0x17, 0x1d, 0x70, 0x00, 0x74, + 0x00, 0x75, 0x00, 0x1d, 0x1d, 0x6f, 0x02, 0x76, + 0x00, 0x25, 0x1d, 0xb2, 0x03, 0xb3, 0x03, 0xb4, + 0x03, 0xc6, 0x03, 0xc7, 0x03, 0x69, 0x00, 0x72, + 0x00, 0x75, 0x00, 0x76, 0x00, 0xb2, 0x03, 0xb3, + 0x03, 0xc1, 0x03, 0xc6, 0x03, 0xc7, 0x03, 0x52, + 0x02, 0x63, 0x00, 0x55, 0x02, 0xf0, 0x00, 0x5c, + 0x02, 0x66, 0x00, 0x5f, 0x02, 0x61, 0x02, 0x65, + 0x02, 0x68, 0x02, 0x69, 0x02, 0x6a, 0x02, 0x7b, + 0x1d, 0x9d, 0x02, 0x6d, 0x02, 0x85, 0x1d, 0x9f, + 0x02, 0x71, 0x02, 0x70, 0x02, 0x72, 0x02, 0x73, + 0x02, 0x74, 0x02, 0x75, 0x02, 0x78, 0x02, 0x82, + 0x02, 0x83, 0x02, 0xab, 0x01, 0x89, 0x02, 0x8a, + 0x02, 0x1c, 0x1d, 0x8b, 0x02, 0x8c, 0x02, 0x7a, + 0x00, 0x90, 0x02, 0x91, 0x02, 0x92, 0x02, 0xb8, + 0x03, 0x41, 0x00, 0xa5, 0x42, 0x00, 0x87, 0x42, + 0x00, 0xa3, 0x42, 0x00, 0xb1, 0xc7, 0x00, 0x81, + 0x44, 0x00, 0x87, 0x44, 0x00, 0xa3, 0x44, 0x00, + 0xb1, 0x44, 0x00, 0xa7, 0x44, 0x00, 0xad, 0x12, + 0x01, 0x80, 0x12, 0x01, 0x81, 0x45, 0x00, 0xad, + 0x45, 0x00, 0xb0, 0x28, 0x02, 0x86, 0x46, 0x00, + 0x87, 0x47, 0x00, 0x84, 0x48, 0x00, 0x87, 0x48, + 0x00, 0xa3, 0x48, 0x00, 0x88, 0x48, 0x00, 0xa7, + 0x48, 0x00, 0xae, 0x49, 0x00, 0xb0, 0xcf, 0x00, + 0x81, 0x4b, 0x00, 0x81, 0x4b, 0x00, 0xa3, 0x4b, + 0x00, 0xb1, 0x4c, 0x00, 0xa3, 0x36, 0x1e, 0x84, + 0x4c, 0xb1, 0x4c, 0xad, 0x4d, 0x81, 0x4d, 0x87, + 0x4d, 0xa3, 0x4e, 0x87, 0x4e, 0xa3, 0x4e, 0xb1, + 0x4e, 0xad, 0xd5, 0x00, 0x81, 0xd5, 0x00, 0x88, + 0x4c, 0x01, 0x80, 0x4c, 0x01, 0x81, 0x50, 0x00, + 0x81, 0x50, 0x00, 0x87, 0x52, 0x00, 0x87, 0x52, + 0x00, 0xa3, 0x5a, 0x1e, 0x84, 0x52, 0x00, 0xb1, + 0x53, 0x00, 0x87, 0x53, 0x00, 0xa3, 0x5a, 0x01, + 0x87, 0x60, 0x01, 0x87, 0x62, 0x1e, 0x87, 0x54, + 0x00, 0x87, 0x54, 0x00, 0xa3, 0x54, 0x00, 0xb1, + 0x54, 0x00, 0xad, 0x55, 0x00, 0xa4, 0x55, 0x00, + 0xb0, 0x55, 0x00, 0xad, 0x68, 0x01, 0x81, 0x6a, + 0x01, 0x88, 0x56, 0x83, 0x56, 0xa3, 0x57, 0x80, + 0x57, 0x81, 0x57, 0x88, 0x57, 0x87, 0x57, 0xa3, + 0x58, 0x87, 0x58, 0x88, 0x59, 0x87, 0x5a, 0x82, + 0x5a, 0xa3, 0x5a, 0xb1, 0x68, 0xb1, 0x74, 0x88, + 0x77, 0x8a, 0x79, 0x8a, 0x61, 0x00, 0xbe, 0x02, + 0x7f, 0x01, 0x87, 0x41, 0x00, 0xa3, 0x41, 0x00, + 0x89, 0xc2, 0x00, 0x81, 0xc2, 0x00, 0x80, 0xc2, + 0x00, 0x89, 0xc2, 0x00, 0x83, 0xa0, 0x1e, 0x82, + 0x02, 0x01, 0x81, 0x02, 0x01, 0x80, 0x02, 0x01, + 0x89, 0x02, 0x01, 0x83, 0xa0, 0x1e, 0x86, 0x45, + 0x00, 0xa3, 0x45, 0x00, 0x89, 0x45, 0x00, 0x83, + 0xca, 0x00, 0x81, 0xca, 0x00, 0x80, 0xca, 0x00, + 0x89, 0xca, 0x00, 0x83, 0xb8, 0x1e, 0x82, 0x49, + 0x00, 0x89, 0x49, 0x00, 0xa3, 0x4f, 0x00, 0xa3, + 0x4f, 0x00, 0x89, 0xd4, 0x00, 0x81, 0xd4, 0x00, + 0x80, 0xd4, 0x00, 0x89, 0xd4, 0x00, 0x83, 0xcc, + 0x1e, 0x82, 0xa0, 0x01, 0x81, 0xa0, 0x01, 0x80, + 0xa0, 0x01, 0x89, 0xa0, 0x01, 0x83, 0xa0, 0x01, + 0xa3, 0x55, 0x00, 0xa3, 0x55, 0x00, 0x89, 0xaf, + 0x01, 0x81, 0xaf, 0x01, 0x80, 0xaf, 0x01, 0x89, + 0xaf, 0x01, 0x83, 0xaf, 0x01, 0xa3, 0x59, 0x00, + 0x80, 0x59, 0x00, 0xa3, 0x59, 0x00, 0x89, 0x59, + 0x00, 0x83, 0xb1, 0x03, 0x13, 0x03, 0x00, 0x1f, + 0x80, 0x00, 0x1f, 0x81, 0x00, 0x1f, 0xc2, 0x91, + 0x03, 0x13, 0x03, 0x08, 0x1f, 0x80, 0x08, 0x1f, + 0x81, 0x08, 0x1f, 0xc2, 0xb5, 0x03, 0x13, 0x03, + 0x10, 0x1f, 0x80, 0x10, 0x1f, 0x81, 0x95, 0x03, + 0x13, 0x03, 0x18, 0x1f, 0x80, 0x18, 0x1f, 0x81, + 0xb7, 0x03, 0x93, 0xb7, 0x03, 0x94, 0x20, 0x1f, + 0x80, 0x21, 0x1f, 0x80, 0x20, 0x1f, 0x81, 0x21, + 0x1f, 0x81, 0x20, 0x1f, 0xc2, 0x21, 0x1f, 0xc2, + 0x97, 0x03, 0x93, 0x97, 0x03, 0x94, 0x28, 0x1f, + 0x80, 0x29, 0x1f, 0x80, 0x28, 0x1f, 0x81, 0x29, + 0x1f, 0x81, 0x28, 0x1f, 0xc2, 0x29, 0x1f, 0xc2, + 0xb9, 0x03, 0x93, 0xb9, 0x03, 0x94, 0x30, 0x1f, + 0x80, 0x31, 0x1f, 0x80, 0x30, 0x1f, 0x81, 0x31, + 0x1f, 0x81, 0x30, 0x1f, 0xc2, 0x31, 0x1f, 0xc2, + 0x99, 0x03, 0x93, 0x99, 0x03, 0x94, 0x38, 0x1f, + 0x80, 0x39, 0x1f, 0x80, 0x38, 0x1f, 0x81, 0x39, + 0x1f, 0x81, 0x38, 0x1f, 0xc2, 0x39, 0x1f, 0xc2, + 0xbf, 0x03, 0x93, 0xbf, 0x03, 0x94, 0x40, 0x1f, + 0x80, 0x40, 0x1f, 0x81, 0x9f, 0x03, 0x13, 0x03, + 0x48, 0x1f, 0x80, 0x48, 0x1f, 0x81, 0xc5, 0x03, + 0x13, 0x03, 0x50, 0x1f, 0x80, 0x50, 0x1f, 0x81, + 0x50, 0x1f, 0xc2, 0xa5, 0x03, 0x94, 0x00, 0x00, + 0x00, 0x59, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x59, + 0x1f, 0x81, 0x00, 0x00, 0x00, 0x59, 0x1f, 0xc2, + 0xc9, 0x03, 0x93, 0xc9, 0x03, 0x94, 0x60, 0x1f, + 0x80, 0x61, 0x1f, 0x80, 0x60, 0x1f, 0x81, 0x61, + 0x1f, 0x81, 0x60, 0x1f, 0xc2, 0x61, 0x1f, 0xc2, + 0xa9, 0x03, 0x93, 0xa9, 0x03, 0x94, 0x68, 0x1f, + 0x80, 0x69, 0x1f, 0x80, 0x68, 0x1f, 0x81, 0x69, + 0x1f, 0x81, 0x68, 0x1f, 0xc2, 0x69, 0x1f, 0xc2, + 0xb1, 0x03, 0x80, 0xb5, 0x03, 0x80, 0xb7, 0x03, + 0x80, 0xb9, 0x03, 0x80, 0xbf, 0x03, 0x80, 0xc5, + 0x03, 0x80, 0xc9, 0x03, 0x80, 0x00, 0x1f, 0x45, + 0x03, 0x20, 0x1f, 0x45, 0x03, 0x60, 0x1f, 0x45, + 0x03, 0xb1, 0x03, 0x86, 0xb1, 0x03, 0x84, 0x70, + 0x1f, 0xc5, 0xb1, 0x03, 0xc5, 0xac, 0x03, 0xc5, + 0x00, 0x00, 0x00, 0xb1, 0x03, 0xc2, 0xb6, 0x1f, + 0xc5, 0x91, 0x03, 0x86, 0x91, 0x03, 0x84, 0x91, + 0x03, 0x80, 0x91, 0x03, 0xc5, 0x20, 0x93, 0x20, + 0x93, 0x20, 0xc2, 0xa8, 0x00, 0xc2, 0x74, 0x1f, + 0xc5, 0xb7, 0x03, 0xc5, 0xae, 0x03, 0xc5, 0x00, + 0x00, 0x00, 0xb7, 0x03, 0xc2, 0xc6, 0x1f, 0xc5, + 0x95, 0x03, 0x80, 0x97, 0x03, 0x80, 0x97, 0x03, + 0xc5, 0xbf, 0x1f, 0x80, 0xbf, 0x1f, 0x81, 0xbf, + 0x1f, 0xc2, 0xb9, 0x03, 0x86, 0xb9, 0x03, 0x84, + 0xca, 0x03, 0x80, 0x00, 0x03, 0xb9, 0x42, 0xca, + 0x42, 0x99, 0x06, 0x99, 0x04, 0x99, 0x00, 0xfe, + 0x1f, 0x80, 0xfe, 0x1f, 0x81, 0xfe, 0x1f, 0xc2, + 0xc5, 0x03, 0x86, 0xc5, 0x03, 0x84, 0xcb, 0x03, + 0x80, 0x00, 0x03, 0xc1, 0x13, 0xc1, 0x14, 0xc5, + 0x42, 0xcb, 0x42, 0xa5, 0x06, 0xa5, 0x04, 0xa5, + 0x00, 0xa1, 0x03, 0x94, 0xa8, 0x00, 0x80, 0x85, + 0x03, 0x60, 0x00, 0x7c, 0x1f, 0xc5, 0xc9, 0x03, + 0xc5, 0xce, 0x03, 0xc5, 0x00, 0x00, 0x00, 0xc9, + 0x03, 0xc2, 0xf6, 0x1f, 0xc5, 0x9f, 0x03, 0x80, + 0xa9, 0x03, 0x80, 0xa9, 0x03, 0xc5, 0x20, 0x94, + 0x02, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0xb3, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x32, 0x20, 0x32, 0x20, 0x32, 0x20, + 0x00, 0x00, 0x00, 0x35, 0x20, 0x35, 0x20, 0x35, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x21, 0x00, 0x00, + 0x20, 0x85, 0x3f, 0x3f, 0x3f, 0x21, 0x21, 0x3f, + 0x32, 0x20, 0x00, 0x00, 0x00, 0x00, 0x30, 0x69, + 0x00, 0x00, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x2b, 0x3d, 0x28, 0x29, 0x6e, 0x30, 0x00, 0x2b, + 0x00, 0x12, 0x22, 0x3d, 0x00, 0x28, 0x00, 0x29, + 0x00, 0x00, 0x00, 0x61, 0x00, 0x65, 0x00, 0x6f, + 0x00, 0x78, 0x00, 0x59, 0x02, 0x68, 0x6b, 0x6c, + 0x6d, 0x6e, 0x70, 0x73, 0x74, 0x52, 0x73, 0x61, + 0x2f, 0x63, 0x61, 0x2f, 0x73, 0xb0, 0x00, 0x43, + 0x63, 0x2f, 0x6f, 0x63, 0x2f, 0x75, 0xb0, 0x00, + 0x46, 0x48, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x20, + 0xdf, 0x01, 0x01, 0x04, 0x24, 0x4e, 0x6f, 0x50, + 0x51, 0x52, 0x52, 0x52, 0x53, 0x4d, 0x54, 0x45, + 0x4c, 0x54, 0x4d, 0x4b, 0x00, 0xc5, 0x00, 0x42, + 0x43, 0x00, 0x65, 0x45, 0x46, 0x00, 0x4d, 0x6f, + 0xd0, 0x05, 0x46, 0x41, 0x58, 0xc0, 0x03, 0xb3, + 0x03, 0x93, 0x03, 0xa0, 0x03, 0x11, 0x22, 0x44, + 0x64, 0x65, 0x69, 0x6a, 0x31, 0xd0, 0x37, 0x31, + 0xd0, 0x39, 0x31, 0xd0, 0x31, 0x30, 0x31, 0xd0, + 0x33, 0x32, 0xd0, 0x33, 0x31, 0xd0, 0x35, 0x32, + 0xd0, 0x35, 0x33, 0xd0, 0x35, 0x34, 0xd0, 0x35, + 0x31, 0xd0, 0x36, 0x35, 0xd0, 0x36, 0x31, 0xd0, + 0x38, 0x33, 0xd0, 0x38, 0x35, 0xd0, 0x38, 0x37, + 0xd0, 0x38, 0x31, 0xd0, 0x49, 0x49, 0x49, 0x49, + 0x49, 0x49, 0x56, 0x56, 0x49, 0x56, 0x49, 0x49, + 0x56, 0x49, 0x49, 0x49, 0x49, 0x58, 0x58, 0x49, + 0x58, 0x49, 0x49, 0x4c, 0x43, 0x44, 0x4d, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x76, 0x76, + 0x69, 0x76, 0x69, 0x69, 0x76, 0x69, 0x69, 0x69, + 0x69, 0x78, 0x78, 0x69, 0x78, 0x69, 0x69, 0x6c, + 0x63, 0x64, 0x6d, 0x30, 0xd0, 0x33, 0x90, 0x21, + 0xb8, 0x92, 0x21, 0xb8, 0x94, 0x21, 0xb8, 0xd0, + 0x21, 0xb8, 0xd4, 0x21, 0xb8, 0xd2, 0x21, 0xb8, + 0x03, 0x22, 0xb8, 0x08, 0x22, 0xb8, 0x0b, 0x22, + 0xb8, 0x23, 0x22, 0xb8, 0x00, 0x00, 0x00, 0x25, + 0x22, 0xb8, 0x2b, 0x22, 0x2b, 0x22, 0x2b, 0x22, + 0x00, 0x00, 0x00, 0x2e, 0x22, 0x2e, 0x22, 0x2e, + 0x22, 0x00, 0x00, 0x00, 0x3c, 0x22, 0xb8, 0x43, + 0x22, 0xb8, 0x45, 0x22, 0xb8, 0x00, 0x00, 0x00, + 0x48, 0x22, 0xb8, 0x3d, 0x00, 0xb8, 0x00, 0x00, + 0x00, 0x61, 0x22, 0xb8, 0x4d, 0x22, 0xb8, 0x3c, + 0x00, 0xb8, 0x3e, 0x00, 0xb8, 0x64, 0x22, 0xb8, + 0x65, 0x22, 0xb8, 0x72, 0x22, 0xb8, 0x76, 0x22, + 0xb8, 0x7a, 0x22, 0xb8, 0x82, 0x22, 0xb8, 0x86, + 0x22, 0xb8, 0xa2, 0x22, 0xb8, 0xa8, 0x22, 0xb8, + 0xa9, 0x22, 0xb8, 0xab, 0x22, 0xb8, 0x7c, 0x22, + 0xb8, 0x91, 0x22, 0xb8, 0xb2, 0x22, 0x38, 0x03, + 0x08, 0x30, 0x31, 0x00, 0x31, 0x00, 0x30, 0x00, + 0x32, 0x30, 0x28, 0x00, 0x31, 0x00, 0x29, 0x00, + 0x28, 0x00, 0x31, 0x00, 0x30, 0x00, 0x29, 0x00, + 0x28, 0x32, 0x30, 0x29, 0x31, 0x00, 0x2e, 0x00, + 0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x32, 0x30, + 0x2e, 0x28, 0x00, 0x61, 0x00, 0x29, 0x00, 0x41, + 0x00, 0x61, 0x00, 0x2b, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x3a, 0x3a, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, + 0x3d, 0xdd, 0x2a, 0xb8, 0x6a, 0x56, 0x00, 0x4e, + 0x00, 0x28, 0x36, 0x3f, 0x59, 0x85, 0x8c, 0xa0, + 0xba, 0x3f, 0x51, 0x00, 0x26, 0x2c, 0x43, 0x57, + 0x6c, 0xa1, 0xb6, 0xc1, 0x9b, 0x52, 0x00, 0x5e, + 0x7a, 0x7f, 0x9d, 0xa6, 0xc1, 0xce, 0xe7, 0xb6, + 0x53, 0xc8, 0x53, 0xe3, 0x53, 0xd7, 0x56, 0x1f, + 0x57, 0xeb, 0x58, 0x02, 0x59, 0x0a, 0x59, 0x15, + 0x59, 0x27, 0x59, 0x73, 0x59, 0x50, 0x5b, 0x80, + 0x5b, 0xf8, 0x5b, 0x0f, 0x5c, 0x22, 0x5c, 0x38, + 0x5c, 0x6e, 0x5c, 0x71, 0x5c, 0xdb, 0x5d, 0xe5, + 0x5d, 0xf1, 0x5d, 0xfe, 0x5d, 0x72, 0x5e, 0x7a, + 0x5e, 0x7f, 0x5e, 0xf4, 0x5e, 0xfe, 0x5e, 0x0b, + 0x5f, 0x13, 0x5f, 0x50, 0x5f, 0x61, 0x5f, 0x73, + 0x5f, 0xc3, 0x5f, 0x08, 0x62, 0x36, 0x62, 0x4b, + 0x62, 0x2f, 0x65, 0x34, 0x65, 0x87, 0x65, 0x97, + 0x65, 0xa4, 0x65, 0xb9, 0x65, 0xe0, 0x65, 0xe5, + 0x65, 0xf0, 0x66, 0x08, 0x67, 0x28, 0x67, 0x20, + 0x6b, 0x62, 0x6b, 0x79, 0x6b, 0xb3, 0x6b, 0xcb, + 0x6b, 0xd4, 0x6b, 0xdb, 0x6b, 0x0f, 0x6c, 0x14, + 0x6c, 0x34, 0x6c, 0x6b, 0x70, 0x2a, 0x72, 0x36, + 0x72, 0x3b, 0x72, 0x3f, 0x72, 0x47, 0x72, 0x59, + 0x72, 0x5b, 0x72, 0xac, 0x72, 0x84, 0x73, 0x89, + 0x73, 0xdc, 0x74, 0xe6, 0x74, 0x18, 0x75, 0x1f, + 0x75, 0x28, 0x75, 0x30, 0x75, 0x8b, 0x75, 0x92, + 0x75, 0x76, 0x76, 0x7d, 0x76, 0xae, 0x76, 0xbf, + 0x76, 0xee, 0x76, 0xdb, 0x77, 0xe2, 0x77, 0xf3, + 0x77, 0x3a, 0x79, 0xb8, 0x79, 0xbe, 0x79, 0x74, + 0x7a, 0xcb, 0x7a, 0xf9, 0x7a, 0x73, 0x7c, 0xf8, + 0x7c, 0x36, 0x7f, 0x51, 0x7f, 0x8a, 0x7f, 0xbd, + 0x7f, 0x01, 0x80, 0x0c, 0x80, 0x12, 0x80, 0x33, + 0x80, 0x7f, 0x80, 0x89, 0x80, 0xe3, 0x81, 0x00, + 0x07, 0x10, 0x19, 0x29, 0x38, 0x3c, 0x8b, 0x8f, + 0x95, 0x4d, 0x86, 0x6b, 0x86, 0x40, 0x88, 0x4c, + 0x88, 0x63, 0x88, 0x7e, 0x89, 0x8b, 0x89, 0xd2, + 0x89, 0x00, 0x8a, 0x37, 0x8c, 0x46, 0x8c, 0x55, + 0x8c, 0x78, 0x8c, 0x9d, 0x8c, 0x64, 0x8d, 0x70, + 0x8d, 0xb3, 0x8d, 0xab, 0x8e, 0xca, 0x8e, 0x9b, + 0x8f, 0xb0, 0x8f, 0xb5, 0x8f, 0x91, 0x90, 0x49, + 0x91, 0xc6, 0x91, 0xcc, 0x91, 0xd1, 0x91, 0x77, + 0x95, 0x80, 0x95, 0x1c, 0x96, 0xb6, 0x96, 0xb9, + 0x96, 0xe8, 0x96, 0x51, 0x97, 0x5e, 0x97, 0x62, + 0x97, 0x69, 0x97, 0xcb, 0x97, 0xed, 0x97, 0xf3, + 0x97, 0x01, 0x98, 0xa8, 0x98, 0xdb, 0x98, 0xdf, + 0x98, 0x96, 0x99, 0x99, 0x99, 0xac, 0x99, 0xa8, + 0x9a, 0xd8, 0x9a, 0xdf, 0x9a, 0x25, 0x9b, 0x2f, + 0x9b, 0x32, 0x9b, 0x3c, 0x9b, 0x5a, 0x9b, 0xe5, + 0x9c, 0x75, 0x9e, 0x7f, 0x9e, 0xa5, 0x9e, 0x00, + 0x16, 0x1e, 0x28, 0x2c, 0x54, 0x58, 0x69, 0x6e, + 0x7b, 0x96, 0xa5, 0xad, 0xe8, 0xf7, 0xfb, 0x12, + 0x30, 0x00, 0x00, 0x41, 0x53, 0x44, 0x53, 0x45, + 0x53, 0x4b, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x4d, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x4f, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x51, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x53, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x55, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x57, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x59, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x5b, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x5d, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x5f, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x61, 0x30, 0x99, 0x30, 0x64, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x66, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x68, 0x30, 0x99, + 0x30, 0x6f, 0x30, 0x99, 0x30, 0x72, 0x30, 0x99, + 0x30, 0x75, 0x30, 0x99, 0x30, 0x78, 0x30, 0x99, + 0x30, 0x7b, 0x30, 0x99, 0x30, 0x46, 0x30, 0x99, + 0x30, 0x20, 0x00, 0x99, 0x30, 0x9d, 0x30, 0x99, + 0x30, 0x88, 0x30, 0x8a, 0x30, 0xab, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xad, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xaf, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xb3, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xc1, 0x30, 0x99, + 0x30, 0xc4, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0xc8, 0x30, 0x99, 0x30, 0xcf, 0x30, 0x99, + 0x30, 0xd2, 0x30, 0x99, 0x30, 0xd5, 0x30, 0x99, + 0x30, 0xd8, 0x30, 0x99, 0x30, 0xdb, 0x30, 0x99, + 0x30, 0xa6, 0x30, 0x99, 0x30, 0xef, 0x30, 0x99, + 0x30, 0xfd, 0x30, 0x99, 0x30, 0xb3, 0x30, 0xc8, + 0x30, 0x00, 0x11, 0x00, 0x01, 0xaa, 0x02, 0xac, + 0xad, 0x03, 0x04, 0x05, 0xb0, 0xb1, 0xb2, 0xb3, + 0xb4, 0xb5, 0x1a, 0x06, 0x07, 0x08, 0x21, 0x09, + 0x11, 0x61, 0x11, 0x14, 0x11, 0x4c, 0x00, 0x01, + 0xb3, 0xb4, 0xb8, 0xba, 0xbf, 0xc3, 0xc5, 0x08, + 0xc9, 0xcb, 0x09, 0x0a, 0x0c, 0x0e, 0x0f, 0x13, + 0x15, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1e, 0x22, + 0x2c, 0x33, 0x38, 0xdd, 0xde, 0x43, 0x44, 0x45, + 0x70, 0x71, 0x74, 0x7d, 0x7e, 0x80, 0x8a, 0x8d, + 0x00, 0x4e, 0x8c, 0x4e, 0x09, 0x4e, 0xdb, 0x56, + 0x0a, 0x4e, 0x2d, 0x4e, 0x0b, 0x4e, 0x32, 0x75, + 0x59, 0x4e, 0x19, 0x4e, 0x01, 0x4e, 0x29, 0x59, + 0x30, 0x57, 0xba, 0x4e, 0x28, 0x00, 0x29, 0x00, + 0x00, 0x11, 0x02, 0x11, 0x03, 0x11, 0x05, 0x11, + 0x06, 0x11, 0x07, 0x11, 0x09, 0x11, 0x0b, 0x11, + 0x0c, 0x11, 0x0e, 0x11, 0x0f, 0x11, 0x10, 0x11, + 0x11, 0x11, 0x12, 0x11, 0x28, 0x00, 0x00, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x02, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x05, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x09, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0b, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0e, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0c, 0x11, + 0x6e, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0b, 0x11, + 0x69, 0x11, 0x0c, 0x11, 0x65, 0x11, 0xab, 0x11, + 0x29, 0x00, 0x28, 0x00, 0x0b, 0x11, 0x69, 0x11, + 0x12, 0x11, 0x6e, 0x11, 0x29, 0x00, 0x28, 0x00, + 0x29, 0x00, 0x00, 0x4e, 0x8c, 0x4e, 0x09, 0x4e, + 0xdb, 0x56, 0x94, 0x4e, 0x6d, 0x51, 0x03, 0x4e, + 0x6b, 0x51, 0x5d, 0x4e, 0x41, 0x53, 0x08, 0x67, + 0x6b, 0x70, 0x34, 0x6c, 0x28, 0x67, 0xd1, 0x91, + 0x1f, 0x57, 0xe5, 0x65, 0x2a, 0x68, 0x09, 0x67, + 0x3e, 0x79, 0x0d, 0x54, 0x79, 0x72, 0xa1, 0x8c, + 0x5d, 0x79, 0xb4, 0x52, 0xe3, 0x4e, 0x7c, 0x54, + 0x66, 0x5b, 0xe3, 0x76, 0x01, 0x4f, 0xc7, 0x8c, + 0x54, 0x53, 0x6d, 0x79, 0x11, 0x4f, 0xea, 0x81, + 0xf3, 0x81, 0x4f, 0x55, 0x7c, 0x5e, 0x87, 0x65, + 0x8f, 0x7b, 0x50, 0x54, 0x45, 0x32, 0x00, 0x31, + 0x00, 0x33, 0x00, 0x30, 0x00, 0x00, 0x11, 0x00, + 0x02, 0x03, 0x05, 0x06, 0x07, 0x09, 0x0b, 0x0c, + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x00, 0x11, 0x00, + 0x61, 0x02, 0x61, 0x03, 0x61, 0x05, 0x61, 0x06, + 0x61, 0x07, 0x61, 0x09, 0x61, 0x0b, 0x61, 0x0c, + 0x61, 0x0e, 0x11, 0x61, 0x11, 0x00, 0x11, 0x0e, + 0x61, 0xb7, 0x00, 0x69, 0x0b, 0x11, 0x01, 0x63, + 0x00, 0x69, 0x0b, 0x11, 0x6e, 0x11, 0x00, 0x4e, + 0x8c, 0x4e, 0x09, 0x4e, 0xdb, 0x56, 0x94, 0x4e, + 0x6d, 0x51, 0x03, 0x4e, 0x6b, 0x51, 0x5d, 0x4e, + 0x41, 0x53, 0x08, 0x67, 0x6b, 0x70, 0x34, 0x6c, + 0x28, 0x67, 0xd1, 0x91, 0x1f, 0x57, 0xe5, 0x65, + 0x2a, 0x68, 0x09, 0x67, 0x3e, 0x79, 0x0d, 0x54, + 0x79, 0x72, 0xa1, 0x8c, 0x5d, 0x79, 0xb4, 0x52, + 0xd8, 0x79, 0x37, 0x75, 0x73, 0x59, 0x69, 0x90, + 0x2a, 0x51, 0x70, 0x53, 0xe8, 0x6c, 0x05, 0x98, + 0x11, 0x4f, 0x99, 0x51, 0x63, 0x6b, 0x0a, 0x4e, + 0x2d, 0x4e, 0x0b, 0x4e, 0xe6, 0x5d, 0xf3, 0x53, + 0x3b, 0x53, 0x97, 0x5b, 0x66, 0x5b, 0xe3, 0x76, + 0x01, 0x4f, 0xc7, 0x8c, 0x54, 0x53, 0x1c, 0x59, + 0x33, 0x00, 0x36, 0x00, 0x34, 0x00, 0x30, 0x00, + 0x35, 0x30, 0x31, 0x00, 0x08, 0x67, 0x31, 0x00, + 0x30, 0x00, 0x08, 0x67, 0x48, 0x67, 0x65, 0x72, + 0x67, 0x65, 0x56, 0x4c, 0x54, 0x44, 0xa2, 0x30, + 0x00, 0x02, 0x04, 0x06, 0x08, 0x09, 0x0b, 0x0d, + 0x0f, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1b, 0x1d, + 0x1f, 0x22, 0x24, 0x26, 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x30, 0x33, 0x36, 0x39, 0x3c, 0x3d, + 0x3e, 0x3f, 0x40, 0x42, 0x44, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x4b, 0x4d, 0x4e, 0x4f, 0x50, 0xe4, + 0x4e, 0x8c, 0x54, 0xa1, 0x30, 0x01, 0x30, 0x5b, + 0x27, 0x01, 0x4a, 0x34, 0x00, 0x01, 0x52, 0x39, + 0x01, 0xa2, 0x30, 0x00, 0x5a, 0x49, 0xa4, 0x30, + 0x00, 0x27, 0x4f, 0x0c, 0xa4, 0x30, 0x00, 0x4f, + 0x1d, 0x02, 0x05, 0x4f, 0xa8, 0x30, 0x00, 0x11, + 0x07, 0x54, 0x21, 0xa8, 0x30, 0x00, 0x54, 0x03, + 0x54, 0xa4, 0x30, 0x06, 0x4f, 0x15, 0x06, 0x58, + 0x3c, 0x07, 0x00, 0x46, 0xab, 0x30, 0x00, 0x3e, + 0x18, 0x1d, 0x00, 0x42, 0x3f, 0x51, 0xac, 0x30, + 0x00, 0x41, 0x47, 0x00, 0x47, 0x32, 0xae, 0x30, + 0xac, 0x30, 0xae, 0x30, 0x00, 0x1d, 0x4e, 0xad, + 0x30, 0x00, 0x38, 0x3d, 0x4f, 0x01, 0x3e, 0x13, + 0x4f, 0xad, 0x30, 0xed, 0x30, 0xad, 0x30, 0x00, + 0x40, 0x03, 0x3c, 0x33, 0xad, 0x30, 0x00, 0x40, + 0x34, 0x4f, 0x1b, 0x3e, 0xad, 0x30, 0x00, 0x40, + 0x42, 0x16, 0x1b, 0xb0, 0x30, 0x00, 0x39, 0x30, + 0xa4, 0x30, 0x0c, 0x45, 0x3c, 0x24, 0x4f, 0x0b, + 0x47, 0x18, 0x00, 0x49, 0xaf, 0x30, 0x00, 0x3e, + 0x4d, 0x1e, 0xb1, 0x30, 0x00, 0x4b, 0x08, 0x02, + 0x3a, 0x19, 0x02, 0x4b, 0x2c, 0xa4, 0x30, 0x11, + 0x00, 0x0b, 0x47, 0xb5, 0x30, 0x00, 0x3e, 0x0c, + 0x47, 0x2b, 0xb0, 0x30, 0x07, 0x3a, 0x43, 0x00, + 0xb9, 0x30, 0x02, 0x3a, 0x08, 0x02, 0x3a, 0x0f, + 0x07, 0x43, 0x00, 0xb7, 0x30, 0x10, 0x00, 0x12, + 0x34, 0x11, 0x3c, 0x13, 0x17, 0xa4, 0x30, 0x2a, + 0x1f, 0x24, 0x2b, 0x00, 0x20, 0xbb, 0x30, 0x16, + 0x41, 0x00, 0x38, 0x0d, 0xc4, 0x30, 0x0d, 0x38, + 0x00, 0xd0, 0x30, 0x00, 0x2c, 0x1c, 0x1b, 0xa2, + 0x30, 0x32, 0x00, 0x17, 0x26, 0x49, 0xaf, 0x30, + 0x25, 0x00, 0x3c, 0xb3, 0x30, 0x21, 0x00, 0x20, + 0x38, 0xa1, 0x30, 0x34, 0x00, 0x48, 0x22, 0x28, + 0xa3, 0x30, 0x32, 0x00, 0x59, 0x25, 0xa7, 0x30, + 0x2f, 0x1c, 0x10, 0x00, 0x44, 0xd5, 0x30, 0x00, + 0x14, 0x1e, 0xaf, 0x30, 0x29, 0x00, 0x10, 0x4d, + 0x3c, 0xda, 0x30, 0xbd, 0x30, 0xb8, 0x30, 0x22, + 0x13, 0x1a, 0x20, 0x33, 0x0c, 0x22, 0x3b, 0x01, + 0x22, 0x44, 0x00, 0x21, 0x44, 0x07, 0xa4, 0x30, + 0x39, 0x00, 0x4f, 0x24, 0xc8, 0x30, 0x14, 0x23, + 0x00, 0xdb, 0x30, 0xf3, 0x30, 0xc9, 0x30, 0x14, + 0x2a, 0x00, 0x12, 0x33, 0x22, 0x12, 0x33, 0x2a, + 0xa4, 0x30, 0x3a, 0x00, 0x0b, 0x49, 0xa4, 0x30, + 0x3a, 0x00, 0x47, 0x3a, 0x1f, 0x2b, 0x3a, 0x47, + 0x0b, 0xb7, 0x30, 0x27, 0x3c, 0x00, 0x30, 0x3c, + 0xaf, 0x30, 0x30, 0x00, 0x3e, 0x44, 0xdf, 0x30, + 0xea, 0x30, 0xd0, 0x30, 0x0f, 0x1a, 0x00, 0x2c, + 0x1b, 0xe1, 0x30, 0xac, 0x30, 0xac, 0x30, 0x35, + 0x00, 0x1c, 0x47, 0x35, 0x50, 0x1c, 0x3f, 0xa2, + 0x30, 0x42, 0x5a, 0x27, 0x42, 0x5a, 0x49, 0x44, + 0x00, 0x51, 0xc3, 0x30, 0x27, 0x00, 0x05, 0x28, + 0xea, 0x30, 0xe9, 0x30, 0xd4, 0x30, 0x17, 0x00, + 0x28, 0xd6, 0x30, 0x15, 0x26, 0x00, 0x15, 0xec, + 0x30, 0xe0, 0x30, 0xb2, 0x30, 0x3a, 0x41, 0x16, + 0x00, 0x41, 0xc3, 0x30, 0x2c, 0x00, 0x05, 0x30, + 0x00, 0xb9, 0x70, 0x31, 0x00, 0x30, 0x00, 0xb9, + 0x70, 0x32, 0x00, 0x30, 0x00, 0xb9, 0x70, 0x68, + 0x50, 0x61, 0x64, 0x61, 0x41, 0x55, 0x62, 0x61, + 0x72, 0x6f, 0x56, 0x70, 0x63, 0x64, 0x6d, 0x64, + 0x00, 0x6d, 0x00, 0xb2, 0x00, 0x49, 0x00, 0x55, + 0x00, 0x73, 0x5e, 0x10, 0x62, 0x2d, 0x66, 0x8c, + 0x54, 0x27, 0x59, 0x63, 0x6b, 0x0e, 0x66, 0xbb, + 0x6c, 0x2a, 0x68, 0x0f, 0x5f, 0x1a, 0x4f, 0x3e, + 0x79, 0x70, 0x00, 0x41, 0x6e, 0x00, 0x41, 0xbc, + 0x03, 0x41, 0x6d, 0x00, 0x41, 0x6b, 0x00, 0x41, + 0x4b, 0x00, 0x42, 0x4d, 0x00, 0x42, 0x47, 0x00, + 0x42, 0x63, 0x61, 0x6c, 0x6b, 0x63, 0x61, 0x6c, + 0x70, 0x00, 0x46, 0x6e, 0x00, 0x46, 0xbc, 0x03, + 0x46, 0xbc, 0x03, 0x67, 0x6d, 0x00, 0x67, 0x6b, + 0x00, 0x67, 0x48, 0x00, 0x7a, 0x6b, 0x48, 0x7a, + 0x4d, 0x48, 0x7a, 0x47, 0x48, 0x7a, 0x54, 0x48, + 0x7a, 0xbc, 0x03, 0x13, 0x21, 0x6d, 0x00, 0x13, + 0x21, 0x64, 0x00, 0x13, 0x21, 0x6b, 0x00, 0x13, + 0x21, 0x66, 0x00, 0x6d, 0x6e, 0x00, 0x6d, 0xbc, + 0x03, 0x6d, 0x6d, 0x00, 0x6d, 0x63, 0x00, 0x6d, + 0x6b, 0x00, 0x6d, 0x63, 0x00, 0x0a, 0x0a, 0x4f, + 0x00, 0x0a, 0x4f, 0x6d, 0x00, 0xb2, 0x00, 0x63, + 0x00, 0x08, 0x0a, 0x4f, 0x0a, 0x0a, 0x50, 0x00, + 0x0a, 0x50, 0x6d, 0x00, 0xb3, 0x00, 0x6b, 0x00, + 0x6d, 0x00, 0xb3, 0x00, 0x6d, 0x00, 0x15, 0x22, + 0x73, 0x00, 0x6d, 0x00, 0x15, 0x22, 0x73, 0x00, + 0xb2, 0x00, 0x50, 0x61, 0x6b, 0x50, 0x61, 0x4d, + 0x50, 0x61, 0x47, 0x50, 0x61, 0x72, 0x61, 0x64, + 0x72, 0x61, 0x64, 0xd1, 0x73, 0x72, 0x00, 0x61, + 0x00, 0x64, 0x00, 0x15, 0x22, 0x73, 0x00, 0xb2, + 0x00, 0x70, 0x00, 0x73, 0x6e, 0x00, 0x73, 0xbc, + 0x03, 0x73, 0x6d, 0x00, 0x73, 0x70, 0x00, 0x56, + 0x6e, 0x00, 0x56, 0xbc, 0x03, 0x56, 0x6d, 0x00, + 0x56, 0x6b, 0x00, 0x56, 0x4d, 0x00, 0x56, 0x70, + 0x00, 0x57, 0x6e, 0x00, 0x57, 0xbc, 0x03, 0x57, + 0x6d, 0x00, 0x57, 0x6b, 0x00, 0x57, 0x4d, 0x00, + 0x57, 0x6b, 0x00, 0xa9, 0x03, 0x4d, 0x00, 0xa9, + 0x03, 0x61, 0x2e, 0x6d, 0x2e, 0x42, 0x71, 0x63, + 0x63, 0x63, 0x64, 0x43, 0xd1, 0x6b, 0x67, 0x43, + 0x6f, 0x2e, 0x64, 0x42, 0x47, 0x79, 0x68, 0x61, + 0x48, 0x50, 0x69, 0x6e, 0x4b, 0x4b, 0x4b, 0x4d, + 0x6b, 0x74, 0x6c, 0x6d, 0x6c, 0x6e, 0x6c, 0x6f, + 0x67, 0x6c, 0x78, 0x6d, 0x62, 0x6d, 0x69, 0x6c, + 0x6d, 0x6f, 0x6c, 0x50, 0x48, 0x70, 0x2e, 0x6d, + 0x2e, 0x50, 0x50, 0x4d, 0x50, 0x52, 0x73, 0x72, + 0x53, 0x76, 0x57, 0x62, 0x56, 0xd1, 0x6d, 0x41, + 0xd1, 0x6d, 0x31, 0x00, 0xe5, 0x65, 0x31, 0x00, + 0x30, 0x00, 0xe5, 0x65, 0x32, 0x00, 0x30, 0x00, + 0xe5, 0x65, 0x33, 0x00, 0x30, 0x00, 0xe5, 0x65, + 0x67, 0x61, 0x6c, 0x4a, 0x04, 0x4c, 0x04, 0x53, + 0x43, 0x46, 0x51, 0x26, 0x01, 0x53, 0x01, 0x27, + 0xa7, 0x37, 0xab, 0x6b, 0x02, 0x52, 0xab, 0x48, + 0x8c, 0xf4, 0x66, 0xca, 0x8e, 0xc8, 0x8c, 0xd1, + 0x6e, 0x32, 0x4e, 0xe5, 0x53, 0x9c, 0x9f, 0x9c, + 0x9f, 0x51, 0x59, 0xd1, 0x91, 0x87, 0x55, 0x48, + 0x59, 0xf6, 0x61, 0x69, 0x76, 0x85, 0x7f, 0x3f, + 0x86, 0xba, 0x87, 0xf8, 0x88, 0x8f, 0x90, 0x02, + 0x6a, 0x1b, 0x6d, 0xd9, 0x70, 0xde, 0x73, 0x3d, + 0x84, 0x6a, 0x91, 0xf1, 0x99, 0x82, 0x4e, 0x75, + 0x53, 0x04, 0x6b, 0x1b, 0x72, 0x2d, 0x86, 0x1e, + 0x9e, 0x50, 0x5d, 0xeb, 0x6f, 0xcd, 0x85, 0x64, + 0x89, 0xc9, 0x62, 0xd8, 0x81, 0x1f, 0x88, 0xca, + 0x5e, 0x17, 0x67, 0x6a, 0x6d, 0xfc, 0x72, 0xce, + 0x90, 0x86, 0x4f, 0xb7, 0x51, 0xde, 0x52, 0xc4, + 0x64, 0xd3, 0x6a, 0x10, 0x72, 0xe7, 0x76, 0x01, + 0x80, 0x06, 0x86, 0x5c, 0x86, 0xef, 0x8d, 0x32, + 0x97, 0x6f, 0x9b, 0xfa, 0x9d, 0x8c, 0x78, 0x7f, + 0x79, 0xa0, 0x7d, 0xc9, 0x83, 0x04, 0x93, 0x7f, + 0x9e, 0xd6, 0x8a, 0xdf, 0x58, 0x04, 0x5f, 0x60, + 0x7c, 0x7e, 0x80, 0x62, 0x72, 0xca, 0x78, 0xc2, + 0x8c, 0xf7, 0x96, 0xd8, 0x58, 0x62, 0x5c, 0x13, + 0x6a, 0xda, 0x6d, 0x0f, 0x6f, 0x2f, 0x7d, 0x37, + 0x7e, 0x4b, 0x96, 0xd2, 0x52, 0x8b, 0x80, 0xdc, + 0x51, 0xcc, 0x51, 0x1c, 0x7a, 0xbe, 0x7d, 0xf1, + 0x83, 0x75, 0x96, 0x80, 0x8b, 0xcf, 0x62, 0x02, + 0x6a, 0xfe, 0x8a, 0x39, 0x4e, 0xe7, 0x5b, 0x12, + 0x60, 0x87, 0x73, 0x70, 0x75, 0x17, 0x53, 0xfb, + 0x78, 0xbf, 0x4f, 0xa9, 0x5f, 0x0d, 0x4e, 0xcc, + 0x6c, 0x78, 0x65, 0x22, 0x7d, 0xc3, 0x53, 0x5e, + 0x58, 0x01, 0x77, 0x49, 0x84, 0xaa, 0x8a, 0xba, + 0x6b, 0xb0, 0x8f, 0x88, 0x6c, 0xfe, 0x62, 0xe5, + 0x82, 0xa0, 0x63, 0x65, 0x75, 0xae, 0x4e, 0x69, + 0x51, 0xc9, 0x51, 0x81, 0x68, 0xe7, 0x7c, 0x6f, + 0x82, 0xd2, 0x8a, 0xcf, 0x91, 0xf5, 0x52, 0x42, + 0x54, 0x73, 0x59, 0xec, 0x5e, 0xc5, 0x65, 0xfe, + 0x6f, 0x2a, 0x79, 0xad, 0x95, 0x6a, 0x9a, 0x97, + 0x9e, 0xce, 0x9e, 0x9b, 0x52, 0xc6, 0x66, 0x77, + 0x6b, 0x62, 0x8f, 0x74, 0x5e, 0x90, 0x61, 0x00, + 0x62, 0x9a, 0x64, 0x23, 0x6f, 0x49, 0x71, 0x89, + 0x74, 0xca, 0x79, 0xf4, 0x7d, 0x6f, 0x80, 0x26, + 0x8f, 0xee, 0x84, 0x23, 0x90, 0x4a, 0x93, 0x17, + 0x52, 0xa3, 0x52, 0xbd, 0x54, 0xc8, 0x70, 0xc2, + 0x88, 0xaa, 0x8a, 0xc9, 0x5e, 0xf5, 0x5f, 0x7b, + 0x63, 0xae, 0x6b, 0x3e, 0x7c, 0x75, 0x73, 0xe4, + 0x4e, 0xf9, 0x56, 0xe7, 0x5b, 0xba, 0x5d, 0x1c, + 0x60, 0xb2, 0x73, 0x69, 0x74, 0x9a, 0x7f, 0x46, + 0x80, 0x34, 0x92, 0xf6, 0x96, 0x48, 0x97, 0x18, + 0x98, 0x8b, 0x4f, 0xae, 0x79, 0xb4, 0x91, 0xb8, + 0x96, 0xe1, 0x60, 0x86, 0x4e, 0xda, 0x50, 0xee, + 0x5b, 0x3f, 0x5c, 0x99, 0x65, 0x02, 0x6a, 0xce, + 0x71, 0x42, 0x76, 0xfc, 0x84, 0x7c, 0x90, 0x8d, + 0x9f, 0x88, 0x66, 0x2e, 0x96, 0x89, 0x52, 0x7b, + 0x67, 0xf3, 0x67, 0x41, 0x6d, 0x9c, 0x6e, 0x09, + 0x74, 0x59, 0x75, 0x6b, 0x78, 0x10, 0x7d, 0x5e, + 0x98, 0x6d, 0x51, 0x2e, 0x62, 0x78, 0x96, 0x2b, + 0x50, 0x19, 0x5d, 0xea, 0x6d, 0x2a, 0x8f, 0x8b, + 0x5f, 0x44, 0x61, 0x17, 0x68, 0x87, 0x73, 0x86, + 0x96, 0x29, 0x52, 0x0f, 0x54, 0x65, 0x5c, 0x13, + 0x66, 0x4e, 0x67, 0xa8, 0x68, 0xe5, 0x6c, 0x06, + 0x74, 0xe2, 0x75, 0x79, 0x7f, 0xcf, 0x88, 0xe1, + 0x88, 0xcc, 0x91, 0xe2, 0x96, 0x3f, 0x53, 0xba, + 0x6e, 0x1d, 0x54, 0xd0, 0x71, 0x98, 0x74, 0xfa, + 0x85, 0xa3, 0x96, 0x57, 0x9c, 0x9f, 0x9e, 0x97, + 0x67, 0xcb, 0x6d, 0xe8, 0x81, 0xcb, 0x7a, 0x20, + 0x7b, 0x92, 0x7c, 0xc0, 0x72, 0x99, 0x70, 0x58, + 0x8b, 0xc0, 0x4e, 0x36, 0x83, 0x3a, 0x52, 0x07, + 0x52, 0xa6, 0x5e, 0xd3, 0x62, 0xd6, 0x7c, 0x85, + 0x5b, 0x1e, 0x6d, 0xb4, 0x66, 0x3b, 0x8f, 0x4c, + 0x88, 0x4d, 0x96, 0x8b, 0x89, 0xd3, 0x5e, 0x40, + 0x51, 0xc0, 0x55, 0x00, 0x00, 0x00, 0x00, 0x5a, + 0x58, 0x00, 0x00, 0x74, 0x66, 0x00, 0x00, 0x00, + 0x00, 0xde, 0x51, 0x2a, 0x73, 0xca, 0x76, 0x3c, + 0x79, 0x5e, 0x79, 0x65, 0x79, 0x8f, 0x79, 0x56, + 0x97, 0xbe, 0x7c, 0xbd, 0x7f, 0x00, 0x00, 0x12, + 0x86, 0x00, 0x00, 0xf8, 0x8a, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x90, 0xfd, 0x90, 0xef, 0x98, 0xfc, + 0x98, 0x28, 0x99, 0xb4, 0x9d, 0xde, 0x90, 0xb7, + 0x96, 0xae, 0x4f, 0xe7, 0x50, 0x4d, 0x51, 0xc9, + 0x52, 0xe4, 0x52, 0x51, 0x53, 0x9d, 0x55, 0x06, + 0x56, 0x68, 0x56, 0x40, 0x58, 0xa8, 0x58, 0x64, + 0x5c, 0x6e, 0x5c, 0x94, 0x60, 0x68, 0x61, 0x8e, + 0x61, 0xf2, 0x61, 0x4f, 0x65, 0xe2, 0x65, 0x91, + 0x66, 0x85, 0x68, 0x77, 0x6d, 0x1a, 0x6e, 0x22, + 0x6f, 0x6e, 0x71, 0x2b, 0x72, 0x22, 0x74, 0x91, + 0x78, 0x3e, 0x79, 0x49, 0x79, 0x48, 0x79, 0x50, + 0x79, 0x56, 0x79, 0x5d, 0x79, 0x8d, 0x79, 0x8e, + 0x79, 0x40, 0x7a, 0x81, 0x7a, 0xc0, 0x7b, 0xf4, + 0x7d, 0x09, 0x7e, 0x41, 0x7e, 0x72, 0x7f, 0x05, + 0x80, 0xed, 0x81, 0x79, 0x82, 0x79, 0x82, 0x57, + 0x84, 0x10, 0x89, 0x96, 0x89, 0x01, 0x8b, 0x39, + 0x8b, 0xd3, 0x8c, 0x08, 0x8d, 0xb6, 0x8f, 0x38, + 0x90, 0xe3, 0x96, 0xff, 0x97, 0x3b, 0x98, 0x75, + 0x60, 0xee, 0x42, 0x18, 0x82, 0x02, 0x26, 0x4e, + 0xb5, 0x51, 0x68, 0x51, 0x80, 0x4f, 0x45, 0x51, + 0x80, 0x51, 0xc7, 0x52, 0xfa, 0x52, 0x9d, 0x55, + 0x55, 0x55, 0x99, 0x55, 0xe2, 0x55, 0x5a, 0x58, + 0xb3, 0x58, 0x44, 0x59, 0x54, 0x59, 0x62, 0x5a, + 0x28, 0x5b, 0xd2, 0x5e, 0xd9, 0x5e, 0x69, 0x5f, + 0xad, 0x5f, 0xd8, 0x60, 0x4e, 0x61, 0x08, 0x61, + 0x8e, 0x61, 0x60, 0x61, 0xf2, 0x61, 0x34, 0x62, + 0xc4, 0x63, 0x1c, 0x64, 0x52, 0x64, 0x56, 0x65, + 0x74, 0x66, 0x17, 0x67, 0x1b, 0x67, 0x56, 0x67, + 0x79, 0x6b, 0xba, 0x6b, 0x41, 0x6d, 0xdb, 0x6e, + 0xcb, 0x6e, 0x22, 0x6f, 0x1e, 0x70, 0x6e, 0x71, + 0xa7, 0x77, 0x35, 0x72, 0xaf, 0x72, 0x2a, 0x73, + 0x71, 0x74, 0x06, 0x75, 0x3b, 0x75, 0x1d, 0x76, + 0x1f, 0x76, 0xca, 0x76, 0xdb, 0x76, 0xf4, 0x76, + 0x4a, 0x77, 0x40, 0x77, 0xcc, 0x78, 0xb1, 0x7a, + 0xc0, 0x7b, 0x7b, 0x7c, 0x5b, 0x7d, 0xf4, 0x7d, + 0x3e, 0x7f, 0x05, 0x80, 0x52, 0x83, 0xef, 0x83, + 0x79, 0x87, 0x41, 0x89, 0x86, 0x89, 0x96, 0x89, + 0xbf, 0x8a, 0xf8, 0x8a, 0xcb, 0x8a, 0x01, 0x8b, + 0xfe, 0x8a, 0xed, 0x8a, 0x39, 0x8b, 0x8a, 0x8b, + 0x08, 0x8d, 0x38, 0x8f, 0x72, 0x90, 0x99, 0x91, + 0x76, 0x92, 0x7c, 0x96, 0xe3, 0x96, 0x56, 0x97, + 0xdb, 0x97, 0xff, 0x97, 0x0b, 0x98, 0x3b, 0x98, + 0x12, 0x9b, 0x9c, 0x9f, 0x4a, 0x28, 0x44, 0x28, + 0xd5, 0x33, 0x9d, 0x3b, 0x18, 0x40, 0x39, 0x40, + 0x49, 0x52, 0xd0, 0x5c, 0xd3, 0x7e, 0x43, 0x9f, + 0x8e, 0x9f, 0x2a, 0xa0, 0x02, 0x66, 0x66, 0x66, + 0x69, 0x66, 0x6c, 0x66, 0x66, 0x69, 0x66, 0x66, + 0x6c, 0x7f, 0x01, 0x74, 0x73, 0x00, 0x74, 0x65, + 0x05, 0x0f, 0x11, 0x0f, 0x00, 0x0f, 0x06, 0x19, + 0x11, 0x0f, 0x08, 0xd9, 0x05, 0xb4, 0x05, 0x00, + 0x00, 0x00, 0x00, 0xf2, 0x05, 0xb7, 0x05, 0xd0, + 0x05, 0x12, 0x00, 0x03, 0x04, 0x0b, 0x0c, 0x0d, + 0x18, 0x1a, 0xe9, 0x05, 0xc1, 0x05, 0xe9, 0x05, + 0xc2, 0x05, 0x49, 0xfb, 0xc1, 0x05, 0x49, 0xfb, + 0xc2, 0x05, 0xd0, 0x05, 0xb7, 0x05, 0xd0, 0x05, + 0xb8, 0x05, 0xd0, 0x05, 0xbc, 0x05, 0xd8, 0x05, + 0xbc, 0x05, 0xde, 0x05, 0xbc, 0x05, 0xe0, 0x05, + 0xbc, 0x05, 0xe3, 0x05, 0xbc, 0x05, 0xb9, 0x05, + 0x2d, 0x03, 0x2e, 0x03, 0x2f, 0x03, 0x30, 0x03, + 0x31, 0x03, 0x1c, 0x00, 0x18, 0x06, 0x22, 0x06, + 0x2b, 0x06, 0xd0, 0x05, 0xdc, 0x05, 0x71, 0x06, + 0x00, 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0f, 0x0f, 0x0f, 0x0f, 0x09, 0x09, + 0x09, 0x09, 0x0e, 0x0e, 0x0e, 0x0e, 0x08, 0x08, + 0x08, 0x08, 0x33, 0x33, 0x33, 0x33, 0x35, 0x35, + 0x35, 0x35, 0x13, 0x13, 0x13, 0x13, 0x12, 0x12, + 0x12, 0x12, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, + 0x16, 0x16, 0x1c, 0x1c, 0x1b, 0x1b, 0x1d, 0x1d, + 0x17, 0x17, 0x27, 0x27, 0x20, 0x20, 0x38, 0x38, + 0x38, 0x38, 0x3e, 0x3e, 0x3e, 0x3e, 0x42, 0x42, + 0x42, 0x42, 0x40, 0x40, 0x40, 0x40, 0x49, 0x49, + 0x4a, 0x4a, 0x4a, 0x4a, 0x4f, 0x4f, 0x50, 0x50, + 0x50, 0x50, 0x4d, 0x4d, 0x4d, 0x4d, 0x61, 0x61, + 0x62, 0x62, 0x49, 0x06, 0x64, 0x64, 0x64, 0x64, + 0x7e, 0x7e, 0x7d, 0x7d, 0x7f, 0x7f, 0x2e, 0x82, + 0x82, 0x7c, 0x7c, 0x80, 0x80, 0x87, 0x87, 0x87, + 0x87, 0x00, 0x00, 0x26, 0x06, 0x00, 0x01, 0x00, + 0x01, 0x00, 0xaf, 0x00, 0xaf, 0x00, 0x22, 0x00, + 0x22, 0x00, 0xa1, 0x00, 0xa1, 0x00, 0xa0, 0x00, + 0xa0, 0x00, 0xa2, 0x00, 0xa2, 0x00, 0xaa, 0x00, + 0xaa, 0x00, 0xaa, 0x00, 0x23, 0x00, 0x23, 0x00, + 0x23, 0xcc, 0x06, 0x00, 0x00, 0x00, 0x00, 0x26, + 0x06, 0x00, 0x06, 0x00, 0x07, 0x00, 0x1f, 0x00, + 0x23, 0x00, 0x24, 0x02, 0x06, 0x02, 0x07, 0x02, + 0x08, 0x02, 0x1f, 0x02, 0x23, 0x02, 0x24, 0x04, + 0x06, 0x04, 0x07, 0x04, 0x08, 0x04, 0x1f, 0x04, + 0x23, 0x04, 0x24, 0x05, 0x06, 0x05, 0x1f, 0x05, + 0x23, 0x05, 0x24, 0x06, 0x07, 0x06, 0x1f, 0x07, + 0x06, 0x07, 0x1f, 0x08, 0x06, 0x08, 0x07, 0x08, + 0x1f, 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x08, 0x0d, + 0x1f, 0x0f, 0x07, 0x0f, 0x1f, 0x10, 0x06, 0x10, + 0x07, 0x10, 0x08, 0x10, 0x1f, 0x11, 0x07, 0x11, + 0x1f, 0x12, 0x1f, 0x13, 0x06, 0x13, 0x1f, 0x14, + 0x06, 0x14, 0x1f, 0x1b, 0x06, 0x1b, 0x07, 0x1b, + 0x08, 0x1b, 0x1f, 0x1b, 0x23, 0x1b, 0x24, 0x1c, + 0x07, 0x1c, 0x1f, 0x1c, 0x23, 0x1c, 0x24, 0x1d, + 0x01, 0x1d, 0x06, 0x1d, 0x07, 0x1d, 0x08, 0x1d, + 0x1e, 0x1d, 0x1f, 0x1d, 0x23, 0x1d, 0x24, 0x1e, + 0x06, 0x1e, 0x07, 0x1e, 0x08, 0x1e, 0x1f, 0x1e, + 0x23, 0x1e, 0x24, 0x1f, 0x06, 0x1f, 0x07, 0x1f, + 0x08, 0x1f, 0x1f, 0x1f, 0x23, 0x1f, 0x24, 0x20, + 0x06, 0x20, 0x07, 0x20, 0x08, 0x20, 0x1f, 0x20, + 0x23, 0x20, 0x24, 0x21, 0x06, 0x21, 0x1f, 0x21, + 0x23, 0x21, 0x24, 0x24, 0x06, 0x24, 0x07, 0x24, + 0x08, 0x24, 0x1f, 0x24, 0x23, 0x24, 0x24, 0x0a, + 0x4a, 0x0b, 0x4a, 0x23, 0x4a, 0x20, 0x00, 0x4c, + 0x06, 0x51, 0x06, 0x51, 0x06, 0xff, 0x00, 0x1f, + 0x26, 0x06, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x1f, + 0x00, 0x20, 0x00, 0x23, 0x00, 0x24, 0x02, 0x0b, + 0x02, 0x0c, 0x02, 0x1f, 0x02, 0x20, 0x02, 0x23, + 0x02, 0x24, 0x04, 0x0b, 0x04, 0x0c, 0x04, 0x1f, + 0x26, 0x06, 0x04, 0x20, 0x04, 0x23, 0x04, 0x24, + 0x05, 0x0b, 0x05, 0x0c, 0x05, 0x1f, 0x05, 0x20, + 0x05, 0x23, 0x05, 0x24, 0x1b, 0x23, 0x1b, 0x24, + 0x1c, 0x23, 0x1c, 0x24, 0x1d, 0x01, 0x1d, 0x1e, + 0x1d, 0x1f, 0x1d, 0x23, 0x1d, 0x24, 0x1e, 0x1f, + 0x1e, 0x23, 0x1e, 0x24, 0x1f, 0x01, 0x1f, 0x1f, + 0x20, 0x0b, 0x20, 0x0c, 0x20, 0x1f, 0x20, 0x20, + 0x20, 0x23, 0x20, 0x24, 0x23, 0x4a, 0x24, 0x0b, + 0x24, 0x0c, 0x24, 0x1f, 0x24, 0x20, 0x24, 0x23, + 0x24, 0x24, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, + 0x00, 0x1f, 0x00, 0x21, 0x02, 0x06, 0x02, 0x07, + 0x02, 0x08, 0x02, 0x1f, 0x02, 0x21, 0x04, 0x06, + 0x04, 0x07, 0x04, 0x08, 0x04, 0x1f, 0x04, 0x21, + 0x05, 0x1f, 0x06, 0x07, 0x06, 0x1f, 0x07, 0x06, + 0x07, 0x1f, 0x08, 0x06, 0x08, 0x1f, 0x0d, 0x06, + 0x0d, 0x07, 0x0d, 0x08, 0x0d, 0x1f, 0x0f, 0x07, + 0x0f, 0x08, 0x0f, 0x1f, 0x10, 0x06, 0x10, 0x07, + 0x10, 0x08, 0x10, 0x1f, 0x11, 0x07, 0x12, 0x1f, + 0x13, 0x06, 0x13, 0x1f, 0x14, 0x06, 0x14, 0x1f, + 0x1b, 0x06, 0x1b, 0x07, 0x1b, 0x08, 0x1b, 0x1f, + 0x1c, 0x07, 0x1c, 0x1f, 0x1d, 0x06, 0x1d, 0x07, + 0x1d, 0x08, 0x1d, 0x1e, 0x1d, 0x1f, 0x1e, 0x06, + 0x1e, 0x07, 0x1e, 0x08, 0x1e, 0x1f, 0x1e, 0x21, + 0x1f, 0x06, 0x1f, 0x07, 0x1f, 0x08, 0x1f, 0x1f, + 0x20, 0x06, 0x20, 0x07, 0x20, 0x08, 0x20, 0x1f, + 0x20, 0x21, 0x21, 0x06, 0x21, 0x1f, 0x21, 0x4a, + 0x24, 0x06, 0x24, 0x07, 0x24, 0x08, 0x24, 0x1f, + 0x24, 0x21, 0x00, 0x1f, 0x00, 0x21, 0x02, 0x1f, + 0x02, 0x21, 0x04, 0x1f, 0x04, 0x21, 0x05, 0x1f, + 0x05, 0x21, 0x0d, 0x1f, 0x0d, 0x21, 0x0e, 0x1f, + 0x0e, 0x21, 0x1d, 0x1e, 0x1d, 0x1f, 0x1e, 0x1f, + 0x20, 0x1f, 0x20, 0x21, 0x24, 0x1f, 0x24, 0x21, + 0x40, 0x06, 0x4e, 0x06, 0x51, 0x06, 0x27, 0x06, + 0x10, 0x22, 0x10, 0x23, 0x12, 0x22, 0x12, 0x23, + 0x13, 0x22, 0x13, 0x23, 0x0c, 0x22, 0x0c, 0x23, + 0x0d, 0x22, 0x0d, 0x23, 0x06, 0x22, 0x06, 0x23, + 0x05, 0x22, 0x05, 0x23, 0x07, 0x22, 0x07, 0x23, + 0x0e, 0x22, 0x0e, 0x23, 0x0f, 0x22, 0x0f, 0x23, + 0x0d, 0x05, 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x1e, + 0x0d, 0x0a, 0x0c, 0x0a, 0x0e, 0x0a, 0x0f, 0x0a, + 0x10, 0x22, 0x10, 0x23, 0x12, 0x22, 0x12, 0x23, + 0x13, 0x22, 0x13, 0x23, 0x0c, 0x22, 0x0c, 0x23, + 0x0d, 0x22, 0x0d, 0x23, 0x06, 0x22, 0x06, 0x23, + 0x05, 0x22, 0x05, 0x23, 0x07, 0x22, 0x07, 0x23, + 0x0e, 0x22, 0x0e, 0x23, 0x0f, 0x22, 0x0f, 0x23, + 0x0d, 0x05, 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x1e, + 0x0d, 0x0a, 0x0c, 0x0a, 0x0e, 0x0a, 0x0f, 0x0a, + 0x0d, 0x05, 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x1e, + 0x0c, 0x20, 0x0d, 0x20, 0x10, 0x1e, 0x0c, 0x05, + 0x0c, 0x06, 0x0c, 0x07, 0x0d, 0x05, 0x0d, 0x06, + 0x0d, 0x07, 0x10, 0x1e, 0x11, 0x1e, 0x00, 0x24, + 0x00, 0x24, 0x2a, 0x06, 0x00, 0x02, 0x1b, 0x00, + 0x03, 0x02, 0x00, 0x03, 0x02, 0x00, 0x03, 0x1b, + 0x00, 0x04, 0x1b, 0x00, 0x1b, 0x02, 0x00, 0x1b, + 0x03, 0x00, 0x1b, 0x04, 0x02, 0x1b, 0x03, 0x02, + 0x1b, 0x03, 0x03, 0x1b, 0x20, 0x03, 0x1b, 0x1f, + 0x09, 0x03, 0x02, 0x09, 0x02, 0x03, 0x09, 0x02, + 0x1f, 0x09, 0x1b, 0x03, 0x09, 0x1b, 0x03, 0x09, + 0x1b, 0x02, 0x09, 0x1b, 0x1b, 0x09, 0x1b, 0x1b, + 0x0b, 0x03, 0x03, 0x0b, 0x03, 0x03, 0x0b, 0x1b, + 0x1b, 0x0a, 0x03, 0x1b, 0x0a, 0x03, 0x1b, 0x0a, + 0x02, 0x20, 0x0a, 0x1b, 0x04, 0x0a, 0x1b, 0x04, + 0x0a, 0x1b, 0x1b, 0x0a, 0x1b, 0x1b, 0x0c, 0x03, + 0x1f, 0x0c, 0x04, 0x1b, 0x0c, 0x04, 0x1b, 0x0d, + 0x1b, 0x03, 0x0d, 0x1b, 0x03, 0x0d, 0x1b, 0x1b, + 0x0d, 0x1b, 0x20, 0x0f, 0x02, 0x1b, 0x0f, 0x1b, + 0x1b, 0x0f, 0x1b, 0x1b, 0x0f, 0x1b, 0x1f, 0x10, + 0x1b, 0x1b, 0x10, 0x1b, 0x20, 0x10, 0x1b, 0x1f, + 0x17, 0x04, 0x1b, 0x17, 0x04, 0x1b, 0x18, 0x1b, + 0x03, 0x18, 0x1b, 0x1b, 0x1a, 0x03, 0x1b, 0x1a, + 0x03, 0x20, 0x1a, 0x03, 0x1f, 0x1a, 0x02, 0x02, + 0x1a, 0x02, 0x02, 0x1a, 0x04, 0x1b, 0x1a, 0x04, + 0x1b, 0x1a, 0x1b, 0x03, 0x1a, 0x1b, 0x03, 0x1b, + 0x03, 0x02, 0x1b, 0x03, 0x1b, 0x1b, 0x03, 0x20, + 0x1b, 0x02, 0x03, 0x1b, 0x02, 0x1b, 0x1b, 0x04, + 0x02, 0x1b, 0x04, 0x1b, 0x28, 0x06, 0x1d, 0x04, + 0x06, 0x1f, 0x1d, 0x04, 0x1f, 0x1d, 0x1d, 0x1e, + 0x05, 0x1d, 0x1e, 0x05, 0x21, 0x1e, 0x04, 0x1d, + 0x1e, 0x04, 0x1d, 0x1e, 0x04, 0x21, 0x1e, 0x1d, + 0x22, 0x1e, 0x1d, 0x21, 0x22, 0x1d, 0x1d, 0x22, + 0x1d, 0x1d, 0x00, 0x06, 0x22, 0x02, 0x04, 0x22, + 0x02, 0x04, 0x21, 0x02, 0x06, 0x22, 0x02, 0x06, + 0x21, 0x02, 0x1d, 0x22, 0x02, 0x1d, 0x21, 0x04, + 0x1d, 0x22, 0x04, 0x05, 0x21, 0x04, 0x1d, 0x21, + 0x0b, 0x06, 0x21, 0x0d, 0x05, 0x22, 0x0c, 0x05, + 0x22, 0x0e, 0x05, 0x22, 0x1c, 0x04, 0x22, 0x1c, + 0x1d, 0x22, 0x22, 0x05, 0x22, 0x22, 0x04, 0x22, + 0x22, 0x1d, 0x22, 0x1d, 0x1d, 0x22, 0x1a, 0x1d, + 0x22, 0x1e, 0x05, 0x22, 0x1a, 0x1d, 0x05, 0x1c, + 0x05, 0x1d, 0x11, 0x1d, 0x22, 0x1b, 0x1d, 0x22, + 0x1e, 0x04, 0x05, 0x1d, 0x06, 0x22, 0x1c, 0x04, + 0x1d, 0x1b, 0x1d, 0x1d, 0x1c, 0x04, 0x1d, 0x1e, + 0x04, 0x05, 0x04, 0x05, 0x22, 0x05, 0x04, 0x22, + 0x1d, 0x04, 0x22, 0x19, 0x1d, 0x22, 0x00, 0x05, + 0x22, 0x1b, 0x1d, 0x1d, 0x11, 0x04, 0x1d, 0x0d, + 0x1d, 0x1d, 0x0b, 0x06, 0x22, 0x1e, 0x04, 0x22, + 0x35, 0x06, 0x00, 0x0f, 0x9d, 0x0d, 0x0f, 0x9d, + 0x27, 0x06, 0x00, 0x1d, 0x1d, 0x20, 0x00, 0x1c, + 0x01, 0x0a, 0x1e, 0x06, 0x1e, 0x08, 0x0e, 0x1d, + 0x12, 0x1e, 0x0a, 0x0c, 0x21, 0x1d, 0x12, 0x1d, + 0x23, 0x20, 0x21, 0x0c, 0x1d, 0x1e, 0x35, 0x06, + 0x00, 0x0f, 0x14, 0x27, 0x06, 0x0e, 0x1d, 0x22, + 0xff, 0x00, 0x1d, 0x1d, 0x20, 0xff, 0x12, 0x1d, + 0x23, 0x20, 0xff, 0x21, 0x0c, 0x1d, 0x1e, 0x27, + 0x06, 0x05, 0x1d, 0xff, 0x05, 0x1d, 0x00, 0x1d, + 0x20, 0x27, 0x06, 0x0a, 0xa5, 0x00, 0x1d, 0x2c, + 0x00, 0x01, 0x30, 0x02, 0x30, 0x3a, 0x00, 0x3b, + 0x00, 0x21, 0x00, 0x3f, 0x00, 0x16, 0x30, 0x17, + 0x30, 0x26, 0x20, 0x13, 0x20, 0x12, 0x01, 0x00, + 0x5f, 0x5f, 0x28, 0x29, 0x7b, 0x7d, 0x08, 0x30, + 0x0c, 0x0d, 0x08, 0x09, 0x02, 0x03, 0x00, 0x01, + 0x04, 0x05, 0x06, 0x07, 0x5b, 0x00, 0x5d, 0x00, + 0x3e, 0x20, 0x3e, 0x20, 0x3e, 0x20, 0x3e, 0x20, + 0x5f, 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x2c, 0x00, + 0x01, 0x30, 0x2e, 0x00, 0x00, 0x00, 0x3b, 0x00, + 0x3a, 0x00, 0x3f, 0x00, 0x21, 0x00, 0x14, 0x20, + 0x28, 0x00, 0x29, 0x00, 0x7b, 0x00, 0x7d, 0x00, + 0x14, 0x30, 0x15, 0x30, 0x23, 0x26, 0x2a, 0x2b, + 0x2d, 0x3c, 0x3e, 0x3d, 0x00, 0x5c, 0x24, 0x25, + 0x40, 0x40, 0x06, 0xff, 0x0b, 0x00, 0x0b, 0xff, + 0x0c, 0x20, 0x00, 0x4d, 0x06, 0x40, 0x06, 0xff, + 0x0e, 0x00, 0x0e, 0xff, 0x0f, 0x00, 0x0f, 0xff, + 0x10, 0x00, 0x10, 0xff, 0x11, 0x00, 0x11, 0xff, + 0x12, 0x00, 0x12, 0x21, 0x06, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, + 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, + 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, + 0x0f, 0x0f, 0x10, 0x10, 0x11, 0x11, 0x12, 0x12, + 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, + 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, + 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, + 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x20, 0x20, + 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x22, 0x22, + 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, + 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26, + 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, 0x29, 0x29, + 0x29, 0x29, 0x22, 0x06, 0x22, 0x00, 0x22, 0x00, + 0x22, 0x01, 0x22, 0x01, 0x22, 0x03, 0x22, 0x03, + 0x22, 0x05, 0x22, 0x05, 0x21, 0x00, 0x85, 0x29, + 0x01, 0x30, 0x01, 0x0b, 0x0c, 0x00, 0xfa, 0xf1, + 0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xe2, 0xe4, 0xe6, + 0xc2, 0xfb, 0xa1, 0xa3, 0xa5, 0xa7, 0xa9, 0xaa, + 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, + 0xbc, 0xbe, 0xc0, 0xc3, 0xc5, 0xc7, 0xc9, 0xca, + 0xcb, 0xcc, 0xcd, 0xce, 0xd1, 0xd4, 0xd7, 0xda, + 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe3, 0xe5, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xee, 0xf2, 0x98, + 0x99, 0x31, 0x31, 0x4f, 0x31, 0x55, 0x31, 0x5b, + 0x31, 0x61, 0x31, 0xa2, 0x00, 0xa3, 0x00, 0xac, + 0x00, 0xaf, 0x00, 0xa6, 0x00, 0xa5, 0x00, 0xa9, + 0x20, 0x00, 0x00, 0x02, 0x25, 0x90, 0x21, 0x91, + 0x21, 0x92, 0x21, 0x93, 0x21, 0xa0, 0x25, 0xcb, + 0x25, 0xd2, 0x05, 0x07, 0x03, 0x01, 0xda, 0x05, + 0x07, 0x03, 0x01, 0xd0, 0x02, 0xd1, 0x02, 0xe6, + 0x00, 0x99, 0x02, 0x53, 0x02, 0x00, 0x00, 0xa3, + 0x02, 0x66, 0xab, 0xa5, 0x02, 0xa4, 0x02, 0x56, + 0x02, 0x57, 0x02, 0x91, 0x1d, 0x58, 0x02, 0x5e, + 0x02, 0xa9, 0x02, 0x64, 0x02, 0x62, 0x02, 0x60, + 0x02, 0x9b, 0x02, 0x27, 0x01, 0x9c, 0x02, 0x67, + 0x02, 0x84, 0x02, 0xaa, 0x02, 0xab, 0x02, 0x6c, + 0x02, 0x04, 0xdf, 0x8e, 0xa7, 0x6e, 0x02, 0x05, + 0xdf, 0x8e, 0x02, 0x06, 0xdf, 0xf8, 0x00, 0x76, + 0x02, 0x77, 0x02, 0x71, 0x00, 0x7a, 0x02, 0x08, + 0xdf, 0x7d, 0x02, 0x7e, 0x02, 0x80, 0x02, 0xa8, + 0x02, 0xa6, 0x02, 0x67, 0xab, 0xa7, 0x02, 0x88, + 0x02, 0x71, 0x2c, 0x00, 0x00, 0x8f, 0x02, 0xa1, + 0x02, 0xa2, 0x02, 0x98, 0x02, 0xc0, 0x01, 0xc1, + 0x01, 0xc2, 0x01, 0x0a, 0xdf, 0x1e, 0xdf, 0x41, + 0x04, 0x40, 0x00, 0x00, 0x00, 0x00, 0x14, 0x99, + 0x10, 0xba, 0x10, 0x00, 0x00, 0x00, 0x00, 0x9b, + 0x10, 0xba, 0x10, 0x05, 0x05, 0xa5, 0x10, 0xba, + 0x10, 0x05, 0x31, 0x11, 0x27, 0x11, 0x32, 0x11, + 0x27, 0x11, 0x55, 0x47, 0x13, 0x3e, 0x13, 0x47, + 0x13, 0x57, 0x13, 0x55, 0x82, 0x13, 0xc9, 0x13, + 0x00, 0x00, 0x00, 0x00, 0x84, 0x13, 0xbb, 0x13, + 0x05, 0x05, 0x8b, 0x13, 0xc2, 0x13, 0x05, 0x90, + 0x13, 0xc9, 0x13, 0x05, 0xc2, 0x13, 0xc2, 0x13, + 0x00, 0x00, 0x00, 0x00, 0xc2, 0x13, 0xb8, 0x13, + 0xc2, 0x13, 0xc9, 0x13, 0x05, 0x55, 0xb9, 0x14, + 0xba, 0x14, 0xb9, 0x14, 0xb0, 0x14, 0x00, 0x00, + 0x00, 0x00, 0xb9, 0x14, 0xbd, 0x14, 0x55, 0x50, + 0xb8, 0x15, 0xaf, 0x15, 0xb9, 0x15, 0xaf, 0x15, + 0x55, 0x35, 0x19, 0x30, 0x19, 0x05, 0x1e, 0x61, + 0x1e, 0x61, 0x1e, 0x61, 0x29, 0x61, 0x1e, 0x61, + 0x1f, 0x61, 0x29, 0x61, 0x1f, 0x61, 0x1e, 0x61, + 0x20, 0x61, 0x21, 0x61, 0x1f, 0x61, 0x22, 0x61, + 0x1f, 0x61, 0x21, 0x61, 0x20, 0x61, 0x55, 0x55, + 0x55, 0x55, 0x67, 0x6d, 0x67, 0x6d, 0x63, 0x6d, + 0x67, 0x6d, 0x69, 0x6d, 0x67, 0x6d, 0x55, 0x05, + 0x41, 0x00, 0x30, 0x00, 0x57, 0xd1, 0x65, 0xd1, + 0x58, 0xd1, 0x65, 0xd1, 0x5f, 0xd1, 0x6e, 0xd1, + 0x5f, 0xd1, 0x6f, 0xd1, 0x5f, 0xd1, 0x70, 0xd1, + 0x5f, 0xd1, 0x71, 0xd1, 0x5f, 0xd1, 0x72, 0xd1, + 0x55, 0x55, 0x55, 0x05, 0xb9, 0xd1, 0x65, 0xd1, + 0xba, 0xd1, 0x65, 0xd1, 0xbb, 0xd1, 0x6e, 0xd1, + 0xbc, 0xd1, 0x6e, 0xd1, 0xbb, 0xd1, 0x6f, 0xd1, + 0xbc, 0xd1, 0x6f, 0xd1, 0x55, 0x55, 0x55, 0x41, + 0x00, 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, 0x69, + 0x00, 0x41, 0x00, 0x61, 0x00, 0x41, 0x00, 0x43, + 0x44, 0x00, 0x00, 0x47, 0x00, 0x00, 0x4a, 0x4b, + 0x00, 0x00, 0x4e, 0x4f, 0x50, 0x51, 0x00, 0x53, + 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x61, + 0x62, 0x63, 0x64, 0x00, 0x66, 0x68, 0x00, 0x70, + 0x00, 0x41, 0x00, 0x61, 0x00, 0x41, 0x42, 0x00, + 0x44, 0x45, 0x46, 0x47, 0x4a, 0x00, 0x53, 0x00, + 0x61, 0x00, 0x41, 0x42, 0x00, 0x44, 0x45, 0x46, + 0x47, 0x00, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x00, + 0x4f, 0x53, 0x00, 0x61, 0x00, 0x41, 0x00, 0x61, + 0x00, 0x41, 0x00, 0x61, 0x00, 0x41, 0x00, 0x61, + 0x00, 0x41, 0x00, 0x61, 0x00, 0x41, 0x00, 0x61, + 0x00, 0x41, 0x00, 0x61, 0x00, 0x31, 0x01, 0x37, + 0x02, 0x91, 0x03, 0xa3, 0x03, 0xb1, 0x03, 0xd1, + 0x03, 0x24, 0x00, 0x1f, 0x04, 0x20, 0x05, 0x91, + 0x03, 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03, 0x24, + 0x00, 0x1f, 0x04, 0x20, 0x05, 0x91, 0x03, 0xa3, + 0x03, 0xb1, 0x03, 0xd1, 0x03, 0x24, 0x00, 0x1f, + 0x04, 0x20, 0x05, 0x91, 0x03, 0xa3, 0x03, 0xb1, + 0x03, 0xd1, 0x03, 0x24, 0x00, 0x1f, 0x04, 0x20, + 0x05, 0x91, 0x03, 0xa3, 0x03, 0xb1, 0x03, 0xd1, + 0x03, 0x24, 0x00, 0x1f, 0x04, 0x20, 0x05, 0x0b, + 0x0c, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, + 0x00, 0x30, 0x00, 0x30, 0x04, 0x3a, 0x04, 0x3e, + 0x04, 0x4b, 0x04, 0x4d, 0x04, 0x4e, 0x04, 0x89, + 0xa6, 0x30, 0x04, 0xa9, 0x26, 0x28, 0xb9, 0x7f, + 0x9f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x0a, 0x0b, 0x0e, 0x0f, 0x11, 0x13, + 0x14, 0x15, 0x16, 0x17, 0x18, 0x1a, 0x1b, 0x61, + 0x26, 0x25, 0x2f, 0x7b, 0x51, 0xa6, 0xb1, 0x04, + 0x27, 0x06, 0x00, 0x01, 0x05, 0x08, 0x2a, 0x06, + 0x1e, 0x08, 0x03, 0x0d, 0x20, 0x19, 0x1a, 0x1b, + 0x1c, 0x09, 0x0f, 0x17, 0x0b, 0x18, 0x07, 0x0a, + 0x00, 0x01, 0x04, 0x06, 0x0c, 0x0e, 0x10, 0x44, + 0x90, 0x77, 0x45, 0x28, 0x06, 0x2c, 0x06, 0x00, + 0x00, 0x47, 0x06, 0x33, 0x06, 0x17, 0x10, 0x11, + 0x12, 0x13, 0x00, 0x06, 0x0e, 0x02, 0x0f, 0x34, + 0x06, 0x2a, 0x06, 0x2b, 0x06, 0x2e, 0x06, 0x00, + 0x00, 0x36, 0x06, 0x00, 0x00, 0x3a, 0x06, 0x2d, + 0x06, 0x00, 0x00, 0x4a, 0x06, 0x00, 0x00, 0x44, + 0x06, 0x00, 0x00, 0x46, 0x06, 0x33, 0x06, 0x39, + 0x06, 0x00, 0x00, 0x35, 0x06, 0x42, 0x06, 0x00, + 0x00, 0x34, 0x06, 0x00, 0x00, 0x00, 0x00, 0x2e, + 0x06, 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, 0x3a, + 0x06, 0x00, 0x00, 0xba, 0x06, 0x00, 0x00, 0x6f, + 0x06, 0x00, 0x00, 0x28, 0x06, 0x2c, 0x06, 0x00, + 0x00, 0x47, 0x06, 0x00, 0x00, 0x00, 0x00, 0x2d, + 0x06, 0x37, 0x06, 0x4a, 0x06, 0x43, 0x06, 0x00, + 0x00, 0x45, 0x06, 0x46, 0x06, 0x33, 0x06, 0x39, + 0x06, 0x41, 0x06, 0x35, 0x06, 0x42, 0x06, 0x00, + 0x00, 0x34, 0x06, 0x2a, 0x06, 0x2b, 0x06, 0x2e, + 0x06, 0x00, 0x00, 0x36, 0x06, 0x38, 0x06, 0x3a, + 0x06, 0x6e, 0x06, 0x00, 0x00, 0xa1, 0x06, 0x27, + 0x06, 0x00, 0x01, 0x05, 0x08, 0x20, 0x21, 0x0b, + 0x06, 0x10, 0x23, 0x2a, 0x06, 0x1a, 0x1b, 0x1c, + 0x09, 0x0f, 0x17, 0x0b, 0x18, 0x07, 0x0a, 0x00, + 0x01, 0x04, 0x06, 0x0c, 0x0e, 0x10, 0x28, 0x06, + 0x2c, 0x06, 0x2f, 0x06, 0x00, 0x00, 0x48, 0x06, + 0x32, 0x06, 0x2d, 0x06, 0x37, 0x06, 0x4a, 0x06, + 0x2a, 0x06, 0x1a, 0x1b, 0x1c, 0x09, 0x0f, 0x17, + 0x0b, 0x18, 0x07, 0x0a, 0x00, 0x01, 0x04, 0x06, + 0x0c, 0x0e, 0x10, 0x30, 0x2e, 0x30, 0x00, 0x2c, + 0x00, 0x28, 0x00, 0x41, 0x00, 0x29, 0x00, 0x14, + 0x30, 0x53, 0x00, 0x15, 0x30, 0x43, 0x52, 0x43, + 0x44, 0x57, 0x5a, 0x41, 0x00, 0x48, 0x56, 0x4d, + 0x56, 0x53, 0x44, 0x53, 0x53, 0x50, 0x50, 0x56, + 0x57, 0x43, 0x4d, 0x43, 0x4d, 0x44, 0x4d, 0x52, + 0x44, 0x4a, 0x4b, 0x30, 0x30, 0x00, 0x68, 0x68, + 0x4b, 0x62, 0x57, 0x5b, 0xcc, 0x53, 0xc7, 0x30, + 0x8c, 0x4e, 0x1a, 0x59, 0xe3, 0x89, 0x29, 0x59, + 0xa4, 0x4e, 0x20, 0x66, 0x21, 0x71, 0x99, 0x65, + 0x4d, 0x52, 0x8c, 0x5f, 0x8d, 0x51, 0xb0, 0x65, + 0x1d, 0x52, 0x42, 0x7d, 0x1f, 0x75, 0xa9, 0x8c, + 0xf0, 0x58, 0x39, 0x54, 0x14, 0x6f, 0x95, 0x62, + 0x55, 0x63, 0x00, 0x4e, 0x09, 0x4e, 0x4a, 0x90, + 0xe6, 0x5d, 0x2d, 0x4e, 0xf3, 0x53, 0x07, 0x63, + 0x70, 0x8d, 0x53, 0x62, 0x81, 0x79, 0x7a, 0x7a, + 0x08, 0x54, 0x80, 0x6e, 0x09, 0x67, 0x08, 0x67, + 0x33, 0x75, 0x72, 0x52, 0xb6, 0x55, 0x4d, 0x91, + 0x14, 0x30, 0x15, 0x30, 0x2c, 0x67, 0x09, 0x4e, + 0x8c, 0x4e, 0x89, 0x5b, 0xb9, 0x70, 0x53, 0x62, + 0xd7, 0x76, 0xdd, 0x52, 0x57, 0x65, 0x97, 0x5f, + 0xef, 0x53, 0x30, 0x00, 0x38, 0x4e, 0x05, 0x00, + 0x09, 0x22, 0x01, 0x60, 0x4f, 0xae, 0x4f, 0xbb, + 0x4f, 0x02, 0x50, 0x7a, 0x50, 0x99, 0x50, 0xe7, + 0x50, 0xcf, 0x50, 0x9e, 0x34, 0x3a, 0x06, 0x4d, + 0x51, 0x54, 0x51, 0x64, 0x51, 0x77, 0x51, 0x1c, + 0x05, 0xb9, 0x34, 0x67, 0x51, 0x8d, 0x51, 0x4b, + 0x05, 0x97, 0x51, 0xa4, 0x51, 0xcc, 0x4e, 0xac, + 0x51, 0xb5, 0x51, 0xdf, 0x91, 0xf5, 0x51, 0x03, + 0x52, 0xdf, 0x34, 0x3b, 0x52, 0x46, 0x52, 0x72, + 0x52, 0x77, 0x52, 0x15, 0x35, 0x02, 0x00, 0x20, + 0x80, 0x80, 0x00, 0x08, 0x00, 0x00, 0xc7, 0x52, + 0x00, 0x02, 0x1d, 0x33, 0x3e, 0x3f, 0x50, 0x82, + 0x8a, 0x93, 0xac, 0xb6, 0xb8, 0xb8, 0xb8, 0x2c, + 0x0a, 0x70, 0x70, 0xca, 0x53, 0xdf, 0x53, 0x63, + 0x0b, 0xeb, 0x53, 0xf1, 0x53, 0x06, 0x54, 0x9e, + 0x54, 0x38, 0x54, 0x48, 0x54, 0x68, 0x54, 0xa2, + 0x54, 0xf6, 0x54, 0x10, 0x55, 0x53, 0x55, 0x63, + 0x55, 0x84, 0x55, 0x84, 0x55, 0x99, 0x55, 0xab, + 0x55, 0xb3, 0x55, 0xc2, 0x55, 0x16, 0x57, 0x06, + 0x56, 0x17, 0x57, 0x51, 0x56, 0x74, 0x56, 0x07, + 0x52, 0xee, 0x58, 0xce, 0x57, 0xf4, 0x57, 0x0d, + 0x58, 0x8b, 0x57, 0x32, 0x58, 0x31, 0x58, 0xac, + 0x58, 0xe4, 0x14, 0xf2, 0x58, 0xf7, 0x58, 0x06, + 0x59, 0x1a, 0x59, 0x22, 0x59, 0x62, 0x59, 0xa8, + 0x16, 0xea, 0x16, 0xec, 0x59, 0x1b, 0x5a, 0x27, + 0x5a, 0xd8, 0x59, 0x66, 0x5a, 0xee, 0x36, 0xfc, + 0x36, 0x08, 0x5b, 0x3e, 0x5b, 0x3e, 0x5b, 0xc8, + 0x19, 0xc3, 0x5b, 0xd8, 0x5b, 0xe7, 0x5b, 0xf3, + 0x5b, 0x18, 0x1b, 0xff, 0x5b, 0x06, 0x5c, 0x53, + 0x5f, 0x22, 0x5c, 0x81, 0x37, 0x60, 0x5c, 0x6e, + 0x5c, 0xc0, 0x5c, 0x8d, 0x5c, 0xe4, 0x1d, 0x43, + 0x5d, 0xe6, 0x1d, 0x6e, 0x5d, 0x6b, 0x5d, 0x7c, + 0x5d, 0xe1, 0x5d, 0xe2, 0x5d, 0x2f, 0x38, 0xfd, + 0x5d, 0x28, 0x5e, 0x3d, 0x5e, 0x69, 0x5e, 0x62, + 0x38, 0x83, 0x21, 0x7c, 0x38, 0xb0, 0x5e, 0xb3, + 0x5e, 0xb6, 0x5e, 0xca, 0x5e, 0x92, 0xa3, 0xfe, + 0x5e, 0x31, 0x23, 0x31, 0x23, 0x01, 0x82, 0x22, + 0x5f, 0x22, 0x5f, 0xc7, 0x38, 0xb8, 0x32, 0xda, + 0x61, 0x62, 0x5f, 0x6b, 0x5f, 0xe3, 0x38, 0x9a, + 0x5f, 0xcd, 0x5f, 0xd7, 0x5f, 0xf9, 0x5f, 0x81, + 0x60, 0x3a, 0x39, 0x1c, 0x39, 0x94, 0x60, 0xd4, + 0x26, 0xc7, 0x60, 0x02, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0a, 0x00, + 0x00, 0x02, 0x08, 0x00, 0x80, 0x08, 0x00, 0x00, + 0x08, 0x80, 0x28, 0x80, 0x02, 0x00, 0x00, 0x02, + 0x48, 0x61, 0x00, 0x04, 0x06, 0x04, 0x32, 0x46, + 0x6a, 0x5c, 0x67, 0x96, 0xaa, 0xae, 0xc8, 0xd3, + 0x5d, 0x62, 0x00, 0x54, 0x77, 0xf3, 0x0c, 0x2b, + 0x3d, 0x63, 0xfc, 0x62, 0x68, 0x63, 0x83, 0x63, + 0xe4, 0x63, 0xf1, 0x2b, 0x22, 0x64, 0xc5, 0x63, + 0xa9, 0x63, 0x2e, 0x3a, 0x69, 0x64, 0x7e, 0x64, + 0x9d, 0x64, 0x77, 0x64, 0x6c, 0x3a, 0x4f, 0x65, + 0x6c, 0x65, 0x0a, 0x30, 0xe3, 0x65, 0xf8, 0x66, + 0x49, 0x66, 0x19, 0x3b, 0x91, 0x66, 0x08, 0x3b, + 0xe4, 0x3a, 0x92, 0x51, 0x95, 0x51, 0x00, 0x67, + 0x9c, 0x66, 0xad, 0x80, 0xd9, 0x43, 0x17, 0x67, + 0x1b, 0x67, 0x21, 0x67, 0x5e, 0x67, 0x53, 0x67, + 0xc3, 0x33, 0x49, 0x3b, 0xfa, 0x67, 0x85, 0x67, + 0x52, 0x68, 0x85, 0x68, 0x6d, 0x34, 0x8e, 0x68, + 0x1f, 0x68, 0x14, 0x69, 0x9d, 0x3b, 0x42, 0x69, + 0xa3, 0x69, 0xea, 0x69, 0xa8, 0x6a, 0xa3, 0x36, + 0xdb, 0x6a, 0x18, 0x3c, 0x21, 0x6b, 0xa7, 0x38, + 0x54, 0x6b, 0x4e, 0x3c, 0x72, 0x6b, 0x9f, 0x6b, + 0xba, 0x6b, 0xbb, 0x6b, 0x8d, 0x3a, 0x0b, 0x1d, + 0xfa, 0x3a, 0x4e, 0x6c, 0xbc, 0x3c, 0xbf, 0x6c, + 0xcd, 0x6c, 0x67, 0x6c, 0x16, 0x6d, 0x3e, 0x6d, + 0x77, 0x6d, 0x41, 0x6d, 0x69, 0x6d, 0x78, 0x6d, + 0x85, 0x6d, 0x1e, 0x3d, 0x34, 0x6d, 0x2f, 0x6e, + 0x6e, 0x6e, 0x33, 0x3d, 0xcb, 0x6e, 0xc7, 0x6e, + 0xd1, 0x3e, 0xf9, 0x6d, 0x6e, 0x6f, 0x5e, 0x3f, + 0x8e, 0x3f, 0xc6, 0x6f, 0x39, 0x70, 0x1e, 0x70, + 0x1b, 0x70, 0x96, 0x3d, 0x4a, 0x70, 0x7d, 0x70, + 0x77, 0x70, 0xad, 0x70, 0x25, 0x05, 0x45, 0x71, + 0x63, 0x42, 0x9c, 0x71, 0xab, 0x43, 0x28, 0x72, + 0x35, 0x72, 0x50, 0x72, 0x08, 0x46, 0x80, 0x72, + 0x95, 0x72, 0x35, 0x47, 0x02, 0x20, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x08, 0x80, 0x00, + 0x00, 0x02, 0x02, 0x80, 0x8a, 0x00, 0x00, 0x20, + 0x00, 0x08, 0x0a, 0x00, 0x80, 0x88, 0x80, 0x20, + 0x14, 0x48, 0x7a, 0x73, 0x8b, 0x73, 0xac, 0x3e, + 0xa5, 0x73, 0xb8, 0x3e, 0xb8, 0x3e, 0x47, 0x74, + 0x5c, 0x74, 0x71, 0x74, 0x85, 0x74, 0xca, 0x74, + 0x1b, 0x3f, 0x24, 0x75, 0x36, 0x4c, 0x3e, 0x75, + 0x92, 0x4c, 0x70, 0x75, 0x9f, 0x21, 0x10, 0x76, + 0xa1, 0x4f, 0xb8, 0x4f, 0x44, 0x50, 0xfc, 0x3f, + 0x08, 0x40, 0xf4, 0x76, 0xf3, 0x50, 0xf2, 0x50, + 0x19, 0x51, 0x33, 0x51, 0x1e, 0x77, 0x1f, 0x77, + 0x1f, 0x77, 0x4a, 0x77, 0x39, 0x40, 0x8b, 0x77, + 0x46, 0x40, 0x96, 0x40, 0x1d, 0x54, 0x4e, 0x78, + 0x8c, 0x78, 0xcc, 0x78, 0xe3, 0x40, 0x26, 0x56, + 0x56, 0x79, 0x9a, 0x56, 0xc5, 0x56, 0x8f, 0x79, + 0xeb, 0x79, 0x2f, 0x41, 0x40, 0x7a, 0x4a, 0x7a, + 0x4f, 0x7a, 0x7c, 0x59, 0xa7, 0x5a, 0xa7, 0x5a, + 0xee, 0x7a, 0x02, 0x42, 0xab, 0x5b, 0xc6, 0x7b, + 0xc9, 0x7b, 0x27, 0x42, 0x80, 0x5c, 0xd2, 0x7c, + 0xa0, 0x42, 0xe8, 0x7c, 0xe3, 0x7c, 0x00, 0x7d, + 0x86, 0x5f, 0x63, 0x7d, 0x01, 0x43, 0xc7, 0x7d, + 0x02, 0x7e, 0x45, 0x7e, 0x34, 0x43, 0x28, 0x62, + 0x47, 0x62, 0x59, 0x43, 0xd9, 0x62, 0x7a, 0x7f, + 0x3e, 0x63, 0x95, 0x7f, 0xfa, 0x7f, 0x05, 0x80, + 0xda, 0x64, 0x23, 0x65, 0x60, 0x80, 0xa8, 0x65, + 0x70, 0x80, 0x5f, 0x33, 0xd5, 0x43, 0xb2, 0x80, + 0x03, 0x81, 0x0b, 0x44, 0x3e, 0x81, 0xb5, 0x5a, + 0xa7, 0x67, 0xb5, 0x67, 0x93, 0x33, 0x9c, 0x33, + 0x01, 0x82, 0x04, 0x82, 0x9e, 0x8f, 0x6b, 0x44, + 0x91, 0x82, 0x8b, 0x82, 0x9d, 0x82, 0xb3, 0x52, + 0xb1, 0x82, 0xb3, 0x82, 0xbd, 0x82, 0xe6, 0x82, + 0x3c, 0x6b, 0xe5, 0x82, 0x1d, 0x83, 0x63, 0x83, + 0xad, 0x83, 0x23, 0x83, 0xbd, 0x83, 0xe7, 0x83, + 0x57, 0x84, 0x53, 0x83, 0xca, 0x83, 0xcc, 0x83, + 0xdc, 0x83, 0x36, 0x6c, 0x6b, 0x6d, 0x02, 0x00, + 0x00, 0x20, 0x22, 0x2a, 0xa0, 0x0a, 0x00, 0x20, + 0x80, 0x28, 0x00, 0xa8, 0x20, 0x20, 0x00, 0x02, + 0x80, 0x22, 0x02, 0x8a, 0x08, 0x00, 0xaa, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x28, 0xd5, 0x6c, + 0x2b, 0x45, 0xf1, 0x84, 0xf3, 0x84, 0x16, 0x85, + 0xca, 0x73, 0x64, 0x85, 0x2c, 0x6f, 0x5d, 0x45, + 0x61, 0x45, 0xb1, 0x6f, 0xd2, 0x70, 0x6b, 0x45, + 0x50, 0x86, 0x5c, 0x86, 0x67, 0x86, 0x69, 0x86, + 0xa9, 0x86, 0x88, 0x86, 0x0e, 0x87, 0xe2, 0x86, + 0x79, 0x87, 0x28, 0x87, 0x6b, 0x87, 0x86, 0x87, + 0xd7, 0x45, 0xe1, 0x87, 0x01, 0x88, 0xf9, 0x45, + 0x60, 0x88, 0x63, 0x88, 0x67, 0x76, 0xd7, 0x88, + 0xde, 0x88, 0x35, 0x46, 0xfa, 0x88, 0xbb, 0x34, + 0xae, 0x78, 0x66, 0x79, 0xbe, 0x46, 0xc7, 0x46, + 0xa0, 0x8a, 0xed, 0x8a, 0x8a, 0x8b, 0x55, 0x8c, + 0xa8, 0x7c, 0xab, 0x8c, 0xc1, 0x8c, 0x1b, 0x8d, + 0x77, 0x8d, 0x2f, 0x7f, 0x04, 0x08, 0xcb, 0x8d, + 0xbc, 0x8d, 0xf0, 0x8d, 0xde, 0x08, 0xd4, 0x8e, + 0x38, 0x8f, 0xd2, 0x85, 0xed, 0x85, 0x94, 0x90, + 0xf1, 0x90, 0x11, 0x91, 0x2e, 0x87, 0x1b, 0x91, + 0x38, 0x92, 0xd7, 0x92, 0xd8, 0x92, 0x7c, 0x92, + 0xf9, 0x93, 0x15, 0x94, 0xfa, 0x8b, 0x8b, 0x95, + 0x95, 0x49, 0xb7, 0x95, 0x77, 0x8d, 0xe6, 0x49, + 0xc3, 0x96, 0xb2, 0x5d, 0x23, 0x97, 0x45, 0x91, + 0x1a, 0x92, 0x6e, 0x4a, 0x76, 0x4a, 0xe0, 0x97, + 0x0a, 0x94, 0xb2, 0x4a, 0x96, 0x94, 0x0b, 0x98, + 0x0b, 0x98, 0x29, 0x98, 0xb6, 0x95, 0xe2, 0x98, + 0x33, 0x4b, 0x29, 0x99, 0xa7, 0x99, 0xc2, 0x99, + 0xfe, 0x99, 0xce, 0x4b, 0x30, 0x9b, 0x12, 0x9b, + 0x40, 0x9c, 0xfd, 0x9c, 0xce, 0x4c, 0xed, 0x4c, + 0x67, 0x9d, 0xce, 0xa0, 0xf8, 0x4c, 0x05, 0xa1, + 0x0e, 0xa2, 0x91, 0xa2, 0xbb, 0x9e, 0x56, 0x4d, + 0xf9, 0x9e, 0xfe, 0x9e, 0x05, 0x9f, 0x0f, 0x9f, + 0x16, 0x9f, 0x3b, 0x9f, 0x00, 0xa6, 0x02, 0x88, + 0xa0, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x28, + 0x00, 0x08, 0xa0, 0x80, 0xa0, 0x80, 0x00, 0x80, + 0x80, 0x00, 0x0a, 0x88, 0x80, 0x00, 0x80, 0x00, + 0x20, 0x2a, 0x00, 0x80, +}; + +static const uint16_t unicode_comp_table[965] = { + 0x4a01, 0x49c0, 0x4a02, 0x0280, 0x0281, 0x0282, 0x0283, 0x02c0, + 0x02c2, 0x0a00, 0x0284, 0x2442, 0x0285, 0x07c0, 0x0980, 0x0982, + 0x2440, 0x2280, 0x02c4, 0x2282, 0x2284, 0x2286, 0x02c6, 0x02c8, + 0x02ca, 0x02cc, 0x0287, 0x228a, 0x02ce, 0x228c, 0x2290, 0x2292, + 0x228e, 0x0288, 0x0289, 0x028a, 0x2482, 0x0300, 0x0302, 0x0304, + 0x028b, 0x2480, 0x0308, 0x0984, 0x0986, 0x2458, 0x0a02, 0x0306, + 0x2298, 0x229a, 0x229e, 0x0900, 0x030a, 0x22a0, 0x030c, 0x030e, + 0x0840, 0x0310, 0x0312, 0x22a2, 0x22a6, 0x09c0, 0x22a4, 0x22a8, + 0x22aa, 0x028c, 0x028d, 0x028e, 0x0340, 0x0342, 0x0344, 0x0380, + 0x028f, 0x248e, 0x07c2, 0x0988, 0x098a, 0x2490, 0x0346, 0x22ac, + 0x0400, 0x22b0, 0x0842, 0x22b2, 0x0402, 0x22b4, 0x0440, 0x0444, + 0x22b6, 0x0442, 0x22c2, 0x22c0, 0x22c4, 0x22c6, 0x22c8, 0x0940, + 0x04c0, 0x0291, 0x22ca, 0x04c4, 0x22cc, 0x04c2, 0x22d0, 0x22ce, + 0x0292, 0x0293, 0x0294, 0x0295, 0x0540, 0x0542, 0x0a08, 0x0296, + 0x2494, 0x0544, 0x07c4, 0x098c, 0x098e, 0x06c0, 0x2492, 0x0844, + 0x2308, 0x230a, 0x0580, 0x230c, 0x0584, 0x0990, 0x0992, 0x230e, + 0x0582, 0x2312, 0x0586, 0x0588, 0x2314, 0x058c, 0x2316, 0x0998, + 0x058a, 0x231e, 0x0590, 0x2320, 0x099a, 0x058e, 0x2324, 0x2322, + 0x0299, 0x029a, 0x029b, 0x05c0, 0x05c2, 0x05c4, 0x029c, 0x24ac, + 0x05c6, 0x05c8, 0x07c6, 0x0994, 0x0996, 0x0700, 0x24aa, 0x2326, + 0x05ca, 0x232a, 0x2328, 0x2340, 0x2342, 0x2344, 0x2346, 0x05cc, + 0x234a, 0x2348, 0x234c, 0x234e, 0x2350, 0x24b8, 0x029d, 0x05ce, + 0x24be, 0x0a0c, 0x2352, 0x0600, 0x24bc, 0x24ba, 0x0640, 0x2354, + 0x0642, 0x0644, 0x2356, 0x2358, 0x02a0, 0x02a1, 0x02a2, 0x02a3, + 0x02c1, 0x02c3, 0x0a01, 0x02a4, 0x2443, 0x02a5, 0x07c1, 0x0981, + 0x0983, 0x2441, 0x2281, 0x02c5, 0x2283, 0x2285, 0x2287, 0x02c7, + 0x02c9, 0x02cb, 0x02cd, 0x02a7, 0x228b, 0x02cf, 0x228d, 0x2291, + 0x2293, 0x228f, 0x02a8, 0x02a9, 0x02aa, 0x2483, 0x0301, 0x0303, + 0x0305, 0x02ab, 0x2481, 0x0309, 0x0985, 0x0987, 0x2459, 0x0a03, + 0x0307, 0x2299, 0x229b, 0x229f, 0x0901, 0x030b, 0x22a1, 0x030d, + 0x030f, 0x0841, 0x0311, 0x0313, 0x22a3, 0x22a7, 0x09c1, 0x22a5, + 0x22a9, 0x22ab, 0x2380, 0x02ac, 0x02ad, 0x02ae, 0x0341, 0x0343, + 0x0345, 0x02af, 0x248f, 0x07c3, 0x0989, 0x098b, 0x2491, 0x0347, + 0x22ad, 0x0401, 0x0884, 0x22b1, 0x0843, 0x22b3, 0x0403, 0x22b5, + 0x0441, 0x0445, 0x22b7, 0x0443, 0x22c3, 0x22c1, 0x22c5, 0x22c7, + 0x22c9, 0x0941, 0x04c1, 0x02b1, 0x22cb, 0x04c5, 0x22cd, 0x04c3, + 0x22d1, 0x22cf, 0x02b2, 0x02b3, 0x02b4, 0x02b5, 0x0541, 0x0543, + 0x0a09, 0x02b6, 0x2495, 0x0545, 0x07c5, 0x098d, 0x098f, 0x06c1, + 0x2493, 0x0845, 0x2309, 0x230b, 0x0581, 0x230d, 0x0585, 0x0991, + 0x0993, 0x230f, 0x0583, 0x2313, 0x0587, 0x0589, 0x2315, 0x058d, + 0x2317, 0x0999, 0x058b, 0x231f, 0x2381, 0x0591, 0x2321, 0x099b, + 0x058f, 0x2325, 0x2323, 0x02b9, 0x02ba, 0x02bb, 0x05c1, 0x05c3, + 0x05c5, 0x02bc, 0x24ad, 0x05c7, 0x05c9, 0x07c7, 0x0995, 0x0997, + 0x0701, 0x24ab, 0x2327, 0x05cb, 0x232b, 0x2329, 0x2341, 0x2343, + 0x2345, 0x2347, 0x05cd, 0x234b, 0x2349, 0x2382, 0x234d, 0x234f, + 0x2351, 0x24b9, 0x02bd, 0x05cf, 0x24bf, 0x0a0d, 0x2353, 0x02bf, + 0x24bd, 0x2383, 0x24bb, 0x0641, 0x2355, 0x0643, 0x0645, 0x2357, + 0x2359, 0x3101, 0x0c80, 0x2e00, 0x2446, 0x2444, 0x244a, 0x2448, + 0x0800, 0x0942, 0x0944, 0x0804, 0x2288, 0x2486, 0x2484, 0x248a, + 0x2488, 0x22ae, 0x2498, 0x2496, 0x249c, 0x249a, 0x2300, 0x0a06, + 0x2302, 0x0a04, 0x0946, 0x07ce, 0x07ca, 0x07c8, 0x07cc, 0x2447, + 0x2445, 0x244b, 0x2449, 0x0801, 0x0943, 0x0945, 0x0805, 0x2289, + 0x2487, 0x2485, 0x248b, 0x2489, 0x22af, 0x2499, 0x2497, 0x249d, + 0x249b, 0x2301, 0x0a07, 0x2303, 0x0a05, 0x0947, 0x07cf, 0x07cb, + 0x07c9, 0x07cd, 0x2450, 0x244e, 0x2454, 0x2452, 0x2451, 0x244f, + 0x2455, 0x2453, 0x2294, 0x2296, 0x2295, 0x2297, 0x2304, 0x2306, + 0x2305, 0x2307, 0x2318, 0x2319, 0x231a, 0x231b, 0x232c, 0x232d, + 0x232e, 0x232f, 0x2400, 0x24a2, 0x24a0, 0x24a6, 0x24a4, 0x24a8, + 0x24a3, 0x24a1, 0x24a7, 0x24a5, 0x24a9, 0x24b0, 0x24ae, 0x24b4, + 0x24b2, 0x24b6, 0x24b1, 0x24af, 0x24b5, 0x24b3, 0x24b7, 0x0882, + 0x0880, 0x0881, 0x0802, 0x0803, 0x229c, 0x229d, 0x0a0a, 0x0a0b, + 0x0883, 0x0b40, 0x2c8a, 0x0c81, 0x2c89, 0x2c88, 0x2540, 0x2541, + 0x2d00, 0x2e07, 0x0d00, 0x2640, 0x2641, 0x2e80, 0x0d01, 0x26c8, + 0x26c9, 0x2f00, 0x2f84, 0x0d02, 0x2f83, 0x2f82, 0x0d40, 0x26d8, + 0x26d9, 0x3186, 0x0d04, 0x2740, 0x2741, 0x3100, 0x3086, 0x0d06, + 0x3085, 0x3084, 0x0d41, 0x2840, 0x3200, 0x0d07, 0x284f, 0x2850, + 0x3280, 0x2c84, 0x2e03, 0x2857, 0x0d42, 0x2c81, 0x2c80, 0x24c0, + 0x24c1, 0x2c86, 0x2c83, 0x28c0, 0x0d43, 0x25c0, 0x25c1, 0x2940, + 0x0d44, 0x26c0, 0x26c1, 0x2e05, 0x2e02, 0x29c0, 0x0d45, 0x2f05, + 0x2f04, 0x0d80, 0x26d0, 0x26d1, 0x2f80, 0x2a40, 0x0d82, 0x26e0, + 0x26e1, 0x3080, 0x3081, 0x2ac0, 0x0d83, 0x3004, 0x3003, 0x0d81, + 0x27c0, 0x27c1, 0x3082, 0x2b40, 0x0d84, 0x2847, 0x2848, 0x3184, + 0x3181, 0x2f06, 0x0d08, 0x2f81, 0x3005, 0x0d46, 0x3083, 0x3182, + 0x0e00, 0x0e01, 0x0f40, 0x1180, 0x1182, 0x0f03, 0x0f00, 0x11c0, + 0x0f01, 0x1140, 0x1202, 0x1204, 0x0f81, 0x1240, 0x0fc0, 0x1242, + 0x0f80, 0x1244, 0x1284, 0x0f82, 0x1286, 0x1288, 0x128a, 0x12c0, + 0x1282, 0x1181, 0x1183, 0x1043, 0x1040, 0x11c1, 0x1041, 0x1141, + 0x1203, 0x1205, 0x10c1, 0x1241, 0x1000, 0x1243, 0x10c0, 0x1245, + 0x1285, 0x10c2, 0x1287, 0x1289, 0x128b, 0x12c1, 0x1283, 0x1080, + 0x1100, 0x1101, 0x1200, 0x1201, 0x1280, 0x1281, 0x1340, 0x1341, + 0x1343, 0x1342, 0x1344, 0x13c2, 0x1400, 0x13c0, 0x1440, 0x1480, + 0x14c0, 0x1540, 0x1541, 0x1740, 0x1700, 0x1741, 0x17c0, 0x1800, + 0x1802, 0x1801, 0x1840, 0x1880, 0x1900, 0x18c0, 0x18c1, 0x1901, + 0x1940, 0x1942, 0x1941, 0x1980, 0x19c0, 0x19c2, 0x19c1, 0x1c80, + 0x1cc0, 0x1dc0, 0x1f80, 0x2000, 0x2002, 0x2004, 0x2006, 0x2008, + 0x2040, 0x2080, 0x2082, 0x20c0, 0x20c1, 0x2100, 0x22b8, 0x22b9, + 0x2310, 0x2311, 0x231c, 0x231d, 0x244c, 0x2456, 0x244d, 0x2457, + 0x248c, 0x248d, 0x249e, 0x249f, 0x2500, 0x2502, 0x2504, 0x2bc0, + 0x2501, 0x2503, 0x2505, 0x2bc1, 0x2bc2, 0x2bc3, 0x2bc4, 0x2bc5, + 0x2bc6, 0x2bc7, 0x2580, 0x2582, 0x2584, 0x2bc8, 0x2581, 0x2583, + 0x2585, 0x2bc9, 0x2bca, 0x2bcb, 0x2bcc, 0x2bcd, 0x2bce, 0x2bcf, + 0x2600, 0x2602, 0x2601, 0x2603, 0x2680, 0x2682, 0x2681, 0x2683, + 0x26c2, 0x26c4, 0x26c6, 0x2c00, 0x26c3, 0x26c5, 0x26c7, 0x2c01, + 0x2c02, 0x2c03, 0x2c04, 0x2c05, 0x2c06, 0x2c07, 0x26ca, 0x26cc, + 0x26ce, 0x2c08, 0x26cb, 0x26cd, 0x26cf, 0x2c09, 0x2c0a, 0x2c0b, + 0x2c0c, 0x2c0d, 0x2c0e, 0x2c0f, 0x26d2, 0x26d4, 0x26d6, 0x26d3, + 0x26d5, 0x26d7, 0x26da, 0x26dc, 0x26de, 0x26db, 0x26dd, 0x26df, + 0x2700, 0x2702, 0x2701, 0x2703, 0x2780, 0x2782, 0x2781, 0x2783, + 0x2800, 0x2802, 0x2804, 0x2801, 0x2803, 0x2805, 0x2842, 0x2844, + 0x2846, 0x2849, 0x284b, 0x284d, 0x2c40, 0x284a, 0x284c, 0x284e, + 0x2c41, 0x2c42, 0x2c43, 0x2c44, 0x2c45, 0x2c46, 0x2c47, 0x2851, + 0x2853, 0x2855, 0x2c48, 0x2852, 0x2854, 0x2856, 0x2c49, 0x2c4a, + 0x2c4b, 0x2c4c, 0x2c4d, 0x2c4e, 0x2c4f, 0x2c82, 0x2e01, 0x3180, + 0x2c87, 0x2f01, 0x2f02, 0x2f03, 0x2e06, 0x3185, 0x3000, 0x3001, + 0x3002, 0x4640, 0x4641, 0x4680, 0x46c0, 0x46c2, 0x46c1, 0x4700, + 0x4740, 0x4780, 0x47c0, 0x47c2, 0x4900, 0x4940, 0x4980, 0x4982, + 0x4a00, 0x49c2, 0x4a03, 0x4a04, 0x4a40, 0x4a41, 0x4a80, 0x4a81, + 0x4ac0, 0x4ac1, 0x4bc0, 0x4bc1, 0x4b00, 0x4b01, 0x4b40, 0x4b41, + 0x4bc2, 0x4bc3, 0x4b80, 0x4b81, 0x4b82, 0x4b83, 0x4c00, 0x4c01, + 0x4c02, 0x4c03, 0x5600, 0x5440, 0x5442, 0x5444, 0x5446, 0x5448, + 0x544a, 0x544c, 0x544e, 0x5450, 0x5452, 0x5454, 0x5456, 0x5480, + 0x5482, 0x5484, 0x54c0, 0x54c1, 0x5500, 0x5501, 0x5540, 0x5541, + 0x5580, 0x5581, 0x55c0, 0x55c1, 0x5680, 0x58c0, 0x5700, 0x5702, + 0x5704, 0x5706, 0x5708, 0x570a, 0x570c, 0x570e, 0x5710, 0x5712, + 0x5714, 0x5716, 0x5740, 0x5742, 0x5744, 0x5780, 0x5781, 0x57c0, + 0x57c1, 0x5800, 0x5801, 0x5840, 0x5841, 0x5880, 0x5881, 0x5900, + 0x5901, 0x5902, 0x5903, 0x5940, 0x8ec0, 0x8f00, 0x8fc0, 0x8fc2, + 0x9000, 0x9040, 0x9041, 0x9080, 0x9081, 0x90c0, 0x90c2, 0x9100, + 0x9140, 0x9182, 0x9180, 0x9183, 0x91c1, 0x91c0, 0x91c3, 0x9200, + 0x9201, 0x9240, 0x9280, 0x9282, 0x9284, 0x9281, 0x9285, 0x9287, + 0x9286, 0x9283, 0x92c1, 0x92c0, 0x92c2, +}; + +typedef enum { + UNICODE_GC_Cn, + UNICODE_GC_Lu, + UNICODE_GC_Ll, + UNICODE_GC_Lt, + UNICODE_GC_Lm, + UNICODE_GC_Lo, + UNICODE_GC_Mn, + UNICODE_GC_Mc, + UNICODE_GC_Me, + UNICODE_GC_Nd, + UNICODE_GC_Nl, + UNICODE_GC_No, + UNICODE_GC_Sm, + UNICODE_GC_Sc, + UNICODE_GC_Sk, + UNICODE_GC_So, + UNICODE_GC_Pc, + UNICODE_GC_Pd, + UNICODE_GC_Ps, + UNICODE_GC_Pe, + UNICODE_GC_Pi, + UNICODE_GC_Pf, + UNICODE_GC_Po, + UNICODE_GC_Zs, + UNICODE_GC_Zl, + UNICODE_GC_Zp, + UNICODE_GC_Cc, + UNICODE_GC_Cf, + UNICODE_GC_Cs, + UNICODE_GC_Co, + UNICODE_GC_LC, + UNICODE_GC_L, + UNICODE_GC_M, + UNICODE_GC_N, + UNICODE_GC_S, + UNICODE_GC_P, + UNICODE_GC_Z, + UNICODE_GC_C, + UNICODE_GC_COUNT, +} UnicodeGCEnum; + +static const char unicode_gc_name_table[] = + "Cn,Unassigned" "\0" + "Lu,Uppercase_Letter" "\0" + "Ll,Lowercase_Letter" "\0" + "Lt,Titlecase_Letter" "\0" + "Lm,Modifier_Letter" "\0" + "Lo,Other_Letter" "\0" + "Mn,Nonspacing_Mark" "\0" + "Mc,Spacing_Mark" "\0" + "Me,Enclosing_Mark" "\0" + "Nd,Decimal_Number,digit" "\0" + "Nl,Letter_Number" "\0" + "No,Other_Number" "\0" + "Sm,Math_Symbol" "\0" + "Sc,Currency_Symbol" "\0" + "Sk,Modifier_Symbol" "\0" + "So,Other_Symbol" "\0" + "Pc,Connector_Punctuation" "\0" + "Pd,Dash_Punctuation" "\0" + "Ps,Open_Punctuation" "\0" + "Pe,Close_Punctuation" "\0" + "Pi,Initial_Punctuation" "\0" + "Pf,Final_Punctuation" "\0" + "Po,Other_Punctuation" "\0" + "Zs,Space_Separator" "\0" + "Zl,Line_Separator" "\0" + "Zp,Paragraph_Separator" "\0" + "Cc,Control,cntrl" "\0" + "Cf,Format" "\0" + "Cs,Surrogate" "\0" + "Co,Private_Use" "\0" + "LC,Cased_Letter" "\0" + "L,Letter" "\0" + "M,Mark,Combining_Mark" "\0" + "N,Number" "\0" + "S,Symbol" "\0" + "P,Punctuation,punct" "\0" + "Z,Separator" "\0" + "C,Other" "\0" +; + +static const uint8_t unicode_gc_table[4122] = { + 0xfa, 0x18, 0x17, 0x56, 0x0d, 0x56, 0x12, 0x13, + 0x16, 0x0c, 0x16, 0x11, 0x36, 0xe9, 0x02, 0x36, + 0x4c, 0x36, 0xe1, 0x12, 0x12, 0x16, 0x13, 0x0e, + 0x10, 0x0e, 0xe2, 0x12, 0x12, 0x0c, 0x13, 0x0c, + 0xfa, 0x19, 0x17, 0x16, 0x6d, 0x0f, 0x16, 0x0e, + 0x0f, 0x05, 0x14, 0x0c, 0x1b, 0x0f, 0x0e, 0x0f, + 0x0c, 0x2b, 0x0e, 0x02, 0x36, 0x0e, 0x0b, 0x05, + 0x15, 0x4b, 0x16, 0xe1, 0x0f, 0x0c, 0xc1, 0xe2, + 0x10, 0x0c, 0xe2, 0x00, 0xff, 0x30, 0x02, 0xff, + 0x08, 0x02, 0xff, 0x27, 0xbf, 0x22, 0x21, 0x02, + 0x5f, 0x5f, 0x21, 0x22, 0x61, 0x02, 0x21, 0x02, + 0x41, 0x42, 0x21, 0x02, 0x21, 0x02, 0x9f, 0x7f, + 0x02, 0x5f, 0x5f, 0x21, 0x02, 0x5f, 0x3f, 0x02, + 0x05, 0x3f, 0x22, 0x65, 0x01, 0x03, 0x02, 0x01, + 0x03, 0x02, 0x01, 0x03, 0x02, 0xff, 0x08, 0x02, + 0xff, 0x0a, 0x02, 0x01, 0x03, 0x02, 0x5f, 0x21, + 0x02, 0xff, 0x32, 0xa2, 0x21, 0x02, 0x21, 0x22, + 0x5f, 0x41, 0x02, 0xff, 0x00, 0xe2, 0x3c, 0x25, + 0xe2, 0x12, 0xe4, 0x0a, 0x6e, 0xe4, 0x04, 0xee, + 0x06, 0x84, 0xce, 0x04, 0x0e, 0x04, 0xee, 0x09, + 0xe6, 0x68, 0x7f, 0x04, 0x0e, 0x3f, 0x20, 0x04, + 0x42, 0x16, 0x01, 0x60, 0x2e, 0x01, 0x16, 0x41, + 0x00, 0x01, 0x00, 0x21, 0x02, 0xe1, 0x09, 0x00, + 0xe1, 0x01, 0xe2, 0x1b, 0x3f, 0x02, 0x41, 0x42, + 0xff, 0x10, 0x62, 0x3f, 0x0c, 0x5f, 0x3f, 0x02, + 0xe1, 0x2b, 0xe2, 0x28, 0xff, 0x1a, 0x0f, 0x86, + 0x28, 0xff, 0x2f, 0xff, 0x06, 0x02, 0xff, 0x58, + 0x00, 0xe1, 0x1e, 0x20, 0x04, 0xb6, 0xe2, 0x21, + 0x16, 0x11, 0x20, 0x2f, 0x0d, 0x00, 0xe6, 0x25, + 0x11, 0x06, 0x16, 0x26, 0x16, 0x26, 0x16, 0x06, + 0xe0, 0x00, 0xe5, 0x13, 0x60, 0x65, 0x36, 0xe0, + 0x03, 0xbb, 0x4c, 0x36, 0x0d, 0x36, 0x2f, 0xe6, + 0x03, 0x16, 0x1b, 0x56, 0xe5, 0x18, 0x04, 0xe5, + 0x02, 0xe6, 0x0d, 0xe9, 0x02, 0x76, 0x25, 0x06, + 0xe5, 0x5b, 0x16, 0x05, 0xc6, 0x1b, 0x0f, 0xa6, + 0x24, 0x26, 0x0f, 0x66, 0x25, 0xe9, 0x02, 0x45, + 0x2f, 0x05, 0xf6, 0x06, 0x00, 0x1b, 0x05, 0x06, + 0xe5, 0x16, 0xe6, 0x13, 0x20, 0xe5, 0x51, 0xe6, + 0x03, 0x05, 0xe0, 0x06, 0xe9, 0x02, 0xe5, 0x19, + 0xe6, 0x01, 0x24, 0x0f, 0x56, 0x04, 0x20, 0x06, + 0x2d, 0xe5, 0x0e, 0x66, 0x04, 0xe6, 0x01, 0x04, + 0x46, 0x04, 0x86, 0x20, 0xf6, 0x07, 0x00, 0xe5, + 0x11, 0x46, 0x20, 0x16, 0x00, 0xe5, 0x03, 0x80, + 0xe5, 0x10, 0x0e, 0xc5, 0x3b, 0x80, 0xe6, 0x01, + 0xe5, 0x21, 0x04, 0xe6, 0x10, 0x1b, 0xe6, 0x18, + 0x07, 0xe5, 0x2e, 0x06, 0x07, 0x06, 0x05, 0x47, + 0xe6, 0x00, 0x67, 0x06, 0x27, 0x05, 0xc6, 0xe5, + 0x02, 0x26, 0x36, 0xe9, 0x02, 0x16, 0x04, 0xe5, + 0x07, 0x06, 0x27, 0x00, 0xe5, 0x00, 0x20, 0x25, + 0x20, 0xe5, 0x0e, 0x00, 0xc5, 0x00, 0x05, 0x40, + 0x65, 0x20, 0x06, 0x05, 0x47, 0x66, 0x20, 0x27, + 0x20, 0x27, 0x06, 0x05, 0xe0, 0x00, 0x07, 0x60, + 0x25, 0x00, 0x45, 0x26, 0x20, 0xe9, 0x02, 0x25, + 0x2d, 0xab, 0x0f, 0x0d, 0x05, 0x16, 0x06, 0x20, + 0x26, 0x07, 0x00, 0xa5, 0x60, 0x25, 0x20, 0xe5, + 0x0e, 0x00, 0xc5, 0x00, 0x25, 0x00, 0x25, 0x00, + 0x25, 0x20, 0x06, 0x00, 0x47, 0x26, 0x60, 0x26, + 0x20, 0x46, 0x40, 0x06, 0xc0, 0x65, 0x00, 0x05, + 0xc0, 0xe9, 0x02, 0x26, 0x45, 0x06, 0x16, 0xe0, + 0x02, 0x26, 0x07, 0x00, 0xe5, 0x01, 0x00, 0x45, + 0x00, 0xe5, 0x0e, 0x00, 0xc5, 0x00, 0x25, 0x00, + 0x85, 0x20, 0x06, 0x05, 0x47, 0x86, 0x00, 0x26, + 0x07, 0x00, 0x27, 0x06, 0x20, 0x05, 0xe0, 0x07, + 0x25, 0x26, 0x20, 0xe9, 0x02, 0x16, 0x0d, 0xc0, + 0x05, 0xa6, 0x00, 0x06, 0x27, 0x00, 0xe5, 0x00, + 0x20, 0x25, 0x20, 0xe5, 0x0e, 0x00, 0xc5, 0x00, + 0x25, 0x00, 0x85, 0x20, 0x06, 0x05, 0x07, 0x06, + 0x07, 0x66, 0x20, 0x27, 0x20, 0x27, 0x06, 0xc0, + 0x26, 0x07, 0x60, 0x25, 0x00, 0x45, 0x26, 0x20, + 0xe9, 0x02, 0x0f, 0x05, 0xab, 0xe0, 0x02, 0x06, + 0x05, 0x00, 0xa5, 0x40, 0x45, 0x00, 0x65, 0x40, + 0x25, 0x00, 0x05, 0x00, 0x25, 0x40, 0x25, 0x40, + 0x45, 0x40, 0xe5, 0x04, 0x60, 0x27, 0x06, 0x27, + 0x40, 0x47, 0x00, 0x47, 0x06, 0x20, 0x05, 0xa0, + 0x07, 0xe0, 0x06, 0xe9, 0x02, 0x4b, 0xaf, 0x0d, + 0x0f, 0x80, 0x06, 0x47, 0x06, 0xe5, 0x00, 0x00, + 0x45, 0x00, 0xe5, 0x0f, 0x00, 0xe5, 0x08, 0x20, + 0x06, 0x05, 0x46, 0x67, 0x00, 0x46, 0x00, 0x66, + 0xc0, 0x26, 0x00, 0x45, 0x00, 0x25, 0x20, 0x25, + 0x26, 0x20, 0xe9, 0x02, 0xc0, 0x16, 0xcb, 0x0f, + 0x05, 0x06, 0x27, 0x16, 0xe5, 0x00, 0x00, 0x45, + 0x00, 0xe5, 0x0f, 0x00, 0xe5, 0x02, 0x00, 0x85, + 0x20, 0x06, 0x05, 0x07, 0x06, 0x87, 0x00, 0x06, + 0x27, 0x00, 0x27, 0x26, 0xc0, 0x27, 0x80, 0x45, + 0x00, 0x25, 0x26, 0x20, 0xe9, 0x02, 0x00, 0x25, + 0x07, 0xe0, 0x04, 0x26, 0x27, 0xe5, 0x01, 0x00, + 0x45, 0x00, 0xe5, 0x21, 0x26, 0x05, 0x47, 0x66, + 0x00, 0x47, 0x00, 0x47, 0x06, 0x05, 0x0f, 0x60, + 0x45, 0x07, 0xcb, 0x45, 0x26, 0x20, 0xe9, 0x02, + 0xeb, 0x01, 0x0f, 0xa5, 0x00, 0x06, 0x27, 0x00, + 0xe5, 0x0a, 0x40, 0xe5, 0x10, 0x00, 0xe5, 0x01, + 0x00, 0x05, 0x20, 0xc5, 0x40, 0x06, 0x60, 0x47, + 0x46, 0x00, 0x06, 0x00, 0xe7, 0x00, 0xa0, 0xe9, + 0x02, 0x20, 0x27, 0x16, 0xe0, 0x04, 0xe5, 0x28, + 0x06, 0x25, 0xc6, 0x60, 0x0d, 0xa5, 0x04, 0xe6, + 0x00, 0x16, 0xe9, 0x02, 0x36, 0xe0, 0x1d, 0x25, + 0x00, 0x05, 0x00, 0x85, 0x00, 0xe5, 0x10, 0x00, + 0x05, 0x00, 0xe5, 0x02, 0x06, 0x25, 0xe6, 0x01, + 0x05, 0x20, 0x85, 0x00, 0x04, 0x00, 0xc6, 0x00, + 0xe9, 0x02, 0x20, 0x65, 0xe0, 0x18, 0x05, 0x4f, + 0xf6, 0x07, 0x0f, 0x16, 0x4f, 0x26, 0xaf, 0xe9, + 0x02, 0xeb, 0x02, 0x0f, 0x06, 0x0f, 0x06, 0x0f, + 0x06, 0x12, 0x13, 0x12, 0x13, 0x27, 0xe5, 0x00, + 0x00, 0xe5, 0x1c, 0x60, 0xe6, 0x06, 0x07, 0x86, + 0x16, 0x26, 0x85, 0xe6, 0x03, 0x00, 0xe6, 0x1c, + 0x00, 0xef, 0x00, 0x06, 0xaf, 0x00, 0x2f, 0x96, + 0x6f, 0x36, 0xe0, 0x1d, 0xe5, 0x23, 0x27, 0x66, + 0x07, 0xa6, 0x07, 0x26, 0x27, 0x26, 0x05, 0xe9, + 0x02, 0xb6, 0xa5, 0x27, 0x26, 0x65, 0x46, 0x05, + 0x47, 0x25, 0xc7, 0x45, 0x66, 0xe5, 0x05, 0x06, + 0x27, 0x26, 0xa7, 0x06, 0x05, 0x07, 0xe9, 0x02, + 0x47, 0x06, 0x2f, 0xe1, 0x1e, 0x00, 0x01, 0x80, + 0x01, 0x20, 0xe2, 0x23, 0x16, 0x04, 0x42, 0xe5, + 0x80, 0xc1, 0x00, 0x65, 0x20, 0xc5, 0x00, 0x05, + 0x00, 0x65, 0x20, 0xe5, 0x21, 0x00, 0x65, 0x20, + 0xe5, 0x19, 0x00, 0x65, 0x20, 0xc5, 0x00, 0x05, + 0x00, 0x65, 0x20, 0xe5, 0x07, 0x00, 0xe5, 0x31, + 0x00, 0x65, 0x20, 0xe5, 0x3b, 0x20, 0x46, 0xf6, + 0x01, 0xeb, 0x0c, 0x40, 0xe5, 0x08, 0xef, 0x02, + 0xa0, 0xe1, 0x4e, 0x20, 0xa2, 0x20, 0x11, 0xe5, + 0x81, 0xe4, 0x0f, 0x16, 0xe5, 0x09, 0x17, 0xe5, + 0x12, 0x12, 0x13, 0x40, 0xe5, 0x43, 0x56, 0x4a, + 0xe5, 0x00, 0xc0, 0xe5, 0x0a, 0x46, 0x07, 0xe0, + 0x01, 0xe5, 0x0b, 0x26, 0x07, 0x36, 0xe0, 0x01, + 0xe5, 0x0a, 0x26, 0xe0, 0x04, 0xe5, 0x05, 0x00, + 0x45, 0x00, 0x26, 0xe0, 0x04, 0xe5, 0x2c, 0x26, + 0x07, 0xc6, 0xe7, 0x00, 0x06, 0x27, 0xe6, 0x03, + 0x56, 0x04, 0x56, 0x0d, 0x05, 0x06, 0x20, 0xe9, + 0x02, 0xa0, 0xeb, 0x02, 0xa0, 0xb6, 0x11, 0x76, + 0x46, 0x1b, 0x06, 0xe9, 0x02, 0xa0, 0xe5, 0x1b, + 0x04, 0xe5, 0x2d, 0xc0, 0x85, 0x26, 0xe5, 0x1a, + 0x06, 0x05, 0x80, 0xe5, 0x3e, 0xe0, 0x02, 0xe5, + 0x17, 0x00, 0x46, 0x67, 0x26, 0x47, 0x60, 0x27, + 0x06, 0xa7, 0x46, 0x60, 0x0f, 0x40, 0x36, 0xe9, + 0x02, 0xe5, 0x16, 0x20, 0x85, 0xe0, 0x03, 0xe5, + 0x24, 0x60, 0xe5, 0x12, 0xa0, 0xe9, 0x02, 0x0b, + 0x40, 0xef, 0x1a, 0xe5, 0x0f, 0x26, 0x27, 0x06, + 0x20, 0x36, 0xe5, 0x2d, 0x07, 0x06, 0x07, 0xc6, + 0x00, 0x06, 0x07, 0x06, 0x27, 0xe6, 0x00, 0xa7, + 0xe6, 0x02, 0x20, 0x06, 0xe9, 0x02, 0xa0, 0xe9, + 0x02, 0xa0, 0xd6, 0x04, 0xb6, 0x20, 0xe6, 0x06, + 0x08, 0xe6, 0x17, 0x20, 0xe6, 0x04, 0xe0, 0x0c, + 0x66, 0x07, 0xe5, 0x27, 0x06, 0x07, 0x86, 0x07, + 0x06, 0x87, 0x06, 0x27, 0xe5, 0x00, 0x00, 0x36, + 0xe9, 0x02, 0xd6, 0xef, 0x02, 0xe6, 0x01, 0xef, + 0x01, 0x56, 0x26, 0x07, 0xe5, 0x16, 0x07, 0x66, + 0x27, 0x26, 0x07, 0x46, 0x25, 0xe9, 0x02, 0xe5, + 0x24, 0x06, 0x07, 0x26, 0x47, 0x06, 0x07, 0x46, + 0x27, 0xe0, 0x00, 0x76, 0xe5, 0x1c, 0xe7, 0x00, + 0xe6, 0x00, 0x27, 0x26, 0x40, 0x96, 0xe9, 0x02, + 0x40, 0x45, 0xe9, 0x02, 0xe5, 0x16, 0xa4, 0x36, + 0xe2, 0x01, 0x3f, 0x80, 0xe1, 0x23, 0x20, 0x41, + 0xf6, 0x00, 0xe0, 0x00, 0x46, 0x16, 0xe6, 0x05, + 0x07, 0xc6, 0x65, 0x06, 0xa5, 0x06, 0x25, 0x07, + 0x26, 0x05, 0x80, 0xe2, 0x24, 0xe4, 0x37, 0xe2, + 0x05, 0x04, 0xe2, 0x1a, 0xe4, 0x1d, 0xe6, 0x38, + 0xff, 0x80, 0x0e, 0xe2, 0x00, 0xff, 0x5a, 0xe2, + 0x00, 0xe1, 0x00, 0xa2, 0x20, 0xa1, 0x20, 0xe2, + 0x00, 0xe1, 0x00, 0xe2, 0x00, 0xe1, 0x00, 0xa2, + 0x20, 0xa1, 0x20, 0xe2, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x3f, 0xc2, 0xe1, 0x00, + 0xe2, 0x06, 0x20, 0xe2, 0x00, 0xe3, 0x00, 0xe2, + 0x00, 0xe3, 0x00, 0xe2, 0x00, 0xe3, 0x00, 0x82, + 0x00, 0x22, 0x61, 0x03, 0x0e, 0x02, 0x4e, 0x42, + 0x00, 0x22, 0x61, 0x03, 0x4e, 0x62, 0x20, 0x22, + 0x61, 0x00, 0x4e, 0xe2, 0x00, 0x81, 0x4e, 0x20, + 0x42, 0x00, 0x22, 0x61, 0x03, 0x2e, 0x00, 0xf7, + 0x03, 0x9b, 0xb1, 0x36, 0x14, 0x15, 0x12, 0x34, + 0x15, 0x12, 0x14, 0xf6, 0x00, 0x18, 0x19, 0x9b, + 0x17, 0xf6, 0x01, 0x14, 0x15, 0x76, 0x30, 0x56, + 0x0c, 0x12, 0x13, 0xf6, 0x03, 0x0c, 0x16, 0x10, + 0xf6, 0x02, 0x17, 0x9b, 0x00, 0xfb, 0x02, 0x0b, + 0x04, 0x20, 0xab, 0x4c, 0x12, 0x13, 0x04, 0xeb, + 0x02, 0x4c, 0x12, 0x13, 0x00, 0xe4, 0x05, 0x40, + 0xed, 0x1a, 0xe0, 0x06, 0xe6, 0x05, 0x68, 0x06, + 0x48, 0xe6, 0x04, 0xe0, 0x07, 0x2f, 0x01, 0x6f, + 0x01, 0x2f, 0x02, 0x41, 0x22, 0x41, 0x02, 0x0f, + 0x01, 0x2f, 0x0c, 0x81, 0xaf, 0x01, 0x0f, 0x01, + 0x0f, 0x01, 0x0f, 0x61, 0x0f, 0x02, 0x61, 0x02, + 0x65, 0x02, 0x2f, 0x22, 0x21, 0x8c, 0x3f, 0x42, + 0x0f, 0x0c, 0x2f, 0x02, 0x0f, 0xeb, 0x08, 0xea, + 0x1b, 0x3f, 0x6a, 0x0b, 0x2f, 0x60, 0x8c, 0x8f, + 0x2c, 0x6f, 0x0c, 0x2f, 0x0c, 0x2f, 0x0c, 0xcf, + 0x0c, 0xef, 0x17, 0x2c, 0x2f, 0x0c, 0x0f, 0x0c, + 0xef, 0x17, 0xec, 0x80, 0x84, 0xef, 0x00, 0x12, + 0x13, 0x12, 0x13, 0xef, 0x0c, 0x2c, 0xcf, 0x12, + 0x13, 0xef, 0x49, 0x0c, 0xef, 0x16, 0xec, 0x11, + 0xef, 0x20, 0xac, 0xef, 0x40, 0xe0, 0x0e, 0xef, + 0x03, 0xe0, 0x0d, 0xeb, 0x34, 0xef, 0x46, 0xeb, + 0x0e, 0xef, 0x80, 0x2f, 0x0c, 0xef, 0x01, 0x0c, + 0xef, 0x2e, 0xec, 0x00, 0xef, 0x67, 0x0c, 0xef, + 0x80, 0x70, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, + 0xeb, 0x16, 0xef, 0x24, 0x8c, 0x12, 0x13, 0xec, + 0x17, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, + 0x13, 0x12, 0x13, 0xec, 0x08, 0xef, 0x80, 0x78, + 0xec, 0x7b, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, + 0xec, 0x37, 0x12, 0x13, 0x12, 0x13, 0xec, 0x18, + 0x12, 0x13, 0xec, 0x80, 0x7a, 0xef, 0x28, 0xec, + 0x0d, 0x2f, 0xac, 0xef, 0x1f, 0x20, 0xef, 0x80, + 0x02, 0xe1, 0x28, 0xe2, 0x28, 0x5f, 0x21, 0x22, + 0xdf, 0x41, 0x02, 0x3f, 0x02, 0x3f, 0x82, 0x24, + 0x41, 0x02, 0xff, 0x5a, 0x02, 0xaf, 0x7f, 0x46, + 0x3f, 0x80, 0x76, 0x0b, 0x36, 0xe2, 0x1e, 0x00, + 0x02, 0x80, 0x02, 0x20, 0xe5, 0x30, 0xc0, 0x04, + 0x16, 0xe0, 0x06, 0x06, 0xe5, 0x0f, 0xe0, 0x01, + 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, + 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, + 0xe6, 0x18, 0x36, 0x14, 0x15, 0x14, 0x15, 0x56, + 0x14, 0x15, 0x16, 0x14, 0x15, 0xf6, 0x01, 0x11, + 0x36, 0x11, 0x16, 0x14, 0x15, 0x36, 0x14, 0x15, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, + 0x96, 0x04, 0xf6, 0x02, 0x31, 0x76, 0x11, 0x16, + 0x12, 0xf6, 0x05, 0x2f, 0x56, 0x12, 0x13, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0x11, 0xe0, 0x1a, + 0xef, 0x12, 0x00, 0xef, 0x51, 0xe0, 0x04, 0xef, + 0x80, 0x4e, 0xe0, 0x12, 0xef, 0x08, 0x17, 0x56, + 0x0f, 0x04, 0x05, 0x0a, 0x12, 0x13, 0x12, 0x13, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x2f, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x11, + 0x12, 0x33, 0x0f, 0xea, 0x01, 0x66, 0x27, 0x11, + 0x84, 0x2f, 0x4a, 0x04, 0x05, 0x16, 0x2f, 0x00, + 0xe5, 0x4e, 0x20, 0x26, 0x2e, 0x24, 0x05, 0x11, + 0xe5, 0x52, 0x16, 0x44, 0x05, 0x80, 0xe5, 0x23, + 0x00, 0xe5, 0x56, 0x00, 0x2f, 0x6b, 0xef, 0x02, + 0xe5, 0x18, 0xef, 0x1e, 0xe0, 0x01, 0x0f, 0xe5, + 0x08, 0xef, 0x17, 0x00, 0xeb, 0x02, 0xef, 0x16, + 0xeb, 0x00, 0x0f, 0xeb, 0x07, 0xef, 0x18, 0xeb, + 0x02, 0xef, 0x1f, 0xeb, 0x07, 0xef, 0x80, 0xb8, + 0xe5, 0x99, 0x38, 0xef, 0x38, 0xe5, 0xc0, 0x11, + 0x8d, 0x04, 0xe5, 0x83, 0xef, 0x40, 0xef, 0x2f, + 0xe0, 0x01, 0xe5, 0x20, 0xa4, 0x36, 0xe5, 0x80, + 0x84, 0x04, 0x56, 0xe5, 0x08, 0xe9, 0x02, 0x25, + 0xe0, 0x0c, 0xff, 0x26, 0x05, 0x06, 0x48, 0x16, + 0xe6, 0x02, 0x16, 0x04, 0xff, 0x14, 0x24, 0x26, + 0xe5, 0x3e, 0xea, 0x02, 0x26, 0xb6, 0xe0, 0x00, + 0xee, 0x0f, 0xe4, 0x01, 0x2e, 0xff, 0x06, 0x22, + 0xff, 0x36, 0x04, 0xe2, 0x00, 0x9f, 0xff, 0x02, + 0x04, 0x2e, 0x7f, 0x05, 0x7f, 0x22, 0xff, 0x0d, + 0x61, 0x02, 0x81, 0x02, 0xff, 0x07, 0x41, 0x02, + 0x5f, 0xff, 0x09, 0xe0, 0x0c, 0x64, 0x3f, 0x05, + 0x24, 0x02, 0xc5, 0x06, 0x45, 0x06, 0x65, 0x06, + 0xe5, 0x0f, 0x27, 0x26, 0x07, 0x6f, 0x06, 0x40, + 0xab, 0x2f, 0x0d, 0x0f, 0xa0, 0xe5, 0x2c, 0x76, + 0xe0, 0x00, 0x27, 0xe5, 0x2a, 0xe7, 0x08, 0x26, + 0xe0, 0x00, 0x36, 0xe9, 0x02, 0xa0, 0xe6, 0x0a, + 0xa5, 0x56, 0x05, 0x16, 0x25, 0x06, 0xe9, 0x02, + 0xe5, 0x14, 0xe6, 0x00, 0x36, 0xe5, 0x0f, 0xe6, + 0x03, 0x27, 0xe0, 0x03, 0x16, 0xe5, 0x15, 0x40, + 0x46, 0x07, 0xe5, 0x27, 0x06, 0x27, 0x66, 0x27, + 0x26, 0x47, 0xf6, 0x05, 0x00, 0x04, 0xe9, 0x02, + 0x60, 0x36, 0x85, 0x06, 0x04, 0xe5, 0x01, 0xe9, + 0x02, 0x85, 0x00, 0xe5, 0x21, 0xa6, 0x27, 0x26, + 0x27, 0x26, 0xe0, 0x01, 0x45, 0x06, 0xe5, 0x00, + 0x06, 0x07, 0x20, 0xe9, 0x02, 0x20, 0x76, 0xe5, + 0x08, 0x04, 0xa5, 0x4f, 0x05, 0x07, 0x06, 0x07, + 0xe5, 0x2a, 0x06, 0x05, 0x46, 0x25, 0x26, 0x85, + 0x26, 0x05, 0x06, 0x05, 0xe0, 0x10, 0x25, 0x04, + 0x36, 0xe5, 0x03, 0x07, 0x26, 0x27, 0x36, 0x05, + 0x24, 0x07, 0x06, 0xe0, 0x02, 0xa5, 0x20, 0xa5, + 0x20, 0xa5, 0xe0, 0x01, 0xc5, 0x00, 0xc5, 0x00, + 0xe2, 0x23, 0x0e, 0x64, 0xe2, 0x01, 0x04, 0x2e, + 0x60, 0xe2, 0x48, 0xe5, 0x1b, 0x27, 0x06, 0x27, + 0x06, 0x27, 0x16, 0x07, 0x06, 0x20, 0xe9, 0x02, + 0xa0, 0xe5, 0xab, 0x1c, 0xe0, 0x04, 0xe5, 0x0f, + 0x60, 0xe5, 0x29, 0x60, 0xfc, 0x87, 0x78, 0xfd, + 0x98, 0x78, 0xe5, 0x80, 0xe6, 0x20, 0xe5, 0x62, + 0xe0, 0x1e, 0xc2, 0xe0, 0x04, 0x82, 0x80, 0x05, + 0x06, 0xe5, 0x02, 0x0c, 0xe5, 0x05, 0x00, 0x85, + 0x00, 0x05, 0x00, 0x25, 0x00, 0x25, 0x00, 0xe5, + 0x64, 0xee, 0x09, 0xef, 0x08, 0xe5, 0x80, 0xe3, + 0x13, 0x12, 0xef, 0x08, 0xe5, 0x38, 0x2f, 0xe5, + 0x2e, 0xef, 0x00, 0xe0, 0x18, 0xe5, 0x04, 0x0d, + 0x4f, 0xe6, 0x08, 0xd6, 0x12, 0x13, 0x16, 0xa0, + 0xe6, 0x08, 0x16, 0x31, 0x30, 0x12, 0x13, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0x36, 0x12, 0x13, + 0x76, 0x50, 0x56, 0x00, 0x76, 0x11, 0x12, 0x13, + 0x12, 0x13, 0x12, 0x13, 0x56, 0x0c, 0x11, 0x4c, + 0x00, 0x16, 0x0d, 0x36, 0x60, 0x85, 0x00, 0xe5, + 0x7f, 0x20, 0x1b, 0x00, 0x56, 0x0d, 0x56, 0x12, + 0x13, 0x16, 0x0c, 0x16, 0x11, 0x36, 0xe9, 0x02, + 0x36, 0x4c, 0x36, 0xe1, 0x12, 0x12, 0x16, 0x13, + 0x0e, 0x10, 0x0e, 0xe2, 0x12, 0x12, 0x0c, 0x13, + 0x0c, 0x12, 0x13, 0x16, 0x12, 0x13, 0x36, 0xe5, + 0x02, 0x04, 0xe5, 0x25, 0x24, 0xe5, 0x17, 0x40, + 0xa5, 0x20, 0xa5, 0x20, 0xa5, 0x20, 0x45, 0x40, + 0x2d, 0x0c, 0x0e, 0x0f, 0x2d, 0x00, 0x0f, 0x6c, + 0x2f, 0xe0, 0x02, 0x5b, 0x2f, 0x20, 0xe5, 0x04, + 0x00, 0xe5, 0x12, 0x00, 0xe5, 0x0b, 0x00, 0x25, + 0x00, 0xe5, 0x07, 0x20, 0xe5, 0x06, 0xe0, 0x1a, + 0xe5, 0x73, 0x80, 0x56, 0x60, 0xeb, 0x25, 0x40, + 0xef, 0x01, 0xea, 0x2d, 0x6b, 0xef, 0x09, 0x2b, + 0x4f, 0x00, 0xef, 0x05, 0x40, 0x0f, 0xe0, 0x27, + 0xef, 0x25, 0x06, 0xe0, 0x7a, 0xe5, 0x15, 0x40, + 0xe5, 0x29, 0xe0, 0x07, 0x06, 0xeb, 0x13, 0x60, + 0xe5, 0x18, 0x6b, 0xe0, 0x01, 0xe5, 0x0c, 0x0a, + 0xe5, 0x00, 0x0a, 0x80, 0xe5, 0x1e, 0x86, 0x80, + 0xe5, 0x16, 0x00, 0x16, 0xe5, 0x1c, 0x60, 0xe5, + 0x00, 0x16, 0x8a, 0xe0, 0x22, 0xe1, 0x20, 0xe2, + 0x20, 0xe5, 0x46, 0x20, 0xe9, 0x02, 0xa0, 0xe1, + 0x1c, 0x60, 0xe2, 0x1c, 0x60, 0xe5, 0x20, 0xe0, + 0x00, 0xe5, 0x2c, 0xe0, 0x03, 0x16, 0xe1, 0x03, + 0x00, 0xe1, 0x07, 0x00, 0xc1, 0x00, 0x21, 0x00, + 0xe2, 0x03, 0x00, 0xe2, 0x07, 0x00, 0xc2, 0x00, + 0x22, 0x40, 0xe5, 0x2c, 0xe0, 0x04, 0xe5, 0x80, + 0xaf, 0xe0, 0x01, 0xe5, 0x0e, 0xe0, 0x02, 0xe5, + 0x00, 0xe0, 0x10, 0xa4, 0x00, 0xe4, 0x22, 0x00, + 0xe4, 0x01, 0xe0, 0x3d, 0xa5, 0x20, 0x05, 0x00, + 0xe5, 0x24, 0x00, 0x25, 0x40, 0x05, 0x20, 0xe5, + 0x0f, 0x00, 0x16, 0xeb, 0x00, 0xe5, 0x0f, 0x2f, + 0xcb, 0xe5, 0x17, 0xe0, 0x00, 0xeb, 0x01, 0xe0, + 0x28, 0xe5, 0x0b, 0x00, 0x25, 0x80, 0x8b, 0xe5, + 0x0e, 0xab, 0x40, 0x16, 0xe5, 0x12, 0x80, 0x16, + 0xe5, 0x12, 0xe0, 0x1e, 0xe5, 0x30, 0x60, 0x2b, + 0x25, 0xeb, 0x08, 0x20, 0xeb, 0x26, 0x05, 0x46, + 0x00, 0x26, 0x80, 0x66, 0x65, 0x00, 0x45, 0x00, + 0xe5, 0x15, 0x20, 0x46, 0x60, 0x06, 0xeb, 0x01, + 0xc0, 0xf6, 0x01, 0xc0, 0xe5, 0x15, 0x2b, 0x16, + 0xe5, 0x15, 0x4b, 0xe0, 0x18, 0xe5, 0x00, 0x0f, + 0xe5, 0x14, 0x26, 0x60, 0x8b, 0xd6, 0xe0, 0x01, + 0xe5, 0x2e, 0x40, 0xd6, 0xe5, 0x0e, 0x20, 0xeb, + 0x00, 0xe5, 0x0b, 0x80, 0xeb, 0x00, 0xe5, 0x0a, + 0xc0, 0x76, 0xe0, 0x04, 0xcb, 0xe0, 0x48, 0xe5, + 0x41, 0xe0, 0x2f, 0xe1, 0x2b, 0xe0, 0x05, 0xe2, + 0x2b, 0xc0, 0xab, 0xe5, 0x1c, 0x66, 0xe0, 0x00, + 0xe9, 0x02, 0xa0, 0xe9, 0x02, 0x65, 0x04, 0x05, + 0xe1, 0x0e, 0x40, 0x86, 0x11, 0x04, 0xe2, 0x0e, + 0xe0, 0x00, 0x2c, 0xe0, 0x80, 0x48, 0xeb, 0x17, + 0x00, 0xe5, 0x22, 0x00, 0x26, 0x11, 0x20, 0x25, + 0xe0, 0x08, 0x45, 0x04, 0x25, 0xe0, 0x00, 0x16, + 0xef, 0x00, 0xe0, 0x19, 0xa6, 0xe5, 0x15, 0xeb, + 0x02, 0x05, 0xe0, 0x00, 0xe5, 0x0e, 0xe6, 0x03, + 0x6b, 0x96, 0xe0, 0x0e, 0xe5, 0x0a, 0x66, 0x76, + 0xe0, 0x1e, 0xe5, 0x0d, 0xcb, 0xe0, 0x0c, 0xe5, + 0x0f, 0xe0, 0x01, 0x07, 0x06, 0x07, 0xe5, 0x2d, + 0xe6, 0x07, 0xd6, 0x60, 0xeb, 0x0c, 0xe9, 0x02, + 0x06, 0x25, 0x26, 0x05, 0xe0, 0x01, 0x46, 0x07, + 0xe5, 0x25, 0x47, 0x66, 0x27, 0x26, 0x36, 0x1b, + 0x76, 0x06, 0xe0, 0x02, 0x1b, 0x20, 0xe5, 0x11, + 0xc0, 0xe9, 0x02, 0xa0, 0x46, 0xe5, 0x1c, 0x86, + 0x07, 0xe6, 0x00, 0x00, 0xe9, 0x02, 0x76, 0x05, + 0x27, 0x05, 0xe0, 0x00, 0xe5, 0x1b, 0x06, 0x36, + 0x05, 0xe0, 0x01, 0x26, 0x07, 0xe5, 0x28, 0x47, + 0xe6, 0x01, 0x27, 0x65, 0x76, 0x66, 0x16, 0x07, + 0x06, 0xe9, 0x02, 0x05, 0x16, 0x05, 0x56, 0x00, + 0xeb, 0x0c, 0xe0, 0x03, 0xe5, 0x0a, 0x00, 0xe5, + 0x11, 0x47, 0x46, 0x27, 0x06, 0x07, 0x26, 0xb6, + 0x06, 0x25, 0x06, 0xe0, 0x36, 0xc5, 0x00, 0x05, + 0x00, 0x65, 0x00, 0xe5, 0x07, 0x00, 0xe5, 0x02, + 0x16, 0xa0, 0xe5, 0x27, 0x06, 0x47, 0xe6, 0x00, + 0x80, 0xe9, 0x02, 0xa0, 0x26, 0x27, 0x00, 0xe5, + 0x00, 0x20, 0x25, 0x20, 0xe5, 0x0e, 0x00, 0xc5, + 0x00, 0x25, 0x00, 0x85, 0x00, 0x26, 0x05, 0x27, + 0x06, 0x67, 0x20, 0x27, 0x20, 0x47, 0x20, 0x05, + 0xa0, 0x07, 0x80, 0x85, 0x27, 0x20, 0xc6, 0x40, + 0x86, 0xe0, 0x03, 0xe5, 0x02, 0x00, 0x05, 0x20, + 0x05, 0x00, 0xe5, 0x1e, 0x00, 0x05, 0x47, 0xa6, + 0x00, 0x07, 0x20, 0x07, 0x00, 0x67, 0x00, 0x27, + 0x06, 0x07, 0x06, 0x05, 0x06, 0x05, 0x36, 0x00, + 0x36, 0xe0, 0x00, 0x26, 0xe0, 0x15, 0xe5, 0x2d, + 0x47, 0xe6, 0x00, 0x27, 0x46, 0x07, 0x06, 0x65, + 0x96, 0xe9, 0x02, 0x36, 0x00, 0x16, 0x06, 0x45, + 0xe0, 0x16, 0xe5, 0x28, 0x47, 0xa6, 0x07, 0x06, + 0x67, 0x26, 0x07, 0x26, 0x25, 0x16, 0x05, 0xe0, + 0x00, 0xe9, 0x02, 0xe0, 0x80, 0x1e, 0xe5, 0x27, + 0x47, 0x66, 0x20, 0x67, 0x26, 0x07, 0x26, 0xf6, + 0x0f, 0x65, 0x26, 0xe0, 0x1a, 0xe5, 0x28, 0x47, + 0xe6, 0x00, 0x27, 0x06, 0x07, 0x26, 0x56, 0x05, + 0xe0, 0x03, 0xe9, 0x02, 0xa0, 0xf6, 0x05, 0xe0, + 0x0b, 0xe5, 0x23, 0x06, 0x07, 0x06, 0x27, 0xa6, + 0x07, 0x06, 0x05, 0x16, 0xa0, 0xe9, 0x02, 0xa0, + 0xe9, 0x0c, 0xe0, 0x14, 0xe5, 0x13, 0x20, 0x06, + 0x07, 0x06, 0x27, 0x66, 0x07, 0x86, 0x60, 0xe9, + 0x02, 0x2b, 0x56, 0x0f, 0xc5, 0xe0, 0x80, 0x31, + 0xe5, 0x24, 0x47, 0xe6, 0x01, 0x07, 0x26, 0x16, + 0xe0, 0x5c, 0xe1, 0x18, 0xe2, 0x18, 0xe9, 0x02, + 0xeb, 0x01, 0xe0, 0x04, 0xe5, 0x00, 0x20, 0x05, + 0x20, 0xe5, 0x00, 0x00, 0x25, 0x00, 0xe5, 0x10, + 0xa7, 0x00, 0x27, 0x20, 0x26, 0x07, 0x06, 0x05, + 0x07, 0x05, 0x07, 0x06, 0x56, 0xe0, 0x01, 0xe9, + 0x02, 0xe0, 0x3e, 0xe5, 0x00, 0x20, 0xe5, 0x1f, + 0x47, 0x66, 0x20, 0x26, 0x67, 0x06, 0x05, 0x16, + 0x05, 0x07, 0xe0, 0x13, 0x05, 0xe6, 0x02, 0xe5, + 0x20, 0xa6, 0x07, 0x05, 0x66, 0xf6, 0x00, 0x06, + 0xe0, 0x00, 0x05, 0xa6, 0x27, 0x46, 0xe5, 0x26, + 0xe6, 0x05, 0x07, 0x26, 0x56, 0x05, 0x96, 0xe0, + 0x05, 0xe5, 0x41, 0xc0, 0xf6, 0x02, 0xe0, 0x4e, + 0x06, 0x07, 0x46, 0x07, 0x06, 0x07, 0xe0, 0x50, + 0xe5, 0x19, 0x16, 0xe0, 0x06, 0xe9, 0x02, 0xa0, + 0xe5, 0x01, 0x00, 0xe5, 0x1d, 0x07, 0xc6, 0x00, + 0xa6, 0x07, 0x06, 0x05, 0x96, 0xe0, 0x02, 0xe9, + 0x02, 0xeb, 0x0b, 0x40, 0x36, 0xe5, 0x16, 0x20, + 0xe6, 0x0e, 0x00, 0x07, 0xc6, 0x07, 0x26, 0x07, + 0x26, 0xe0, 0x41, 0xc5, 0x00, 0x25, 0x00, 0xe5, + 0x1e, 0xa6, 0x40, 0x06, 0x00, 0x26, 0x00, 0xc6, + 0x05, 0x06, 0xe0, 0x00, 0xe9, 0x02, 0xa0, 0xa5, + 0x00, 0x25, 0x00, 0xe5, 0x18, 0x87, 0x00, 0x26, + 0x00, 0x27, 0x06, 0x07, 0x06, 0x05, 0xc0, 0xe9, + 0x02, 0xa0, 0xe5, 0x21, 0x04, 0x25, 0x60, 0xe9, + 0x02, 0xe0, 0x80, 0x6e, 0xe5, 0x0b, 0x26, 0x27, + 0x36, 0xc0, 0x26, 0x05, 0x07, 0xe5, 0x05, 0x00, + 0xe5, 0x1a, 0x27, 0x86, 0x40, 0x27, 0x06, 0x07, + 0x06, 0xf6, 0x05, 0xe9, 0x02, 0x06, 0xe0, 0x4d, + 0x05, 0xe0, 0x07, 0xeb, 0x0d, 0xef, 0x00, 0x6d, + 0xef, 0x09, 0xe0, 0x05, 0x16, 0xe5, 0x83, 0x12, + 0xe0, 0x5e, 0xea, 0x67, 0x00, 0x96, 0xe0, 0x03, + 0xe5, 0x80, 0x3c, 0xe0, 0x89, 0xc4, 0xe5, 0x59, + 0x36, 0xe0, 0x05, 0xe5, 0x83, 0xa8, 0xfb, 0x08, + 0x06, 0xa5, 0xe6, 0x07, 0xe0, 0x02, 0xe5, 0x8f, + 0x13, 0x80, 0xe5, 0x81, 0xbf, 0xe0, 0x9a, 0x31, + 0xe5, 0x16, 0xe6, 0x04, 0x47, 0x46, 0xe9, 0x02, + 0xe0, 0x86, 0x3e, 0xe5, 0x81, 0xb1, 0xc0, 0xe5, + 0x17, 0x00, 0xe9, 0x02, 0x60, 0x36, 0xe5, 0x47, + 0x00, 0xe9, 0x02, 0xa0, 0xe5, 0x16, 0x20, 0x86, + 0x16, 0xe0, 0x02, 0xe5, 0x28, 0xc6, 0x96, 0x6f, + 0x64, 0x16, 0x0f, 0xe0, 0x02, 0xe9, 0x02, 0x00, + 0xcb, 0x00, 0xe5, 0x0d, 0x80, 0xe5, 0x0b, 0xe0, + 0x81, 0x28, 0x44, 0xe5, 0x20, 0x24, 0x56, 0xe9, + 0x02, 0xe0, 0x80, 0x3e, 0xe1, 0x18, 0xe2, 0x18, + 0xeb, 0x0f, 0x76, 0x80, 0xe1, 0x11, 0x20, 0xe2, + 0x11, 0xe0, 0x24, 0xe5, 0x43, 0x60, 0x06, 0x05, + 0xe7, 0x2f, 0xc0, 0x66, 0xe4, 0x05, 0xe0, 0x38, + 0x24, 0x16, 0x04, 0x06, 0xe0, 0x03, 0x27, 0x24, + 0x4a, 0xe0, 0x01, 0xe5, 0x9c, 0x4e, 0xe0, 0x21, + 0xe5, 0x18, 0xe0, 0x59, 0xe5, 0x6b, 0xe0, 0xa1, + 0x75, 0x64, 0x00, 0xc4, 0x00, 0x24, 0x00, 0xe5, + 0x80, 0x9b, 0xe0, 0x07, 0x05, 0xe0, 0x15, 0x45, + 0x20, 0x05, 0xe0, 0x06, 0x65, 0xe0, 0x00, 0xe5, + 0x81, 0x04, 0xe0, 0x88, 0x7c, 0xe5, 0x63, 0x80, + 0xe5, 0x05, 0x40, 0xe5, 0x01, 0xc0, 0xe5, 0x02, + 0x20, 0x0f, 0x26, 0x16, 0x7b, 0xe0, 0x8e, 0xd4, + 0xef, 0x80, 0x68, 0xe9, 0x02, 0x4f, 0x40, 0xef, + 0x81, 0x2c, 0xa0, 0xef, 0x0f, 0xe0, 0x07, 0xef, + 0x08, 0x0c, 0xe0, 0x07, 0xe6, 0x26, 0x20, 0xe6, + 0x0f, 0xe0, 0x01, 0xef, 0x6c, 0xe0, 0x34, 0xef, + 0x80, 0x6e, 0xe0, 0x02, 0xef, 0x1f, 0x20, 0xef, + 0x34, 0x27, 0x46, 0x4f, 0xa7, 0xfb, 0x00, 0xe6, + 0x00, 0x2f, 0xc6, 0xef, 0x16, 0x66, 0xef, 0x35, + 0xe0, 0x0d, 0xef, 0x3a, 0x46, 0x0f, 0xe0, 0x72, + 0xeb, 0x0c, 0xe0, 0x04, 0xeb, 0x0c, 0xe0, 0x04, + 0xef, 0x4f, 0xe0, 0x01, 0xeb, 0x11, 0xe0, 0x7f, + 0xe1, 0x12, 0xe2, 0x12, 0xe1, 0x12, 0xc2, 0x00, + 0xe2, 0x0a, 0xe1, 0x12, 0xe2, 0x12, 0x01, 0x00, + 0x21, 0x20, 0x01, 0x20, 0x21, 0x20, 0x61, 0x00, + 0xe1, 0x00, 0x62, 0x00, 0x02, 0x00, 0xc2, 0x00, + 0xe2, 0x03, 0xe1, 0x12, 0xe2, 0x12, 0x21, 0x00, + 0x61, 0x20, 0xe1, 0x00, 0x00, 0xc1, 0x00, 0xe2, + 0x12, 0x21, 0x00, 0x61, 0x00, 0x81, 0x00, 0x01, + 0x40, 0xc1, 0x00, 0xe2, 0x12, 0xe1, 0x12, 0xe2, + 0x12, 0xe1, 0x12, 0xe2, 0x12, 0xe1, 0x12, 0xe2, + 0x12, 0xe1, 0x12, 0xe2, 0x12, 0xe1, 0x12, 0xe2, + 0x12, 0xe1, 0x12, 0xe2, 0x14, 0x20, 0xe1, 0x11, + 0x0c, 0xe2, 0x11, 0x0c, 0xa2, 0xe1, 0x11, 0x0c, + 0xe2, 0x11, 0x0c, 0xa2, 0xe1, 0x11, 0x0c, 0xe2, + 0x11, 0x0c, 0xa2, 0xe1, 0x11, 0x0c, 0xe2, 0x11, + 0x0c, 0xa2, 0xe1, 0x11, 0x0c, 0xe2, 0x11, 0x0c, + 0xa2, 0x3f, 0x20, 0xe9, 0x2a, 0xef, 0x81, 0x78, + 0xe6, 0x2f, 0x6f, 0xe6, 0x2a, 0xef, 0x00, 0x06, + 0xef, 0x06, 0x06, 0x2f, 0x96, 0xe0, 0x07, 0x86, + 0x00, 0xe6, 0x07, 0xe0, 0x83, 0xc8, 0xe2, 0x02, + 0x05, 0xe2, 0x0c, 0xa0, 0xa2, 0xe0, 0x80, 0x4d, + 0xc6, 0x00, 0xe6, 0x09, 0x20, 0xc6, 0x00, 0x26, + 0x00, 0x86, 0x80, 0xe4, 0x36, 0xe0, 0x19, 0x06, + 0xe0, 0x68, 0xe5, 0x25, 0x40, 0xc6, 0xc4, 0x20, + 0xe9, 0x02, 0x60, 0x05, 0x0f, 0xe0, 0x80, 0xb8, + 0xe5, 0x16, 0x06, 0xe0, 0x09, 0xe5, 0x24, 0x66, + 0xe9, 0x02, 0x80, 0x0d, 0xe0, 0x81, 0x48, 0xe5, + 0x13, 0x04, 0x66, 0xe9, 0x02, 0xe0, 0x80, 0x4e, + 0xe5, 0x16, 0x26, 0x05, 0xe9, 0x02, 0x60, 0x16, + 0xe0, 0x80, 0x38, 0xe5, 0x17, 0x00, 0x45, 0x06, + 0x25, 0x06, 0xc5, 0x26, 0x85, 0x06, 0xe0, 0x00, + 0x05, 0x04, 0xe0, 0x80, 0x58, 0xc5, 0x00, 0x65, + 0x00, 0x25, 0x00, 0xe5, 0x07, 0x00, 0xe5, 0x80, + 0x3d, 0x20, 0xeb, 0x01, 0xc6, 0xe0, 0x21, 0xe1, + 0x1a, 0xe2, 0x1a, 0xc6, 0x04, 0x60, 0xe9, 0x02, + 0x60, 0x36, 0xe0, 0x82, 0x89, 0xeb, 0x33, 0x0f, + 0x4b, 0x0d, 0x6b, 0xe0, 0x44, 0xeb, 0x25, 0x0f, + 0xeb, 0x07, 0xe0, 0x80, 0x3a, 0x65, 0x00, 0xe5, + 0x13, 0x00, 0x25, 0x00, 0x05, 0x20, 0x05, 0x00, + 0xe5, 0x02, 0x00, 0x65, 0x00, 0x05, 0x00, 0x05, + 0xa0, 0x05, 0x60, 0x05, 0x00, 0x05, 0x00, 0x05, + 0x00, 0x45, 0x00, 0x25, 0x00, 0x05, 0x20, 0x05, + 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, + 0x00, 0x25, 0x00, 0x05, 0x20, 0x65, 0x00, 0xc5, + 0x00, 0x65, 0x00, 0x65, 0x00, 0x05, 0x00, 0xe5, + 0x02, 0x00, 0xe5, 0x09, 0x80, 0x45, 0x00, 0x85, + 0x00, 0xe5, 0x09, 0xe0, 0x2c, 0x2c, 0xe0, 0x80, + 0x86, 0xef, 0x24, 0x60, 0xef, 0x5c, 0xe0, 0x04, + 0xef, 0x07, 0x20, 0xef, 0x07, 0x00, 0xef, 0x07, + 0x00, 0xef, 0x1d, 0xe0, 0x02, 0xeb, 0x05, 0xef, + 0x80, 0x19, 0xe0, 0x30, 0xef, 0x15, 0xe0, 0x05, + 0xef, 0x24, 0x60, 0xef, 0x01, 0xc0, 0x2f, 0xe0, + 0x06, 0xaf, 0xe0, 0x80, 0x12, 0xef, 0x80, 0x73, + 0x8e, 0xef, 0x82, 0x51, 0x40, 0xef, 0x09, 0x40, + 0xef, 0x05, 0x40, 0xef, 0x80, 0x52, 0xa0, 0xef, + 0x04, 0x60, 0x0f, 0xe0, 0x07, 0xef, 0x04, 0x60, + 0xef, 0x30, 0xe0, 0x00, 0xef, 0x02, 0xa0, 0xef, + 0x20, 0xe0, 0x00, 0xef, 0x16, 0x20, 0xef, 0x04, + 0x60, 0x2f, 0xe0, 0x06, 0xec, 0x01, 0xe0, 0x1f, + 0xef, 0x80, 0xd0, 0xe0, 0x00, 0xef, 0x06, 0x20, + 0xef, 0x05, 0x40, 0xef, 0x03, 0x40, 0xef, 0x31, + 0x00, 0x0f, 0x60, 0xef, 0x08, 0x20, 0xef, 0x04, + 0x60, 0xef, 0x02, 0xc0, 0xef, 0x80, 0x0b, 0x00, + 0xef, 0x54, 0xe9, 0x02, 0x0f, 0xe0, 0x83, 0x7d, + 0xe5, 0xc0, 0x66, 0x58, 0xe0, 0x18, 0xe5, 0x90, + 0x96, 0x20, 0xe5, 0x96, 0x06, 0x20, 0xe5, 0x9c, + 0xa9, 0xe0, 0x07, 0xe5, 0x81, 0xe6, 0xe0, 0x89, + 0x1a, 0xe5, 0x81, 0x96, 0xe0, 0x85, 0x5a, 0xe5, + 0x92, 0xc3, 0x80, 0xe5, 0xa0, 0xa2, 0xe0, 0xca, + 0x8a, 0xff, 0x1b, 0xe0, 0x16, 0xfb, 0x58, 0xe0, + 0x78, 0xe6, 0x80, 0x68, 0xe0, 0xc0, 0xbd, 0x88, + 0xfd, 0xc0, 0xbf, 0x76, 0x20, 0xfd, 0xc0, 0xbf, + 0x76, 0x20, +}; + +typedef enum { + UNICODE_SCRIPT_Unknown, + UNICODE_SCRIPT_Adlam, + UNICODE_SCRIPT_Ahom, + UNICODE_SCRIPT_Anatolian_Hieroglyphs, + UNICODE_SCRIPT_Arabic, + UNICODE_SCRIPT_Armenian, + UNICODE_SCRIPT_Avestan, + UNICODE_SCRIPT_Balinese, + UNICODE_SCRIPT_Bamum, + UNICODE_SCRIPT_Bassa_Vah, + UNICODE_SCRIPT_Batak, + UNICODE_SCRIPT_Bengali, + UNICODE_SCRIPT_Beria_Erfe, + UNICODE_SCRIPT_Bhaiksuki, + UNICODE_SCRIPT_Bopomofo, + UNICODE_SCRIPT_Brahmi, + UNICODE_SCRIPT_Braille, + UNICODE_SCRIPT_Buginese, + UNICODE_SCRIPT_Buhid, + UNICODE_SCRIPT_Canadian_Aboriginal, + UNICODE_SCRIPT_Carian, + UNICODE_SCRIPT_Caucasian_Albanian, + UNICODE_SCRIPT_Chakma, + UNICODE_SCRIPT_Cham, + UNICODE_SCRIPT_Cherokee, + UNICODE_SCRIPT_Chorasmian, + UNICODE_SCRIPT_Common, + UNICODE_SCRIPT_Coptic, + UNICODE_SCRIPT_Cuneiform, + UNICODE_SCRIPT_Cypriot, + UNICODE_SCRIPT_Cyrillic, + UNICODE_SCRIPT_Cypro_Minoan, + UNICODE_SCRIPT_Deseret, + UNICODE_SCRIPT_Devanagari, + UNICODE_SCRIPT_Dives_Akuru, + UNICODE_SCRIPT_Dogra, + UNICODE_SCRIPT_Duployan, + UNICODE_SCRIPT_Egyptian_Hieroglyphs, + UNICODE_SCRIPT_Elbasan, + UNICODE_SCRIPT_Elymaic, + UNICODE_SCRIPT_Ethiopic, + UNICODE_SCRIPT_Georgian, + UNICODE_SCRIPT_Glagolitic, + UNICODE_SCRIPT_Gothic, + UNICODE_SCRIPT_Garay, + UNICODE_SCRIPT_Grantha, + UNICODE_SCRIPT_Greek, + UNICODE_SCRIPT_Gujarati, + UNICODE_SCRIPT_Gunjala_Gondi, + UNICODE_SCRIPT_Gurmukhi, + UNICODE_SCRIPT_Gurung_Khema, + UNICODE_SCRIPT_Han, + UNICODE_SCRIPT_Hangul, + UNICODE_SCRIPT_Hanifi_Rohingya, + UNICODE_SCRIPT_Hanunoo, + UNICODE_SCRIPT_Hatran, + UNICODE_SCRIPT_Hebrew, + UNICODE_SCRIPT_Hiragana, + UNICODE_SCRIPT_Imperial_Aramaic, + UNICODE_SCRIPT_Inherited, + UNICODE_SCRIPT_Inscriptional_Pahlavi, + UNICODE_SCRIPT_Inscriptional_Parthian, + UNICODE_SCRIPT_Javanese, + UNICODE_SCRIPT_Kaithi, + UNICODE_SCRIPT_Kannada, + UNICODE_SCRIPT_Katakana, + UNICODE_SCRIPT_Katakana_Or_Hiragana, + UNICODE_SCRIPT_Kawi, + UNICODE_SCRIPT_Kayah_Li, + UNICODE_SCRIPT_Kharoshthi, + UNICODE_SCRIPT_Khmer, + UNICODE_SCRIPT_Khojki, + UNICODE_SCRIPT_Khitan_Small_Script, + UNICODE_SCRIPT_Khudawadi, + UNICODE_SCRIPT_Kirat_Rai, + UNICODE_SCRIPT_Lao, + UNICODE_SCRIPT_Latin, + UNICODE_SCRIPT_Lepcha, + UNICODE_SCRIPT_Limbu, + UNICODE_SCRIPT_Linear_A, + UNICODE_SCRIPT_Linear_B, + UNICODE_SCRIPT_Lisu, + UNICODE_SCRIPT_Lycian, + UNICODE_SCRIPT_Lydian, + UNICODE_SCRIPT_Makasar, + UNICODE_SCRIPT_Mahajani, + UNICODE_SCRIPT_Malayalam, + UNICODE_SCRIPT_Mandaic, + UNICODE_SCRIPT_Manichaean, + UNICODE_SCRIPT_Marchen, + UNICODE_SCRIPT_Masaram_Gondi, + UNICODE_SCRIPT_Medefaidrin, + UNICODE_SCRIPT_Meetei_Mayek, + UNICODE_SCRIPT_Mende_Kikakui, + UNICODE_SCRIPT_Meroitic_Cursive, + UNICODE_SCRIPT_Meroitic_Hieroglyphs, + UNICODE_SCRIPT_Miao, + UNICODE_SCRIPT_Modi, + UNICODE_SCRIPT_Mongolian, + UNICODE_SCRIPT_Mro, + UNICODE_SCRIPT_Multani, + UNICODE_SCRIPT_Myanmar, + UNICODE_SCRIPT_Nabataean, + UNICODE_SCRIPT_Nag_Mundari, + UNICODE_SCRIPT_Nandinagari, + UNICODE_SCRIPT_New_Tai_Lue, + UNICODE_SCRIPT_Newa, + UNICODE_SCRIPT_Nko, + UNICODE_SCRIPT_Nushu, + UNICODE_SCRIPT_Nyiakeng_Puachue_Hmong, + UNICODE_SCRIPT_Ogham, + UNICODE_SCRIPT_Ol_Chiki, + UNICODE_SCRIPT_Ol_Onal, + UNICODE_SCRIPT_Old_Hungarian, + UNICODE_SCRIPT_Old_Italic, + UNICODE_SCRIPT_Old_North_Arabian, + UNICODE_SCRIPT_Old_Permic, + UNICODE_SCRIPT_Old_Persian, + UNICODE_SCRIPT_Old_Sogdian, + UNICODE_SCRIPT_Old_South_Arabian, + UNICODE_SCRIPT_Old_Turkic, + UNICODE_SCRIPT_Old_Uyghur, + UNICODE_SCRIPT_Oriya, + UNICODE_SCRIPT_Osage, + UNICODE_SCRIPT_Osmanya, + UNICODE_SCRIPT_Pahawh_Hmong, + UNICODE_SCRIPT_Palmyrene, + UNICODE_SCRIPT_Pau_Cin_Hau, + UNICODE_SCRIPT_Phags_Pa, + UNICODE_SCRIPT_Phoenician, + UNICODE_SCRIPT_Psalter_Pahlavi, + UNICODE_SCRIPT_Rejang, + UNICODE_SCRIPT_Runic, + UNICODE_SCRIPT_Samaritan, + UNICODE_SCRIPT_Saurashtra, + UNICODE_SCRIPT_Sharada, + UNICODE_SCRIPT_Shavian, + UNICODE_SCRIPT_Siddham, + UNICODE_SCRIPT_Sidetic, + UNICODE_SCRIPT_SignWriting, + UNICODE_SCRIPT_Sinhala, + UNICODE_SCRIPT_Sogdian, + UNICODE_SCRIPT_Sora_Sompeng, + UNICODE_SCRIPT_Soyombo, + UNICODE_SCRIPT_Sundanese, + UNICODE_SCRIPT_Sunuwar, + UNICODE_SCRIPT_Syloti_Nagri, + UNICODE_SCRIPT_Syriac, + UNICODE_SCRIPT_Tagalog, + UNICODE_SCRIPT_Tagbanwa, + UNICODE_SCRIPT_Tai_Le, + UNICODE_SCRIPT_Tai_Tham, + UNICODE_SCRIPT_Tai_Viet, + UNICODE_SCRIPT_Tai_Yo, + UNICODE_SCRIPT_Takri, + UNICODE_SCRIPT_Tamil, + UNICODE_SCRIPT_Tangut, + UNICODE_SCRIPT_Telugu, + UNICODE_SCRIPT_Thaana, + UNICODE_SCRIPT_Thai, + UNICODE_SCRIPT_Tibetan, + UNICODE_SCRIPT_Tifinagh, + UNICODE_SCRIPT_Tirhuta, + UNICODE_SCRIPT_Tangsa, + UNICODE_SCRIPT_Todhri, + UNICODE_SCRIPT_Tolong_Siki, + UNICODE_SCRIPT_Toto, + UNICODE_SCRIPT_Tulu_Tigalari, + UNICODE_SCRIPT_Ugaritic, + UNICODE_SCRIPT_Vai, + UNICODE_SCRIPT_Vithkuqi, + UNICODE_SCRIPT_Wancho, + UNICODE_SCRIPT_Warang_Citi, + UNICODE_SCRIPT_Yezidi, + UNICODE_SCRIPT_Yi, + UNICODE_SCRIPT_Zanabazar_Square, + UNICODE_SCRIPT_COUNT, +} UnicodeScriptEnum; + +static const char unicode_script_name_table[] = + "Adlam,Adlm" "\0" + "Ahom,Ahom" "\0" + "Anatolian_Hieroglyphs,Hluw" "\0" + "Arabic,Arab" "\0" + "Armenian,Armn" "\0" + "Avestan,Avst" "\0" + "Balinese,Bali" "\0" + "Bamum,Bamu" "\0" + "Bassa_Vah,Bass" "\0" + "Batak,Batk" "\0" + "Bengali,Beng" "\0" + "Beria_Erfe,Berf" "\0" + "Bhaiksuki,Bhks" "\0" + "Bopomofo,Bopo" "\0" + "Brahmi,Brah" "\0" + "Braille,Brai" "\0" + "Buginese,Bugi" "\0" + "Buhid,Buhd" "\0" + "Canadian_Aboriginal,Cans" "\0" + "Carian,Cari" "\0" + "Caucasian_Albanian,Aghb" "\0" + "Chakma,Cakm" "\0" + "Cham,Cham" "\0" + "Cherokee,Cher" "\0" + "Chorasmian,Chrs" "\0" + "Common,Zyyy" "\0" + "Coptic,Copt,Qaac" "\0" + "Cuneiform,Xsux" "\0" + "Cypriot,Cprt" "\0" + "Cyrillic,Cyrl" "\0" + "Cypro_Minoan,Cpmn" "\0" + "Deseret,Dsrt" "\0" + "Devanagari,Deva" "\0" + "Dives_Akuru,Diak" "\0" + "Dogra,Dogr" "\0" + "Duployan,Dupl" "\0" + "Egyptian_Hieroglyphs,Egyp" "\0" + "Elbasan,Elba" "\0" + "Elymaic,Elym" "\0" + "Ethiopic,Ethi" "\0" + "Georgian,Geor" "\0" + "Glagolitic,Glag" "\0" + "Gothic,Goth" "\0" + "Garay,Gara" "\0" + "Grantha,Gran" "\0" + "Greek,Grek" "\0" + "Gujarati,Gujr" "\0" + "Gunjala_Gondi,Gong" "\0" + "Gurmukhi,Guru" "\0" + "Gurung_Khema,Gukh" "\0" + "Han,Hani" "\0" + "Hangul,Hang" "\0" + "Hanifi_Rohingya,Rohg" "\0" + "Hanunoo,Hano" "\0" + "Hatran,Hatr" "\0" + "Hebrew,Hebr" "\0" + "Hiragana,Hira" "\0" + "Imperial_Aramaic,Armi" "\0" + "Inherited,Zinh,Qaai" "\0" + "Inscriptional_Pahlavi,Phli" "\0" + "Inscriptional_Parthian,Prti" "\0" + "Javanese,Java" "\0" + "Kaithi,Kthi" "\0" + "Kannada,Knda" "\0" + "Katakana,Kana" "\0" + "Katakana_Or_Hiragana,Hrkt" "\0" + "Kawi,Kawi" "\0" + "Kayah_Li,Kali" "\0" + "Kharoshthi,Khar" "\0" + "Khmer,Khmr" "\0" + "Khojki,Khoj" "\0" + "Khitan_Small_Script,Kits" "\0" + "Khudawadi,Sind" "\0" + "Kirat_Rai,Krai" "\0" + "Lao,Laoo" "\0" + "Latin,Latn" "\0" + "Lepcha,Lepc" "\0" + "Limbu,Limb" "\0" + "Linear_A,Lina" "\0" + "Linear_B,Linb" "\0" + "Lisu,Lisu" "\0" + "Lycian,Lyci" "\0" + "Lydian,Lydi" "\0" + "Makasar,Maka" "\0" + "Mahajani,Mahj" "\0" + "Malayalam,Mlym" "\0" + "Mandaic,Mand" "\0" + "Manichaean,Mani" "\0" + "Marchen,Marc" "\0" + "Masaram_Gondi,Gonm" "\0" + "Medefaidrin,Medf" "\0" + "Meetei_Mayek,Mtei" "\0" + "Mende_Kikakui,Mend" "\0" + "Meroitic_Cursive,Merc" "\0" + "Meroitic_Hieroglyphs,Mero" "\0" + "Miao,Plrd" "\0" + "Modi,Modi" "\0" + "Mongolian,Mong" "\0" + "Mro,Mroo" "\0" + "Multani,Mult" "\0" + "Myanmar,Mymr" "\0" + "Nabataean,Nbat" "\0" + "Nag_Mundari,Nagm" "\0" + "Nandinagari,Nand" "\0" + "New_Tai_Lue,Talu" "\0" + "Newa,Newa" "\0" + "Nko,Nkoo" "\0" + "Nushu,Nshu" "\0" + "Nyiakeng_Puachue_Hmong,Hmnp" "\0" + "Ogham,Ogam" "\0" + "Ol_Chiki,Olck" "\0" + "Ol_Onal,Onao" "\0" + "Old_Hungarian,Hung" "\0" + "Old_Italic,Ital" "\0" + "Old_North_Arabian,Narb" "\0" + "Old_Permic,Perm" "\0" + "Old_Persian,Xpeo" "\0" + "Old_Sogdian,Sogo" "\0" + "Old_South_Arabian,Sarb" "\0" + "Old_Turkic,Orkh" "\0" + "Old_Uyghur,Ougr" "\0" + "Oriya,Orya" "\0" + "Osage,Osge" "\0" + "Osmanya,Osma" "\0" + "Pahawh_Hmong,Hmng" "\0" + "Palmyrene,Palm" "\0" + "Pau_Cin_Hau,Pauc" "\0" + "Phags_Pa,Phag" "\0" + "Phoenician,Phnx" "\0" + "Psalter_Pahlavi,Phlp" "\0" + "Rejang,Rjng" "\0" + "Runic,Runr" "\0" + "Samaritan,Samr" "\0" + "Saurashtra,Saur" "\0" + "Sharada,Shrd" "\0" + "Shavian,Shaw" "\0" + "Siddham,Sidd" "\0" + "Sidetic,Sidt" "\0" + "SignWriting,Sgnw" "\0" + "Sinhala,Sinh" "\0" + "Sogdian,Sogd" "\0" + "Sora_Sompeng,Sora" "\0" + "Soyombo,Soyo" "\0" + "Sundanese,Sund" "\0" + "Sunuwar,Sunu" "\0" + "Syloti_Nagri,Sylo" "\0" + "Syriac,Syrc" "\0" + "Tagalog,Tglg" "\0" + "Tagbanwa,Tagb" "\0" + "Tai_Le,Tale" "\0" + "Tai_Tham,Lana" "\0" + "Tai_Viet,Tavt" "\0" + "Tai_Yo,Tayo" "\0" + "Takri,Takr" "\0" + "Tamil,Taml" "\0" + "Tangut,Tang" "\0" + "Telugu,Telu" "\0" + "Thaana,Thaa" "\0" + "Thai,Thai" "\0" + "Tibetan,Tibt" "\0" + "Tifinagh,Tfng" "\0" + "Tirhuta,Tirh" "\0" + "Tangsa,Tnsa" "\0" + "Todhri,Todr" "\0" + "Tolong_Siki,Tols" "\0" + "Toto,Toto" "\0" + "Tulu_Tigalari,Tutg" "\0" + "Ugaritic,Ugar" "\0" + "Vai,Vaii" "\0" + "Vithkuqi,Vith" "\0" + "Wancho,Wcho" "\0" + "Warang_Citi,Wara" "\0" + "Yezidi,Yezi" "\0" + "Yi,Yiii" "\0" + "Zanabazar_Square,Zanb" "\0" +; + +static const uint8_t unicode_script_table[2818] = { + 0xc0, 0x1a, 0x99, 0x4c, 0x85, 0x1a, 0x99, 0x4c, + 0xae, 0x1a, 0x80, 0x4c, 0x8e, 0x1a, 0x80, 0x4c, + 0x84, 0x1a, 0x96, 0x4c, 0x80, 0x1a, 0x9e, 0x4c, + 0x80, 0x1a, 0xe1, 0x60, 0x4c, 0xa6, 0x1a, 0x84, + 0x4c, 0x84, 0x1a, 0x81, 0x0e, 0x93, 0x1a, 0xe0, + 0x0f, 0x3b, 0x83, 0x2e, 0x80, 0x1a, 0x82, 0x2e, + 0x01, 0x83, 0x2e, 0x80, 0x1a, 0x80, 0x2e, 0x03, + 0x80, 0x2e, 0x80, 0x1a, 0x80, 0x2e, 0x80, 0x1a, + 0x82, 0x2e, 0x00, 0x80, 0x2e, 0x00, 0x93, 0x2e, + 0x00, 0xbe, 0x2e, 0x8d, 0x1b, 0x8f, 0x2e, 0xe0, + 0x24, 0x1e, 0x81, 0x3b, 0xe0, 0x48, 0x1e, 0x00, + 0xa5, 0x05, 0x01, 0xb1, 0x05, 0x01, 0x82, 0x05, + 0x00, 0xb6, 0x38, 0x07, 0x9a, 0x38, 0x03, 0x85, + 0x38, 0x0a, 0x84, 0x04, 0x80, 0x1a, 0x85, 0x04, + 0x80, 0x1a, 0x8d, 0x04, 0x80, 0x1a, 0x82, 0x04, + 0x80, 0x1a, 0x9f, 0x04, 0x80, 0x1a, 0x89, 0x04, + 0x8a, 0x3b, 0x99, 0x04, 0x80, 0x3b, 0xe0, 0x0b, + 0x04, 0x80, 0x1a, 0xa1, 0x04, 0x8d, 0x93, 0x00, + 0xbb, 0x93, 0x01, 0x82, 0x93, 0xaf, 0x04, 0xb1, + 0x9e, 0x0d, 0xba, 0x6b, 0x01, 0x82, 0x6b, 0xad, + 0x85, 0x01, 0x8e, 0x85, 0x00, 0x9b, 0x57, 0x01, + 0x80, 0x57, 0x00, 0x8a, 0x93, 0x04, 0xa1, 0x04, + 0x04, 0xca, 0x04, 0x80, 0x1a, 0x9c, 0x04, 0xd0, + 0x21, 0x83, 0x3b, 0x8e, 0x21, 0x81, 0x1a, 0x99, + 0x21, 0x83, 0x0b, 0x00, 0x87, 0x0b, 0x01, 0x81, + 0x0b, 0x01, 0x95, 0x0b, 0x00, 0x86, 0x0b, 0x00, + 0x80, 0x0b, 0x02, 0x83, 0x0b, 0x01, 0x88, 0x0b, + 0x01, 0x81, 0x0b, 0x01, 0x83, 0x0b, 0x07, 0x80, + 0x0b, 0x03, 0x81, 0x0b, 0x00, 0x84, 0x0b, 0x01, + 0x98, 0x0b, 0x01, 0x82, 0x31, 0x00, 0x85, 0x31, + 0x03, 0x81, 0x31, 0x01, 0x95, 0x31, 0x00, 0x86, + 0x31, 0x00, 0x81, 0x31, 0x00, 0x81, 0x31, 0x00, + 0x81, 0x31, 0x01, 0x80, 0x31, 0x00, 0x84, 0x31, + 0x03, 0x81, 0x31, 0x01, 0x82, 0x31, 0x02, 0x80, + 0x31, 0x06, 0x83, 0x31, 0x00, 0x80, 0x31, 0x06, + 0x90, 0x31, 0x09, 0x82, 0x2f, 0x00, 0x88, 0x2f, + 0x00, 0x82, 0x2f, 0x00, 0x95, 0x2f, 0x00, 0x86, + 0x2f, 0x00, 0x81, 0x2f, 0x00, 0x84, 0x2f, 0x01, + 0x89, 0x2f, 0x00, 0x82, 0x2f, 0x00, 0x82, 0x2f, + 0x01, 0x80, 0x2f, 0x0e, 0x83, 0x2f, 0x01, 0x8b, + 0x2f, 0x06, 0x86, 0x2f, 0x00, 0x82, 0x7a, 0x00, + 0x87, 0x7a, 0x01, 0x81, 0x7a, 0x01, 0x95, 0x7a, + 0x00, 0x86, 0x7a, 0x00, 0x81, 0x7a, 0x00, 0x84, + 0x7a, 0x01, 0x88, 0x7a, 0x01, 0x81, 0x7a, 0x01, + 0x82, 0x7a, 0x06, 0x82, 0x7a, 0x03, 0x81, 0x7a, + 0x00, 0x84, 0x7a, 0x01, 0x91, 0x7a, 0x09, 0x81, + 0x9b, 0x00, 0x85, 0x9b, 0x02, 0x82, 0x9b, 0x00, + 0x83, 0x9b, 0x02, 0x81, 0x9b, 0x00, 0x80, 0x9b, + 0x00, 0x81, 0x9b, 0x02, 0x81, 0x9b, 0x02, 0x82, + 0x9b, 0x02, 0x8b, 0x9b, 0x03, 0x84, 0x9b, 0x02, + 0x82, 0x9b, 0x00, 0x83, 0x9b, 0x01, 0x80, 0x9b, + 0x05, 0x80, 0x9b, 0x0d, 0x94, 0x9b, 0x04, 0x8c, + 0x9d, 0x00, 0x82, 0x9d, 0x00, 0x96, 0x9d, 0x00, + 0x8f, 0x9d, 0x01, 0x88, 0x9d, 0x00, 0x82, 0x9d, + 0x00, 0x83, 0x9d, 0x06, 0x81, 0x9d, 0x00, 0x82, + 0x9d, 0x00, 0x81, 0x9d, 0x01, 0x83, 0x9d, 0x01, + 0x89, 0x9d, 0x06, 0x88, 0x9d, 0x8c, 0x40, 0x00, + 0x82, 0x40, 0x00, 0x96, 0x40, 0x00, 0x89, 0x40, + 0x00, 0x84, 0x40, 0x01, 0x88, 0x40, 0x00, 0x82, + 0x40, 0x00, 0x83, 0x40, 0x06, 0x81, 0x40, 0x04, + 0x82, 0x40, 0x00, 0x83, 0x40, 0x01, 0x89, 0x40, + 0x00, 0x82, 0x40, 0x0b, 0x8c, 0x56, 0x00, 0x82, + 0x56, 0x00, 0xb2, 0x56, 0x00, 0x82, 0x56, 0x00, + 0x85, 0x56, 0x03, 0x8f, 0x56, 0x01, 0x99, 0x56, + 0x00, 0x82, 0x8c, 0x00, 0x91, 0x8c, 0x02, 0x97, + 0x8c, 0x00, 0x88, 0x8c, 0x00, 0x80, 0x8c, 0x01, + 0x86, 0x8c, 0x02, 0x80, 0x8c, 0x03, 0x85, 0x8c, + 0x00, 0x80, 0x8c, 0x00, 0x87, 0x8c, 0x05, 0x89, + 0x8c, 0x01, 0x82, 0x8c, 0x0b, 0xb9, 0x9f, 0x03, + 0x80, 0x1a, 0x9b, 0x9f, 0x24, 0x81, 0x4b, 0x00, + 0x80, 0x4b, 0x00, 0x84, 0x4b, 0x00, 0x97, 0x4b, + 0x00, 0x80, 0x4b, 0x00, 0x96, 0x4b, 0x01, 0x84, + 0x4b, 0x00, 0x80, 0x4b, 0x00, 0x86, 0x4b, 0x00, + 0x89, 0x4b, 0x01, 0x83, 0x4b, 0x1f, 0xc7, 0xa0, + 0x00, 0xa3, 0xa0, 0x03, 0xa6, 0xa0, 0x00, 0xa3, + 0xa0, 0x00, 0x8e, 0xa0, 0x00, 0x86, 0xa0, 0x83, + 0x1a, 0x81, 0xa0, 0x24, 0xe0, 0x3f, 0x65, 0xa5, + 0x29, 0x00, 0x80, 0x29, 0x04, 0x80, 0x29, 0x01, + 0xaa, 0x29, 0x80, 0x1a, 0x83, 0x29, 0xe0, 0x9f, + 0x34, 0xc8, 0x28, 0x00, 0x83, 0x28, 0x01, 0x86, + 0x28, 0x00, 0x80, 0x28, 0x00, 0x83, 0x28, 0x01, + 0xa8, 0x28, 0x00, 0x83, 0x28, 0x01, 0xa0, 0x28, + 0x00, 0x83, 0x28, 0x01, 0x86, 0x28, 0x00, 0x80, + 0x28, 0x00, 0x83, 0x28, 0x01, 0x8e, 0x28, 0x00, + 0xb8, 0x28, 0x00, 0x83, 0x28, 0x01, 0xc2, 0x28, + 0x01, 0x9f, 0x28, 0x02, 0x99, 0x28, 0x05, 0xd5, + 0x18, 0x01, 0x85, 0x18, 0x01, 0xe2, 0x1f, 0x13, + 0x9c, 0x6e, 0x02, 0xca, 0x84, 0x82, 0x1a, 0x8a, + 0x84, 0x06, 0x95, 0x94, 0x08, 0x80, 0x94, 0x94, + 0x36, 0x81, 0x1a, 0x08, 0x93, 0x12, 0x0b, 0x8c, + 0x95, 0x00, 0x82, 0x95, 0x00, 0x81, 0x95, 0x0b, + 0xdd, 0x46, 0x01, 0x89, 0x46, 0x05, 0x89, 0x46, + 0x05, 0x81, 0x62, 0x81, 0x1a, 0x80, 0x62, 0x80, + 0x1a, 0x93, 0x62, 0x05, 0xd8, 0x62, 0x06, 0xaa, + 0x62, 0x04, 0xc5, 0x13, 0x09, 0x9e, 0x4e, 0x00, + 0x8b, 0x4e, 0x03, 0x8b, 0x4e, 0x03, 0x80, 0x4e, + 0x02, 0x8b, 0x4e, 0x9d, 0x96, 0x01, 0x84, 0x96, + 0x0a, 0xab, 0x69, 0x03, 0x99, 0x69, 0x05, 0x8a, + 0x69, 0x02, 0x81, 0x69, 0x9f, 0x46, 0x9b, 0x11, + 0x01, 0x81, 0x11, 0xbe, 0x97, 0x00, 0x9c, 0x97, + 0x01, 0x8a, 0x97, 0x05, 0x89, 0x97, 0x05, 0x8d, + 0x97, 0x01, 0xad, 0x3b, 0x01, 0x8b, 0x3b, 0x13, + 0xcc, 0x07, 0x00, 0xb1, 0x07, 0xbf, 0x90, 0xb3, + 0x0a, 0x07, 0x83, 0x0a, 0xb7, 0x4d, 0x02, 0x8e, + 0x4d, 0x02, 0x82, 0x4d, 0xaf, 0x6f, 0x8a, 0x1e, + 0x04, 0xaa, 0x29, 0x01, 0x82, 0x29, 0x87, 0x90, + 0x07, 0x82, 0x3b, 0x80, 0x1a, 0x8c, 0x3b, 0x80, + 0x1a, 0x86, 0x3b, 0x83, 0x1a, 0x80, 0x3b, 0x85, + 0x1a, 0x80, 0x3b, 0x82, 0x1a, 0x81, 0x3b, 0x80, + 0x1a, 0x04, 0xa5, 0x4c, 0x84, 0x2e, 0x80, 0x1e, + 0xb0, 0x4c, 0x84, 0x2e, 0x83, 0x4c, 0x84, 0x2e, + 0x8c, 0x4c, 0x80, 0x1e, 0xc5, 0x4c, 0x80, 0x2e, + 0xbf, 0x3b, 0xe0, 0x9f, 0x4c, 0x95, 0x2e, 0x01, + 0x85, 0x2e, 0x01, 0xa5, 0x2e, 0x01, 0x85, 0x2e, + 0x01, 0x87, 0x2e, 0x00, 0x80, 0x2e, 0x00, 0x80, + 0x2e, 0x00, 0x80, 0x2e, 0x00, 0x9e, 0x2e, 0x01, + 0xb4, 0x2e, 0x00, 0x8e, 0x2e, 0x00, 0x8d, 0x2e, + 0x01, 0x85, 0x2e, 0x00, 0x92, 0x2e, 0x01, 0x82, + 0x2e, 0x00, 0x88, 0x2e, 0x00, 0x8b, 0x1a, 0x81, + 0x3b, 0xd6, 0x1a, 0x00, 0x8a, 0x1a, 0x80, 0x4c, + 0x01, 0x8a, 0x1a, 0x80, 0x4c, 0x8e, 0x1a, 0x00, + 0x8c, 0x4c, 0x02, 0xa1, 0x1a, 0x0d, 0xa0, 0x3b, + 0x0e, 0xa5, 0x1a, 0x80, 0x2e, 0x82, 0x1a, 0x81, + 0x4c, 0x85, 0x1a, 0x80, 0x4c, 0x9a, 0x1a, 0x80, + 0x4c, 0x90, 0x1a, 0xa8, 0x4c, 0x82, 0x1a, 0x03, + 0xe2, 0x39, 0x1a, 0x15, 0x8a, 0x1a, 0x14, 0xe3, + 0x3f, 0x1a, 0xe0, 0x9f, 0x10, 0xe2, 0x13, 0x1a, + 0x01, 0xe0, 0x29, 0x1a, 0xdf, 0x2a, 0x9f, 0x4c, + 0xe0, 0x13, 0x1b, 0x04, 0x86, 0x1b, 0xa5, 0x29, + 0x00, 0x80, 0x29, 0x04, 0x80, 0x29, 0x01, 0xb7, + 0xa1, 0x06, 0x81, 0xa1, 0x0d, 0x80, 0xa1, 0x96, + 0x28, 0x08, 0x86, 0x28, 0x00, 0x86, 0x28, 0x00, + 0x86, 0x28, 0x00, 0x86, 0x28, 0x00, 0x86, 0x28, + 0x00, 0x86, 0x28, 0x00, 0x86, 0x28, 0x00, 0x86, + 0x28, 0x00, 0x9f, 0x1e, 0xdd, 0x1a, 0x21, 0x99, + 0x33, 0x00, 0xd8, 0x33, 0x0b, 0xe0, 0x75, 0x33, + 0x19, 0x94, 0x1a, 0x80, 0x33, 0x80, 0x1a, 0x80, + 0x33, 0x98, 0x1a, 0x88, 0x33, 0x83, 0x3b, 0x81, + 0x34, 0x87, 0x1a, 0x83, 0x33, 0x83, 0x1a, 0x00, + 0xd5, 0x39, 0x01, 0x81, 0x3b, 0x81, 0x1a, 0x82, + 0x39, 0x80, 0x1a, 0xd9, 0x41, 0x81, 0x1a, 0x82, + 0x41, 0x04, 0xaa, 0x0e, 0x00, 0xdd, 0x34, 0x00, + 0x8f, 0x1a, 0x9f, 0x0e, 0xa5, 0x1a, 0x08, 0x80, + 0x1a, 0x8f, 0x41, 0x9e, 0x34, 0x00, 0xbf, 0x1a, + 0x9e, 0x34, 0xd0, 0x1a, 0xae, 0x41, 0x80, 0x1a, + 0xd7, 0x41, 0xe0, 0x47, 0x1a, 0xf0, 0x09, 0x5f, + 0x33, 0xbf, 0x1a, 0xf0, 0x41, 0x9f, 0x33, 0xe4, + 0x2c, 0xae, 0x02, 0xb6, 0xae, 0x08, 0xaf, 0x51, + 0xe0, 0xcb, 0xa9, 0x13, 0xdf, 0x1e, 0xd7, 0x08, + 0x07, 0xa1, 0x1a, 0xe0, 0x05, 0x4c, 0x82, 0x1a, + 0xd1, 0x4c, 0x13, 0x8e, 0x4c, 0xac, 0x92, 0x02, + 0x89, 0x1a, 0x05, 0xb7, 0x80, 0x07, 0xc5, 0x86, + 0x07, 0x8b, 0x86, 0x05, 0x9f, 0x21, 0xad, 0x44, + 0x80, 0x1a, 0x80, 0x44, 0xa3, 0x83, 0x0a, 0x80, + 0x83, 0x9c, 0x34, 0x02, 0xcd, 0x3e, 0x00, 0x80, + 0x1a, 0x89, 0x3e, 0x03, 0x81, 0x3e, 0x9e, 0x65, + 0x00, 0xb6, 0x17, 0x08, 0x8d, 0x17, 0x01, 0x89, + 0x17, 0x01, 0x83, 0x17, 0x9f, 0x65, 0xc2, 0x98, + 0x17, 0x84, 0x98, 0x96, 0x5c, 0x09, 0x85, 0x28, + 0x01, 0x85, 0x28, 0x01, 0x85, 0x28, 0x08, 0x86, + 0x28, 0x00, 0x86, 0x28, 0x00, 0xaa, 0x4c, 0x80, + 0x1a, 0x88, 0x4c, 0x80, 0x2e, 0x83, 0x4c, 0x81, + 0x1a, 0x03, 0xcf, 0x18, 0xad, 0x5c, 0x01, 0x89, + 0x5c, 0x05, 0xf0, 0x1b, 0x43, 0x34, 0x0b, 0x96, + 0x34, 0x03, 0xb0, 0x34, 0x70, 0x10, 0xa3, 0xe1, + 0x0d, 0x33, 0x01, 0xe0, 0x09, 0x33, 0x25, 0x86, + 0x4c, 0x0b, 0x84, 0x05, 0x04, 0x99, 0x38, 0x00, + 0x84, 0x38, 0x00, 0x80, 0x38, 0x00, 0x81, 0x38, + 0x00, 0x81, 0x38, 0x00, 0x89, 0x38, 0xe1, 0x8d, + 0x04, 0x81, 0x1a, 0xe0, 0x2f, 0x04, 0x1f, 0x8f, + 0x04, 0x8f, 0x3b, 0x89, 0x1a, 0x05, 0x8d, 0x3b, + 0x81, 0x1e, 0xa2, 0x1a, 0x00, 0x92, 0x1a, 0x00, + 0x83, 0x1a, 0x03, 0x84, 0x04, 0x00, 0xe0, 0x26, + 0x04, 0x01, 0x80, 0x1a, 0x00, 0x9f, 0x1a, 0x99, + 0x4c, 0x85, 0x1a, 0x99, 0x4c, 0x8a, 0x1a, 0x89, + 0x41, 0x80, 0x1a, 0xac, 0x41, 0x81, 0x1a, 0x9e, + 0x34, 0x02, 0x85, 0x34, 0x01, 0x85, 0x34, 0x01, + 0x85, 0x34, 0x01, 0x82, 0x34, 0x02, 0x86, 0x1a, + 0x00, 0x86, 0x1a, 0x09, 0x84, 0x1a, 0x01, 0x8b, + 0x50, 0x00, 0x99, 0x50, 0x00, 0x92, 0x50, 0x00, + 0x81, 0x50, 0x00, 0x8e, 0x50, 0x01, 0x8d, 0x50, + 0x21, 0xe0, 0x1a, 0x50, 0x04, 0x82, 0x1a, 0x03, + 0xac, 0x1a, 0x02, 0x88, 0x1a, 0xce, 0x2e, 0x00, + 0x8c, 0x1a, 0x02, 0x80, 0x2e, 0x2e, 0xac, 0x1a, + 0x80, 0x3b, 0x60, 0x21, 0x9c, 0x52, 0x02, 0xb0, + 0x14, 0x0e, 0x80, 0x3b, 0x9a, 0x1a, 0x03, 0xa3, + 0x72, 0x08, 0x82, 0x72, 0x9a, 0x2b, 0x04, 0xaa, + 0x74, 0x04, 0x9d, 0xa8, 0x00, 0x80, 0xa8, 0xa3, + 0x75, 0x03, 0x8d, 0x75, 0x29, 0xcf, 0x20, 0xaf, + 0x88, 0x9d, 0x7c, 0x01, 0x89, 0x7c, 0x05, 0xa3, + 0x7b, 0x03, 0xa3, 0x7b, 0x03, 0xa7, 0x26, 0x07, + 0xb3, 0x15, 0x0a, 0x80, 0x15, 0x8a, 0xaa, 0x00, + 0x8e, 0xaa, 0x00, 0x86, 0xaa, 0x00, 0x81, 0xaa, + 0x00, 0x8a, 0xaa, 0x00, 0x8e, 0xaa, 0x00, 0x86, + 0xaa, 0x00, 0x81, 0xaa, 0x02, 0xb3, 0xa4, 0x0b, + 0xe0, 0xd6, 0x4f, 0x08, 0x95, 0x4f, 0x09, 0x87, + 0x4f, 0x17, 0x85, 0x4c, 0x00, 0xa9, 0x4c, 0x00, + 0x88, 0x4c, 0x44, 0x85, 0x1d, 0x01, 0x80, 0x1d, + 0x00, 0xab, 0x1d, 0x00, 0x81, 0x1d, 0x02, 0x80, + 0x1d, 0x01, 0x80, 0x1d, 0x95, 0x3a, 0x00, 0x88, + 0x3a, 0x9f, 0x7e, 0x9e, 0x66, 0x07, 0x88, 0x66, + 0x2f, 0x92, 0x37, 0x00, 0x81, 0x37, 0x04, 0x84, + 0x37, 0x9b, 0x81, 0x02, 0x80, 0x81, 0x99, 0x53, + 0x04, 0x80, 0x53, 0x99, 0x8a, 0x25, 0x9f, 0x5f, + 0x97, 0x5e, 0x03, 0x93, 0x5e, 0x01, 0xad, 0x5e, + 0x83, 0x45, 0x00, 0x81, 0x45, 0x04, 0x87, 0x45, + 0x00, 0x82, 0x45, 0x00, 0x9c, 0x45, 0x01, 0x82, + 0x45, 0x03, 0x89, 0x45, 0x06, 0x88, 0x45, 0x06, + 0x9f, 0x77, 0x9f, 0x73, 0x1f, 0xa6, 0x58, 0x03, + 0x8b, 0x58, 0x08, 0xb5, 0x06, 0x02, 0x86, 0x06, + 0x95, 0x3d, 0x01, 0x87, 0x3d, 0x92, 0x3c, 0x04, + 0x87, 0x3c, 0x91, 0x82, 0x06, 0x83, 0x82, 0x0b, + 0x86, 0x82, 0x4f, 0xc8, 0x78, 0x36, 0xb2, 0x71, + 0x0c, 0xb2, 0x71, 0x06, 0x85, 0x71, 0xa7, 0x35, + 0x07, 0x89, 0x35, 0x05, 0xa5, 0x2c, 0x02, 0x9c, + 0x2c, 0x07, 0x81, 0x2c, 0x60, 0x6f, 0x9e, 0x04, + 0x00, 0xa9, 0xad, 0x00, 0x82, 0xad, 0x01, 0x81, + 0xad, 0x0f, 0x85, 0x04, 0x07, 0x88, 0x04, 0x20, + 0x85, 0x04, 0xa7, 0x76, 0x07, 0xa9, 0x8d, 0x15, + 0x99, 0x79, 0x25, 0x9b, 0x19, 0x13, 0x96, 0x27, + 0x08, 0xcd, 0x0f, 0x03, 0xa3, 0x0f, 0x08, 0x80, + 0x0f, 0xc2, 0x3f, 0x09, 0x80, 0x3f, 0x01, 0x98, + 0x8e, 0x06, 0x89, 0x8e, 0x05, 0xb4, 0x16, 0x00, + 0x91, 0x16, 0x07, 0xa6, 0x55, 0x08, 0xdf, 0x87, + 0x00, 0x93, 0x8c, 0x0a, 0x91, 0x47, 0x00, 0xae, + 0x47, 0x3d, 0x86, 0x64, 0x00, 0x80, 0x64, 0x00, + 0x83, 0x64, 0x00, 0x8e, 0x64, 0x00, 0x8a, 0x64, + 0x05, 0xba, 0x49, 0x04, 0x89, 0x49, 0x05, 0x83, + 0x2d, 0x00, 0x87, 0x2d, 0x01, 0x81, 0x2d, 0x01, + 0x95, 0x2d, 0x00, 0x86, 0x2d, 0x00, 0x81, 0x2d, + 0x00, 0x84, 0x2d, 0x00, 0x80, 0x3b, 0x88, 0x2d, + 0x01, 0x81, 0x2d, 0x01, 0x82, 0x2d, 0x01, 0x80, + 0x2d, 0x05, 0x80, 0x2d, 0x04, 0x86, 0x2d, 0x01, + 0x86, 0x2d, 0x02, 0x84, 0x2d, 0x0a, 0x89, 0xa7, + 0x00, 0x80, 0xa7, 0x01, 0x80, 0xa7, 0x00, 0xa5, + 0xa7, 0x00, 0x89, 0xa7, 0x00, 0x80, 0xa7, 0x01, + 0x80, 0xa7, 0x00, 0x83, 0xa7, 0x00, 0x89, 0xa7, + 0x00, 0x81, 0xa7, 0x07, 0x81, 0xa7, 0x1c, 0xdb, + 0x6a, 0x00, 0x84, 0x6a, 0x1d, 0xc7, 0xa2, 0x07, + 0x89, 0xa2, 0x60, 0x45, 0xb5, 0x89, 0x01, 0xa5, + 0x89, 0x21, 0xc4, 0x61, 0x0a, 0x89, 0x61, 0x05, + 0x8c, 0x62, 0x12, 0xb9, 0x9a, 0x05, 0x89, 0x9a, + 0x05, 0x93, 0x65, 0x1b, 0x9a, 0x02, 0x01, 0x8e, + 0x02, 0x03, 0x96, 0x02, 0x60, 0x58, 0xbb, 0x23, + 0x60, 0x03, 0xd2, 0xac, 0x0b, 0x80, 0xac, 0x86, + 0x22, 0x01, 0x80, 0x22, 0x01, 0x87, 0x22, 0x00, + 0x81, 0x22, 0x00, 0x9d, 0x22, 0x00, 0x81, 0x22, + 0x01, 0x8b, 0x22, 0x08, 0x89, 0x22, 0x45, 0x87, + 0x68, 0x01, 0xad, 0x68, 0x01, 0x8a, 0x68, 0x1a, + 0xc7, 0xaf, 0x07, 0xd2, 0x8f, 0x0c, 0x8f, 0x13, + 0xb8, 0x7f, 0x06, 0x89, 0x21, 0x55, 0x87, 0x87, + 0x57, 0xa1, 0x91, 0x0d, 0x89, 0x91, 0x05, 0x88, + 0x0d, 0x00, 0xac, 0x0d, 0x00, 0x8d, 0x0d, 0x09, + 0x9c, 0x0d, 0x02, 0x9f, 0x59, 0x01, 0x95, 0x59, + 0x00, 0x8d, 0x59, 0x48, 0x86, 0x5a, 0x00, 0x81, + 0x5a, 0x00, 0xab, 0x5a, 0x02, 0x80, 0x5a, 0x00, + 0x81, 0x5a, 0x00, 0x88, 0x5a, 0x07, 0x89, 0x5a, + 0x05, 0x85, 0x30, 0x00, 0x81, 0x30, 0x00, 0xa4, + 0x30, 0x00, 0x81, 0x30, 0x00, 0x85, 0x30, 0x06, + 0x89, 0x30, 0x05, 0xab, 0xa5, 0x03, 0x89, 0xa5, + 0x60, 0x95, 0x98, 0x54, 0x06, 0x90, 0x43, 0x00, + 0xa8, 0x43, 0x02, 0x9c, 0x43, 0x54, 0x80, 0x51, + 0x0e, 0xb1, 0x9b, 0x0c, 0x80, 0x9b, 0xe3, 0x39, + 0x1c, 0x60, 0x05, 0xe0, 0x0e, 0x1c, 0x00, 0x84, + 0x1c, 0x0a, 0xe0, 0x63, 0x1c, 0x69, 0xeb, 0xe0, + 0x02, 0x1f, 0x0c, 0xe3, 0xf5, 0x25, 0x09, 0xef, + 0x3a, 0x25, 0x04, 0xe1, 0xe6, 0x03, 0x70, 0x0a, + 0x58, 0xb9, 0x32, 0x66, 0x65, 0xe1, 0xd8, 0x08, + 0x06, 0x9e, 0x63, 0x00, 0x89, 0x63, 0x03, 0x81, + 0x63, 0xce, 0xa3, 0x00, 0x89, 0xa3, 0x05, 0x9d, + 0x09, 0x01, 0x85, 0x09, 0x09, 0xc5, 0x7d, 0x09, + 0x89, 0x7d, 0x00, 0x86, 0x7d, 0x00, 0x94, 0x7d, + 0x04, 0x92, 0x7d, 0x61, 0x4f, 0xb9, 0x4a, 0x60, + 0x65, 0xda, 0x5b, 0x04, 0x98, 0x0c, 0x01, 0x98, + 0x0c, 0x2b, 0xca, 0x60, 0x03, 0xb8, 0x60, 0x06, + 0x90, 0x60, 0x3f, 0x80, 0x9c, 0x80, 0x6c, 0x81, + 0x33, 0x80, 0x48, 0x0a, 0x86, 0x33, 0x08, 0xf0, + 0x0a, 0x9f, 0x9c, 0xe1, 0x75, 0x48, 0x28, 0x80, + 0x48, 0x9e, 0x9c, 0x60, 0x00, 0xe0, 0x12, 0x9c, + 0x70, 0x11, 0x9c, 0x83, 0x41, 0x00, 0x86, 0x41, + 0x00, 0x81, 0x41, 0x00, 0x80, 0x41, 0xe0, 0xbe, + 0x39, 0x82, 0x41, 0x0e, 0x80, 0x39, 0x1c, 0x82, + 0x39, 0x01, 0x80, 0x41, 0x0d, 0x83, 0x41, 0x07, + 0xe1, 0x2b, 0x6c, 0x68, 0xa3, 0xe0, 0x0a, 0x24, + 0x04, 0x8c, 0x24, 0x02, 0x88, 0x24, 0x06, 0x89, + 0x24, 0x01, 0x83, 0x24, 0x83, 0x1a, 0x6e, 0xfb, + 0xe0, 0x9c, 0x1a, 0x02, 0xe1, 0x53, 0x1a, 0x05, + 0x96, 0x1a, 0x0e, 0x90, 0x1a, 0x0e, 0xad, 0x3b, + 0x01, 0x96, 0x3b, 0x08, 0xe0, 0x13, 0x1a, 0x3b, + 0xe0, 0x95, 0x1a, 0x09, 0xa6, 0x1a, 0x01, 0xbd, + 0x1a, 0x82, 0x3b, 0x90, 0x1a, 0x87, 0x3b, 0x81, + 0x1a, 0x86, 0x3b, 0x9d, 0x1a, 0x83, 0x3b, 0xbc, + 0x1a, 0x14, 0xc5, 0x2e, 0x60, 0x19, 0x93, 0x1a, + 0x0b, 0x93, 0x1a, 0x0b, 0xd6, 0x1a, 0x08, 0x98, + 0x1a, 0x60, 0x26, 0xd4, 0x1a, 0x00, 0xc6, 0x1a, + 0x00, 0x81, 0x1a, 0x01, 0x80, 0x1a, 0x01, 0x81, + 0x1a, 0x01, 0x83, 0x1a, 0x00, 0x8b, 0x1a, 0x00, + 0x80, 0x1a, 0x00, 0x86, 0x1a, 0x00, 0xc0, 0x1a, + 0x00, 0x83, 0x1a, 0x01, 0x87, 0x1a, 0x00, 0x86, + 0x1a, 0x00, 0x9b, 0x1a, 0x00, 0x83, 0x1a, 0x00, + 0x84, 0x1a, 0x00, 0x80, 0x1a, 0x02, 0x86, 0x1a, + 0x00, 0xe0, 0xf3, 0x1a, 0x01, 0xe0, 0xc3, 0x1a, + 0x01, 0xb1, 0x1a, 0xe2, 0x2b, 0x8b, 0x0e, 0x84, + 0x8b, 0x00, 0x8e, 0x8b, 0x63, 0xef, 0x9e, 0x4c, + 0x05, 0x85, 0x4c, 0x60, 0x74, 0x86, 0x2a, 0x00, + 0x90, 0x2a, 0x01, 0x86, 0x2a, 0x00, 0x81, 0x2a, + 0x00, 0x84, 0x2a, 0x04, 0xbd, 0x1e, 0x20, 0x80, + 0x1e, 0x60, 0x0f, 0xac, 0x6d, 0x02, 0x8d, 0x6d, + 0x01, 0x89, 0x6d, 0x03, 0x81, 0x6d, 0x60, 0xdf, + 0x9e, 0xa6, 0x10, 0xb9, 0xab, 0x04, 0x80, 0xab, + 0x61, 0x6f, 0xa9, 0x67, 0x60, 0x75, 0xaa, 0x70, + 0x03, 0x80, 0x70, 0x60, 0x5f, 0x9e, 0x99, 0x00, + 0x95, 0x99, 0x07, 0x81, 0x99, 0x60, 0x7f, 0x86, + 0x28, 0x00, 0x83, 0x28, 0x00, 0x81, 0x28, 0x00, + 0x8e, 0x28, 0x00, 0xe0, 0x64, 0x5d, 0x01, 0x8f, + 0x5d, 0x28, 0xcb, 0x01, 0x03, 0x89, 0x01, 0x03, + 0x81, 0x01, 0x62, 0xb0, 0xc3, 0x1a, 0x4b, 0xbc, + 0x1a, 0x60, 0x61, 0x83, 0x04, 0x00, 0x9a, 0x04, + 0x00, 0x81, 0x04, 0x00, 0x80, 0x04, 0x01, 0x80, + 0x04, 0x00, 0x89, 0x04, 0x00, 0x83, 0x04, 0x00, + 0x80, 0x04, 0x00, 0x80, 0x04, 0x05, 0x80, 0x04, + 0x03, 0x80, 0x04, 0x00, 0x80, 0x04, 0x00, 0x80, + 0x04, 0x00, 0x82, 0x04, 0x00, 0x81, 0x04, 0x00, + 0x80, 0x04, 0x01, 0x80, 0x04, 0x00, 0x80, 0x04, + 0x00, 0x80, 0x04, 0x00, 0x80, 0x04, 0x00, 0x80, + 0x04, 0x00, 0x81, 0x04, 0x00, 0x80, 0x04, 0x01, + 0x83, 0x04, 0x00, 0x86, 0x04, 0x00, 0x83, 0x04, + 0x00, 0x83, 0x04, 0x00, 0x80, 0x04, 0x00, 0x89, + 0x04, 0x00, 0x90, 0x04, 0x04, 0x82, 0x04, 0x00, + 0x84, 0x04, 0x00, 0x90, 0x04, 0x33, 0x81, 0x04, + 0x60, 0xad, 0xab, 0x1a, 0x03, 0xe0, 0x03, 0x1a, + 0x0b, 0x8e, 0x1a, 0x01, 0x8e, 0x1a, 0x00, 0x8e, + 0x1a, 0x00, 0xa4, 0x1a, 0x09, 0xe0, 0x4d, 0x1a, + 0x37, 0x99, 0x1a, 0x80, 0x39, 0x81, 0x1a, 0x0c, + 0xab, 0x1a, 0x03, 0x88, 0x1a, 0x06, 0x81, 0x1a, + 0x0d, 0x85, 0x1a, 0x60, 0x39, 0xe3, 0x78, 0x1a, + 0x02, 0x90, 0x1a, 0x02, 0x8c, 0x1a, 0x02, 0xe0, + 0x79, 0x1a, 0x05, 0x8b, 0x1a, 0x03, 0x80, 0x1a, + 0x0e, 0x8b, 0x1a, 0x03, 0xb7, 0x1a, 0x07, 0x89, + 0x1a, 0x05, 0xa7, 0x1a, 0x07, 0x9d, 0x1a, 0x01, + 0x8b, 0x1a, 0x03, 0x81, 0x1a, 0x0d, 0x88, 0x1a, + 0x26, 0xe0, 0xf7, 0x1a, 0x07, 0x8d, 0x1a, 0x01, + 0x8c, 0x1a, 0x02, 0x8a, 0x1a, 0x02, 0xb8, 0x1a, + 0x00, 0x80, 0x1a, 0x03, 0x8f, 0x1a, 0x01, 0x8b, + 0x1a, 0x03, 0x89, 0x1a, 0x06, 0xe0, 0x32, 0x1a, + 0x00, 0xe0, 0x06, 0x1a, 0x63, 0xa4, 0xf0, 0x96, + 0x7f, 0x33, 0x1f, 0xf0, 0x00, 0xbd, 0x33, 0x01, + 0xf0, 0x06, 0x2d, 0x33, 0x01, 0xf0, 0x0c, 0xd0, + 0x33, 0x0e, 0xe2, 0x0d, 0x33, 0x69, 0x41, 0xe1, + 0xbd, 0x33, 0x65, 0x81, 0xf0, 0x02, 0xea, 0x33, + 0x04, 0xf0, 0x10, 0xc9, 0x33, 0x7a, 0xbb, 0x26, + 0x80, 0x1a, 0x1d, 0xdf, 0x1a, 0x60, 0x1f, 0xe0, + 0x8f, 0x3b, +}; + +static const uint8_t unicode_script_ext_table[1278] = { + 0x80, 0x36, 0x00, 0x00, 0x10, 0x06, 0x14, 0x1b, + 0x24, 0x26, 0x29, 0x2a, 0x30, 0x2b, 0x2e, 0x33, + 0x4c, 0x53, 0x55, 0x74, 0x88, 0x81, 0x83, 0x00, + 0x00, 0x07, 0x0b, 0x1e, 0x21, 0x4c, 0x51, 0x9f, + 0xa6, 0x09, 0x00, 0x00, 0x02, 0x0e, 0x4c, 0x00, + 0x00, 0x02, 0x02, 0x0e, 0x4c, 0x00, 0x00, 0x00, + 0x02, 0x4c, 0x51, 0x08, 0x00, 0x00, 0x02, 0x4c, + 0x9f, 0x00, 0x00, 0x00, 0x02, 0x0e, 0x4c, 0x25, + 0x00, 0x00, 0x08, 0x18, 0x1b, 0x1e, 0x2e, 0x4c, + 0x74, 0x91, 0x96, 0x00, 0x08, 0x18, 0x1e, 0x2e, + 0x4c, 0x7b, 0x91, 0x96, 0xa4, 0x00, 0x04, 0x18, + 0x1e, 0x4c, 0xa1, 0x00, 0x05, 0x2a, 0x4c, 0x91, + 0x93, 0x9f, 0x00, 0x0b, 0x15, 0x18, 0x1b, 0x1e, + 0x2b, 0x2e, 0x4c, 0x7b, 0x93, 0xa1, 0xa4, 0x00, + 0x06, 0x1b, 0x26, 0x2a, 0x2b, 0x41, 0x4c, 0x00, + 0x05, 0x1e, 0x2e, 0x4c, 0x74, 0xa1, 0x00, 0x09, + 0x1b, 0x24, 0x38, 0x4c, 0x74, 0x93, 0x96, 0xa1, + 0xa4, 0x00, 0x0b, 0x05, 0x1e, 0x24, 0x2b, 0x2e, + 0x38, 0x4c, 0x74, 0x93, 0x96, 0xa1, 0x00, 0x02, + 0x4c, 0xa1, 0x00, 0x03, 0x24, 0x4c, 0x93, 0x00, + 0x04, 0x18, 0x1e, 0x4c, 0x7b, 0x00, 0x03, 0x18, + 0x4c, 0x96, 0x00, 0x02, 0x4c, 0x91, 0x00, 0x02, + 0x28, 0x4c, 0x00, 0x00, 0x00, 0x02, 0x4c, 0x91, + 0x00, 0x03, 0x1e, 0x4c, 0xa4, 0x00, 0x00, 0x00, + 0x04, 0x2e, 0x4c, 0x74, 0xa4, 0x0e, 0x00, 0x00, + 0x06, 0x18, 0x24, 0x41, 0x4c, 0x93, 0xa1, 0x00, + 0x04, 0x18, 0x24, 0x4c, 0x93, 0x00, 0x02, 0x4c, + 0x93, 0x06, 0x00, 0x00, 0x03, 0x4c, 0x91, 0x93, + 0x00, 0x02, 0x4c, 0x93, 0x00, 0x00, 0x00, 0x03, + 0x18, 0x4c, 0x93, 0x00, 0x07, 0x15, 0x18, 0x2b, + 0x4c, 0x91, 0x93, 0x9f, 0x0f, 0x00, 0x00, 0x01, + 0x2e, 0x01, 0x00, 0x00, 0x01, 0x2e, 0x11, 0x00, + 0x00, 0x02, 0x4c, 0x7b, 0x04, 0x00, 0x00, 0x03, + 0x15, 0x4c, 0xa4, 0x03, 0x00, 0x0c, 0x01, 0x4c, + 0x03, 0x00, 0x01, 0x02, 0x1b, 0x2e, 0x80, 0x8c, + 0x00, 0x00, 0x02, 0x1e, 0x74, 0x00, 0x02, 0x1e, + 0x2a, 0x01, 0x02, 0x1e, 0x4c, 0x00, 0x02, 0x1e, + 0x2a, 0x80, 0x80, 0x00, 0x00, 0x03, 0x05, 0x29, + 0x2a, 0x80, 0x01, 0x00, 0x00, 0x07, 0x04, 0x2c, + 0x6b, 0x35, 0x93, 0x9e, 0xad, 0x0d, 0x00, 0x00, + 0x07, 0x04, 0x2c, 0x6b, 0x35, 0x93, 0x9e, 0xad, + 0x00, 0x03, 0x04, 0x93, 0x9e, 0x01, 0x00, 0x00, + 0x08, 0x01, 0x04, 0x2c, 0x6b, 0x35, 0x93, 0x9e, + 0xad, 0x1f, 0x00, 0x00, 0x09, 0x01, 0x04, 0x57, + 0x58, 0x79, 0x82, 0x35, 0x8d, 0x93, 0x09, 0x00, + 0x0a, 0x02, 0x04, 0x93, 0x09, 0x00, 0x09, 0x03, + 0x04, 0x9e, 0xad, 0x05, 0x00, 0x00, 0x02, 0x04, + 0x93, 0x62, 0x00, 0x00, 0x02, 0x04, 0x35, 0x81, + 0xfb, 0x00, 0x00, 0x0f, 0x0b, 0x21, 0x2d, 0x2f, + 0x31, 0x40, 0x4c, 0x56, 0x68, 0x6a, 0x7a, 0x87, + 0x9b, 0x9d, 0xa2, 0x00, 0x0d, 0x0b, 0x21, 0x2d, + 0x2f, 0x31, 0x40, 0x4c, 0x56, 0x6a, 0x7a, 0x9b, + 0x9d, 0xa2, 0x10, 0x00, 0x00, 0x15, 0x0b, 0x21, + 0x23, 0x30, 0x5a, 0x2d, 0x2f, 0x31, 0x40, 0x55, + 0x56, 0x68, 0x70, 0x7a, 0x49, 0x8c, 0x92, 0x9a, + 0x9b, 0x9d, 0xa2, 0x00, 0x17, 0x0b, 0x21, 0x23, + 0x30, 0x5a, 0x2d, 0x2f, 0x32, 0x31, 0x40, 0x4e, + 0x55, 0x56, 0x68, 0x70, 0x7a, 0x49, 0x8c, 0x92, + 0x9a, 0x9b, 0x9d, 0xa2, 0x09, 0x04, 0x21, 0x23, + 0x3f, 0x55, 0x75, 0x00, 0x09, 0x03, 0x0b, 0x16, + 0x92, 0x75, 0x00, 0x09, 0x02, 0x31, 0x64, 0x75, + 0x00, 0x09, 0x02, 0x2f, 0x47, 0x80, 0x75, 0x00, + 0x0d, 0x02, 0x2d, 0x9b, 0x80, 0x71, 0x00, 0x09, + 0x03, 0x40, 0x68, 0xa7, 0x82, 0xcf, 0x00, 0x09, + 0x03, 0x16, 0x65, 0x96, 0x80, 0x30, 0x00, 0x00, + 0x03, 0x29, 0x2a, 0x4c, 0x85, 0x6e, 0x00, 0x02, + 0x01, 0x84, 0x46, 0x00, 0x01, 0x04, 0x12, 0x36, + 0x95, 0x94, 0x80, 0x4a, 0x00, 0x01, 0x02, 0x62, + 0x80, 0x00, 0x00, 0x00, 0x02, 0x62, 0x80, 0x84, + 0x49, 0x00, 0x00, 0x04, 0x0b, 0x21, 0x2d, 0x40, + 0x00, 0x01, 0x21, 0x00, 0x04, 0x0b, 0x21, 0x2d, + 0x40, 0x00, 0x03, 0x21, 0x2d, 0x40, 0x00, 0x01, + 0x21, 0x00, 0x05, 0x0b, 0x21, 0x6a, 0x9d, 0xa2, + 0x00, 0x03, 0x0b, 0x21, 0x9d, 0x00, 0x03, 0x21, + 0x6a, 0x87, 0x00, 0x04, 0x0b, 0x21, 0x6a, 0x9d, + 0x00, 0x02, 0x21, 0x87, 0x00, 0x06, 0x21, 0x40, + 0x56, 0x7a, 0x9b, 0x9d, 0x00, 0x01, 0x21, 0x01, + 0x02, 0x21, 0x87, 0x01, 0x01, 0x21, 0x00, 0x02, + 0x21, 0x87, 0x00, 0x02, 0x0b, 0x21, 0x00, 0x03, + 0x21, 0x6a, 0xa2, 0x05, 0x01, 0x21, 0x00, 0x03, + 0x21, 0x68, 0x6a, 0x00, 0x03, 0x0b, 0x21, 0x87, + 0x00, 0x02, 0x21, 0x6a, 0x00, 0x01, 0x21, 0x00, + 0x04, 0x0b, 0x21, 0x6a, 0x87, 0x03, 0x01, 0x21, + 0x00, 0x0b, 0x0b, 0x21, 0x2d, 0x40, 0x56, 0x68, + 0x7a, 0x8c, 0x9d, 0xa2, 0xa7, 0x00, 0x02, 0x21, + 0x2d, 0x00, 0x04, 0x21, 0x2d, 0x40, 0xa7, 0x01, + 0x02, 0x0b, 0x21, 0x00, 0x01, 0x0b, 0x01, 0x02, + 0x21, 0x2d, 0x00, 0x01, 0x68, 0x80, 0x44, 0x00, + 0x01, 0x01, 0x2e, 0x35, 0x00, 0x00, 0x03, 0x1e, + 0x4c, 0x93, 0x00, 0x00, 0x00, 0x01, 0x93, 0x81, + 0xb3, 0x00, 0x00, 0x03, 0x4c, 0x62, 0x80, 0x1e, + 0x00, 0x00, 0x02, 0x01, 0x04, 0x09, 0x00, 0x00, + 0x06, 0x14, 0x29, 0x2a, 0x71, 0x52, 0x78, 0x01, + 0x00, 0x00, 0x04, 0x14, 0x2e, 0x71, 0x5f, 0x80, + 0x11, 0x00, 0x00, 0x03, 0x21, 0x2d, 0x4c, 0x8c, + 0xa5, 0x00, 0x00, 0x02, 0x1b, 0x4c, 0x17, 0x00, + 0x00, 0x02, 0x06, 0x78, 0x00, 0x07, 0x06, 0x14, + 0x29, 0x71, 0x3f, 0x53, 0x85, 0x09, 0x00, 0x00, + 0x01, 0x24, 0x03, 0x00, 0x00, 0x03, 0x01, 0x04, + 0x71, 0x00, 0x00, 0x00, 0x02, 0x1e, 0x2a, 0x81, + 0x2b, 0x00, 0x0f, 0x02, 0x33, 0x9c, 0x00, 0x00, + 0x00, 0x07, 0x0e, 0x34, 0x33, 0x39, 0x41, 0x62, + 0xae, 0x00, 0x08, 0x0e, 0x34, 0x33, 0x39, 0x41, + 0x62, 0x80, 0xae, 0x00, 0x05, 0x0e, 0x34, 0x33, + 0x39, 0x41, 0x01, 0x00, 0x00, 0x01, 0x33, 0x00, + 0x00, 0x01, 0x08, 0x0e, 0x34, 0x33, 0x39, 0x41, + 0x62, 0xa0, 0xae, 0x01, 0x09, 0x0e, 0x34, 0x33, + 0x39, 0x41, 0x51, 0x62, 0xa0, 0xae, 0x05, 0x06, + 0x0e, 0x34, 0x33, 0x39, 0x41, 0xae, 0x00, 0x00, + 0x00, 0x05, 0x0e, 0x34, 0x33, 0x39, 0x41, 0x07, + 0x06, 0x0e, 0x34, 0x33, 0x39, 0x41, 0xae, 0x03, + 0x05, 0x0e, 0x34, 0x33, 0x39, 0x41, 0x09, 0x00, + 0x03, 0x02, 0x0e, 0x33, 0x01, 0x00, 0x00, 0x05, + 0x0e, 0x34, 0x33, 0x39, 0x41, 0x04, 0x02, 0x39, + 0x41, 0x00, 0x00, 0x00, 0x05, 0x0e, 0x34, 0x33, + 0x39, 0x41, 0x03, 0x00, 0x01, 0x03, 0x33, 0x39, + 0x41, 0x01, 0x01, 0x33, 0x58, 0x00, 0x03, 0x02, + 0x39, 0x41, 0x02, 0x00, 0x00, 0x02, 0x39, 0x41, + 0x59, 0x00, 0x00, 0x06, 0x0e, 0x34, 0x33, 0x39, + 0x41, 0xae, 0x00, 0x02, 0x39, 0x41, 0x80, 0x12, + 0x00, 0x0f, 0x01, 0x33, 0x1f, 0x00, 0x25, 0x01, + 0x33, 0x08, 0x00, 0x00, 0x02, 0x33, 0x9c, 0x2f, + 0x00, 0x27, 0x01, 0x33, 0x37, 0x00, 0x30, 0x01, + 0x33, 0x0e, 0x00, 0x0b, 0x01, 0x33, 0x32, 0x00, + 0x00, 0x01, 0x33, 0x57, 0x00, 0x18, 0x01, 0x33, + 0x09, 0x00, 0x04, 0x01, 0x33, 0x5f, 0x00, 0x1e, + 0x01, 0x33, 0xc0, 0x31, 0xef, 0x00, 0x00, 0x02, + 0x1e, 0x2a, 0x80, 0x0f, 0x00, 0x07, 0x02, 0x33, + 0x4c, 0x80, 0xa7, 0x00, 0x02, 0x10, 0x21, 0x23, + 0x2f, 0x31, 0x47, 0x40, 0x3f, 0x55, 0x56, 0x61, + 0x68, 0x87, 0x49, 0x9a, 0xa2, 0xa7, 0x02, 0x0f, + 0x21, 0x23, 0x2f, 0x31, 0x47, 0x40, 0x3f, 0x55, + 0x61, 0x68, 0x87, 0x49, 0x9a, 0xa2, 0xa7, 0x01, + 0x0b, 0x21, 0x23, 0x2f, 0x31, 0x47, 0x3f, 0x55, + 0x61, 0x49, 0x9a, 0xa2, 0x00, 0x0c, 0x21, 0x23, + 0x2f, 0x31, 0x47, 0x3f, 0x55, 0x61, 0x87, 0x49, + 0x9a, 0xa2, 0x00, 0x0b, 0x21, 0x23, 0x2f, 0x31, + 0x47, 0x3f, 0x55, 0x61, 0x49, 0x9a, 0xa2, 0x80, + 0x36, 0x00, 0x00, 0x03, 0x0b, 0x21, 0xa7, 0x00, + 0x00, 0x00, 0x02, 0x21, 0x9b, 0x39, 0x00, 0x00, + 0x03, 0x44, 0x4c, 0x65, 0x80, 0x1f, 0x00, 0x00, + 0x02, 0x11, 0x3e, 0xc0, 0x12, 0xed, 0x00, 0x01, + 0x02, 0x04, 0x6b, 0x80, 0x31, 0x00, 0x00, 0x02, + 0x04, 0x9e, 0x09, 0x00, 0x00, 0x02, 0x04, 0x9e, + 0x46, 0x00, 0x01, 0x05, 0x0e, 0x34, 0x33, 0x39, + 0x41, 0x80, 0x99, 0x00, 0x04, 0x06, 0x0e, 0x34, + 0x33, 0x39, 0x41, 0xae, 0x09, 0x00, 0x00, 0x02, + 0x39, 0x41, 0x2c, 0x00, 0x01, 0x02, 0x39, 0x41, + 0x80, 0xdf, 0x00, 0x01, 0x03, 0x1f, 0x1d, 0x50, + 0x00, 0x02, 0x1d, 0x50, 0x03, 0x00, 0x2c, 0x03, + 0x1d, 0x4f, 0x50, 0x02, 0x00, 0x08, 0x02, 0x1d, + 0x50, 0x81, 0x1f, 0x00, 0x1b, 0x02, 0x04, 0x1b, + 0x87, 0x75, 0x00, 0x00, 0x02, 0x58, 0x79, 0x87, + 0x8d, 0x00, 0x00, 0x02, 0x2d, 0x9b, 0x00, 0x00, + 0x00, 0x02, 0x2d, 0x9b, 0x36, 0x00, 0x01, 0x02, + 0x2d, 0x9b, 0x8c, 0x12, 0x00, 0x01, 0x02, 0x2d, + 0x9b, 0x00, 0x00, 0x00, 0x02, 0x2d, 0x9b, 0xc0, + 0x5c, 0x4b, 0x00, 0x03, 0x01, 0x24, 0x96, 0x3b, + 0x00, 0x11, 0x01, 0x33, 0x9e, 0x5d, 0x00, 0x01, + 0x01, 0x33, 0xce, 0xcd, 0x2d, 0x00, +}; + +static const uint8_t unicode_prop_Hyphen_table[28] = { + 0xac, 0x80, 0xfe, 0x80, 0x44, 0xdb, 0x80, 0x52, + 0x7a, 0x80, 0x48, 0x08, 0x81, 0x4e, 0x04, 0x80, + 0x42, 0xe2, 0x80, 0x60, 0xcd, 0x66, 0x80, 0x40, + 0xa8, 0x80, 0xd6, 0x80, +}; + +static const uint8_t unicode_prop_Other_Math_table[200] = { + 0xdd, 0x80, 0x43, 0x70, 0x11, 0x80, 0x99, 0x09, + 0x81, 0x5c, 0x1f, 0x80, 0x9a, 0x82, 0x8a, 0x80, + 0x9f, 0x83, 0x97, 0x81, 0x8d, 0x81, 0xc0, 0x8c, + 0x18, 0x11, 0x1c, 0x91, 0x03, 0x01, 0x89, 0x00, + 0x14, 0x28, 0x11, 0x09, 0x02, 0x05, 0x13, 0x24, + 0xca, 0x21, 0x18, 0x08, 0x08, 0x00, 0x21, 0x0b, + 0x0b, 0x91, 0x09, 0x00, 0x06, 0x00, 0x29, 0x41, + 0x21, 0x83, 0x40, 0xa7, 0x08, 0x80, 0x97, 0x80, + 0x90, 0x80, 0x41, 0xbc, 0x81, 0x8b, 0x88, 0x24, + 0x21, 0x09, 0x14, 0x8d, 0x00, 0x01, 0x85, 0x97, + 0x81, 0xb8, 0x00, 0x80, 0x9c, 0x83, 0x88, 0x81, + 0x41, 0x55, 0x81, 0x9e, 0x89, 0x41, 0x92, 0x95, + 0xbe, 0x83, 0x9f, 0x81, 0x60, 0xd4, 0x62, 0x00, + 0x03, 0x80, 0x40, 0xd2, 0x00, 0x80, 0x60, 0xd4, + 0xc0, 0xd4, 0x80, 0xc6, 0x01, 0x08, 0x09, 0x0b, + 0x80, 0x8b, 0x00, 0x06, 0x80, 0xc0, 0x03, 0x0f, + 0x06, 0x80, 0x9b, 0x03, 0x04, 0x00, 0x16, 0x80, + 0x41, 0x53, 0x81, 0x98, 0x80, 0x98, 0x80, 0x9e, + 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, + 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x07, 0x81, + 0xb1, 0x55, 0xff, 0x18, 0x9a, 0x01, 0x00, 0x08, + 0x80, 0x89, 0x03, 0x00, 0x00, 0x28, 0x18, 0x00, + 0x00, 0x02, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x0b, 0x06, 0x03, 0x03, 0x00, + 0x80, 0x89, 0x80, 0x90, 0x22, 0x04, 0x80, 0x90, +}; + +static const uint8_t unicode_prop_Other_Alphabetic_table[452] = { + 0x43, 0x44, 0x80, 0x9c, 0x8c, 0x42, 0x3f, 0x8d, + 0x00, 0x01, 0x01, 0x00, 0xc7, 0x8a, 0xaf, 0x8c, + 0x06, 0x8f, 0x80, 0xe4, 0x33, 0x19, 0x0b, 0x80, + 0xa2, 0x80, 0x9d, 0x8f, 0xe5, 0x8a, 0xe4, 0x0a, + 0x88, 0x02, 0x03, 0xe9, 0x80, 0xbb, 0x8b, 0x16, + 0x85, 0x93, 0xb5, 0x09, 0x8e, 0x01, 0x22, 0x89, + 0x81, 0x9c, 0x82, 0xb9, 0x31, 0x09, 0x81, 0x89, + 0x80, 0x89, 0x81, 0x9c, 0x82, 0xb9, 0x23, 0x09, + 0x0b, 0x80, 0x9d, 0x0a, 0x80, 0x8a, 0x82, 0xb9, + 0x38, 0x10, 0x81, 0x94, 0x81, 0x95, 0x13, 0x82, + 0xb9, 0x31, 0x09, 0x81, 0x88, 0x81, 0x89, 0x81, + 0x9d, 0x80, 0xba, 0x22, 0x10, 0x82, 0x89, 0x80, + 0xa7, 0x84, 0xb8, 0x30, 0x10, 0x17, 0x81, 0x8a, + 0x81, 0x9c, 0x82, 0xb9, 0x30, 0x10, 0x17, 0x81, + 0x8a, 0x81, 0x8e, 0x80, 0x8b, 0x83, 0xb9, 0x30, + 0x10, 0x82, 0x89, 0x80, 0x89, 0x81, 0x9c, 0x82, + 0xca, 0x28, 0x00, 0x87, 0x91, 0x81, 0xbc, 0x01, + 0x86, 0x91, 0x80, 0xe2, 0x01, 0x28, 0x81, 0x8f, + 0x80, 0x40, 0xa2, 0x92, 0x88, 0x8a, 0x80, 0xa3, + 0xed, 0x8b, 0x00, 0x0b, 0x96, 0x1b, 0x10, 0x11, + 0x32, 0x83, 0x8c, 0x8b, 0x00, 0x89, 0x83, 0x46, + 0x73, 0x81, 0x9d, 0x81, 0x9d, 0x81, 0x9d, 0x81, + 0xc1, 0x92, 0x40, 0xbb, 0x81, 0xa1, 0x80, 0xf5, + 0x8b, 0x83, 0x88, 0x40, 0xdd, 0x84, 0xb8, 0x89, + 0x81, 0x93, 0xc9, 0x81, 0x8a, 0x82, 0xb0, 0x84, + 0xaf, 0x8e, 0xbb, 0x82, 0x9d, 0x88, 0x09, 0xb8, + 0x8a, 0xb1, 0x92, 0x41, 0x9b, 0xa1, 0x46, 0xc0, + 0xb3, 0x48, 0xf5, 0x9f, 0x60, 0x78, 0x73, 0x87, + 0xa1, 0x81, 0x41, 0x61, 0x07, 0x80, 0x96, 0x84, + 0xd7, 0x81, 0xb1, 0x8f, 0x00, 0xb8, 0x80, 0xa5, + 0x84, 0x9b, 0x8b, 0xac, 0x83, 0xaf, 0x8b, 0xa4, + 0x80, 0xc2, 0x8d, 0x8b, 0x07, 0x81, 0xac, 0x82, + 0xb1, 0x00, 0x11, 0x0c, 0x80, 0xab, 0x24, 0x80, + 0x40, 0xec, 0x87, 0x60, 0x4f, 0x32, 0x80, 0x48, + 0x56, 0x84, 0x46, 0x85, 0x10, 0x0c, 0x83, 0x43, + 0x13, 0x83, 0xc0, 0x80, 0x41, 0x40, 0x81, 0xcc, + 0x82, 0x41, 0x02, 0x82, 0xb4, 0x8d, 0xac, 0x81, + 0x8a, 0x82, 0xac, 0x88, 0x88, 0x80, 0xbc, 0x82, + 0xa3, 0x8b, 0x91, 0x81, 0xb8, 0x82, 0xaf, 0x8c, + 0x8d, 0x81, 0xdb, 0x88, 0x08, 0x28, 0x08, 0x40, + 0x9c, 0x89, 0x96, 0x83, 0xb9, 0x31, 0x09, 0x81, + 0x89, 0x80, 0x89, 0x81, 0xd3, 0x88, 0x00, 0x08, + 0x03, 0x01, 0xe6, 0x8c, 0x02, 0xe9, 0x91, 0x40, + 0xec, 0x31, 0x86, 0x9c, 0x81, 0xd1, 0x8e, 0x00, + 0xe9, 0x8a, 0xe6, 0x8d, 0x41, 0x00, 0x8c, 0x40, + 0xf6, 0x28, 0x09, 0x0a, 0x00, 0x80, 0x40, 0x8d, + 0x31, 0x2b, 0x80, 0x9b, 0x89, 0xa9, 0x20, 0x83, + 0x91, 0x8a, 0xad, 0x8d, 0x40, 0xc7, 0x87, 0x40, + 0xc6, 0x38, 0x86, 0xd2, 0x95, 0x80, 0x8d, 0xf9, + 0x2a, 0x00, 0x08, 0x10, 0x02, 0x80, 0xc1, 0x20, + 0x08, 0x83, 0x41, 0x5b, 0x83, 0x88, 0x08, 0x80, + 0xaf, 0x32, 0x82, 0x60, 0x41, 0xdc, 0x90, 0x4e, + 0x1f, 0x00, 0xb6, 0x33, 0xdc, 0x81, 0x60, 0x4c, + 0xab, 0x80, 0x60, 0x23, 0x60, 0x30, 0x90, 0x0e, + 0x01, 0x04, 0xe3, 0x80, 0x46, 0x52, 0x01, 0x06, + 0x0c, 0x80, 0x42, 0x50, 0x80, 0x47, 0xe7, 0x99, + 0x85, 0x99, 0x85, 0x99, +}; + +static const uint8_t unicode_prop_Other_Lowercase_table[68] = { + 0x40, 0xa9, 0x80, 0x8e, 0x80, 0x41, 0xf4, 0x88, + 0x31, 0x9d, 0x84, 0xdf, 0x80, 0xb3, 0x80, 0x4d, + 0x80, 0x80, 0x4c, 0x2e, 0xbe, 0x8c, 0x80, 0xa1, + 0xa4, 0x42, 0xb0, 0x80, 0x8c, 0x80, 0x8f, 0x8c, + 0x40, 0xd2, 0x8f, 0x43, 0x4f, 0x99, 0x47, 0x91, + 0x81, 0x60, 0x7a, 0x1d, 0x81, 0x40, 0xd1, 0x80, + 0xff, 0x1a, 0x81, 0x43, 0x61, 0x83, 0x88, 0x80, + 0x60, 0x5c, 0x15, 0x01, 0x10, 0xa9, 0x80, 0x88, + 0x60, 0xd8, 0x74, 0xbd, +}; + +static const uint8_t unicode_prop_Other_Uppercase_table[15] = { + 0x60, 0x21, 0x5f, 0x8f, 0x43, 0x45, 0x99, 0x61, + 0xcc, 0x5f, 0x99, 0x85, 0x99, 0x85, 0x99, +}; + +static const uint8_t unicode_prop_Other_Grapheme_Extend_table[112] = { + 0x49, 0xbd, 0x80, 0x97, 0x80, 0x41, 0x65, 0x80, + 0x97, 0x80, 0xe5, 0x80, 0x97, 0x80, 0x40, 0xe7, + 0x00, 0x03, 0x08, 0x81, 0x88, 0x81, 0xe6, 0x80, + 0x97, 0x80, 0xf6, 0x80, 0x8e, 0x80, 0x49, 0x34, + 0x80, 0x9d, 0x80, 0x43, 0xff, 0x04, 0x00, 0x04, + 0x81, 0xe4, 0x80, 0xc6, 0x81, 0x44, 0x17, 0x80, + 0x50, 0x20, 0x81, 0x60, 0x79, 0x22, 0x80, 0xeb, + 0x80, 0x60, 0x55, 0xdc, 0x81, 0x52, 0x1f, 0x80, + 0xf3, 0x80, 0x41, 0x07, 0x80, 0x8d, 0x80, 0x88, + 0x80, 0xdf, 0x80, 0x88, 0x01, 0x00, 0x14, 0x80, + 0x40, 0xdf, 0x80, 0x8b, 0x80, 0x40, 0xf0, 0x80, + 0x41, 0x05, 0x80, 0x42, 0x78, 0x80, 0x8b, 0x80, + 0x46, 0x02, 0x80, 0x60, 0x50, 0xad, 0x81, 0x60, + 0x61, 0x72, 0x0d, 0x85, 0x6c, 0x2e, 0xac, 0xdf, +}; + +static const uint8_t unicode_prop_Other_Default_Ignorable_Code_Point_table[32] = { + 0x43, 0x4e, 0x80, 0x4e, 0x0e, 0x81, 0x46, 0x52, + 0x81, 0x48, 0xae, 0x80, 0x50, 0xfd, 0x80, 0x60, + 0xce, 0x3a, 0x80, 0xce, 0x88, 0x6d, 0x00, 0x06, + 0x00, 0x9d, 0xdf, 0xff, 0x40, 0xef, 0x4e, 0x0f, +}; + +static const uint8_t unicode_prop_Other_ID_Start_table[11] = { + 0x58, 0x84, 0x81, 0x48, 0x90, 0x80, 0x94, 0x80, + 0x4f, 0x6b, 0x81, +}; + +static const uint8_t unicode_prop_Other_ID_Continue_table[22] = { + 0x40, 0xb6, 0x80, 0x42, 0xce, 0x80, 0x4f, 0xe0, + 0x88, 0x46, 0x67, 0x80, 0x46, 0x30, 0x81, 0x50, + 0xec, 0x80, 0x60, 0xce, 0x68, 0x80, +}; + +static const uint8_t unicode_prop_Prepended_Concatenation_Mark_table[19] = { + 0x45, 0xff, 0x85, 0x40, 0xd6, 0x80, 0xb0, 0x80, + 0x41, 0x7f, 0x81, 0xcf, 0x80, 0x61, 0x07, 0xd9, + 0x80, 0x8e, 0x80, +}; + +static const uint8_t unicode_prop_XID_Start1_table[31] = { + 0x43, 0x79, 0x80, 0x4a, 0xb7, 0x80, 0xfe, 0x80, + 0x60, 0x21, 0xe6, 0x81, 0x60, 0xcb, 0xc0, 0x85, + 0x41, 0x95, 0x81, 0xf3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x41, 0x1e, 0x81, +}; + +static const uint8_t unicode_prop_XID_Continue1_table[23] = { + 0x43, 0x79, 0x80, 0x60, 0x2d, 0x1f, 0x81, 0x60, + 0xcb, 0xc0, 0x85, 0x41, 0x95, 0x81, 0xf3, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, +}; + +static const uint8_t unicode_prop_Changes_When_Titlecased1_table[22] = { + 0x41, 0xc3, 0x08, 0x08, 0x81, 0xa4, 0x81, 0x4e, + 0xdc, 0xaa, 0x0a, 0x4e, 0x87, 0x3f, 0x3f, 0x87, + 0x8b, 0x80, 0x8e, 0x80, 0xae, 0x80, +}; + +static const uint8_t unicode_prop_Changes_When_Casefolded1_table[29] = { + 0x41, 0xef, 0x80, 0x41, 0x9e, 0x80, 0x9e, 0x80, + 0x5a, 0xe4, 0x83, 0x40, 0xb5, 0x00, 0x00, 0x00, + 0x80, 0xde, 0x06, 0x06, 0x80, 0x8a, 0x09, 0x81, + 0x89, 0x10, 0x81, 0x8d, 0x80, +}; + +static const uint8_t unicode_prop_Changes_When_NFKC_Casefolded1_table[449] = { + 0x40, 0x9f, 0x06, 0x00, 0x01, 0x00, 0x01, 0x12, + 0x10, 0x82, 0xf3, 0x80, 0x8b, 0x80, 0x40, 0x84, + 0x01, 0x01, 0x80, 0xa2, 0x01, 0x80, 0x40, 0xbb, + 0x88, 0x9e, 0x29, 0x84, 0xda, 0x08, 0x81, 0x89, + 0x80, 0xa3, 0x04, 0x02, 0x04, 0x08, 0x07, 0x80, + 0x9e, 0x80, 0xa0, 0x82, 0x9c, 0x80, 0x42, 0x28, + 0x80, 0xd7, 0x83, 0x42, 0xde, 0x87, 0xfb, 0x08, + 0x80, 0xd2, 0x01, 0x80, 0xa1, 0x11, 0x80, 0x40, + 0xfc, 0x81, 0x42, 0xd4, 0x80, 0xfe, 0x80, 0xa7, + 0x81, 0xad, 0x80, 0xb5, 0x80, 0x88, 0x03, 0x03, + 0x03, 0x80, 0x8b, 0x80, 0x88, 0x00, 0x26, 0x80, + 0x90, 0x80, 0x88, 0x03, 0x03, 0x03, 0x80, 0x8b, + 0x80, 0x41, 0x41, 0x80, 0xe1, 0x81, 0x46, 0x52, + 0x81, 0xd4, 0x84, 0x45, 0x1b, 0x10, 0x8a, 0x80, + 0x91, 0x80, 0x9b, 0x8c, 0x80, 0xa1, 0xa4, 0x40, + 0xd5, 0x83, 0x40, 0xb5, 0x00, 0x00, 0x00, 0x80, + 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xb7, 0x05, 0x00, 0x13, 0x05, 0x11, 0x02, 0x0c, + 0x11, 0x00, 0x00, 0x0c, 0x15, 0x05, 0x08, 0x8f, + 0x00, 0x20, 0x8b, 0x12, 0x2a, 0x08, 0x0b, 0x00, + 0x07, 0x82, 0x8c, 0x06, 0x92, 0x81, 0x9a, 0x80, + 0x8c, 0x8a, 0x80, 0xd6, 0x18, 0x10, 0x8a, 0x01, + 0x0c, 0x0a, 0x00, 0x10, 0x11, 0x02, 0x06, 0x05, + 0x1c, 0x85, 0x8f, 0x8f, 0x8f, 0x88, 0x80, 0x40, + 0xa1, 0x08, 0x81, 0x40, 0xf7, 0x81, 0x41, 0x34, + 0xd5, 0x99, 0x9a, 0x45, 0x20, 0x80, 0xe6, 0x82, + 0xe4, 0x80, 0x41, 0x9e, 0x81, 0x40, 0xf0, 0x80, + 0x41, 0x2e, 0x80, 0xd2, 0x80, 0x8b, 0x40, 0xd5, + 0xa9, 0x80, 0xb4, 0x00, 0x82, 0xdf, 0x09, 0x80, + 0xde, 0x80, 0xb0, 0xdd, 0x82, 0x8d, 0xdf, 0x9e, + 0x80, 0xa7, 0x87, 0xae, 0x80, 0x41, 0x7f, 0x60, + 0x72, 0x9b, 0x81, 0x40, 0xd1, 0x80, 0xff, 0x1a, + 0x81, 0x43, 0x61, 0x83, 0x88, 0x80, 0x60, 0x4d, + 0x95, 0x41, 0x0d, 0x08, 0x00, 0x81, 0x89, 0x00, + 0x00, 0x09, 0x82, 0xc3, 0x81, 0xe9, 0xc2, 0x00, + 0x97, 0x04, 0x00, 0x01, 0x01, 0x80, 0xeb, 0xa0, + 0x41, 0x6a, 0x91, 0xbf, 0x81, 0xb5, 0xa7, 0x8c, + 0x82, 0x99, 0x95, 0x94, 0x81, 0x8b, 0x80, 0x92, + 0x03, 0x1a, 0x00, 0x80, 0x40, 0x86, 0x08, 0x80, + 0x9f, 0x99, 0x40, 0x83, 0x15, 0x0d, 0x0d, 0x0a, + 0x16, 0x06, 0x80, 0x88, 0x47, 0x87, 0x20, 0xa9, + 0x80, 0x88, 0x60, 0xb4, 0xe4, 0x83, 0x50, 0x31, + 0xa3, 0x44, 0x63, 0x86, 0x8d, 0x87, 0xbf, 0x85, + 0x42, 0x3e, 0xd4, 0x80, 0xc6, 0x01, 0x08, 0x09, + 0x0b, 0x80, 0x8b, 0x00, 0x06, 0x80, 0xc0, 0x03, + 0x0f, 0x06, 0x80, 0x9b, 0x03, 0x04, 0x00, 0x16, + 0x80, 0x41, 0x53, 0x81, 0x41, 0x23, 0x81, 0xb1, + 0x48, 0x2f, 0xbd, 0x4d, 0x91, 0x18, 0x9a, 0x01, + 0x00, 0x08, 0x80, 0x89, 0x03, 0x00, 0x00, 0x28, + 0x18, 0x00, 0x00, 0x02, 0x01, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x06, 0x03, + 0x03, 0x00, 0x80, 0x89, 0x80, 0x90, 0x22, 0x04, + 0x80, 0x90, 0x42, 0x43, 0x8a, 0x84, 0x9e, 0x80, + 0x9f, 0x99, 0x82, 0xa2, 0x80, 0xee, 0x82, 0x8c, + 0xab, 0x83, 0x88, 0x31, 0x49, 0x9d, 0x89, 0x60, + 0xfc, 0x05, 0x42, 0x1d, 0x6b, 0x05, 0xe1, 0x4f, + 0xff, +}; + +static const uint8_t unicode_prop_ASCII_Hex_Digit_table[5] = { + 0xaf, 0x89, 0x35, 0x99, 0x85, +}; + +static const uint8_t unicode_prop_Bidi_Control_table[10] = { + 0x46, 0x1b, 0x80, 0x59, 0xf0, 0x81, 0x99, 0x84, + 0xb6, 0x83, +}; + +static const uint8_t unicode_prop_Dash_table[58] = { + 0xac, 0x80, 0x45, 0x5b, 0x80, 0xb2, 0x80, 0x4e, + 0x40, 0x80, 0x44, 0x04, 0x80, 0x48, 0x08, 0x85, + 0xbc, 0x80, 0xa6, 0x80, 0x8e, 0x80, 0x41, 0x85, + 0x80, 0x4c, 0x03, 0x01, 0x80, 0x9e, 0x0b, 0x80, + 0x9b, 0x80, 0x41, 0xbd, 0x80, 0x92, 0x80, 0xee, + 0x80, 0x60, 0xcd, 0x8f, 0x81, 0xa4, 0x80, 0x89, + 0x80, 0x40, 0xa8, 0x80, 0x4e, 0x5f, 0x80, 0x41, + 0x3d, 0x80, +}; + +static const uint8_t unicode_prop_Deprecated_table[23] = { + 0x41, 0x48, 0x80, 0x45, 0x28, 0x80, 0x49, 0x02, + 0x00, 0x80, 0x48, 0x28, 0x81, 0x48, 0xc4, 0x85, + 0x42, 0xb8, 0x81, 0x6d, 0xdc, 0xd5, 0x80, +}; + +static const uint8_t unicode_prop_Diacritic_table[447] = { + 0xdd, 0x00, 0x80, 0xc6, 0x05, 0x03, 0x01, 0x81, + 0x41, 0xf6, 0x40, 0x9e, 0x07, 0x25, 0x90, 0x0b, + 0x80, 0x88, 0x81, 0x40, 0xfc, 0x84, 0x40, 0xd0, + 0x80, 0xb6, 0xac, 0x00, 0x01, 0x01, 0x00, 0x40, + 0x82, 0x3b, 0x81, 0x40, 0x85, 0x0b, 0x0a, 0x82, + 0xc2, 0x9a, 0xda, 0x8a, 0xb9, 0x8a, 0xa1, 0x81, + 0xfd, 0x87, 0xa8, 0x89, 0x8f, 0x9b, 0xbc, 0x80, + 0x8f, 0x02, 0x83, 0x9b, 0x80, 0xc9, 0x80, 0x8f, + 0x80, 0xed, 0x80, 0x8f, 0x80, 0xed, 0x80, 0x8f, + 0x80, 0xae, 0x82, 0xbb, 0x80, 0x8f, 0x06, 0x80, + 0xf6, 0x80, 0xed, 0x80, 0x8f, 0x80, 0xed, 0x80, + 0x8f, 0x80, 0xec, 0x81, 0x8f, 0x80, 0xfb, 0x80, + 0xee, 0x80, 0x8b, 0x28, 0x80, 0xea, 0x80, 0x8c, + 0x84, 0xca, 0x81, 0x9a, 0x00, 0x00, 0x03, 0x81, + 0xc1, 0x10, 0x81, 0xbd, 0x80, 0xef, 0x00, 0x81, + 0xa7, 0x0b, 0x84, 0x98, 0x30, 0x80, 0x89, 0x81, + 0x42, 0xc0, 0x82, 0x43, 0xb3, 0x81, 0x9d, 0x80, + 0x40, 0x93, 0x8a, 0x88, 0x80, 0x41, 0x5a, 0x82, + 0x41, 0x23, 0x80, 0x93, 0x39, 0x80, 0xaf, 0x8e, + 0x81, 0x8a, 0x82, 0x8e, 0x81, 0x8b, 0xc7, 0x80, + 0x8e, 0x80, 0xa5, 0x88, 0xb5, 0x81, 0xb9, 0x80, + 0x8a, 0x81, 0xc1, 0x81, 0xbf, 0x85, 0xd1, 0x98, + 0x18, 0x28, 0x0a, 0xb1, 0xbe, 0xaf, 0xa3, 0x84, + 0x8b, 0xa4, 0x8a, 0x41, 0xbc, 0x00, 0x82, 0x8a, + 0x82, 0x8c, 0x82, 0x8c, 0x82, 0x8c, 0x81, 0x4c, + 0xef, 0x82, 0x41, 0x3c, 0x80, 0x41, 0xf9, 0x85, + 0xe8, 0x83, 0xde, 0x80, 0x60, 0x75, 0x71, 0x80, + 0x8b, 0x08, 0x80, 0x9b, 0x81, 0xd1, 0x81, 0x8d, + 0xa1, 0xe5, 0x82, 0xe5, 0x05, 0x81, 0x8b, 0x80, + 0xa4, 0x80, 0x40, 0x96, 0x80, 0x9a, 0x91, 0xb8, + 0x83, 0xa3, 0x80, 0xde, 0x80, 0x8b, 0x80, 0xa3, + 0x80, 0x40, 0x94, 0x82, 0xc0, 0x83, 0xb2, 0x80, + 0xe3, 0x84, 0x88, 0x82, 0xff, 0x81, 0x60, 0x4f, + 0x2f, 0x80, 0x43, 0x00, 0x8f, 0x41, 0x0d, 0x00, + 0x80, 0xae, 0x80, 0xac, 0x81, 0xc2, 0x80, 0x42, + 0xfb, 0x80, 0x44, 0x9e, 0x28, 0xa9, 0x80, 0x88, + 0x42, 0x7c, 0x13, 0x80, 0x40, 0xa4, 0x81, 0x42, + 0x3a, 0x85, 0xa5, 0x80, 0x99, 0x84, 0x41, 0x8b, + 0x01, 0x82, 0xc5, 0x8a, 0xb0, 0x83, 0x40, 0xbf, + 0x80, 0xa8, 0x80, 0xc7, 0x81, 0xf7, 0x81, 0xbd, + 0x80, 0xcb, 0x80, 0x88, 0x82, 0xe7, 0x81, 0x40, + 0xb1, 0x81, 0xcf, 0x81, 0x8f, 0x80, 0x97, 0x32, + 0x84, 0xd8, 0x10, 0x81, 0x8c, 0x81, 0xde, 0x02, + 0x80, 0xfa, 0x81, 0x40, 0xfa, 0x81, 0xfd, 0x80, + 0xf5, 0x81, 0xf2, 0x80, 0x41, 0x0c, 0x81, 0x41, + 0x01, 0x0b, 0x80, 0x40, 0x9b, 0x80, 0xd2, 0x80, + 0x91, 0x80, 0xd0, 0x80, 0x41, 0xa4, 0x80, 0x41, + 0x01, 0x00, 0x81, 0xd0, 0x80, 0xc0, 0x80, 0x41, + 0x66, 0x81, 0x96, 0x80, 0x54, 0xeb, 0x8e, 0x60, + 0x2c, 0xd8, 0x80, 0x49, 0xbf, 0x84, 0xba, 0x86, + 0x42, 0x33, 0x81, 0x42, 0x21, 0x90, 0xcf, 0x81, + 0x60, 0x3f, 0xfd, 0x18, 0x30, 0x81, 0x5f, 0x00, + 0xad, 0x81, 0x96, 0x42, 0x1f, 0x12, 0x2f, 0x39, + 0x86, 0x9d, 0x83, 0x4e, 0x81, 0xbd, 0x40, 0xc1, + 0x86, 0x41, 0x76, 0x80, 0xbc, 0x83, 0x42, 0xfd, + 0x81, 0x42, 0xdf, 0x86, 0xec, 0x10, 0x82, +}; + +static const uint8_t unicode_prop_Extender_table[116] = { + 0x40, 0xb6, 0x80, 0x42, 0x17, 0x81, 0x43, 0x6d, + 0x80, 0x41, 0xb8, 0x80, 0x42, 0x75, 0x80, 0x40, + 0x88, 0x80, 0xd8, 0x80, 0x42, 0xef, 0x80, 0xfe, + 0x80, 0x49, 0x42, 0x80, 0xb7, 0x80, 0x42, 0x62, + 0x80, 0x41, 0x8d, 0x80, 0xc3, 0x80, 0x53, 0x88, + 0x80, 0xaa, 0x84, 0xe6, 0x81, 0xdc, 0x82, 0x60, + 0x6f, 0x15, 0x80, 0x45, 0xf5, 0x80, 0x43, 0xc1, + 0x80, 0x95, 0x80, 0x40, 0x88, 0x80, 0xeb, 0x80, + 0x94, 0x81, 0x60, 0x54, 0x7a, 0x80, 0x48, 0x0f, + 0x81, 0x45, 0xca, 0x80, 0x9a, 0x03, 0x80, 0x44, + 0xc6, 0x80, 0x41, 0x24, 0x80, 0xf3, 0x81, 0x41, + 0xf1, 0x82, 0x44, 0xce, 0x80, 0x43, 0x3f, 0x80, + 0x60, 0x4d, 0x67, 0x81, 0x44, 0x9b, 0x08, 0x80, + 0x8d, 0x81, 0x60, 0x71, 0x47, 0x81, 0x44, 0xb0, + 0x80, 0x43, 0x53, 0x82, +}; + +static const uint8_t unicode_prop_Hex_Digit_table[12] = { + 0xaf, 0x89, 0x35, 0x99, 0x85, 0x60, 0xfe, 0xa8, + 0x89, 0x35, 0x99, 0x85, +}; + +static const uint8_t unicode_prop_IDS_Unary_Operator_table[4] = { + 0x60, 0x2f, 0xfd, 0x81, +}; + +static const uint8_t unicode_prop_IDS_Binary_Operator_table[8] = { + 0x60, 0x2f, 0xef, 0x09, 0x89, 0x41, 0xf0, 0x80, +}; + +static const uint8_t unicode_prop_IDS_Trinary_Operator_table[4] = { + 0x60, 0x2f, 0xf1, 0x81, +}; + +static const uint8_t unicode_prop_Ideographic_table[71] = { + 0x60, 0x30, 0x05, 0x81, 0x98, 0x88, 0x8d, 0x82, + 0x43, 0xc4, 0x59, 0xbf, 0xbf, 0x60, 0x51, 0xff, + 0x60, 0x58, 0xff, 0x41, 0x6d, 0x81, 0xe9, 0x60, + 0x75, 0x09, 0x80, 0x8c, 0x84, 0x88, 0x5c, 0xd5, + 0xa8, 0x9f, 0xe0, 0xf2, 0x60, 0x23, 0x7c, 0x41, + 0x8b, 0x60, 0x4d, 0x03, 0x60, 0xa6, 0xdf, 0x9f, + 0x51, 0x1d, 0x81, 0x56, 0x8d, 0x81, 0x5d, 0x30, + 0x8e, 0x42, 0x6d, 0x49, 0xa1, 0x42, 0x1d, 0x45, + 0xe1, 0x53, 0x4a, 0x84, 0x60, 0x21, 0x29, +}; + +static const uint8_t unicode_prop_Join_Control_table[4] = { + 0x60, 0x20, 0x0b, 0x81, +}; + +static const uint8_t unicode_prop_Logical_Order_Exception_table[15] = { + 0x4e, 0x3f, 0x84, 0xfa, 0x84, 0x4a, 0xef, 0x11, + 0x80, 0x60, 0x90, 0xf9, 0x09, 0x00, 0x81, +}; + +static const uint8_t unicode_prop_Modifier_Combining_Mark_table[16] = { + 0x46, 0x53, 0x09, 0x80, 0x40, 0x82, 0x05, 0x02, + 0x81, 0x41, 0xe0, 0x08, 0x12, 0x80, 0x9e, 0x80, +}; + +static const uint8_t unicode_prop_Noncharacter_Code_Point_table[71] = { + 0x60, 0xfd, 0xcf, 0x9f, 0x42, 0x0d, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, +}; + +static const uint8_t unicode_prop_Pattern_Syntax_table[58] = { + 0xa0, 0x8e, 0x89, 0x86, 0x99, 0x18, 0x80, 0x99, + 0x83, 0xa1, 0x30, 0x00, 0x08, 0x00, 0x0b, 0x03, + 0x02, 0x80, 0x96, 0x80, 0x9e, 0x80, 0x5f, 0x17, + 0x97, 0x87, 0x8e, 0x81, 0x92, 0x80, 0x89, 0x41, + 0x30, 0x42, 0xcf, 0x40, 0x9f, 0x42, 0x75, 0x9d, + 0x44, 0x6b, 0x41, 0xff, 0xff, 0x41, 0x80, 0x13, + 0x98, 0x8e, 0x80, 0x60, 0xcd, 0x0c, 0x81, 0x41, + 0x04, 0x81, +}; + +static const uint8_t unicode_prop_Pattern_White_Space_table[11] = { + 0x88, 0x84, 0x91, 0x80, 0xe3, 0x80, 0x5f, 0x87, + 0x81, 0x97, 0x81, +}; + +static const uint8_t unicode_prop_Quotation_Mark_table[31] = { + 0xa1, 0x03, 0x80, 0x40, 0x82, 0x80, 0x8e, 0x80, + 0x5f, 0x5b, 0x87, 0x98, 0x81, 0x4e, 0x06, 0x80, + 0x41, 0xc8, 0x83, 0x8c, 0x82, 0x60, 0xce, 0x20, + 0x83, 0x40, 0xbc, 0x03, 0x80, 0xd9, 0x81, +}; + +static const uint8_t unicode_prop_Radical_table[9] = { + 0x60, 0x2e, 0x7f, 0x99, 0x80, 0xd8, 0x8b, 0x40, + 0xd5, +}; + +static const uint8_t unicode_prop_Regional_Indicator_table[4] = { + 0x61, 0xf1, 0xe5, 0x99, +}; + +static const uint8_t unicode_prop_Sentence_Terminal_table[213] = { + 0xa0, 0x80, 0x8b, 0x80, 0x8f, 0x80, 0x45, 0x48, + 0x80, 0x40, 0x92, 0x82, 0x40, 0xb3, 0x80, 0xaa, + 0x82, 0x40, 0xf5, 0x80, 0xbc, 0x00, 0x02, 0x81, + 0x41, 0x24, 0x81, 0x46, 0xe3, 0x81, 0x43, 0x15, + 0x03, 0x81, 0x43, 0x04, 0x80, 0x40, 0xc5, 0x81, + 0x40, 0x9c, 0x81, 0xac, 0x04, 0x80, 0x41, 0x39, + 0x81, 0x41, 0x61, 0x83, 0x40, 0xa1, 0x81, 0x89, + 0x09, 0x81, 0x9c, 0x82, 0x40, 0xba, 0x81, 0xc0, + 0x81, 0x43, 0xa3, 0x80, 0x96, 0x81, 0x88, 0x82, + 0x4c, 0xae, 0x82, 0x41, 0x31, 0x80, 0x8c, 0x80, + 0x95, 0x81, 0x41, 0xac, 0x80, 0x60, 0x74, 0xfb, + 0x80, 0x41, 0x0d, 0x81, 0x40, 0xe2, 0x02, 0x80, + 0x41, 0x7d, 0x81, 0xd5, 0x81, 0xde, 0x80, 0x40, + 0x97, 0x81, 0x40, 0x92, 0x82, 0x40, 0x8f, 0x81, + 0x40, 0xf8, 0x80, 0x60, 0x52, 0x25, 0x01, 0x81, + 0xba, 0x02, 0x81, 0x40, 0xa8, 0x80, 0x8b, 0x80, + 0x8f, 0x80, 0xc0, 0x80, 0x4a, 0xf3, 0x81, 0x44, + 0xfc, 0x84, 0xab, 0x83, 0x40, 0xbc, 0x81, 0xf4, + 0x83, 0xfe, 0x82, 0x40, 0x80, 0x0d, 0x80, 0x8f, + 0x81, 0xd7, 0x08, 0x81, 0xeb, 0x80, 0x41, 0x29, + 0x81, 0xf4, 0x81, 0x41, 0x74, 0x0c, 0x8e, 0xe8, + 0x81, 0x40, 0xf8, 0x82, 0x42, 0x04, 0x00, 0x80, + 0x40, 0xfa, 0x81, 0xd6, 0x81, 0x41, 0xa3, 0x81, + 0x42, 0xb3, 0x81, 0xc9, 0x81, 0x60, 0x4b, 0x28, + 0x81, 0x40, 0x84, 0x80, 0xc0, 0x81, 0x8a, 0x80, + 0x42, 0x28, 0x81, 0x41, 0x27, 0x80, 0x60, 0x4e, + 0x05, 0x80, 0x5d, 0xe7, 0x80, +}; + +static const uint8_t unicode_prop_Soft_Dotted_table[79] = { + 0xe8, 0x81, 0x40, 0xc3, 0x80, 0x41, 0x18, 0x80, + 0x9d, 0x80, 0xb3, 0x80, 0x93, 0x80, 0x41, 0x3f, + 0x80, 0xe1, 0x00, 0x80, 0x59, 0x08, 0x80, 0xb2, + 0x80, 0x8c, 0x02, 0x80, 0x40, 0x83, 0x80, 0x40, + 0x9c, 0x80, 0x41, 0xa4, 0x80, 0x40, 0xd5, 0x81, + 0x4b, 0x31, 0x80, 0x61, 0xa7, 0xa4, 0x81, 0xb1, + 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, + 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, + 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0x48, + 0x85, 0x80, 0x41, 0x30, 0x81, 0x99, 0x80, +}; + +static const uint8_t unicode_prop_Terminal_Punctuation_table[264] = { + 0xa0, 0x80, 0x89, 0x00, 0x80, 0x8a, 0x0a, 0x80, + 0x43, 0x3d, 0x07, 0x80, 0x42, 0x00, 0x80, 0xb8, + 0x80, 0xc7, 0x80, 0x8d, 0x00, 0x82, 0x40, 0xb3, + 0x80, 0xaa, 0x8a, 0x00, 0x40, 0xea, 0x81, 0xb5, + 0x28, 0x87, 0x9e, 0x80, 0x41, 0x04, 0x81, 0x44, + 0xf3, 0x81, 0x40, 0xab, 0x03, 0x85, 0x41, 0x36, + 0x81, 0x43, 0x14, 0x87, 0x43, 0x04, 0x80, 0xfb, + 0x82, 0xc6, 0x81, 0x40, 0x9c, 0x12, 0x80, 0xa6, + 0x19, 0x81, 0x41, 0x39, 0x81, 0x41, 0x61, 0x83, + 0x40, 0xa1, 0x81, 0x89, 0x08, 0x82, 0x9c, 0x82, + 0x40, 0xba, 0x84, 0xbd, 0x81, 0x43, 0xa3, 0x80, + 0x96, 0x81, 0x88, 0x82, 0x4c, 0xae, 0x82, 0x41, + 0x31, 0x80, 0x8c, 0x03, 0x80, 0x89, 0x00, 0x0a, + 0x81, 0x41, 0xab, 0x81, 0x60, 0x74, 0xfa, 0x81, + 0x41, 0x0c, 0x82, 0x40, 0xe2, 0x84, 0x41, 0x7d, + 0x81, 0xd5, 0x81, 0xde, 0x80, 0x40, 0x96, 0x82, + 0x40, 0x92, 0x82, 0xfe, 0x80, 0x8f, 0x81, 0x40, + 0xf8, 0x80, 0x60, 0x52, 0x25, 0x01, 0x81, 0xb8, + 0x10, 0x83, 0x40, 0xa8, 0x80, 0x89, 0x00, 0x80, + 0x8a, 0x0a, 0x80, 0xc0, 0x01, 0x80, 0x44, 0x39, + 0x80, 0xaf, 0x80, 0x44, 0x85, 0x80, 0x40, 0xc6, + 0x80, 0x41, 0x35, 0x81, 0x40, 0x97, 0x85, 0xc3, + 0x85, 0xd8, 0x83, 0x43, 0xb7, 0x84, 0xab, 0x83, + 0x40, 0xbc, 0x86, 0xef, 0x83, 0xfe, 0x82, 0x40, + 0x80, 0x0d, 0x80, 0x8f, 0x81, 0xd7, 0x84, 0xeb, + 0x80, 0x41, 0x29, 0x81, 0xf4, 0x82, 0x8b, 0x81, + 0x41, 0x65, 0x1a, 0x8e, 0xe8, 0x81, 0x40, 0xf8, + 0x82, 0x42, 0x04, 0x00, 0x80, 0x40, 0xfa, 0x81, + 0xd6, 0x0b, 0x81, 0x41, 0x9d, 0x82, 0xac, 0x80, + 0x42, 0x84, 0x81, 0xc9, 0x81, 0x45, 0x2a, 0x84, + 0x60, 0x45, 0xf8, 0x81, 0x40, 0x84, 0x80, 0xc0, + 0x82, 0x89, 0x80, 0x42, 0x28, 0x81, 0x41, 0x26, + 0x81, 0x60, 0x4e, 0x05, 0x80, 0x5d, 0xe6, 0x83, +}; + +static const uint8_t unicode_prop_Unified_Ideograph_table[46] = { + 0x60, 0x33, 0xff, 0x59, 0xbf, 0xbf, 0x60, 0x51, + 0xff, 0x60, 0x5a, 0x0d, 0x08, 0x00, 0x81, 0x89, + 0x00, 0x00, 0x09, 0x82, 0x61, 0x05, 0xd5, 0x60, + 0xa6, 0xdf, 0x9f, 0x51, 0x1d, 0x81, 0x56, 0x8d, + 0x81, 0x5d, 0x30, 0x8e, 0x42, 0x6d, 0x51, 0xa1, + 0x53, 0x4a, 0x84, 0x60, 0x21, 0x29, +}; + +static const uint8_t unicode_prop_Variation_Selector_table[13] = { + 0x58, 0x0a, 0x10, 0x80, 0x60, 0xe5, 0xef, 0x8f, + 0x6d, 0x02, 0xef, 0x40, 0xef, +}; + +static const uint8_t unicode_prop_Bidi_Mirrored_table[173] = { + 0xa7, 0x81, 0x91, 0x00, 0x80, 0x9b, 0x00, 0x80, + 0x9c, 0x00, 0x80, 0xac, 0x80, 0x8e, 0x80, 0x4e, + 0x7d, 0x83, 0x47, 0x5c, 0x81, 0x49, 0x9b, 0x81, + 0x89, 0x81, 0xb5, 0x81, 0x8d, 0x81, 0x40, 0xb0, + 0x80, 0x40, 0xbf, 0x1a, 0x2a, 0x02, 0x0a, 0x18, + 0x18, 0x00, 0x03, 0x88, 0x20, 0x80, 0x91, 0x23, + 0x88, 0x08, 0x00, 0x38, 0x9f, 0x0b, 0x20, 0x88, + 0x09, 0x92, 0x21, 0x88, 0x21, 0x0b, 0x97, 0x81, + 0x8f, 0x3b, 0x93, 0x0e, 0x81, 0x44, 0x3c, 0x8d, + 0xc9, 0x01, 0x18, 0x08, 0x14, 0x1c, 0x12, 0x8d, + 0x41, 0x92, 0x95, 0x0d, 0x80, 0x8d, 0x38, 0x35, + 0x10, 0x1c, 0x01, 0x0c, 0x18, 0x02, 0x09, 0x89, + 0x29, 0x81, 0x8b, 0x92, 0x03, 0x08, 0x00, 0x08, + 0x03, 0x21, 0x2a, 0x97, 0x81, 0x8a, 0x0b, 0x18, + 0x09, 0x0b, 0xaa, 0x0f, 0x80, 0xa7, 0x20, 0x00, + 0x14, 0x22, 0x18, 0x14, 0x00, 0x40, 0xff, 0x80, + 0x42, 0x02, 0x1a, 0x08, 0x81, 0x8d, 0x09, 0x89, + 0xaa, 0x87, 0x41, 0xaa, 0x89, 0x0f, 0x60, 0xce, + 0x3c, 0x2c, 0x81, 0x40, 0xa1, 0x81, 0x91, 0x00, + 0x80, 0x9b, 0x00, 0x80, 0x9c, 0x00, 0x00, 0x08, + 0x81, 0x60, 0xd7, 0x76, 0x80, 0xb8, 0x80, 0xb8, + 0x80, 0xb8, 0x80, 0xb8, 0x80, +}; + +static const uint8_t unicode_prop_Emoji_table[239] = { + 0xa2, 0x05, 0x04, 0x89, 0xee, 0x03, 0x80, 0x5f, + 0x8c, 0x80, 0x8b, 0x80, 0x40, 0xd7, 0x80, 0x95, + 0x80, 0xd9, 0x85, 0x8e, 0x81, 0x41, 0x6e, 0x81, + 0x8b, 0x80, 0x40, 0xa5, 0x80, 0x98, 0x8a, 0x1a, + 0x40, 0xc6, 0x80, 0x40, 0xe6, 0x81, 0x89, 0x80, + 0x88, 0x80, 0xb9, 0x18, 0x84, 0x88, 0x01, 0x01, + 0x09, 0x03, 0x01, 0x00, 0x09, 0x02, 0x02, 0x0f, + 0x14, 0x00, 0x04, 0x8b, 0x8a, 0x09, 0x00, 0x08, + 0x80, 0x91, 0x01, 0x81, 0x91, 0x28, 0x00, 0x0a, + 0x0c, 0x01, 0x0b, 0x81, 0x8a, 0x0c, 0x09, 0x04, + 0x08, 0x00, 0x81, 0x93, 0x0c, 0x28, 0x19, 0x03, + 0x01, 0x01, 0x28, 0x01, 0x00, 0x00, 0x05, 0x02, + 0x05, 0x80, 0x89, 0x81, 0x8e, 0x01, 0x03, 0x00, + 0x03, 0x10, 0x80, 0x8a, 0x81, 0xaf, 0x82, 0x88, + 0x80, 0x8d, 0x80, 0x8d, 0x80, 0x41, 0x73, 0x81, + 0x41, 0xce, 0x82, 0x92, 0x81, 0xb2, 0x03, 0x80, + 0x44, 0xd9, 0x80, 0x8b, 0x80, 0x42, 0x58, 0x00, + 0x80, 0x61, 0xbd, 0x69, 0x80, 0x40, 0xc9, 0x80, + 0x40, 0x9f, 0x81, 0x8b, 0x81, 0x8d, 0x01, 0x89, + 0xca, 0x99, 0x01, 0x96, 0x80, 0x93, 0x01, 0x88, + 0x94, 0x81, 0x40, 0xad, 0xa1, 0x81, 0xef, 0x09, + 0x02, 0x81, 0xd2, 0x0a, 0x80, 0x41, 0x06, 0x80, + 0xbe, 0x8a, 0x28, 0x97, 0x31, 0x0f, 0x8b, 0x01, + 0x19, 0x03, 0x81, 0x8c, 0x09, 0x07, 0x81, 0x88, + 0x04, 0x82, 0x8b, 0x17, 0x11, 0x00, 0x03, 0x05, + 0x02, 0x05, 0xd5, 0xaf, 0xc5, 0x27, 0x0b, 0x82, + 0x89, 0x10, 0x01, 0x10, 0x81, 0x89, 0x40, 0xe2, + 0x8b, 0x18, 0x41, 0x1a, 0xae, 0x80, 0x89, 0x80, + 0x40, 0xb8, 0xef, 0x8c, 0x82, 0x8a, 0x82, 0xb8, + 0x00, 0x83, 0x8f, 0x81, 0x8b, 0x83, 0x89, +}; + +static const uint8_t unicode_prop_Emoji_Component_table[28] = { + 0xa2, 0x05, 0x04, 0x89, 0x5f, 0xd2, 0x80, 0x40, + 0xd4, 0x80, 0x60, 0xdd, 0x2a, 0x80, 0x60, 0xf3, + 0xd5, 0x99, 0x41, 0xfa, 0x84, 0x45, 0xaf, 0x83, + 0x6c, 0x06, 0x6b, 0xdf, +}; + +static const uint8_t unicode_prop_Emoji_Modifier_table[4] = { + 0x61, 0xf3, 0xfa, 0x84, +}; + +static const uint8_t unicode_prop_Emoji_Modifier_Base_table[71] = { + 0x60, 0x26, 0x1c, 0x80, 0x40, 0xda, 0x80, 0x8f, + 0x83, 0x61, 0xcc, 0x76, 0x80, 0xbb, 0x11, 0x01, + 0x82, 0xf4, 0x09, 0x8a, 0x94, 0x92, 0x10, 0x1a, + 0x02, 0x30, 0x00, 0x97, 0x80, 0x40, 0xc8, 0x0b, + 0x80, 0x94, 0x03, 0x81, 0x40, 0xad, 0x12, 0x84, + 0xd2, 0x80, 0x8f, 0x82, 0x88, 0x80, 0x8a, 0x80, + 0x42, 0x3e, 0x01, 0x07, 0x3d, 0x80, 0x88, 0x89, + 0x0a, 0xb7, 0x80, 0xbc, 0x08, 0x08, 0x80, 0x90, + 0x10, 0x8c, 0x40, 0xe4, 0x82, 0xa9, 0x88, +}; + +static const uint8_t unicode_prop_Emoji_Presentation_table[145] = { + 0x60, 0x23, 0x19, 0x81, 0x40, 0xcc, 0x1a, 0x01, + 0x80, 0x42, 0x08, 0x81, 0x94, 0x81, 0xb1, 0x8b, + 0xaa, 0x80, 0x92, 0x80, 0x8c, 0x07, 0x81, 0x90, + 0x0c, 0x0f, 0x04, 0x80, 0x94, 0x06, 0x08, 0x03, + 0x01, 0x06, 0x03, 0x81, 0x9b, 0x80, 0xa2, 0x00, + 0x03, 0x10, 0x80, 0xbc, 0x82, 0x97, 0x80, 0x8d, + 0x80, 0x43, 0x5a, 0x81, 0xb2, 0x03, 0x80, 0x61, + 0xc4, 0xad, 0x80, 0x40, 0xc9, 0x80, 0x40, 0xbd, + 0x01, 0x89, 0xca, 0x99, 0x00, 0x97, 0x80, 0x93, + 0x01, 0x20, 0x82, 0x94, 0x81, 0x40, 0xad, 0xa0, + 0x8b, 0x88, 0x80, 0xc5, 0x80, 0x95, 0x8b, 0xaa, + 0x1c, 0x8b, 0x90, 0x10, 0x82, 0xc6, 0x00, 0x80, + 0x40, 0xba, 0x81, 0xbe, 0x8c, 0x18, 0x97, 0x91, + 0x80, 0x99, 0x81, 0x8c, 0x80, 0xd5, 0xd4, 0xaf, + 0xc5, 0x28, 0x12, 0x0b, 0x13, 0x8a, 0x0e, 0x88, + 0x40, 0xe2, 0x8b, 0x18, 0x41, 0x1a, 0xae, 0x80, + 0x89, 0x80, 0x40, 0xb8, 0xef, 0x8c, 0x82, 0x8a, + 0x82, 0xb8, 0x00, 0x83, 0x8f, 0x81, 0x8b, 0x83, + 0x89, +}; + +static const uint8_t unicode_prop_Extended_Pictographic_table[254] = { + 0x40, 0xa8, 0x03, 0x80, 0x5f, 0x8c, 0x80, 0x8b, + 0x80, 0x40, 0xd7, 0x80, 0x95, 0x80, 0xd9, 0x85, + 0x8e, 0x81, 0x41, 0x6e, 0x81, 0x8b, 0x80, 0x40, + 0xa5, 0x80, 0x98, 0x8a, 0x1a, 0x40, 0xc6, 0x80, + 0x40, 0xe6, 0x81, 0x89, 0x80, 0x88, 0x80, 0xb9, + 0x18, 0x84, 0x88, 0x01, 0x01, 0x09, 0x03, 0x01, + 0x00, 0x09, 0x02, 0x02, 0x0f, 0x14, 0x00, 0x04, + 0x8b, 0x8a, 0x09, 0x00, 0x08, 0x80, 0x91, 0x01, + 0x81, 0x91, 0x28, 0x00, 0x0a, 0x0c, 0x01, 0x0b, + 0x81, 0x8a, 0x0c, 0x09, 0x04, 0x08, 0x00, 0x81, + 0x93, 0x0c, 0x28, 0x19, 0x03, 0x01, 0x01, 0x28, + 0x01, 0x00, 0x00, 0x05, 0x02, 0x05, 0x80, 0x89, + 0x81, 0x8e, 0x01, 0x03, 0x00, 0x03, 0x10, 0x80, + 0x8a, 0x81, 0xaf, 0x82, 0x88, 0x80, 0x8d, 0x80, + 0x8d, 0x80, 0x41, 0x73, 0x81, 0x41, 0xce, 0x82, + 0x92, 0x81, 0xb2, 0x03, 0x80, 0x44, 0xd9, 0x80, + 0x8b, 0x80, 0x42, 0x58, 0x00, 0x80, 0x61, 0xbd, + 0x69, 0x80, 0xa6, 0x83, 0xe3, 0x8b, 0x8e, 0x81, + 0x8e, 0x80, 0x8d, 0x81, 0xa4, 0x89, 0xef, 0x81, + 0x8b, 0x81, 0x8d, 0x01, 0x89, 0x92, 0xb7, 0x9a, + 0x8e, 0x89, 0x80, 0x93, 0x01, 0x88, 0x03, 0x88, + 0x96, 0x85, 0x40, 0xbb, 0x81, 0xef, 0x09, 0x02, + 0x81, 0xd2, 0x0a, 0x03, 0x84, 0x40, 0xfd, 0x80, + 0xbe, 0x8a, 0x28, 0x97, 0x31, 0x0f, 0x8b, 0x01, + 0x19, 0x03, 0x81, 0x8c, 0x09, 0x07, 0x81, 0x88, + 0x04, 0x82, 0x8b, 0x17, 0x11, 0x00, 0x03, 0x05, + 0x02, 0x05, 0xd5, 0xaf, 0xc5, 0x27, 0x81, 0x90, + 0x10, 0x05, 0x81, 0x8c, 0x40, 0xd9, 0xa5, 0x8b, + 0x83, 0xb7, 0x87, 0x89, 0x85, 0xa7, 0x87, 0x9d, + 0x81, 0x8b, 0x19, 0x8d, 0x88, 0xa6, 0x8b, 0xae, + 0x80, 0x89, 0x80, 0x40, 0xb8, 0xd7, 0x87, 0x8d, + 0x40, 0x91, 0x40, 0xff, 0x43, 0xfd, +}; + +static const uint8_t unicode_prop_Default_Ignorable_Code_Point_table[51] = { + 0x40, 0xac, 0x80, 0x42, 0xa0, 0x80, 0x42, 0xcb, + 0x80, 0x4b, 0x41, 0x81, 0x46, 0x52, 0x81, 0xd4, + 0x84, 0x47, 0xfa, 0x84, 0x99, 0x84, 0xb0, 0x8f, + 0x50, 0xf3, 0x80, 0x60, 0xcc, 0x9a, 0x8f, 0x40, + 0xee, 0x80, 0x40, 0x9f, 0x80, 0xce, 0x88, 0x60, + 0xbc, 0xa6, 0x83, 0x54, 0xce, 0x87, 0x6c, 0x2e, + 0x84, 0x4f, 0xff, +}; + +typedef enum { + UNICODE_PROP_Hyphen, + UNICODE_PROP_Other_Math, + UNICODE_PROP_Other_Alphabetic, + UNICODE_PROP_Other_Lowercase, + UNICODE_PROP_Other_Uppercase, + UNICODE_PROP_Other_Grapheme_Extend, + UNICODE_PROP_Other_Default_Ignorable_Code_Point, + UNICODE_PROP_Other_ID_Start, + UNICODE_PROP_Other_ID_Continue, + UNICODE_PROP_Prepended_Concatenation_Mark, + UNICODE_PROP_ID_Continue1, + UNICODE_PROP_XID_Start1, + UNICODE_PROP_XID_Continue1, + UNICODE_PROP_Changes_When_Titlecased1, + UNICODE_PROP_Changes_When_Casefolded1, + UNICODE_PROP_Changes_When_NFKC_Casefolded1, + UNICODE_PROP_ASCII_Hex_Digit, + UNICODE_PROP_Bidi_Control, + UNICODE_PROP_Dash, + UNICODE_PROP_Deprecated, + UNICODE_PROP_Diacritic, + UNICODE_PROP_Extender, + UNICODE_PROP_Hex_Digit, + UNICODE_PROP_IDS_Unary_Operator, + UNICODE_PROP_IDS_Binary_Operator, + UNICODE_PROP_IDS_Trinary_Operator, + UNICODE_PROP_Ideographic, + UNICODE_PROP_Join_Control, + UNICODE_PROP_Logical_Order_Exception, + UNICODE_PROP_Modifier_Combining_Mark, + UNICODE_PROP_Noncharacter_Code_Point, + UNICODE_PROP_Pattern_Syntax, + UNICODE_PROP_Pattern_White_Space, + UNICODE_PROP_Quotation_Mark, + UNICODE_PROP_Radical, + UNICODE_PROP_Regional_Indicator, + UNICODE_PROP_Sentence_Terminal, + UNICODE_PROP_Soft_Dotted, + UNICODE_PROP_Terminal_Punctuation, + UNICODE_PROP_Unified_Ideograph, + UNICODE_PROP_Variation_Selector, + UNICODE_PROP_White_Space, + UNICODE_PROP_Bidi_Mirrored, + UNICODE_PROP_Emoji, + UNICODE_PROP_Emoji_Component, + UNICODE_PROP_Emoji_Modifier, + UNICODE_PROP_Emoji_Modifier_Base, + UNICODE_PROP_Emoji_Presentation, + UNICODE_PROP_Extended_Pictographic, + UNICODE_PROP_Default_Ignorable_Code_Point, + UNICODE_PROP_ID_Start, + UNICODE_PROP_Case_Ignorable, + UNICODE_PROP_ASCII, + UNICODE_PROP_Alphabetic, + UNICODE_PROP_Any, + UNICODE_PROP_Assigned, + UNICODE_PROP_Cased, + UNICODE_PROP_Changes_When_Casefolded, + UNICODE_PROP_Changes_When_Casemapped, + UNICODE_PROP_Changes_When_Lowercased, + UNICODE_PROP_Changes_When_NFKC_Casefolded, + UNICODE_PROP_Changes_When_Titlecased, + UNICODE_PROP_Changes_When_Uppercased, + UNICODE_PROP_Grapheme_Base, + UNICODE_PROP_Grapheme_Extend, + UNICODE_PROP_ID_Continue, + UNICODE_PROP_ID_Compat_Math_Start, + UNICODE_PROP_ID_Compat_Math_Continue, + UNICODE_PROP_Lowercase, + UNICODE_PROP_Math, + UNICODE_PROP_Uppercase, + UNICODE_PROP_XID_Continue, + UNICODE_PROP_XID_Start, + UNICODE_PROP_Cased1, + UNICODE_PROP_InCB, + UNICODE_PROP_COUNT, +} UnicodePropertyEnum; + +static const char unicode_prop_name_table[] = + "ASCII_Hex_Digit,AHex" "\0" + "Bidi_Control,Bidi_C" "\0" + "Dash" "\0" + "Deprecated,Dep" "\0" + "Diacritic,Dia" "\0" + "Extender,Ext" "\0" + "Hex_Digit,Hex" "\0" + "IDS_Unary_Operator,IDSU" "\0" + "IDS_Binary_Operator,IDSB" "\0" + "IDS_Trinary_Operator,IDST" "\0" + "Ideographic,Ideo" "\0" + "Join_Control,Join_C" "\0" + "Logical_Order_Exception,LOE" "\0" + "Modifier_Combining_Mark,MCM" "\0" + "Noncharacter_Code_Point,NChar" "\0" + "Pattern_Syntax,Pat_Syn" "\0" + "Pattern_White_Space,Pat_WS" "\0" + "Quotation_Mark,QMark" "\0" + "Radical" "\0" + "Regional_Indicator,RI" "\0" + "Sentence_Terminal,STerm" "\0" + "Soft_Dotted,SD" "\0" + "Terminal_Punctuation,Term" "\0" + "Unified_Ideograph,UIdeo" "\0" + "Variation_Selector,VS" "\0" + "White_Space,space" "\0" + "Bidi_Mirrored,Bidi_M" "\0" + "Emoji" "\0" + "Emoji_Component,EComp" "\0" + "Emoji_Modifier,EMod" "\0" + "Emoji_Modifier_Base,EBase" "\0" + "Emoji_Presentation,EPres" "\0" + "Extended_Pictographic,ExtPict" "\0" + "Default_Ignorable_Code_Point,DI" "\0" + "ID_Start,IDS" "\0" + "Case_Ignorable,CI" "\0" + "ASCII" "\0" + "Alphabetic,Alpha" "\0" + "Any" "\0" + "Assigned" "\0" + "Cased" "\0" + "Changes_When_Casefolded,CWCF" "\0" + "Changes_When_Casemapped,CWCM" "\0" + "Changes_When_Lowercased,CWL" "\0" + "Changes_When_NFKC_Casefolded,CWKCF" "\0" + "Changes_When_Titlecased,CWT" "\0" + "Changes_When_Uppercased,CWU" "\0" + "Grapheme_Base,Gr_Base" "\0" + "Grapheme_Extend,Gr_Ext" "\0" + "ID_Continue,IDC" "\0" + "ID_Compat_Math_Start" "\0" + "ID_Compat_Math_Continue" "\0" + "Lowercase,Lower" "\0" + "Math" "\0" + "Uppercase,Upper" "\0" + "XID_Continue,XIDC" "\0" + "XID_Start,XIDS" "\0" +; + +static const uint8_t * const unicode_prop_table[] = { + unicode_prop_Hyphen_table, + unicode_prop_Other_Math_table, + unicode_prop_Other_Alphabetic_table, + unicode_prop_Other_Lowercase_table, + unicode_prop_Other_Uppercase_table, + unicode_prop_Other_Grapheme_Extend_table, + unicode_prop_Other_Default_Ignorable_Code_Point_table, + unicode_prop_Other_ID_Start_table, + unicode_prop_Other_ID_Continue_table, + unicode_prop_Prepended_Concatenation_Mark_table, + unicode_prop_ID_Continue1_table, + unicode_prop_XID_Start1_table, + unicode_prop_XID_Continue1_table, + unicode_prop_Changes_When_Titlecased1_table, + unicode_prop_Changes_When_Casefolded1_table, + unicode_prop_Changes_When_NFKC_Casefolded1_table, + unicode_prop_ASCII_Hex_Digit_table, + unicode_prop_Bidi_Control_table, + unicode_prop_Dash_table, + unicode_prop_Deprecated_table, + unicode_prop_Diacritic_table, + unicode_prop_Extender_table, + unicode_prop_Hex_Digit_table, + unicode_prop_IDS_Unary_Operator_table, + unicode_prop_IDS_Binary_Operator_table, + unicode_prop_IDS_Trinary_Operator_table, + unicode_prop_Ideographic_table, + unicode_prop_Join_Control_table, + unicode_prop_Logical_Order_Exception_table, + unicode_prop_Modifier_Combining_Mark_table, + unicode_prop_Noncharacter_Code_Point_table, + unicode_prop_Pattern_Syntax_table, + unicode_prop_Pattern_White_Space_table, + unicode_prop_Quotation_Mark_table, + unicode_prop_Radical_table, + unicode_prop_Regional_Indicator_table, + unicode_prop_Sentence_Terminal_table, + unicode_prop_Soft_Dotted_table, + unicode_prop_Terminal_Punctuation_table, + unicode_prop_Unified_Ideograph_table, + unicode_prop_Variation_Selector_table, + unicode_prop_White_Space_table, + unicode_prop_Bidi_Mirrored_table, + unicode_prop_Emoji_table, + unicode_prop_Emoji_Component_table, + unicode_prop_Emoji_Modifier_table, + unicode_prop_Emoji_Modifier_Base_table, + unicode_prop_Emoji_Presentation_table, + unicode_prop_Extended_Pictographic_table, + unicode_prop_Default_Ignorable_Code_Point_table, + unicode_prop_ID_Start_table, + unicode_prop_Case_Ignorable_table, +}; + +static const uint16_t unicode_prop_len_table[] = { + countof(unicode_prop_Hyphen_table), + countof(unicode_prop_Other_Math_table), + countof(unicode_prop_Other_Alphabetic_table), + countof(unicode_prop_Other_Lowercase_table), + countof(unicode_prop_Other_Uppercase_table), + countof(unicode_prop_Other_Grapheme_Extend_table), + countof(unicode_prop_Other_Default_Ignorable_Code_Point_table), + countof(unicode_prop_Other_ID_Start_table), + countof(unicode_prop_Other_ID_Continue_table), + countof(unicode_prop_Prepended_Concatenation_Mark_table), + countof(unicode_prop_ID_Continue1_table), + countof(unicode_prop_XID_Start1_table), + countof(unicode_prop_XID_Continue1_table), + countof(unicode_prop_Changes_When_Titlecased1_table), + countof(unicode_prop_Changes_When_Casefolded1_table), + countof(unicode_prop_Changes_When_NFKC_Casefolded1_table), + countof(unicode_prop_ASCII_Hex_Digit_table), + countof(unicode_prop_Bidi_Control_table), + countof(unicode_prop_Dash_table), + countof(unicode_prop_Deprecated_table), + countof(unicode_prop_Diacritic_table), + countof(unicode_prop_Extender_table), + countof(unicode_prop_Hex_Digit_table), + countof(unicode_prop_IDS_Unary_Operator_table), + countof(unicode_prop_IDS_Binary_Operator_table), + countof(unicode_prop_IDS_Trinary_Operator_table), + countof(unicode_prop_Ideographic_table), + countof(unicode_prop_Join_Control_table), + countof(unicode_prop_Logical_Order_Exception_table), + countof(unicode_prop_Modifier_Combining_Mark_table), + countof(unicode_prop_Noncharacter_Code_Point_table), + countof(unicode_prop_Pattern_Syntax_table), + countof(unicode_prop_Pattern_White_Space_table), + countof(unicode_prop_Quotation_Mark_table), + countof(unicode_prop_Radical_table), + countof(unicode_prop_Regional_Indicator_table), + countof(unicode_prop_Sentence_Terminal_table), + countof(unicode_prop_Soft_Dotted_table), + countof(unicode_prop_Terminal_Punctuation_table), + countof(unicode_prop_Unified_Ideograph_table), + countof(unicode_prop_Variation_Selector_table), + countof(unicode_prop_White_Space_table), + countof(unicode_prop_Bidi_Mirrored_table), + countof(unicode_prop_Emoji_table), + countof(unicode_prop_Emoji_Component_table), + countof(unicode_prop_Emoji_Modifier_table), + countof(unicode_prop_Emoji_Modifier_Base_table), + countof(unicode_prop_Emoji_Presentation_table), + countof(unicode_prop_Extended_Pictographic_table), + countof(unicode_prop_Default_Ignorable_Code_Point_table), + countof(unicode_prop_ID_Start_table), + countof(unicode_prop_Case_Ignorable_table), +}; + +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2024 Fabrice Bellard + * Copyright (c) 2017-2024 Charlie Gordon + * Copyright (c) 2023-2025 Ben Noordhuis + * Copyright (c) 2023-2025 Saúl Ibarra Corretgé + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef QUICKJS_H +#define QUICKJS_H + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define QUICKJS_NG 1 + +/* Helpers. */ +#if defined(_WIN32) || defined(__CYGWIN__) +# define QUICKJS_NG_PLAT_WIN32 1 +#endif /* defined(_WIN32) || defined(__CYGWIN__) */ + +#if defined(__GNUC__) || defined(__clang__) +# define QUICKJS_NG_CC_GNULIKE 1 +#endif /* defined(__GNUC__) || defined(__clang__) */ + +/* + * `JS_EXTERN` -- helper macro that must be used to mark the external + * interfaces of libqjs. + * + * Define BUILDING_QJS_SHARED when building and USING_QJS_SHARED when using + * shared libqjs. + * + * Windows note: The `__declspec` syntax is supported by both Clang and GCC. + * If building qjs, the BUILDING_QJS_SHARED macro must be defined for libqjs + * (and only for it) to properly export symbols. + */ +#ifdef QUICKJS_NG_PLAT_WIN32 +# if defined(BUILDING_QJS_SHARED) +# define JS_EXTERN __declspec(dllexport) +# elif defined(USING_QJS_SHARED) +# define JS_EXTERN __declspec(dllimport) +# else +# define JS_EXTERN /* nothing */ +# endif +#else +# ifdef QUICKJS_NG_CC_GNULIKE +# define JS_EXTERN __attribute__((visibility("default"))) +# else +# define JS_EXTERN /* nothing */ +# endif +#endif /* QUICKJS_NG_PLAT_WIN32 */ + +/* + * `JS_LIBC_EXTERN` -- helper macro that must be used to mark the extern + * interfaces of quickjs-libc specifically. + */ +#if defined(QUICKJS_NG_BUILD) && !defined(QJS_BUILD_LIBC) && defined(QUICKJS_NG_PLAT_WIN32) +/* + * We are building QuickJS-NG, quickjs-libc is a static library and we are on + * Windows. Then, make sure to not export any interfaces. + */ +# define JS_LIBC_EXTERN /* nothing */ +#else +/* + * Otherwise, if we are either (1) not building QuickJS-NG, (2) libc is built as + * a part of libqjs, or (3) we are not on Windows, define JS_LIBC_EXTERN to + * JS_EXTERN. + */ +# define JS_LIBC_EXTERN JS_EXTERN +#endif + +/* + * `JS_MODULE_EXTERN` -- helper macro that must be used to mark `js_init_module` + * and other public functions of the binary modules. See examples/ for examples + * of the usage. + * + * Windows note: -DQUICKJS_NG_MODULE_BUILD must be set when building a binary + * module to properly set __declspec. + */ +#ifdef QUICKJS_NG_PLAT_WIN32 +# ifdef QUICKJS_NG_MODULE_BUILD +# define JS_MODULE_EXTERN __declspec(dllexport) +# else +# define JS_MODULE_EXTERN __declspec(dllimport) +# endif +#else +# ifdef QUICKJS_NG_CC_GNULIKE +# define JS_MODULE_EXTERN __attribute__((visibility("default"))) +# else +# define JS_MODULE_EXTERN /* nothing */ +# endif +#endif /* QUICKJS_NG_PLAT_WIN32 */ + +/* Borrowed from Folly */ +#ifndef JS_PRINTF_FORMAT +#ifdef _MSC_VER +#include +#define JS_PRINTF_FORMAT _Printf_format_string_ +#define JS_PRINTF_FORMAT_ATTR(format_param, dots_param) +#else +#define JS_PRINTF_FORMAT +#if !defined(__clang__) && defined(__GNUC__) +#define JS_PRINTF_FORMAT_ATTR(format_param, dots_param) \ + __attribute__((format(gnu_printf, format_param, dots_param))) +#else +#define JS_PRINTF_FORMAT_ATTR(format_param, dots_param) \ + __attribute__((format(printf, format_param, dots_param))) +#endif +#endif +#endif + +#undef QUICKJS_NG_CC_GNULIKE +#undef QUICKJS_NG_PLAT_WIN32 + +typedef struct JSRuntime JSRuntime; +typedef struct JSContext JSContext; +typedef struct JSObject JSObject; +typedef struct JSClass JSClass; +typedef uint32_t JSClassID; +typedef uint32_t JSAtom; + +/* Unless documented otherwise, C string pointers (`char *` or `const char *`) + are assumed to verify these constraints: + - unless a length is passed separately, the string has a null terminator + - string contents is either pure ASCII or is UTF-8 encoded. + */ + +/* Overridable purely for testing purposes; don't touch. */ +#ifndef JS_NAN_BOXING +#if INTPTR_MAX < INT64_MAX +#define JS_NAN_BOXING 1 /* Use NAN boxing for 32bit builds. */ +#endif +#endif + +enum { + /* all tags with a reference count are negative */ + JS_TAG_FIRST = -9, /* first negative tag */ + JS_TAG_BIG_INT = -9, + JS_TAG_SYMBOL = -8, + JS_TAG_STRING = -7, + JS_TAG_STRING_ROPE = -6, + JS_TAG_MODULE = -3, /* used internally */ + JS_TAG_FUNCTION_BYTECODE = -2, /* used internally */ + JS_TAG_OBJECT = -1, + + JS_TAG_INT = 0, + JS_TAG_BOOL = 1, + JS_TAG_NULL = 2, + JS_TAG_UNDEFINED = 3, + JS_TAG_UNINITIALIZED = 4, + JS_TAG_CATCH_OFFSET = 5, + JS_TAG_EXCEPTION = 6, + JS_TAG_SHORT_BIG_INT = 7, + JS_TAG_FLOAT64 = 8, + /* any larger tag is FLOAT64 if JS_NAN_BOXING */ +}; + +#if !defined(JS_CHECK_JSVALUE) +#define JSValueConst JSValue +#endif + +// JS_CHECK_JSVALUE build mode does not produce working code but is here to +// help catch reference counting bugs at compile time, by making it harder +// to mix up JSValue and JSValueConst +// +// rules: +// +// - a function with a JSValue parameter takes ownership; +// caller must *not* call JS_FreeValue +// +// - a function with a JSValueConst parameter does not take ownership; +// caller *must* call JS_FreeValue +// +// - a function returning a JSValue transfers ownership to caller; +// caller *must* call JS_FreeValue +// +// - a function returning a JSValueConst does *not* transfer ownership; +// caller must *not* call JS_FreeValue +#if defined(JS_CHECK_JSVALUE) + +typedef struct JSValue *JSValue; +typedef const struct JSValue *JSValueConst; + +#define JS_MKVAL(tag, val) ((JSValue)((tag) | (intptr_t)(val) << 4)) +#define JS_MKPTR(tag, ptr) ((JSValue)((tag) | (intptr_t)(ptr))) +#define JS_VALUE_GET_NORM_TAG(v) ((int)((intptr_t)(v) & 15)) +#define JS_VALUE_GET_TAG(v) ((int)((intptr_t)(v) & 15)) +#define JS_VALUE_GET_SHORT_BIG_INT(v) JS_VALUE_GET_INT(v) +#define JS_VALUE_GET_PTR(v) ((void *)((intptr_t)(v) & ~15)) +#define JS_VALUE_GET_INT(v) ((int)((intptr_t)(v) >> 4)) +#define JS_VALUE_GET_BOOL(v) ((int)((intptr_t)(v) >> 4)) +#define JS_VALUE_GET_FLOAT64(v) ((double)((intptr_t)(v) >> 4)) +#define JS_TAG_IS_FLOAT64(tag) ((int)(tag) == JS_TAG_FLOAT64) +#define JS_NAN JS_MKVAL(JS_TAG_FLOAT64, 0) + +static inline JSValue __JS_NewFloat64(double d) +{ + return JS_MKVAL(JS_TAG_FLOAT64, (int)d); +} + +static inline JSValue __JS_NewShortBigInt(JSContext *ctx, int32_t d) +{ + (void)&ctx; + return JS_MKVAL(JS_TAG_SHORT_BIG_INT, d); +} + +static inline bool JS_VALUE_IS_NAN(JSValue v) +{ + (void)&v; + return false; +} + +#elif defined(JS_NAN_BOXING) && JS_NAN_BOXING + +typedef uint64_t JSValue; + +#define JS_VALUE_GET_TAG(v) (int)((v) >> 32) +#define JS_VALUE_GET_INT(v) (int)(v) +#define JS_VALUE_GET_BOOL(v) (int)(v) +#define JS_VALUE_GET_SHORT_BIG_INT(v) (int)(v) +#define JS_VALUE_GET_PTR(v) (void *)(intptr_t)(v) + +#define JS_MKVAL(tag, val) (((uint64_t)(tag) << 32) | (uint32_t)(val)) +#define JS_MKPTR(tag, ptr) (((uint64_t)(tag) << 32) | (uintptr_t)(ptr)) + +#define JS_FLOAT64_TAG_ADDEND (0x7ff80000 - JS_TAG_FIRST + 1) /* quiet NaN encoding */ + +static inline double JS_VALUE_GET_FLOAT64(JSValue v) +{ + union { + JSValue v; + double d; + } u; + u.v = v; + u.v += (uint64_t)JS_FLOAT64_TAG_ADDEND << 32; + return u.d; +} + +#define JS_NAN (0x7ff8000000000000 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32)) + +static inline JSValue __JS_NewFloat64(double d) +{ + union { + double d; + uint64_t u64; + } u; + JSValue v; + u.d = d; + /* normalize NaN */ + if ((u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000) + v = JS_NAN; + else + v = u.u64 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32); + return v; +} + +static inline JSValue __JS_NewShortBigInt(JSContext *ctx, int32_t d) +{ + (void)&ctx; + return JS_MKVAL(JS_TAG_SHORT_BIG_INT, d); +} + +#define JS_TAG_IS_FLOAT64(tag) ((unsigned)((tag) - JS_TAG_FIRST) >= (JS_TAG_FLOAT64 - JS_TAG_FIRST)) + +/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */ +static inline int JS_VALUE_GET_NORM_TAG(JSValue v) +{ + uint32_t tag; + tag = JS_VALUE_GET_TAG(v); + if (JS_TAG_IS_FLOAT64(tag)) + return JS_TAG_FLOAT64; + else + return tag; +} + +static inline bool JS_VALUE_IS_NAN(JSValue v) +{ + uint32_t tag; + tag = JS_VALUE_GET_TAG(v); + return tag == (JS_NAN >> 32); +} + +#else /* !JS_NAN_BOXING */ + +typedef union JSValueUnion { + int32_t int32; + double float64; + void *ptr; + int32_t short_big_int; +} JSValueUnion; + +typedef struct JSValue { + JSValueUnion u; + int64_t tag; +} JSValue; + +#define JS_VALUE_GET_TAG(v) ((int32_t)(v).tag) +/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */ +#define JS_VALUE_GET_NORM_TAG(v) JS_VALUE_GET_TAG(v) +#define JS_VALUE_GET_INT(v) ((v).u.int32) +#define JS_VALUE_GET_BOOL(v) ((v).u.int32) +#define JS_VALUE_GET_FLOAT64(v) ((v).u.float64) +#define JS_VALUE_GET_SHORT_BIG_INT(v) ((v).u.short_big_int) +#define JS_VALUE_GET_PTR(v) ((v).u.ptr) + +/* msvc doesn't understand designated initializers without /std:c++20 */ +#ifdef __cplusplus +static inline JSValue JS_MKPTR(int64_t tag, void *ptr) +{ + JSValue v; + v.u.ptr = ptr; + v.tag = tag; + return v; +} +static inline JSValue JS_MKVAL(int64_t tag, int32_t int32) +{ + JSValue v; + v.u.int32 = int32; + v.tag = tag; + return v; +} +static inline JSValue JS_MKNAN(void) +{ + JSValue v; + v.u.float64 = NAN; + v.tag = JS_TAG_FLOAT64; + return v; +} +/* provide as macros for consistency and backward compat reasons */ +#define JS_MKPTR(tag, ptr) JS_MKPTR(tag, ptr) +#define JS_MKVAL(tag, val) JS_MKVAL(tag, val) +#define JS_NAN JS_MKNAN() /* alas, not a constant expression */ +#else +#define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag } +#define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .int32 = val }, tag } +#define JS_NAN (JSValue){ (JSValueUnion){ .float64 = NAN }, JS_TAG_FLOAT64 } +#endif + +#define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64) + +static inline JSValue __JS_NewFloat64(double d) +{ + JSValue v; + v.tag = JS_TAG_FLOAT64; + v.u.float64 = d; + return v; +} + +static inline JSValue __JS_NewShortBigInt(JSContext *ctx, int64_t d) +{ + (void)&ctx; + JSValue v; + v.tag = JS_TAG_SHORT_BIG_INT; + v.u.short_big_int = d; + return v; +} + +static inline bool JS_VALUE_IS_NAN(JSValue v) +{ + union { + double d; + uint64_t u64; + } u; + if (v.tag != JS_TAG_FLOAT64) + return 0; + u.d = v.u.float64; + return (u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000; +} + +#endif /* !JS_NAN_BOXING */ + +#define JS_VALUE_IS_BOTH_INT(v1, v2) ((JS_VALUE_GET_TAG(v1) | JS_VALUE_GET_TAG(v2)) == 0) +#define JS_VALUE_IS_BOTH_FLOAT(v1, v2) (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v1)) && JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v2))) + +#define JS_VALUE_HAS_REF_COUNT(v) ((unsigned)JS_VALUE_GET_TAG(v) >= (unsigned)JS_TAG_FIRST) + +/* special values */ +#define JS_NULL JS_MKVAL(JS_TAG_NULL, 0) +#define JS_UNDEFINED JS_MKVAL(JS_TAG_UNDEFINED, 0) +#define JS_FALSE JS_MKVAL(JS_TAG_BOOL, 0) +#define JS_TRUE JS_MKVAL(JS_TAG_BOOL, 1) +#define JS_EXCEPTION JS_MKVAL(JS_TAG_EXCEPTION, 0) +#define JS_UNINITIALIZED JS_MKVAL(JS_TAG_UNINITIALIZED, 0) + +/* flags for object properties */ +#define JS_PROP_CONFIGURABLE (1 << 0) +#define JS_PROP_WRITABLE (1 << 1) +#define JS_PROP_ENUMERABLE (1 << 2) +#define JS_PROP_C_W_E (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE | JS_PROP_ENUMERABLE) +#define JS_PROP_LENGTH (1 << 3) /* used internally in Arrays */ +#define JS_PROP_TMASK (3 << 4) /* mask for NORMAL, GETSET, VARREF, AUTOINIT */ +#define JS_PROP_NORMAL (0 << 4) +#define JS_PROP_GETSET (1 << 4) +#define JS_PROP_VARREF (2 << 4) /* used internally */ +#define JS_PROP_AUTOINIT (3 << 4) /* used internally */ + +/* flags for JS_DefineProperty */ +#define JS_PROP_HAS_SHIFT 8 +#define JS_PROP_HAS_CONFIGURABLE (1 << 8) +#define JS_PROP_HAS_WRITABLE (1 << 9) +#define JS_PROP_HAS_ENUMERABLE (1 << 10) +#define JS_PROP_HAS_GET (1 << 11) +#define JS_PROP_HAS_SET (1 << 12) +#define JS_PROP_HAS_VALUE (1 << 13) + +/* throw an exception if false would be returned + (JS_DefineProperty/JS_SetProperty) */ +#define JS_PROP_THROW (1 << 14) +/* throw an exception if false would be returned in strict mode + (JS_SetProperty) */ +#define JS_PROP_THROW_STRICT (1 << 15) + +#define JS_PROP_NO_ADD (1 << 16) /* internal use */ +#define JS_PROP_NO_EXOTIC (1 << 17) /* internal use */ +#define JS_PROP_DEFINE_PROPERTY (1 << 18) /* internal use */ +#define JS_PROP_REFLECT_DEFINE_PROPERTY (1 << 19) /* internal use */ + +#ifndef JS_DEFAULT_STACK_SIZE +#define JS_DEFAULT_STACK_SIZE (1024 * 1024) +#endif + +/* JS_Eval() flags */ +#define JS_EVAL_TYPE_GLOBAL (0 << 0) /* global code (default) */ +#define JS_EVAL_TYPE_MODULE (1 << 0) /* module code */ +#define JS_EVAL_TYPE_DIRECT (2 << 0) /* direct call (internal use) */ +#define JS_EVAL_TYPE_INDIRECT (3 << 0) /* indirect call (internal use) */ +#define JS_EVAL_TYPE_MASK (3 << 0) + +#define JS_EVAL_FLAG_STRICT (1 << 3) /* force 'strict' mode */ +#define JS_EVAL_FLAG_UNUSED (1 << 4) /* unused */ +/* compile but do not run. The result is an object with a + JS_TAG_FUNCTION_BYTECODE or JS_TAG_MODULE tag. It can be executed + with JS_EvalFunction(). */ +#define JS_EVAL_FLAG_COMPILE_ONLY (1 << 5) +/* don't include the stack frames before this eval in the Error() backtraces */ +#define JS_EVAL_FLAG_BACKTRACE_BARRIER (1 << 6) +/* allow top-level await in normal script. JS_Eval() returns a + promise. Only allowed with JS_EVAL_TYPE_GLOBAL */ +#define JS_EVAL_FLAG_ASYNC (1 << 7) + +typedef JSValue JSCFunction(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); +typedef JSValue JSCFunctionMagic(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); +typedef JSValue JSCFunctionData(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic, JSValueConst *func_data); +typedef JSValue JSCClosure(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic, void *opaque); + +typedef struct JSMallocFunctions { + void *(*js_calloc)(void *opaque, size_t count, size_t size); + void *(*js_malloc)(void *opaque, size_t size); + void (*js_free)(void *opaque, void *ptr); + void *(*js_realloc)(void *opaque, void *ptr, size_t size); + size_t (*js_malloc_usable_size)(const void *ptr); +} JSMallocFunctions; + +// Debug trace system: the debug output will be produced to the dump stream (currently +// stdout) if dumps are enabled and JS_SetDumpFlags is invoked with the corresponding +// bit set. +#define JS_DUMP_BYTECODE_FINAL 0x01 /* dump pass 3 final byte code */ +#define JS_DUMP_BYTECODE_PASS2 0x02 /* dump pass 2 code */ +#define JS_DUMP_BYTECODE_PASS1 0x04 /* dump pass 1 code */ +#define JS_DUMP_BYTECODE_HEX 0x10 /* dump bytecode in hex */ +#define JS_DUMP_BYTECODE_PC2LINE 0x20 /* dump line number table */ +#define JS_DUMP_BYTECODE_STACK 0x40 /* dump compute_stack_size */ +#define JS_DUMP_BYTECODE_STEP 0x80 /* dump executed bytecode */ +#define JS_DUMP_READ_OBJECT 0x100 /* dump the marshalled objects at load time */ +#define JS_DUMP_FREE 0x200 /* dump every object free */ +#define JS_DUMP_GC 0x400 /* dump the occurrence of the automatic GC */ +#define JS_DUMP_GC_FREE 0x800 /* dump objects freed by the GC */ +#define JS_DUMP_MODULE_RESOLVE 0x1000 /* dump module resolution steps */ +#define JS_DUMP_PROMISE 0x2000 /* dump promise steps */ +#define JS_DUMP_LEAKS 0x4000 /* dump leaked objects and strings in JS_FreeRuntime */ +#define JS_DUMP_ATOM_LEAKS 0x8000 /* dump leaked atoms in JS_FreeRuntime */ +#define JS_DUMP_MEM 0x10000 /* dump memory usage in JS_FreeRuntime */ +#define JS_DUMP_OBJECTS 0x20000 /* dump objects in JS_FreeRuntime */ +#define JS_DUMP_ATOMS 0x40000 /* dump atoms in JS_FreeRuntime */ +#define JS_DUMP_SHAPES 0x80000 /* dump shapes in JS_FreeRuntime */ + +// Finalizers run in LIFO order at the very end of JS_FreeRuntime. +// Intended for cleanup of associated resources; the runtime itself +// is no longer usable. +typedef void JSRuntimeFinalizer(JSRuntime *rt, void *arg); + +typedef struct JSGCObjectHeader JSGCObjectHeader; + +JS_EXTERN JSRuntime *JS_NewRuntime(void); +/* info lifetime must exceed that of rt */ +JS_EXTERN void JS_SetRuntimeInfo(JSRuntime *rt, const char *info); +/* use 0 to disable memory limit */ +JS_EXTERN void JS_SetMemoryLimit(JSRuntime *rt, size_t limit); +JS_EXTERN void JS_SetDumpFlags(JSRuntime *rt, uint64_t flags); +JS_EXTERN uint64_t JS_GetDumpFlags(JSRuntime *rt); +JS_EXTERN size_t JS_GetGCThreshold(JSRuntime *rt); +JS_EXTERN void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold); +/* use 0 to disable maximum stack size check */ +JS_EXTERN void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size); +/* should be called when changing thread to update the stack top value + used to check stack overflow. */ +JS_EXTERN void JS_UpdateStackTop(JSRuntime *rt); +JS_EXTERN JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque); +JS_EXTERN void JS_FreeRuntime(JSRuntime *rt); +JS_EXTERN void *JS_GetRuntimeOpaque(JSRuntime *rt); +JS_EXTERN void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque); +JS_EXTERN int JS_AddRuntimeFinalizer(JSRuntime *rt, + JSRuntimeFinalizer *finalizer, void *arg); +typedef void JS_MarkFunc(JSRuntime *rt, JSGCObjectHeader *gp); +JS_EXTERN void JS_MarkValue(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +JS_EXTERN void JS_RunGC(JSRuntime *rt); +JS_EXTERN bool JS_IsLiveObject(JSRuntime *rt, JSValueConst obj); + +JS_EXTERN JSContext *JS_NewContext(JSRuntime *rt); +JS_EXTERN void JS_FreeContext(JSContext *s); +JS_EXTERN JSContext *JS_DupContext(JSContext *ctx); +JS_EXTERN void *JS_GetContextOpaque(JSContext *ctx); +JS_EXTERN void JS_SetContextOpaque(JSContext *ctx, void *opaque); +JS_EXTERN JSRuntime *JS_GetRuntime(JSContext *ctx); +JS_EXTERN void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj); +JS_EXTERN JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id); +JS_EXTERN JSValue JS_GetFunctionProto(JSContext *ctx); + +/* the following functions are used to select the intrinsic object to + save memory */ +JS_EXTERN JSContext *JS_NewContextRaw(JSRuntime *rt); +JS_EXTERN void JS_AddIntrinsicBaseObjects(JSContext *ctx); +JS_EXTERN void JS_AddIntrinsicDate(JSContext *ctx); +JS_EXTERN void JS_AddIntrinsicEval(JSContext *ctx); +JS_EXTERN void JS_AddIntrinsicRegExpCompiler(JSContext *ctx); +JS_EXTERN void JS_AddIntrinsicRegExp(JSContext *ctx); +JS_EXTERN void JS_AddIntrinsicJSON(JSContext *ctx); +JS_EXTERN void JS_AddIntrinsicProxy(JSContext *ctx); +JS_EXTERN void JS_AddIntrinsicMapSet(JSContext *ctx); +JS_EXTERN void JS_AddIntrinsicTypedArrays(JSContext *ctx); +JS_EXTERN void JS_AddIntrinsicPromise(JSContext *ctx); +JS_EXTERN void JS_AddIntrinsicBigInt(JSContext *ctx); +JS_EXTERN void JS_AddIntrinsicWeakRef(JSContext *ctx); +JS_EXTERN void JS_AddPerformance(JSContext *ctx); +JS_EXTERN void JS_AddIntrinsicDOMException(JSContext *ctx); + +/* for equality comparisons and sameness */ +JS_EXTERN int JS_IsEqual(JSContext *ctx, JSValueConst op1, JSValueConst op2); +JS_EXTERN bool JS_IsStrictEqual(JSContext *ctx, JSValueConst op1, JSValueConst op2); +JS_EXTERN bool JS_IsSameValue(JSContext *ctx, JSValueConst op1, JSValueConst op2); +/* Similar to same-value equality, but +0 and -0 are considered equal. */ +JS_EXTERN bool JS_IsSameValueZero(JSContext *ctx, JSValueConst op1, JSValueConst op2); + +/* Only used for running 262 tests. TODO(saghul) add build time flag. */ +JS_EXTERN JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); + +JS_EXTERN void *js_calloc_rt(JSRuntime *rt, size_t count, size_t size); +JS_EXTERN void *js_malloc_rt(JSRuntime *rt, size_t size); +JS_EXTERN void js_free_rt(JSRuntime *rt, void *ptr); +JS_EXTERN void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size); +JS_EXTERN size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr); +JS_EXTERN void *js_mallocz_rt(JSRuntime *rt, size_t size); + +JS_EXTERN void *js_calloc(JSContext *ctx, size_t count, size_t size); +JS_EXTERN void *js_malloc(JSContext *ctx, size_t size); +JS_EXTERN void js_free(JSContext *ctx, void *ptr); +JS_EXTERN void *js_realloc(JSContext *ctx, void *ptr, size_t size); +JS_EXTERN size_t js_malloc_usable_size(JSContext *ctx, const void *ptr); +JS_EXTERN void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack); +JS_EXTERN void *js_mallocz(JSContext *ctx, size_t size); +JS_EXTERN char *js_strdup(JSContext *ctx, const char *str); +JS_EXTERN char *js_strndup(JSContext *ctx, const char *s, size_t n); + +typedef struct JSMemoryUsage { + int64_t malloc_size, malloc_limit, memory_used_size; + int64_t malloc_count; + int64_t memory_used_count; + int64_t atom_count, atom_size; + int64_t str_count, str_size; + int64_t obj_count, obj_size; + int64_t prop_count, prop_size; + int64_t shape_count, shape_size; + int64_t js_func_count, js_func_size, js_func_code_size; + int64_t js_func_pc2line_count, js_func_pc2line_size; + int64_t c_func_count, array_count; + int64_t fast_array_count, fast_array_elements; + int64_t binary_object_count, binary_object_size; +} JSMemoryUsage; + +JS_EXTERN void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s); +JS_EXTERN void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt); + +/* atom support */ +#define JS_ATOM_NULL 0 + +JS_EXTERN JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len); +JS_EXTERN JSAtom JS_NewAtom(JSContext *ctx, const char *str); +JS_EXTERN JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n); +JS_EXTERN JSAtom JS_DupAtom(JSContext *ctx, JSAtom v); +JS_EXTERN JSAtom JS_DupAtomRT(JSRuntime *rt, JSAtom v); +JS_EXTERN void JS_FreeAtom(JSContext *ctx, JSAtom v); +JS_EXTERN void JS_FreeAtomRT(JSRuntime *rt, JSAtom v); +JS_EXTERN JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom); +JS_EXTERN JSValue JS_AtomToString(JSContext *ctx, JSAtom atom); +JS_EXTERN const char *JS_AtomToCStringLen(JSContext *ctx, size_t *plen, JSAtom atom); +static inline const char *JS_AtomToCString(JSContext *ctx, JSAtom atom) +{ + return JS_AtomToCStringLen(ctx, NULL, atom); +} +JS_EXTERN JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val); + +/* object class support */ + +typedef struct JSPropertyEnum { + bool is_enumerable; + JSAtom atom; +} JSPropertyEnum; + +typedef struct JSPropertyDescriptor { + int flags; + JSValue value; + JSValue getter; + JSValue setter; +} JSPropertyDescriptor; + +typedef struct JSClassExoticMethods { + /* Return -1 if exception (can only happen in case of Proxy object), + false if the property does not exists, true if it exists. If 1 is + returned, the property descriptor 'desc' is filled if != NULL. */ + int (*get_own_property)(JSContext *ctx, JSPropertyDescriptor *desc, + JSValueConst obj, JSAtom prop); + /* '*ptab' should hold the '*plen' property keys. Return 0 if OK, + -1 if exception. The 'is_enumerable' field is ignored. + */ + int (*get_own_property_names)(JSContext *ctx, JSPropertyEnum **ptab, + uint32_t *plen, JSValueConst obj); + /* return < 0 if exception, or true/false */ + int (*delete_property)(JSContext *ctx, JSValueConst obj, JSAtom prop); + /* return < 0 if exception or true/false */ + int (*define_own_property)(JSContext *ctx, JSValueConst this_obj, + JSAtom prop, JSValueConst val, + JSValueConst getter, JSValueConst setter, + int flags); + /* The following methods can be emulated with the previous ones, + so they are usually not needed */ + /* return < 0 if exception or true/false */ + int (*has_property)(JSContext *ctx, JSValueConst obj, JSAtom atom); + JSValue (*get_property)(JSContext *ctx, JSValueConst obj, JSAtom atom, + JSValueConst receiver); + /* return < 0 if exception or true/false */ + int (*set_property)(JSContext *ctx, JSValueConst obj, JSAtom atom, + JSValueConst value, JSValueConst receiver, int flags); +} JSClassExoticMethods; + +typedef void JSClassFinalizer(JSRuntime *rt, JSValueConst val); +typedef void JSClassGCMark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +#define JS_CALL_FLAG_CONSTRUCTOR (1 << 0) +typedef JSValue JSClassCall(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_val, int argc, + JSValueConst *argv, int flags); + +typedef struct JSClassDef { + const char *class_name; /* pure ASCII only! */ + JSClassFinalizer *finalizer; + JSClassGCMark *gc_mark; + /* if call != NULL, the object is a function. If (flags & + JS_CALL_FLAG_CONSTRUCTOR) != 0, the function is called as a + constructor. In this case, 'this_val' is new.target. A + constructor call only happens if the object constructor bit is + set (see JS_SetConstructorBit()). */ + JSClassCall *call; + /* XXX: suppress this indirection ? It is here only to save memory + because only a few classes need these methods */ + JSClassExoticMethods *exotic; +} JSClassDef; + +#define JS_EVAL_OPTIONS_VERSION 1 + +typedef struct JSEvalOptions { + int version; + int eval_flags; + const char *filename; + int line_num; + // can add new fields in ABI-compatible manner by incrementing JS_EVAL_OPTIONS_VERSION +} JSEvalOptions; + +#define JS_INVALID_CLASS_ID 0 +JS_EXTERN JSClassID JS_NewClassID(JSRuntime *rt, JSClassID *pclass_id); +/* Returns the class ID if `v` is an object, otherwise returns JS_INVALID_CLASS_ID. */ +JS_EXTERN JSClassID JS_GetClassID(JSValueConst v); +JS_EXTERN int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def); +JS_EXTERN bool JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id); +/* Returns the class name or JS_ATOM_NULL if `id` is not a registered class. Must be freed with JS_FreeAtom. */ +JS_EXTERN JSAtom JS_GetClassName(JSRuntime *rt, JSClassID class_id); + +/* value handling */ + +static inline JSValue JS_NewBool(JSContext *ctx, bool val) +{ + (void)&ctx; + return JS_MKVAL(JS_TAG_BOOL, (val != 0)); +} + +static inline JSValue JS_NewInt32(JSContext *ctx, int32_t val) +{ + (void)&ctx; + return JS_MKVAL(JS_TAG_INT, val); +} + +static inline JSValue JS_NewFloat64(JSContext *ctx, double val) +{ + (void)&ctx; + return __JS_NewFloat64(val); +} + +static inline JSValue JS_NewCatchOffset(JSContext *ctx, int32_t val) +{ + (void)&ctx; + return JS_MKVAL(JS_TAG_CATCH_OFFSET, val); +} + +static inline JSValue JS_NewInt64(JSContext *ctx, int64_t val) +{ + JSValue v; + if (val >= INT32_MIN && val <= INT32_MAX) { + v = JS_NewInt32(ctx, (int32_t)val); + } else { + v = JS_NewFloat64(ctx, (double)val); + } + return v; +} + +static inline JSValue JS_NewUint32(JSContext *ctx, uint32_t val) +{ + JSValue v; + if (val <= INT32_MAX) { + v = JS_NewInt32(ctx, (int32_t)val); + } else { + v = JS_NewFloat64(ctx, (double)val); + } + return v; +} + +JS_EXTERN JSValue JS_NewNumber(JSContext *ctx, double d); +JS_EXTERN JSValue JS_NewBigInt64(JSContext *ctx, int64_t v); +JS_EXTERN JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v); + +static inline bool JS_IsNumber(JSValueConst v) +{ + int tag = JS_VALUE_GET_TAG(v); + return tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag); +} + +static inline bool JS_IsBigInt(JSValueConst v) +{ + int tag = JS_VALUE_GET_TAG(v); + return tag == JS_TAG_BIG_INT || tag == JS_TAG_SHORT_BIG_INT; +} + +static inline bool JS_IsBool(JSValueConst v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_BOOL; +} + +static inline bool JS_IsNull(JSValueConst v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_NULL; +} + +static inline bool JS_IsUndefined(JSValueConst v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_UNDEFINED; +} + +static inline bool JS_IsException(JSValueConst v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_EXCEPTION; +} + +static inline bool JS_IsUninitialized(JSValueConst v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_UNINITIALIZED; +} + +static inline bool JS_IsString(JSValueConst v) +{ + int tag = JS_VALUE_GET_TAG(v); + return tag == JS_TAG_STRING || tag == JS_TAG_STRING_ROPE; +} + +static inline bool JS_IsSymbol(JSValueConst v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_SYMBOL; +} + +static inline bool JS_IsObject(JSValueConst v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_OBJECT; +} + +static inline bool JS_IsModule(JSValueConst v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_MODULE; +} + +JS_EXTERN JSValue JS_Throw(JSContext *ctx, JSValue obj); +JS_EXTERN JSValue JS_GetException(JSContext *ctx); +JS_EXTERN bool JS_HasException(JSContext *ctx); +JS_EXTERN bool JS_IsError(JSValueConst val); +JS_EXTERN bool JS_IsUncatchableError(JSValueConst val); +JS_EXTERN void JS_SetUncatchableError(JSContext *ctx, JSValueConst val); +JS_EXTERN void JS_ClearUncatchableError(JSContext *ctx, JSValueConst val); +// Shorthand for: +// JSValue exc = JS_GetException(ctx); +// JS_ClearUncatchableError(ctx, exc); +// JS_Throw(ctx, exc); +JS_EXTERN void JS_ResetUncatchableError(JSContext *ctx); +JS_EXTERN JSValue JS_NewError(JSContext *ctx); +JS_EXTERN JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_NewInternalError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...); +JS_EXTERN JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_NewPlainError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...); +JS_EXTERN JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_NewRangeError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...); +JS_EXTERN JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_NewReferenceError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...); +JS_EXTERN JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_NewSyntaxError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...); +JS_EXTERN JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_NewTypeError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...); +JS_EXTERN JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_ThrowInternalError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...); +JS_EXTERN JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_ThrowPlainError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...); +JS_EXTERN JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_ThrowRangeError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...); +JS_EXTERN JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_ThrowReferenceError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...); +JS_EXTERN JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_ThrowSyntaxError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...); +JS_EXTERN JSValue JS_PRINTF_FORMAT_ATTR(2, 3) JS_ThrowTypeError(JSContext *ctx, JS_PRINTF_FORMAT const char *fmt, ...); +JS_EXTERN JSValue JS_PRINTF_FORMAT_ATTR(3, 4) JS_ThrowDOMException(JSContext *ctx, const char *name, JS_PRINTF_FORMAT const char *fmt, ...); +JS_EXTERN JSValue JS_ThrowOutOfMemory(JSContext *ctx); +JS_EXTERN void JS_FreeValue(JSContext *ctx, JSValue v); +JS_EXTERN void JS_FreeValueRT(JSRuntime *rt, JSValue v); +JS_EXTERN JSValue JS_DupValue(JSContext *ctx, JSValueConst v); +JS_EXTERN JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst v); +JS_EXTERN int JS_ToBool(JSContext *ctx, JSValueConst val); /* return -1 for JS_EXCEPTION */ +static inline JSValue JS_ToBoolean(JSContext *ctx, JSValueConst val) +{ + return JS_NewBool(ctx, JS_ToBool(ctx, val)); +} +JS_EXTERN JSValue JS_ToNumber(JSContext *ctx, JSValueConst val); +JS_EXTERN int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val); +static inline int JS_ToUint32(JSContext *ctx, uint32_t *pres, JSValueConst val) +{ + return JS_ToInt32(ctx, (int32_t*)pres, val); +} +JS_EXTERN int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val); +JS_EXTERN int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val); +JS_EXTERN int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val); +/* return an exception if 'val' is a Number */ +JS_EXTERN int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val); +JS_EXTERN int JS_ToBigUint64(JSContext *ctx, uint64_t *pres, JSValueConst val); +/* same as JS_ToInt64() but allow BigInt */ +JS_EXTERN int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val); + +JS_EXTERN JSValue JS_NewStringLen(JSContext *ctx, const char *str1, size_t len1); +static inline JSValue JS_NewString(JSContext *ctx, const char *str) { + return JS_NewStringLen(ctx, str, strlen(str)); +} +// makes a copy of the input; does not check if the input is valid UTF-16, +// that is the responsibility of the caller +JS_EXTERN JSValue JS_NewStringUTF16(JSContext *ctx, const uint16_t *buf, + size_t len); +JS_EXTERN JSValue JS_NewAtomString(JSContext *ctx, const char *str); +JS_EXTERN JSValue JS_ToString(JSContext *ctx, JSValueConst val); +JS_EXTERN JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val); +JS_EXTERN const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, bool cesu8); +static inline const char *JS_ToCStringLen(JSContext *ctx, size_t *plen, JSValueConst val1) +{ + return JS_ToCStringLen2(ctx, plen, val1, 0); +} +static inline const char *JS_ToCString(JSContext *ctx, JSValueConst val1) +{ + return JS_ToCStringLen2(ctx, NULL, val1, 0); +} +// returns a utf-16 version of the string in native endianness; the +// string is not nul terminated and can contain unmatched surrogates +// |*plen| is in uint16s, not code points; a surrogate pair such as +// U+D834 U+DF06 has len=2; an unmatched surrogate has len=1 +JS_EXTERN const uint16_t *JS_ToCStringLenUTF16(JSContext *ctx, size_t *plen, + JSValueConst val1); +static inline const uint16_t *JS_ToCStringUTF16(JSContext *ctx, + JSValueConst val1) +{ + return JS_ToCStringLenUTF16(ctx, NULL, val1); +} +JS_EXTERN void JS_FreeCString(JSContext *ctx, const char *ptr); +JS_EXTERN void JS_FreeCStringRT(JSRuntime *rt, const char *ptr); +JS_EXTERN void JS_FreeCStringUTF16(JSContext *ctx, const uint16_t *ptr); +JS_EXTERN void JS_FreeCStringRT_UTF16(JSRuntime *rt, const uint16_t *ptr); + +JS_EXTERN JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto, + JSClassID class_id); +JS_EXTERN JSValue JS_NewObjectClass(JSContext *ctx, JSClassID class_id); +JS_EXTERN JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto); +JS_EXTERN JSValue JS_NewObject(JSContext *ctx); +// takes ownership of the values +JS_EXTERN JSValue JS_NewObjectFrom(JSContext *ctx, int count, + const JSAtom *props, + const JSValue *values); +// takes ownership of the values +JS_EXTERN JSValue JS_NewObjectFromStr(JSContext *ctx, int count, + const char **props, + const JSValue *values); +JS_EXTERN JSValue JS_ToObject(JSContext *ctx, JSValueConst val); +JS_EXTERN JSValue JS_ToObjectString(JSContext *ctx, JSValueConst val); + +JS_EXTERN bool JS_IsFunction(JSContext* ctx, JSValueConst val); +JS_EXTERN bool JS_IsConstructor(JSContext* ctx, JSValueConst val); +JS_EXTERN bool JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, bool val); + +JS_EXTERN bool JS_IsRegExp(JSValueConst val); +JS_EXTERN bool JS_IsMap(JSValueConst val); +JS_EXTERN bool JS_IsSet(JSValueConst val); +JS_EXTERN bool JS_IsWeakRef(JSValueConst val); +JS_EXTERN bool JS_IsWeakSet(JSValueConst val); +JS_EXTERN bool JS_IsWeakMap(JSValueConst val); +JS_EXTERN bool JS_IsDataView(JSValueConst val); + +JS_EXTERN JSValue JS_NewArray(JSContext *ctx); +// takes ownership of the values +JS_EXTERN JSValue JS_NewArrayFrom(JSContext *ctx, int count, + const JSValue *values); +// reader beware: JS_IsArray used to "punch" through proxies and check +// if the target object is an array but it no longer does; use JS_IsProxy +// and JS_GetProxyTarget instead, and remember that the target itself can +// also be a proxy, ad infinitum +JS_EXTERN bool JS_IsArray(JSValueConst val); + +JS_EXTERN bool JS_IsProxy(JSValueConst val); +JS_EXTERN JSValue JS_GetProxyTarget(JSContext *ctx, JSValueConst proxy); +JS_EXTERN JSValue JS_GetProxyHandler(JSContext *ctx, JSValueConst proxy); +JS_EXTERN JSValue JS_NewProxy(JSContext *ctx, JSValueConst target, + JSValueConst handler); + +JS_EXTERN JSValue JS_NewDate(JSContext *ctx, double epoch_ms); +JS_EXTERN bool JS_IsDate(JSValueConst v); + +JS_EXTERN JSValue JS_GetProperty(JSContext *ctx, JSValueConst this_obj, JSAtom prop); +JS_EXTERN JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj, + uint32_t idx); +JS_EXTERN JSValue JS_GetPropertyInt64(JSContext *ctx, JSValueConst this_obj, + int64_t idx); +JS_EXTERN JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj, + const char *prop); + +JS_EXTERN int JS_SetProperty(JSContext *ctx, JSValueConst this_obj, + JSAtom prop, JSValue val); +JS_EXTERN int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj, + uint32_t idx, JSValue val); +JS_EXTERN int JS_SetPropertyInt64(JSContext *ctx, JSValueConst this_obj, + int64_t idx, JSValue val); +JS_EXTERN int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj, + const char *prop, JSValue val); +JS_EXTERN int JS_HasProperty(JSContext *ctx, JSValueConst this_obj, JSAtom prop); +JS_EXTERN int JS_IsExtensible(JSContext *ctx, JSValueConst obj); +JS_EXTERN int JS_PreventExtensions(JSContext *ctx, JSValueConst obj); +JS_EXTERN int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags); +JS_EXTERN int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val); +JS_EXTERN JSValue JS_GetPrototype(JSContext *ctx, JSValueConst val); +JS_EXTERN int JS_GetLength(JSContext *ctx, JSValueConst obj, int64_t *pres); +JS_EXTERN int JS_SetLength(JSContext *ctx, JSValueConst obj, int64_t len); +JS_EXTERN int JS_SealObject(JSContext *ctx, JSValueConst obj); +JS_EXTERN int JS_FreezeObject(JSContext *ctx, JSValueConst obj); + +#define JS_GPN_STRING_MASK (1 << 0) +#define JS_GPN_SYMBOL_MASK (1 << 1) +#define JS_GPN_PRIVATE_MASK (1 << 2) +/* only include the enumerable properties */ +#define JS_GPN_ENUM_ONLY (1 << 4) +/* set theJSPropertyEnum.is_enumerable field */ +#define JS_GPN_SET_ENUM (1 << 5) + +JS_EXTERN int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, + uint32_t *plen, JSValueConst obj, + int flags); +JS_EXTERN int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc, + JSValueConst obj, JSAtom prop); +JS_EXTERN void JS_FreePropertyEnum(JSContext *ctx, JSPropertyEnum *tab, + uint32_t len); + +JS_EXTERN JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, int argc, JSValueConst *argv); +JS_EXTERN JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom, + int argc, JSValueConst *argv); +JS_EXTERN JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj, + int argc, JSValueConst *argv); +JS_EXTERN JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj, + JSValueConst new_target, + int argc, JSValueConst *argv); +/* Try to detect if the input is a module. Returns true if parsing the input + * as a module produces no syntax errors. It's a naive approach that is not + * wholly infallible: non-strict classic scripts may _parse_ okay as a module + * but not _execute_ as one (different runtime semantics.) Use with caution. + * |input| can be either ASCII or UTF-8 encoded source code. + * Returns false if QuickJS was built with -DQJS_DISABLE_PARSER. + */ +JS_EXTERN bool JS_DetectModule(const char *input, size_t input_len); +/* 'input' must be zero terminated i.e. input[input_len] = '\0'. */ +JS_EXTERN JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len, + const char *filename, int eval_flags); +JS_EXTERN JSValue JS_Eval2(JSContext *ctx, const char *input, size_t input_len, + JSEvalOptions *options); +JS_EXTERN JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj, + const char *input, size_t input_len, + const char *filename, int eval_flags); +JS_EXTERN JSValue JS_EvalThis2(JSContext *ctx, JSValueConst this_obj, + const char *input, size_t input_len, + JSEvalOptions *options); +JS_EXTERN JSValue JS_GetGlobalObject(JSContext *ctx); +JS_EXTERN int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj); +JS_EXTERN int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, + JSAtom prop, JSValueConst val, + JSValueConst getter, JSValueConst setter, + int flags); +JS_EXTERN int JS_DefinePropertyValue(JSContext *ctx, JSValueConst this_obj, + JSAtom prop, JSValue val, int flags); +JS_EXTERN int JS_DefinePropertyValueUint32(JSContext *ctx, JSValueConst this_obj, + uint32_t idx, JSValue val, int flags); +JS_EXTERN int JS_DefinePropertyValueStr(JSContext *ctx, JSValueConst this_obj, + const char *prop, JSValue val, int flags); +JS_EXTERN int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj, + JSAtom prop, JSValue getter, JSValue setter, + int flags); +/* Only supported for custom classes, returns 0 on success < 0 otherwise. */ +JS_EXTERN int JS_SetOpaque(JSValueConst obj, void *opaque); +JS_EXTERN void *JS_GetOpaque(JSValueConst obj, JSClassID class_id); +JS_EXTERN void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id); +JS_EXTERN void *JS_GetAnyOpaque(JSValueConst obj, JSClassID *class_id); + +/* 'buf' must be zero terminated i.e. buf[buf_len] = '\0'. */ +JS_EXTERN JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len, + const char *filename); +JS_EXTERN JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj, + JSValueConst replacer, JSValueConst space0); + +typedef void JSFreeArrayBufferDataFunc(JSRuntime *rt, void *opaque, void *ptr); +JS_EXTERN JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len, + JSFreeArrayBufferDataFunc *free_func, void *opaque, + bool is_shared); +JS_EXTERN JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len); +JS_EXTERN void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj); +JS_EXTERN uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj); +JS_EXTERN bool JS_IsArrayBuffer(JSValueConst obj); +JS_EXTERN uint8_t *JS_GetUint8Array(JSContext *ctx, size_t *psize, JSValueConst obj); + +typedef enum JSTypedArrayEnum { + JS_TYPED_ARRAY_UINT8C = 0, + JS_TYPED_ARRAY_INT8, + JS_TYPED_ARRAY_UINT8, + JS_TYPED_ARRAY_INT16, + JS_TYPED_ARRAY_UINT16, + JS_TYPED_ARRAY_INT32, + JS_TYPED_ARRAY_UINT32, + JS_TYPED_ARRAY_BIG_INT64, + JS_TYPED_ARRAY_BIG_UINT64, + JS_TYPED_ARRAY_FLOAT16, + JS_TYPED_ARRAY_FLOAT32, + JS_TYPED_ARRAY_FLOAT64, +} JSTypedArrayEnum; + +JS_EXTERN JSValue JS_NewTypedArray(JSContext *ctx, int argc, JSValueConst *argv, + JSTypedArrayEnum array_type); +JS_EXTERN JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj, + size_t *pbyte_offset, + size_t *pbyte_length, + size_t *pbytes_per_element); +JS_EXTERN JSValue JS_NewUint8Array(JSContext *ctx, uint8_t *buf, size_t len, + JSFreeArrayBufferDataFunc *free_func, void *opaque, + bool is_shared); +/* returns -1 if not a typed array otherwise return a JSTypedArrayEnum value */ +JS_EXTERN int JS_GetTypedArrayType(JSValueConst obj); +JS_EXTERN JSValue JS_NewUint8ArrayCopy(JSContext *ctx, const uint8_t *buf, size_t len); +typedef struct { + void *(*sab_alloc)(void *opaque, size_t size); + void (*sab_free)(void *opaque, void *ptr); + void (*sab_dup)(void *opaque, void *ptr); + void *sab_opaque; +} JSSharedArrayBufferFunctions; +JS_EXTERN void JS_SetSharedArrayBufferFunctions(JSRuntime *rt, const JSSharedArrayBufferFunctions *sf); + +typedef enum JSPromiseStateEnum { + // argument to JS_PromiseState() was not in fact a promise + JS_PROMISE_NOT_A_PROMISE = -1, + JS_PROMISE_PENDING = 0, + JS_PROMISE_FULFILLED, + JS_PROMISE_REJECTED, +} JSPromiseStateEnum; + +JS_EXTERN JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs); +JS_EXTERN JSPromiseStateEnum JS_PromiseState(JSContext *ctx, + JSValueConst promise); +JS_EXTERN JSValue JS_PromiseResult(JSContext *ctx, JSValueConst promise); +JS_EXTERN bool JS_IsPromise(JSValueConst val); + +JS_EXTERN JSValue JS_NewSymbol(JSContext *ctx, const char *description, bool is_global); + +typedef enum JSPromiseHookType { + JS_PROMISE_HOOK_INIT, // emitted when a new promise is created + JS_PROMISE_HOOK_BEFORE, // runs right before promise.then is invoked + JS_PROMISE_HOOK_AFTER, // runs right after promise.then is invoked + JS_PROMISE_HOOK_RESOLVE, // not emitted for rejected promises +} JSPromiseHookType; + +// parent_promise is only passed in when type == JS_PROMISE_HOOK_INIT and +// is then either a promise object or JS_UNDEFINED if the new promise does +// not have a parent promise; only promises created with promise.then have +// a parent promise +typedef void JSPromiseHook(JSContext *ctx, JSPromiseHookType type, + JSValueConst promise, JSValueConst parent_promise, + void *opaque); +JS_EXTERN void JS_SetPromiseHook(JSRuntime *rt, JSPromiseHook promise_hook, + void *opaque); + +/* is_handled = true means that the rejection is handled */ +typedef void JSHostPromiseRejectionTracker(JSContext *ctx, JSValueConst promise, + JSValueConst reason, + bool is_handled, void *opaque); +JS_EXTERN void JS_SetHostPromiseRejectionTracker(JSRuntime *rt, JSHostPromiseRejectionTracker *cb, void *opaque); + +/* return != 0 if the JS code needs to be interrupted */ +typedef int JSInterruptHandler(JSRuntime *rt, void *opaque); +JS_EXTERN void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque); +/* if can_block is true, Atomics.wait() can be used */ +JS_EXTERN void JS_SetCanBlock(JSRuntime *rt, bool can_block); +/* set the [IsHTMLDDA] internal slot */ +JS_EXTERN void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj); + +typedef struct JSModuleDef JSModuleDef; + +/* return the module specifier (allocated with js_malloc()) or NULL if + exception */ +typedef char *JSModuleNormalizeFunc(JSContext *ctx, + const char *module_base_name, + const char *module_name, void *opaque); +typedef JSModuleDef *JSModuleLoaderFunc(JSContext *ctx, + const char *module_name, void *opaque); + +/* module loader with import attributes support */ +typedef JSModuleDef *JSModuleLoaderFunc2(JSContext *ctx, + const char *module_name, void *opaque, + JSValueConst attributes); + +/* return -1 if exception, 0 if OK */ +typedef int JSModuleCheckSupportedImportAttributes(JSContext *ctx, void *opaque, + JSValueConst attributes); + +/* module_normalize = NULL is allowed and invokes the default module + filename normalizer */ +JS_EXTERN void JS_SetModuleLoaderFunc(JSRuntime *rt, + JSModuleNormalizeFunc *module_normalize, + JSModuleLoaderFunc *module_loader, void *opaque); + +/* same as JS_SetModuleLoaderFunc but with import attributes support */ +JS_EXTERN void JS_SetModuleLoaderFunc2(JSRuntime *rt, + JSModuleNormalizeFunc *module_normalize, + JSModuleLoaderFunc2 *module_loader, + JSModuleCheckSupportedImportAttributes *module_check_attrs, + void *opaque); + +/* return the import.meta object of a module */ +JS_EXTERN JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m); +JS_EXTERN JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m); +JS_EXTERN JSValue JS_GetModuleNamespace(JSContext *ctx, JSModuleDef *m); + +/* associate a JSValue to a C module */ +JS_EXTERN int JS_SetModulePrivateValue(JSContext *ctx, JSModuleDef *m, JSValue val); +JS_EXTERN JSValue JS_GetModulePrivateValue(JSContext *ctx, JSModuleDef *m); + +/* JS Job support */ + +typedef JSValue JSJobFunc(JSContext *ctx, int argc, JSValueConst *argv); +JS_EXTERN int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func, + int argc, JSValueConst *argv); + +JS_EXTERN bool JS_IsJobPending(JSRuntime *rt); +JS_EXTERN int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx); + +/* Structure to retrieve (de)serialized SharedArrayBuffer objects. */ +typedef struct JSSABTab { + uint8_t **tab; + size_t len; +} JSSABTab; + +/* Object Writer/Reader (currently only used to handle precompiled code) */ +#define JS_WRITE_OBJ_BYTECODE (1 << 0) /* allow function/module */ +#define JS_WRITE_OBJ_BSWAP (0) /* byte swapped output (obsolete, handled transparently) */ +#define JS_WRITE_OBJ_SAB (1 << 2) /* allow SharedArrayBuffer */ +#define JS_WRITE_OBJ_REFERENCE (1 << 3) /* allow object references to encode arbitrary object graph */ +#define JS_WRITE_OBJ_STRIP_SOURCE (1 << 4) /* do not write source code information */ +#define JS_WRITE_OBJ_STRIP_DEBUG (1 << 5) /* do not write debug information */ +JS_EXTERN uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj, int flags); +JS_EXTERN uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj, + int flags, JSSABTab *psab_tab); + +#define JS_READ_OBJ_BYTECODE (1 << 0) /* allow function/module */ +#define JS_READ_OBJ_ROM_DATA (0) /* avoid duplicating 'buf' data (obsolete, broken by ICs) */ +#define JS_READ_OBJ_SAB (1 << 2) /* allow SharedArrayBuffer */ +#define JS_READ_OBJ_REFERENCE (1 << 3) /* allow object references */ +JS_EXTERN JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len, int flags); +JS_EXTERN JSValue JS_ReadObject2(JSContext *ctx, const uint8_t *buf, size_t buf_len, + int flags, JSSABTab *psab_tab); +/* instantiate and evaluate a bytecode function. Only used when + reading a script or module with JS_ReadObject() */ +JS_EXTERN JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj); +/* load the dependencies of the module 'obj'. Useful when JS_ReadObject() + returns a module. */ +JS_EXTERN int JS_ResolveModule(JSContext *ctx, JSValueConst obj); + +/* only exported for os.Worker() */ +JS_EXTERN JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels); +/* only exported for os.Worker() */ +JS_EXTERN JSValue JS_LoadModule(JSContext *ctx, const char *basename, + const char *filename); + +/* C function definition */ +typedef enum JSCFunctionEnum { /* XXX: should rename for namespace isolation */ + JS_CFUNC_generic, + JS_CFUNC_generic_magic, + JS_CFUNC_constructor, + JS_CFUNC_constructor_magic, + JS_CFUNC_constructor_or_func, + JS_CFUNC_constructor_or_func_magic, + JS_CFUNC_f_f, + JS_CFUNC_f_f_f, + JS_CFUNC_getter, + JS_CFUNC_setter, + JS_CFUNC_getter_magic, + JS_CFUNC_setter_magic, + JS_CFUNC_iterator_next, +} JSCFunctionEnum; + +typedef union JSCFunctionType { + JSCFunction *generic; + JSValue (*generic_magic)(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); + JSCFunction *constructor; + JSValue (*constructor_magic)(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv, int magic); + JSCFunction *constructor_or_func; + double (*f_f)(double); + double (*f_f_f)(double, double); + JSValue (*getter)(JSContext *ctx, JSValueConst this_val); + JSValue (*setter)(JSContext *ctx, JSValueConst this_val, JSValueConst val); + JSValue (*getter_magic)(JSContext *ctx, JSValueConst this_val, int magic); + JSValue (*setter_magic)(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic); + JSValue (*iterator_next)(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int *pdone, int magic); +} JSCFunctionType; + +JS_EXTERN JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func, + const char *name, + int length, JSCFunctionEnum cproto, int magic); +JS_EXTERN JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func, + const char *name, + int length, JSCFunctionEnum cproto, int magic, + JSValueConst proto_val); +JS_EXTERN JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func, + int length, int magic, int data_len, + JSValueConst *data); +JS_EXTERN JSValue JS_NewCFunctionData2(JSContext *ctx, JSCFunctionData *func, + const char *name, + int length, int magic, int data_len, + JSValueConst *data); +typedef void JSCClosureFinalizerFunc(void*); +JS_EXTERN JSValue JS_NewCClosure(JSContext *ctx, JSCClosure *func, + const char *name, + JSCClosureFinalizerFunc *opaque_finalize, + int length, int magic, void *opaque); + +static inline JSValue JS_NewCFunction(JSContext *ctx, JSCFunction *func, + const char *name, int length) +{ + return JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_generic, 0); +} + +static inline JSValue JS_NewCFunctionMagic(JSContext *ctx, JSCFunctionMagic *func, + const char *name, int length, + JSCFunctionEnum cproto, int magic) +{ + /* Used to squelch a -Wcast-function-type warning. */ + JSCFunctionType ft; + ft.generic_magic = func; + return JS_NewCFunction2(ctx, ft.generic, name, length, cproto, magic); +} +JS_EXTERN void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj, + JSValueConst proto); + +/* C property definition */ + +typedef struct JSCFunctionListEntry { + const char *name; /* pure ASCII or UTF-8 encoded */ + uint8_t prop_flags; + uint8_t def_type; + int16_t magic; + union { + struct { + uint8_t length; /* XXX: should move outside union */ + uint8_t cproto; /* XXX: should move outside union */ + JSCFunctionType cfunc; + } func; + struct { + JSCFunctionType get; + JSCFunctionType set; + } getset; + struct { + const char *name; + int base; + } alias; + struct { + const struct JSCFunctionListEntry *tab; + int len; + } prop_list; + const char *str; /* pure ASCII or UTF-8 encoded */ + int32_t i32; + int64_t i64; + uint64_t u64; + double f64; + } u; +} JSCFunctionListEntry; + +#define JS_DEF_CFUNC 0 +#define JS_DEF_CGETSET 1 +#define JS_DEF_CGETSET_MAGIC 2 +#define JS_DEF_PROP_STRING 3 +#define JS_DEF_PROP_INT32 4 +#define JS_DEF_PROP_INT64 5 +#define JS_DEF_PROP_DOUBLE 6 +#define JS_DEF_PROP_UNDEFINED 7 +#define JS_DEF_OBJECT 8 +#define JS_DEF_ALIAS 9 + +/* Note: c++ does not like nested designators */ +#define JS_CFUNC_DEF(name, length, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, { .func = { length, JS_CFUNC_generic, { .generic = func1 } } } } +#define JS_CFUNC_DEF2(name, length, func1, prop_flags) { name, prop_flags, JS_DEF_CFUNC, 0, { .func = { length, JS_CFUNC_generic, { .generic = func1 } } } } +#define JS_CFUNC_MAGIC_DEF(name, length, func1, magic) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, magic, { .func = { length, JS_CFUNC_generic_magic, { .generic_magic = func1 } } } } +#define JS_CFUNC_SPECIAL_DEF(name, length, cproto, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, { .func = { length, JS_CFUNC_ ## cproto, { .cproto = func1 } } } } +#define JS_ITERATOR_NEXT_DEF(name, length, func1, magic) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, magic, { .func = { length, JS_CFUNC_iterator_next, { .iterator_next = func1 } } } } +#define JS_CGETSET_DEF(name, fgetter, fsetter) { name, JS_PROP_CONFIGURABLE, JS_DEF_CGETSET, 0, { .getset = { .get = { .getter = fgetter }, .set = { .setter = fsetter } } } } +#define JS_CGETSET_DEF2(name, fgetter, fsetter, prop_flags) { name, prop_flags, JS_DEF_CGETSET, 0, { .getset = { .get = { .getter = fgetter }, .set = { .setter = fsetter } } } } +#define JS_CGETSET_MAGIC_DEF(name, fgetter, fsetter, magic) { name, JS_PROP_CONFIGURABLE, JS_DEF_CGETSET_MAGIC, magic, { .getset = { .get = { .getter_magic = fgetter }, .set = { .setter_magic = fsetter } } } } +#define JS_PROP_STRING_DEF(name, cstr, prop_flags) { name, prop_flags, JS_DEF_PROP_STRING, 0, { .str = cstr } } +#define JS_PROP_INT32_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_INT32, 0, { .i32 = val } } +#define JS_PROP_INT64_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_INT64, 0, { .i64 = val } } +#define JS_PROP_DOUBLE_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_DOUBLE, 0, { .f64 = val } } +#define JS_PROP_U2D_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_DOUBLE, 0, { .u64 = val } } +#define JS_PROP_UNDEFINED_DEF(name, prop_flags) { name, prop_flags, JS_DEF_PROP_UNDEFINED, 0, { .i32 = 0 } } +#define JS_OBJECT_DEF(name, tab, len, prop_flags) { name, prop_flags, JS_DEF_OBJECT, 0, { .prop_list = { tab, len } } } +#define JS_ALIAS_DEF(name, from) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, { .alias = { from, -1 } } } +#define JS_ALIAS_BASE_DEF(name, from, base) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, { .alias = { from, base } } } + +JS_EXTERN int JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj, + const JSCFunctionListEntry *tab, + int len); + +/* C module definition */ + +typedef int JSModuleInitFunc(JSContext *ctx, JSModuleDef *m); + +JS_EXTERN JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str, + JSModuleInitFunc *func); +/* can only be called before the module is instantiated */ +JS_EXTERN int JS_AddModuleExport(JSContext *ctx, JSModuleDef *m, const char *name_str); +JS_EXTERN int JS_AddModuleExportList(JSContext *ctx, JSModuleDef *m, + const JSCFunctionListEntry *tab, int len); +/* can only be called after the module is instantiated */ +JS_EXTERN int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name, + JSValue val); +JS_EXTERN int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m, + const JSCFunctionListEntry *tab, int len); + +/* Version */ + +#define QJS_VERSION_MAJOR 0 +#define QJS_VERSION_MINOR 12 +#define QJS_VERSION_PATCH 1 +#define QJS_VERSION_SUFFIX "" + +JS_EXTERN const char* JS_GetVersion(void); + +/* Integration point for quickjs-libc.c, not for public use. */ +JS_EXTERN uintptr_t js_std_cmd(int cmd, ...); + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif /* QUICKJS_H */ +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2025 Fabrice Bellard + * Copyright (c) 2017-2025 Charlie Gordon + * Copyright (c) 2023-2025 Ben Noordhuis + * Copyright (c) 2023-2025 Saúl Ibarra Corretgé + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#if !defined(_MSC_VER) +#include +#if defined(_WIN32) +#include +#endif +#endif +#if defined(_WIN32) +#include +#endif +#include +#include + + + + + + + +#if defined(EMSCRIPTEN) || defined(_MSC_VER) +#define DIRECT_DISPATCH 0 +#else +#define DIRECT_DISPATCH 1 +#endif + +#if defined(__APPLE__) +#define MALLOC_OVERHEAD 0 +#else +#define MALLOC_OVERHEAD 8 +#endif + +#if defined(__NEWLIB__) +#define NO_TM_GMTOFF +#endif + +#if defined(__sun) +#include +#define NO_TM_GMTOFF +#endif + +// atomic_store etc. are completely busted in recent versions of tcc; +// somehow the compiler forgets to load |ptr| into %rdi when calling +// the __atomic_*() helpers in its lib/stdatomic.c and lib/atomic.S +#if !defined(__TINYC__) && !defined(EMSCRIPTEN) && !defined(__wasi__) && !__STDC_NO_ATOMICS__ && !defined(__DJGPP) + +#define CONFIG_ATOMICS +#endif + +#ifndef __GNUC__ +#define __extension__ +#endif + +#ifndef NDEBUG +#define ENABLE_DUMPS +#endif + +//#define FORCE_GC_AT_MALLOC /* test the GC by forcing it before each object allocation */ + +#define check_dump_flag(rt, flag) ((rt->dump_flags & (flag +0)) == (flag +0)) + +#define STRINGIFY_(x) #x +#define STRINGIFY(x) STRINGIFY_(x) + +#define QJS_VERSION_STRING \ + STRINGIFY(QJS_VERSION_MAJOR) "." STRINGIFY(QJS_VERSION_MINOR) "." STRINGIFY(QJS_VERSION_PATCH) QJS_VERSION_SUFFIX + +const char* JS_GetVersion(void) { + return QJS_VERSION_STRING; +} + +#undef STRINFIGY_ +#undef STRINGIFY + +static inline JSValueConst *vc(JSValue *vals) +{ + return (JSValueConst *)vals; +} + +static inline JSValue unsafe_unconst(JSValueConst v) +{ +#ifdef JS_CHECK_JSVALUE + return (JSValue)v; +#else + return v; +#endif +} + +static inline JSValueConst safe_const(JSValue v) +{ +#ifdef JS_CHECK_JSVALUE + return (JSValueConst)v; +#else + return v; +#endif +} + +enum { + /* classid tag */ /* union usage | properties */ + JS_CLASS_OBJECT = 1, /* must be first */ + JS_CLASS_ARRAY, /* u.array | length */ + JS_CLASS_ERROR, + JS_CLASS_NUMBER, /* u.object_data */ + JS_CLASS_STRING, /* u.object_data */ + JS_CLASS_BOOLEAN, /* u.object_data */ + JS_CLASS_SYMBOL, /* u.object_data */ + JS_CLASS_ARGUMENTS, /* u.array | length */ + JS_CLASS_MAPPED_ARGUMENTS, /* | length */ + JS_CLASS_DATE, /* u.object_data */ + JS_CLASS_MODULE_NS, + JS_CLASS_C_FUNCTION, /* u.cfunc */ + JS_CLASS_BYTECODE_FUNCTION, /* u.func */ + JS_CLASS_BOUND_FUNCTION, /* u.bound_function */ + JS_CLASS_C_FUNCTION_DATA, /* u.c_function_data_record */ + JS_CLASS_C_CLOSURE, /* u.c_closure_record */ + JS_CLASS_GENERATOR_FUNCTION, /* u.func */ + JS_CLASS_FOR_IN_ITERATOR, /* u.for_in_iterator */ + JS_CLASS_REGEXP, /* u.regexp */ + JS_CLASS_ARRAY_BUFFER, /* u.array_buffer */ + JS_CLASS_SHARED_ARRAY_BUFFER, /* u.array_buffer */ + JS_CLASS_UINT8C_ARRAY, /* u.array (typed_array) */ + JS_CLASS_INT8_ARRAY, /* u.array (typed_array) */ + JS_CLASS_UINT8_ARRAY, /* u.array (typed_array) */ + JS_CLASS_INT16_ARRAY, /* u.array (typed_array) */ + JS_CLASS_UINT16_ARRAY, /* u.array (typed_array) */ + JS_CLASS_INT32_ARRAY, /* u.array (typed_array) */ + JS_CLASS_UINT32_ARRAY, /* u.array (typed_array) */ + JS_CLASS_BIG_INT64_ARRAY, /* u.array (typed_array) */ + JS_CLASS_BIG_UINT64_ARRAY, /* u.array (typed_array) */ + JS_CLASS_FLOAT16_ARRAY, /* u.array (typed_array) */ + JS_CLASS_FLOAT32_ARRAY, /* u.array (typed_array) */ + JS_CLASS_FLOAT64_ARRAY, /* u.array (typed_array) */ + JS_CLASS_DATAVIEW, /* u.typed_array */ + JS_CLASS_BIG_INT, /* u.object_data */ + JS_CLASS_MAP, /* u.map_state */ + JS_CLASS_SET, /* u.map_state */ + JS_CLASS_WEAKMAP, /* u.map_state */ + JS_CLASS_WEAKSET, /* u.map_state */ + JS_CLASS_ITERATOR, + JS_CLASS_ITERATOR_CONCAT, /* u.iterator_concat_data */ + JS_CLASS_ITERATOR_HELPER, /* u.iterator_helper_data */ + JS_CLASS_ITERATOR_WRAP, /* u.iterator_wrap_data */ + JS_CLASS_MAP_ITERATOR, /* u.map_iterator_data */ + JS_CLASS_SET_ITERATOR, /* u.map_iterator_data */ + JS_CLASS_ARRAY_ITERATOR, /* u.array_iterator_data */ + JS_CLASS_STRING_ITERATOR, /* u.array_iterator_data */ + JS_CLASS_REGEXP_STRING_ITERATOR, /* u.regexp_string_iterator_data */ + JS_CLASS_GENERATOR, /* u.generator_data */ + JS_CLASS_PROXY, /* u.proxy_data */ + JS_CLASS_PROMISE, /* u.promise_data */ + JS_CLASS_PROMISE_RESOLVE_FUNCTION, /* u.promise_function_data */ + JS_CLASS_PROMISE_REJECT_FUNCTION, /* u.promise_function_data */ + JS_CLASS_ASYNC_FUNCTION, /* u.func */ + JS_CLASS_ASYNC_FUNCTION_RESOLVE, /* u.async_function_data */ + JS_CLASS_ASYNC_FUNCTION_REJECT, /* u.async_function_data */ + JS_CLASS_ASYNC_FROM_SYNC_ITERATOR, /* u.async_from_sync_iterator_data */ + JS_CLASS_ASYNC_GENERATOR_FUNCTION, /* u.func */ + JS_CLASS_ASYNC_GENERATOR, /* u.async_generator_data */ + JS_CLASS_WEAK_REF, + JS_CLASS_FINALIZATION_REGISTRY, + JS_CLASS_DOM_EXCEPTION, + JS_CLASS_CALL_SITE, + + JS_CLASS_INIT_COUNT, /* last entry for predefined classes */ +}; + +/* number of typed array types */ +#define JS_TYPED_ARRAY_COUNT (JS_CLASS_FLOAT64_ARRAY - JS_CLASS_UINT8C_ARRAY + 1) +static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT]; +#define typed_array_size_log2(classid) (typed_array_size_log2[(classid)- JS_CLASS_UINT8C_ARRAY]) + +typedef enum JSErrorEnum { + JS_EVAL_ERROR, + JS_RANGE_ERROR, + JS_REFERENCE_ERROR, + JS_SYNTAX_ERROR, + JS_TYPE_ERROR, + JS_URI_ERROR, + JS_INTERNAL_ERROR, + JS_AGGREGATE_ERROR, + + JS_NATIVE_ERROR_COUNT, /* number of different NativeError objects */ + JS_PLAIN_ERROR = JS_NATIVE_ERROR_COUNT +} JSErrorEnum; + +#define JS_MAX_LOCAL_VARS 65535 +#define JS_STACK_SIZE_MAX 65534 +#define JS_STRING_LEN_MAX ((1 << 30) - 1) +// 1,024 bytes is about the cutoff point where it starts getting +// more profitable to ref slice than to copy +#define JS_STRING_SLICE_LEN_MAX 1024 // in bytes + +/* strings <= this length are not concatenated using ropes. if too + small, the rope memory overhead becomes high. */ +#define JS_STRING_ROPE_SHORT_LEN 512 +/* specific threshold for initial rope use */ +#define JS_STRING_ROPE_SHORT2_LEN 8192 +/* rope depth at which we rebalance */ +#define JS_STRING_ROPE_MAX_DEPTH 60 + +#define __exception __attribute__((warn_unused_result)) + +typedef struct JSShape JSShape; +typedef struct JSString JSString; +typedef struct JSString JSAtomStruct; +typedef struct JSStringRope JSStringRope; + +#define JS_VALUE_GET_OBJ(v) ((JSObject *)JS_VALUE_GET_PTR(v)) +#define JS_VALUE_GET_STRING(v) ((JSString *)JS_VALUE_GET_PTR(v)) +#define JS_VALUE_GET_STRING_ROPE(v) ((JSStringRope *)JS_VALUE_GET_PTR(v)) + +typedef enum { + JS_GC_PHASE_NONE, + JS_GC_PHASE_DECREF, + JS_GC_PHASE_REMOVE_CYCLES, +} JSGCPhaseEnum; + +typedef struct JSMallocState { + size_t malloc_count; + size_t malloc_size; + size_t malloc_limit; + void *opaque; /* user opaque */ +} JSMallocState; + +typedef struct JSRuntimeFinalizerState { + struct JSRuntimeFinalizerState *next; + JSRuntimeFinalizer *finalizer; + void *arg; +} JSRuntimeFinalizerState; + +typedef struct JSValueLink { + struct JSValueLink *next; + JSValueConst value; +} JSValueLink; + +struct JSRuntime { + JSMallocFunctions mf; + JSMallocState malloc_state; + const char *rt_info; + + int atom_hash_size; /* power of two */ + int atom_count; + int atom_size; + int atom_count_resize; /* resize hash table at this count */ + uint32_t *atom_hash; + JSAtomStruct **atom_array; + int atom_free_index; /* 0 = none */ + + JSClassID js_class_id_alloc; /* counter for user defined classes */ + int class_count; /* size of class_array */ + JSClass *class_array; + + struct list_head context_list; /* list of JSContext.link */ + /* list of JSGCObjectHeader.link. List of allocated GC objects (used + by the garbage collector) */ + struct list_head gc_obj_list; + /* list of JSGCObjectHeader.link. Used during JS_FreeValueRT() */ + struct list_head gc_zero_ref_count_list; + struct list_head tmp_obj_list; /* used during GC */ + JSGCPhaseEnum gc_phase : 8; + size_t malloc_gc_threshold; +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + struct list_head string_list; /* list of JSString.link */ +#endif + /* stack limitation */ + uintptr_t stack_size; /* in bytes, 0 if no limit */ + uintptr_t stack_top; + uintptr_t stack_limit; /* lower stack limit */ + + JSValue current_exception; + /* true if inside an out of memory error, to avoid recursing */ + bool in_out_of_memory; + /* true if inside build_backtrace, to avoid recursing */ + bool in_build_stack_trace; + /* true if inside JS_FreeRuntime */ + bool in_free; + + struct JSStackFrame *current_stack_frame; + + JSInterruptHandler *interrupt_handler; + void *interrupt_opaque; + + JSPromiseHook *promise_hook; + void *promise_hook_opaque; + // for smuggling the parent promise from js_promise_then + // to js_promise_constructor + JSValueLink *parent_promise; + + JSHostPromiseRejectionTracker *host_promise_rejection_tracker; + void *host_promise_rejection_tracker_opaque; + + struct list_head job_list; /* list of JSJobEntry.link */ + + JSModuleNormalizeFunc *module_normalize_func; + bool module_loader_has_attr; + union { + JSModuleLoaderFunc *module_loader_func; + JSModuleLoaderFunc2 *module_loader_func2; + } u; + JSModuleCheckSupportedImportAttributes *module_check_attrs; + void *module_loader_opaque; + /* timestamp for internal use in module evaluation */ + int64_t module_async_evaluation_next_timestamp; + + /* used to allocate, free and clone SharedArrayBuffers */ + JSSharedArrayBufferFunctions sab_funcs; + + bool can_block; /* true if Atomics.wait can block */ + uint32_t dump_flags : 24; + + /* Shape hash table */ + int shape_hash_bits; + int shape_hash_size; + int shape_hash_count; /* number of hashed shapes */ + JSShape **shape_hash; + void *user_opaque; + void *libc_opaque; + JSRuntimeFinalizerState *finalizers; +}; + +struct JSClass { + uint32_t class_id; /* 0 means free entry */ + JSAtom class_name; + JSClassFinalizer *finalizer; + JSClassGCMark *gc_mark; + JSClassCall *call; + /* pointers for exotic behavior, can be NULL if none are present */ + const JSClassExoticMethods *exotic; +}; + +typedef struct JSStackFrame { + struct JSStackFrame *prev_frame; /* NULL if first stack frame */ + JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */ + JSValue *arg_buf; /* arguments */ + JSValue *var_buf; /* variables */ + struct JSVarRef **var_refs; /* references to arguments or local variables */ + uint8_t *cur_pc; /* only used in bytecode functions : PC of the + instruction after the call */ + uint16_t var_ref_count; /* number of var refs */ + uint16_t arg_count; + bool is_strict_mode; + /* only used in generators. Current stack pointer value. NULL if + the function is running. */ + JSValue *cur_sp; +} JSStackFrame; + +typedef enum { + JS_GC_OBJ_TYPE_JS_OBJECT, + JS_GC_OBJ_TYPE_FUNCTION_BYTECODE, + JS_GC_OBJ_TYPE_SHAPE, + JS_GC_OBJ_TYPE_VAR_REF, + JS_GC_OBJ_TYPE_ASYNC_FUNCTION, + JS_GC_OBJ_TYPE_JS_CONTEXT, +} JSGCObjectTypeEnum; + +/* header for GC objects. GC objects are C data structures with a + reference count that can reference other GC objects. JS Objects are + a particular type of GC object. */ +struct JSGCObjectHeader { + int ref_count; /* must come first, 32-bit */ + JSGCObjectTypeEnum gc_obj_type : 4; + uint8_t mark : 1; /* used by the GC */ + uint8_t dummy0 : 3; + uint8_t dummy1; /* not used by the GC */ + uint16_t dummy2; /* not used by the GC */ + struct list_head link; +}; + +typedef struct JSVarRef { + union { + JSGCObjectHeader header; /* must come first */ + struct { + int __gc_ref_count; /* corresponds to header.ref_count */ + uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ + uint8_t is_detached; + uint8_t is_lexical; /* only used with global variables */ + uint8_t is_const; /* only used with global variables */ + }; + }; + JSValue *pvalue; /* pointer to the value, either on the stack or + to 'value' */ + union { + JSValue value; /* used when is_detached = true */ + struct { + uint16_t var_ref_idx; /* index in JSStackFrame.var_refs[] */ + JSStackFrame *stack_frame; + }; /* used when is_detached = false */ + }; +} JSVarRef; + +typedef struct JSRefCountHeader { + int ref_count; +} JSRefCountHeader; + +/* bigint */ +typedef int32_t js_slimb_t; +typedef uint32_t js_limb_t; +typedef int64_t js_sdlimb_t; +typedef uint64_t js_dlimb_t; + +#define JS_LIMB_DIGITS 9 + +/* Must match the size of short_big_int in JSValueUnion */ +#define JS_LIMB_BITS 32 +#define JS_SHORT_BIG_INT_BITS JS_LIMB_BITS +#define JS_BIGINT_MAX_SIZE ((1024 * 1024) / JS_LIMB_BITS) /* in limbs */ +#define JS_SHORT_BIG_INT_MIN INT32_MIN +#define JS_SHORT_BIG_INT_MAX INT32_MAX + + +typedef struct JSBigInt { + JSRefCountHeader header; /* must come first, 32-bit */ + uint32_t len; /* number of limbs, >= 1 */ + js_limb_t tab[]; /* two's complement representation, always + normalized so that 'len' is the minimum + possible length >= 1 */ +} JSBigInt; + +/* this bigint structure can hold a 64 bit integer */ +typedef struct { + js_limb_t big_int_buf[sizeof(JSBigInt) / sizeof(js_limb_t)]; /* for JSBigInt */ + /* must come just after */ + js_limb_t tab[(64 + JS_LIMB_BITS - 1) / JS_LIMB_BITS]; +} JSBigIntBuf; + +typedef enum { + JS_AUTOINIT_ID_PROTOTYPE, + JS_AUTOINIT_ID_MODULE_NS, + JS_AUTOINIT_ID_PROP, + JS_AUTOINIT_ID_BYTECODE, +} JSAutoInitIDEnum; + +enum { + JS_BUILTIN_ARRAY_FROMASYNC = 1, + JS_BUILTIN_ITERATOR_ZIP, + JS_BUILTIN_ITERATOR_ZIP_KEYED, +}; + +/* must be large enough to have a negligible runtime cost and small + enough to call the interrupt callback often. */ +#define JS_INTERRUPT_COUNTER_INIT 10000 + +struct JSContext { + JSGCObjectHeader header; /* must come first */ + JSRuntime *rt; + struct list_head link; + + uint16_t binary_object_count; + uint32_t binary_object_size : 31; + + /* true if the array prototype is "normal": + - no small index properties which are get/set or non writable + - its prototype is Object.prototype + - Object.prototype has no small index properties which are get/set or non writable + - the prototype of Object.prototype is null (always true as it is immutable) + */ + uint8_t std_array_prototype : 1; + + JSShape *array_shape; /* initial shape for Array objects */ + JSShape *arguments_shape; /* shape for arguments objects */ + JSShape *mapped_arguments_shape; /* shape for mapped arguments objects */ + JSShape *regexp_shape; /* shape for regexp objects */ + JSShape *regexp_result_shape; /* shape for regexp result objects */ + + JSValue *class_proto; + JSValue function_proto; + JSValue function_ctor; + JSValue array_ctor; + JSValue regexp_ctor; + JSValue promise_ctor; + JSValue native_error_proto[JS_NATIVE_ERROR_COUNT]; + JSValue error_ctor; + JSValue error_back_trace; + JSValue error_prepare_stack; + JSValue error_stack_trace_limit; + JSValue iterator_ctor; + JSValue iterator_ctor_getset; + JSValue iterator_proto; + JSValue async_iterator_proto; + JSValue array_proto_values; + JSValue throw_type_error; + JSValue eval_obj; + + JSValue global_obj; /* global object */ + JSValue global_var_obj; /* contains the global let/const definitions */ + + double time_origin; + + uint64_t random_state; + + /* when the counter reaches zero, JSRutime.interrupt_handler is called */ + int interrupt_counter; + + struct list_head loaded_modules; /* list of JSModuleDef.link */ + + /* if NULL, RegExp compilation is not supported */ + JSValue (*compile_regexp)(JSContext *ctx, JSValueConst pattern, + JSValueConst flags); + /* if NULL, eval is not supported */ + JSValue (*eval_internal)(JSContext *ctx, JSValueConst this_obj, + const char *input, size_t input_len, + const char *filename, int line, int flags, int scope_idx); + void *user_opaque; +}; + +typedef union JSFloat64Union { + double d; + uint64_t u64; + uint32_t u32[2]; +} JSFloat64Union; + +typedef enum { + JS_WEAK_REF_KIND_MAP, + JS_WEAK_REF_KIND_WEAK_REF, + JS_WEAK_REF_KIND_FINALIZATION_REGISTRY_ENTRY, +} JSWeakRefKindEnum; + +typedef struct JSWeakRefRecord { + JSWeakRefKindEnum kind; + struct JSWeakRefRecord *next_weak_ref; + union { + struct JSMapRecord *map_record; + struct JSWeakRefData *weak_ref_data; + struct JSFinRecEntry *fin_rec_entry; + } u; +} JSWeakRefRecord; + +typedef struct JSMapRecord { + int ref_count; /* used during enumeration to avoid freeing the record */ + bool empty; /* true if the record is deleted */ + struct JSMapState *map; + struct list_head link; + struct list_head hash_link; + JSValue key; + JSValue value; +} JSMapRecord; + +typedef struct JSMapState { + bool is_weak; /* true if WeakSet/WeakMap */ + struct list_head records; /* list of JSMapRecord.link */ + uint32_t record_count; + struct list_head *hash_table; + uint32_t hash_size; /* must be a power of two */ + uint32_t record_count_threshold; /* count at which a hash table + resize is needed */ +} JSMapState; + +enum +{ + JS_TO_STRING_IS_PROPERTY_KEY = 1 << 0, + JS_TO_STRING_NO_SIDE_EFFECTS = 1 << 1, +}; + +enum { + JS_ATOM_TYPE_STRING = 1, + JS_ATOM_TYPE_GLOBAL_SYMBOL, + JS_ATOM_TYPE_SYMBOL, + JS_ATOM_TYPE_PRIVATE, +}; + +enum { + JS_ATOM_HASH_SYMBOL, + JS_ATOM_HASH_PRIVATE, +}; + +typedef enum { + JS_ATOM_KIND_STRING, + JS_ATOM_KIND_SYMBOL, + JS_ATOM_KIND_PRIVATE, +} JSAtomKindEnum; + +typedef enum { + JS_STRING_KIND_NORMAL, + JS_STRING_KIND_SLICE, + JS_STRING_KIND_INDIRECT, +} JSStringKind; + +#define JS_ATOM_HASH_MASK ((1 << 28) - 1) + +struct JSString { + JSRefCountHeader header; /* must come first, 32-bit */ + uint32_t len : 31; + uint32_t is_wide_char : 1; /* 0 = 8 bits, 1 = 16 bits characters */ + /* for JS_ATOM_TYPE_SYMBOL: hash = 0, atom_type = 3, + for JS_ATOM_TYPE_PRIVATE: hash = 1, atom_type = 3 + XXX: could change encoding to have one more bit in hash */ + uint32_t hash : 28; + uint32_t kind : 2; + uint32_t atom_type : 2; /* != 0 if atom, JS_ATOM_TYPE_x */ + uint32_t hash_next; /* atom_index for JS_ATOM_TYPE_SYMBOL */ + JSWeakRefRecord *first_weak_ref; +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + struct list_head link; /* string list */ +#endif +}; + +typedef struct JSStringSlice { + JSString *parent; + uint32_t start; // in bytes, not characters +} JSStringSlice; + +struct JSStringRope { + JSRefCountHeader header; /* must come first, 32-bit */ + uint32_t len; + uint8_t is_wide_char; /* 0 = 8 bits, 1 = 16 bits characters */ + uint8_t depth; /* max depth of the rope tree */ + JSValue left; + JSValue right; /* might be the empty string */ +}; + +static inline void *strv(JSString *p) +{ + JSStringSlice *slice; + void **indirect; + + switch (p->kind) { + case JS_STRING_KIND_NORMAL: + return (void *)&p[1]; + case JS_STRING_KIND_SLICE: + slice = (void *)&p[1]; + return (char *)&slice->parent[1] + slice->start; + case JS_STRING_KIND_INDIRECT: + indirect = (void *)&p[1]; + return *indirect; + } + abort(); + return NULL; +} + +static inline uint8_t *str8(JSString *p) +{ + return strv(p); +} + +static inline uint16_t *str16(JSString *p) +{ + return strv(p); +} + +typedef enum { + JS_CLOSURE_LOCAL, /* 'var_idx' is the index of a local variable in the parent function */ + JS_CLOSURE_ARG, /* 'var_idx' is the index of an argument variable in the parent function */ + JS_CLOSURE_REF, /* 'var_idx' is the index of a closure variable in the parent function */ + JS_CLOSURE_GLOBAL_REF, /* 'var_idx' is the index of a closure variable in the parent + function referencing a global variable */ + JS_CLOSURE_GLOBAL_DECL, /* global variable declaration (eval code only) */ + JS_CLOSURE_GLOBAL, /* global variable (eval code only) */ + JS_CLOSURE_MODULE_DECL, /* definition of a module variable (eval code only) */ + JS_CLOSURE_MODULE_IMPORT, /* definition of a module import (eval code only) */ +} JSClosureTypeEnum; + +typedef struct JSClosureVar { + uint8_t closure_type : 3; /* see JSClosureTypeEnum */ + uint8_t is_lexical : 1; /* lexical variable */ + uint8_t is_const : 1; /* const variable (is_lexical = 1 if is_const = 1) */ + uint8_t var_kind : 4; /* see JSVarKindEnum */ + /* 7 bits available */ + uint16_t var_idx; /* JS_CLOSURE_LOCAL/JS_CLOSURE_ARG: index to a normal variable of the + parent function. otherwise: index to a closure + variable of the parent function */ + JSAtom var_name; +} JSClosureVar; + +#define ARG_SCOPE_INDEX 1 +#define ARG_SCOPE_END (-2) + +typedef struct JSVarScope { + int parent; /* index into fd->scopes of the enclosing scope */ + int first; /* index into fd->vars of the last variable in this scope */ +} JSVarScope; + +typedef enum { + /* XXX: add more variable kinds here instead of using bit fields */ + JS_VAR_NORMAL, + JS_VAR_FUNCTION_DECL, /* lexical var with function declaration */ + JS_VAR_NEW_FUNCTION_DECL, /* lexical var with async/generator + function declaration */ + JS_VAR_CATCH, + JS_VAR_FUNCTION_NAME, /* function expression name */ + JS_VAR_PRIVATE_FIELD, + JS_VAR_PRIVATE_METHOD, + JS_VAR_PRIVATE_GETTER, + JS_VAR_PRIVATE_SETTER, /* must come after JS_VAR_PRIVATE_GETTER */ + JS_VAR_PRIVATE_GETTER_SETTER, /* must come after JS_VAR_PRIVATE_SETTER */ +} JSVarKindEnum; + +/* XXX: could use a different structure in bytecode functions to save + memory */ +typedef struct JSVarDef { + JSAtom var_name; + /* index into fd->scopes of this variable lexical scope */ + int scope_level; + /* during compilation: + - if scope_level = 0: scope in which the variable is defined + - if scope_level != 0: index into fd->vars of the next + variable in the same or enclosing lexical scope + in a bytecode function: + index into fd->vars of the next + variable in the same or enclosing lexical scope + */ + int scope_next; + uint8_t is_const : 1; + uint8_t is_lexical : 1; + uint8_t is_captured : 1; + uint8_t is_static_private : 1; /* only used during private class field parsing */ + uint8_t var_kind : 4; /* see JSVarKindEnum */ + /* if is_captured = true, provides the index of the corresponding + JSVarRef on stack */ + uint16_t var_ref_idx; + /* only used during compilation: function pool index for lexical + variables with var_kind = + JS_VAR_FUNCTION_DECL/JS_VAR_NEW_FUNCTION_DECL or scope level of + the definition of the 'var' variables (they have scope_level = + 0) */ + int func_pool_idx; /* only used during compilation : index in + the constant pool for hoisted function + definition */ +} JSVarDef; + +/* for the encoding of the pc2line table */ +#define PC2LINE_BASE (-1) +#define PC2LINE_RANGE 5 +#define PC2LINE_OP_FIRST 1 +#define PC2LINE_DIFF_PC_MAX ((255 - PC2LINE_OP_FIRST) / PC2LINE_RANGE) + +typedef enum JSFunctionKindEnum { + JS_FUNC_NORMAL = 0, + JS_FUNC_GENERATOR = (1 << 0), + JS_FUNC_ASYNC = (1 << 1), + JS_FUNC_ASYNC_GENERATOR = (JS_FUNC_GENERATOR | JS_FUNC_ASYNC), +} JSFunctionKindEnum; + +typedef struct JSFunctionBytecode { + JSGCObjectHeader header; /* must come first */ + uint8_t is_strict_mode : 1; + uint8_t has_prototype : 1; /* true if a prototype field is necessary */ + uint8_t has_simple_parameter_list : 1; + uint8_t is_derived_class_constructor : 1; + /* true if home_object needs to be initialized */ + uint8_t need_home_object : 1; + uint8_t func_kind : 2; + uint8_t new_target_allowed : 1; + uint8_t super_call_allowed : 1; + uint8_t super_allowed : 1; + uint8_t arguments_allowed : 1; + uint8_t backtrace_barrier : 1; /* stop backtrace on this function */ + /* XXX: 5 bits available */ + uint8_t *byte_code_buf; /* (self pointer) */ + int byte_code_len; + JSAtom func_name; + JSVarDef *vardefs; /* arguments + local variables (arg_count + var_count) (self pointer) */ + JSClosureVar *closure_var; /* list of variables in the closure (self pointer) */ + uint16_t arg_count; + uint16_t var_count; + uint16_t defined_arg_count; /* for length function property */ + uint16_t stack_size; /* maximum stack size */ + uint16_t var_ref_count; /* number of local variable references */ + uint16_t closure_var_count; + int cpool_count; + JSContext *realm; /* function realm */ + JSValue *cpool; /* constant pool (self pointer) */ + JSAtom filename; + int line_num; + int col_num; + int source_len; + int pc2line_len; + uint8_t *pc2line_buf; + char *source; +} JSFunctionBytecode; + +typedef struct JSBoundFunction { + JSValue func_obj; + JSValue this_val; + int argc; + JSValue argv[]; +} JSBoundFunction; + +typedef enum JSIteratorKindEnum { + JS_ITERATOR_KIND_KEY, + JS_ITERATOR_KIND_VALUE, + JS_ITERATOR_KIND_KEY_AND_VALUE, +} JSIteratorKindEnum; + +typedef enum JSIteratorHelperKindEnum { + JS_ITERATOR_HELPER_KIND_DROP, + JS_ITERATOR_HELPER_KIND_EVERY, + JS_ITERATOR_HELPER_KIND_FILTER, + JS_ITERATOR_HELPER_KIND_FIND, + JS_ITERATOR_HELPER_KIND_FLAT_MAP, + JS_ITERATOR_HELPER_KIND_FOR_EACH, + JS_ITERATOR_HELPER_KIND_MAP, + JS_ITERATOR_HELPER_KIND_SOME, + JS_ITERATOR_HELPER_KIND_TAKE, +} JSIteratorHelperKindEnum; + +typedef struct JSForInIterator { + JSValue obj; + bool is_array; + uint32_t array_length; + uint32_t idx; +} JSForInIterator; + +typedef struct JSRegExp { + JSString *pattern; + JSString *bytecode; /* also contains the flags */ +} JSRegExp; + +typedef struct JSProxyData { + JSValue target; + JSValue handler; + uint8_t is_func; + uint8_t is_revoked; +} JSProxyData; + +typedef struct JSArrayBuffer { + int byte_length; /* 0 if detached */ + int max_byte_length; /* -1 if not resizable; >= byte_length otherwise */ + uint8_t detached; + uint8_t immutable; + uint8_t shared; /* if shared, the array buffer cannot be detached */ + uint8_t *data; /* NULL if detached */ + struct list_head array_list; + void *opaque; + JSFreeArrayBufferDataFunc *free_func; +} JSArrayBuffer; + +typedef struct JSTypedArray { + struct list_head link; /* link to arraybuffer */ + JSObject *obj; /* back pointer to the TypedArray/DataView object */ + JSObject *buffer; /* based array buffer */ + uint32_t offset; /* byte offset in the array buffer */ + uint32_t length; /* byte length in the array buffer */ + bool track_rab; /* auto-track length of backing array buffer */ +} JSTypedArray; + +typedef struct JSAsyncFunctionState { + JSValue this_val; /* 'this' generator argument */ + int argc; /* number of function arguments */ + bool throw_flag; /* used to throw an exception in JS_CallInternal() */ + JSStackFrame frame; +} JSAsyncFunctionState; + +/* XXX: could use an object instead to avoid the + JS_TAG_ASYNC_FUNCTION tag for the GC */ +typedef struct JSAsyncFunctionData { + JSGCObjectHeader header; /* must come first */ + JSValue resolving_funcs[2]; + bool is_active; /* true if the async function state is valid */ + JSAsyncFunctionState func_state; +} JSAsyncFunctionData; + +typedef struct JSReqModuleEntry { + JSAtom module_name; + JSModuleDef *module; /* used using resolution */ + JSValue attributes; /* JS_UNDEFINED or an object containing the attributes as key/value */ +} JSReqModuleEntry; + +typedef enum JSExportTypeEnum { + JS_EXPORT_TYPE_LOCAL, + JS_EXPORT_TYPE_INDIRECT, +} JSExportTypeEnum; + +typedef struct JSExportEntry { + union { + struct { + int var_idx; /* closure variable index */ + JSVarRef *var_ref; /* if != NULL, reference to the variable */ + } local; /* for local export */ + int req_module_idx; /* module for indirect export */ + } u; + JSExportTypeEnum export_type; + JSAtom local_name; /* '*' if export ns from. not used for local + export after compilation */ + JSAtom export_name; /* exported variable name */ +} JSExportEntry; + +typedef struct JSStarExportEntry { + int req_module_idx; /* in req_module_entries */ +} JSStarExportEntry; + +typedef struct JSImportEntry { + int var_idx; /* closure variable index */ + JSAtom import_name; + int req_module_idx; /* in req_module_entries */ +} JSImportEntry; + +typedef enum { + JS_MODULE_STATUS_UNLINKED, + JS_MODULE_STATUS_LINKING, + JS_MODULE_STATUS_LINKED, + JS_MODULE_STATUS_EVALUATING, + JS_MODULE_STATUS_EVALUATING_ASYNC, + JS_MODULE_STATUS_EVALUATED, +} JSModuleStatus; + +struct JSModuleDef { + JSRefCountHeader header; /* must come first, 32-bit */ + JSAtom module_name; + struct list_head link; + + JSReqModuleEntry *req_module_entries; + int req_module_entries_count; + int req_module_entries_size; + + JSExportEntry *export_entries; + int export_entries_count; + int export_entries_size; + + JSStarExportEntry *star_export_entries; + int star_export_entries_count; + int star_export_entries_size; + + JSImportEntry *import_entries; + int import_entries_count; + int import_entries_size; + + JSValue module_ns; + JSValue func_obj; /* only used for JS modules */ + JSModuleInitFunc *init_func; /* only used for C modules */ + bool has_tla; /* true if func_obj contains await */ + bool resolved; + bool func_created; + JSModuleStatus status : 8; + /* temp use during js_module_link() & js_module_evaluate() */ + int dfs_index, dfs_ancestor_index; + JSModuleDef *stack_prev; + /* temp use during js_module_evaluate() */ + JSModuleDef **async_parent_modules; + int async_parent_modules_count; + int async_parent_modules_size; + int pending_async_dependencies; + bool async_evaluation; + int64_t async_evaluation_timestamp; + JSModuleDef *cycle_root; + JSValue promise; /* corresponds to spec field: capability */ + JSValue resolving_funcs[2]; /* corresponds to spec field: capability */ + /* true if evaluation yielded an exception. It is saved in + eval_exception */ + bool eval_has_exception; + JSValue eval_exception; + JSValue meta_obj; /* for import.meta */ + JSValue private_value; /* private value for C modules */ +}; + +typedef struct JSJobEntry { + struct list_head link; + JSContext *ctx; + JSJobFunc *job_func; + int argc; + JSValue argv[]; +} JSJobEntry; + +typedef struct JSProperty { + union { + JSValue value; /* JS_PROP_NORMAL */ + struct { /* JS_PROP_GETSET */ + JSObject *getter; /* NULL if undefined */ + JSObject *setter; /* NULL if undefined */ + } getset; + JSVarRef *var_ref; /* JS_PROP_VARREF */ + struct { /* JS_PROP_AUTOINIT */ + /* in order to use only 2 pointers, we compress the realm + and the init function pointer */ + uintptr_t realm_and_id; /* realm and init_id (JS_AUTOINIT_ID_x) + in the 2 low bits */ + void *opaque; + } init; + } u; +} JSProperty; + +#define JS_PROP_INITIAL_SIZE 2 +#define JS_PROP_INITIAL_HASH_SIZE 4 /* must be a power of two */ +#define JS_ARRAY_INITIAL_SIZE 2 + +typedef struct JSShapeProperty { + uint32_t hash_next : 26; /* 0 if last in list */ + uint32_t flags : 6; /* JS_PROP_XXX */ + JSAtom atom; /* JS_ATOM_NULL = free property entry */ +} JSShapeProperty; + +struct JSShape { + /* hash table of size hash_mask + 1 before the start of the + structure (see prop_hash_end()). */ + JSGCObjectHeader header; + /* true if the shape is inserted in the shape hash table. If not, + JSShape.hash is not valid */ + uint8_t is_hashed; + uint32_t hash; /* current hash value */ + uint32_t prop_hash_mask; + int prop_size; /* allocated properties */ + int prop_count; /* include deleted properties */ + int deleted_prop_count; + JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list */ + JSObject *proto; + JSShapeProperty prop[]; /* prop_size elements */ +}; + +struct JSObject { + union { + JSGCObjectHeader header; + struct { + int __gc_ref_count; /* corresponds to header.ref_count */ + uint8_t __gc_mark : 7; /* corresponds to header.mark/gc_obj_type */ + uint8_t is_prototype : 1; /* object may be used as prototype */ + + uint8_t extensible : 1; + uint8_t free_mark : 1; /* only used when freeing objects with cycles */ + uint8_t is_exotic : 1; /* true if object has exotic property handlers */ + uint8_t fast_array : 1; /* true if u.array is used for get/put (for JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS, JS_CLASS_MAPPED_ARGUMENTS and typed arrays) */ + uint8_t is_constructor : 1; /* true if object is a constructor function */ + uint8_t is_uncatchable_error : 1; /* if true, error is not catchable */ + uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */ + uint8_t is_HTMLDDA : 1; /* specific annex B IsHtmlDDA behavior */ + uint16_t class_id; /* see JS_CLASS_x */ + }; + }; + /* byte offsets: 16/24 */ + JSShape *shape; /* prototype and property names + flag */ + JSProperty *prop; /* array of properties */ + /* byte offsets: 24/40 */ + JSWeakRefRecord *first_weak_ref; + /* byte offsets: 28/48 */ + union { + void *opaque; + struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */ + struct JSCFunctionDataRecord *c_function_data_record; /* JS_CLASS_C_FUNCTION_DATA */ + struct JSCClosureRecord *c_closure_record; /* JS_CLASS_C_CLOSURE */ + struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */ + struct JSArrayBuffer *array_buffer; /* JS_CLASS_ARRAY_BUFFER, JS_CLASS_SHARED_ARRAY_BUFFER */ + struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */ + struct JSMapState *map_state; /* JS_CLASS_MAP..JS_CLASS_WEAKSET */ + struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, JS_CLASS_SET_ITERATOR */ + struct JSArrayIteratorData *array_iterator_data; /* JS_CLASS_ARRAY_ITERATOR, JS_CLASS_STRING_ITERATOR */ + struct JSRegExpStringIteratorData *regexp_string_iterator_data; /* JS_CLASS_REGEXP_STRING_ITERATOR */ + struct JSGeneratorData *generator_data; /* JS_CLASS_GENERATOR */ + struct JSIteratorConcatData *iterator_concat_data; /* JS_CLASS_ITERATOR_CONCAT */ + struct JSIteratorHelperData *iterator_helper_data; /* JS_CLASS_ITERATOR_HELPER */ + struct JSIteratorWrapData *iterator_wrap_data; /* JS_CLASS_ITERATOR_WRAP */ + struct JSProxyData *proxy_data; /* JS_CLASS_PROXY */ + struct JSPromiseData *promise_data; /* JS_CLASS_PROMISE */ + struct JSPromiseFunctionData *promise_function_data; /* JS_CLASS_PROMISE_RESOLVE_FUNCTION, JS_CLASS_PROMISE_REJECT_FUNCTION */ + struct JSAsyncFunctionData *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, JS_CLASS_ASYNC_FUNCTION_REJECT */ + struct JSAsyncFromSyncIteratorData *async_from_sync_iterator_data; /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */ + struct JSAsyncGeneratorData *async_generator_data; /* JS_CLASS_ASYNC_GENERATOR */ + struct { /* JS_CLASS_BYTECODE_FUNCTION: 12/24 bytes */ + /* also used by JS_CLASS_GENERATOR_FUNCTION, JS_CLASS_ASYNC_FUNCTION and JS_CLASS_ASYNC_GENERATOR_FUNCTION */ + struct JSFunctionBytecode *function_bytecode; + JSVarRef **var_refs; + JSObject *home_object; /* for 'super' access */ + } func; + struct { /* JS_CLASS_C_FUNCTION: 12/20 bytes */ + JSContext *realm; + JSCFunctionType c_function; + uint8_t length; + uint8_t cproto; + int16_t magic; + } cfunc; + /* array part for fast arrays and typed arrays */ + struct { /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS, JS_CLASS_MAPPED_ARGUMENTS, JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ + union { + uint32_t size; /* JS_CLASS_ARRAY */ + struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ + } u1; + union { + JSValue *values; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */ + JSVarRef **var_refs; /* JS_CLASS_MAPPED_ARGUMENTS */ + void *ptr; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ + int8_t *int8_ptr; /* JS_CLASS_INT8_ARRAY */ + uint8_t *uint8_ptr; /* JS_CLASS_UINT8_ARRAY, JS_CLASS_UINT8C_ARRAY */ + int16_t *int16_ptr; /* JS_CLASS_INT16_ARRAY */ + uint16_t *uint16_ptr; /* JS_CLASS_UINT16_ARRAY */ + int32_t *int32_ptr; /* JS_CLASS_INT32_ARRAY */ + uint32_t *uint32_ptr; /* JS_CLASS_UINT32_ARRAY */ + int64_t *int64_ptr; /* JS_CLASS_INT64_ARRAY */ + uint64_t *uint64_ptr; /* JS_CLASS_UINT64_ARRAY */ + uint16_t *fp16_ptr; /* JS_CLASS_FLOAT16_ARRAY */ + float *float_ptr; /* JS_CLASS_FLOAT32_ARRAY */ + double *double_ptr; /* JS_CLASS_FLOAT64_ARRAY */ + } u; + uint32_t count; /* <= 2^31-1. 0 for a detached typed array */ + } array; /* 12/20 bytes */ + JSRegExp regexp; /* JS_CLASS_REGEXP: 8/16 bytes */ + JSValue object_data; /* for JS_SetObjectData(): 8/16/16 bytes */ + } u; + /* byte sizes: 40/48/72 */ +}; + +typedef struct JSCallSiteData { + JSValue filename; + JSValue func; + JSValue func_name; + bool native; + int line_num; + int col_num; +} JSCallSiteData; + +enum { + __JS_ATOM_NULL = JS_ATOM_NULL, +#define DEF(name, str) JS_ATOM_ ## name, +/* + * QuickJS atom definitions + * + * Copyright (c) 2017-2018 Fabrice Bellard + * Copyright (c) 2017-2018 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef DEF + +/* Note: first atoms are considered as keywords in the parser */ +DEF(null, "null") /* must be first */ +DEF(false, "false") +DEF(true, "true") +DEF(if, "if") +DEF(else, "else") +DEF(return, "return") +DEF(var, "var") +DEF(this, "this") +DEF(delete, "delete") +DEF(void, "void") +DEF(typeof, "typeof") +DEF(new, "new") +DEF(in, "in") +DEF(instanceof, "instanceof") +DEF(do, "do") +DEF(while, "while") +DEF(for, "for") +DEF(break, "break") +DEF(continue, "continue") +DEF(switch, "switch") +DEF(case, "case") +DEF(default, "default") +DEF(throw, "throw") +DEF(try, "try") +DEF(catch, "catch") +DEF(finally, "finally") +DEF(function, "function") +DEF(debugger, "debugger") +DEF(with, "with") +/* FutureReservedWord */ +DEF(class, "class") +DEF(const, "const") +DEF(enum, "enum") +DEF(export, "export") +DEF(extends, "extends") +DEF(import, "import") +DEF(super, "super") +/* FutureReservedWords when parsing strict mode code */ +DEF(implements, "implements") +DEF(interface, "interface") +DEF(let, "let") +DEF(package, "package") +DEF(private, "private") +DEF(protected, "protected") +DEF(public, "public") +DEF(static, "static") +DEF(yield, "yield") +DEF(await, "await") + +/* empty string */ +DEF(empty_string, "") +/* identifiers */ +DEF(keys, "keys") +DEF(size, "size") +DEF(length, "length") +DEF(message, "message") +DEF(cause, "cause") +DEF(errors, "errors") +DEF(stack, "stack") +DEF(name, "name") +DEF(toString, "toString") +DEF(toLocaleString, "toLocaleString") +DEF(valueOf, "valueOf") +DEF(eval, "eval") +DEF(prototype, "prototype") +DEF(constructor, "constructor") +DEF(configurable, "configurable") +DEF(writable, "writable") +DEF(enumerable, "enumerable") +DEF(value, "value") +DEF(get, "get") +DEF(set, "set") +DEF(of, "of") +DEF(__proto__, "__proto__") +DEF(undefined, "undefined") +DEF(number, "number") +DEF(boolean, "boolean") +DEF(string, "string") +DEF(object, "object") +DEF(symbol, "symbol") +DEF(integer, "integer") +DEF(unknown, "unknown") +DEF(arguments, "arguments") +DEF(callee, "callee") +DEF(caller, "caller") +DEF(_eval_, "") +DEF(_ret_, "") +DEF(_var_, "") +DEF(_arg_var_, "") +DEF(_with_, "") +DEF(lastIndex, "lastIndex") +DEF(target, "target") +DEF(index, "index") +DEF(input, "input") +DEF(defineProperties, "defineProperties") +DEF(apply, "apply") +DEF(join, "join") +DEF(concat, "concat") +DEF(split, "split") +DEF(construct, "construct") +DEF(getPrototypeOf, "getPrototypeOf") +DEF(setPrototypeOf, "setPrototypeOf") +DEF(isExtensible, "isExtensible") +DEF(preventExtensions, "preventExtensions") +DEF(has, "has") +DEF(deleteProperty, "deleteProperty") +DEF(defineProperty, "defineProperty") +DEF(getOwnPropertyDescriptor, "getOwnPropertyDescriptor") +DEF(ownKeys, "ownKeys") +DEF(add, "add") +DEF(done, "done") +DEF(next, "next") +DEF(values, "values") +DEF(source, "source") +DEF(flags, "flags") +DEF(global, "global") +DEF(unicode, "unicode") +DEF(raw, "raw") +DEF(new_target, "new.target") +DEF(this_active_func, "this.active_func") +DEF(home_object, "") +DEF(computed_field, "") +DEF(static_computed_field, "") /* must come after computed_fields */ +DEF(class_fields_init, "") +DEF(brand, "") +DEF(hash_constructor, "#constructor") +DEF(as, "as") +DEF(from, "from") +DEF(fromAsync, "fromAsync") +DEF(meta, "meta") +DEF(_default_, "*default*") +DEF(_star_, "*") +DEF(Module, "Module") +DEF(then, "then") +DEF(resolve, "resolve") +DEF(reject, "reject") +DEF(promise, "promise") +DEF(proxy, "proxy") +DEF(revoke, "revoke") +DEF(async, "async") +DEF(exec, "exec") +DEF(groups, "groups") +DEF(indices, "indices") +DEF(status, "status") +DEF(reason, "reason") +DEF(globalThis, "globalThis") +DEF(bigint, "bigint") +DEF(not_equal, "not-equal") +DEF(timed_out, "timed-out") +DEF(ok, "ok") +DEF(toJSON, "toJSON") +DEF(maxByteLength, "maxByteLength") +DEF(zip, "zip") +DEF(zipKeyed, "zipKeyed") +/* class names */ +DEF(Object, "Object") +DEF(Array, "Array") +DEF(Error, "Error") +DEF(Number, "Number") +DEF(String, "String") +DEF(Boolean, "Boolean") +DEF(Symbol, "Symbol") +DEF(Arguments, "Arguments") +DEF(Math, "Math") +DEF(JSON, "JSON") +DEF(Date, "Date") +DEF(Function, "Function") +DEF(GeneratorFunction, "GeneratorFunction") +DEF(ForInIterator, "ForInIterator") +DEF(RegExp, "RegExp") +DEF(ArrayBuffer, "ArrayBuffer") +DEF(SharedArrayBuffer, "SharedArrayBuffer") +/* must keep same order as class IDs for typed arrays */ +DEF(Uint8ClampedArray, "Uint8ClampedArray") +DEF(Int8Array, "Int8Array") +DEF(Uint8Array, "Uint8Array") +DEF(Int16Array, "Int16Array") +DEF(Uint16Array, "Uint16Array") +DEF(Int32Array, "Int32Array") +DEF(Uint32Array, "Uint32Array") +DEF(BigInt64Array, "BigInt64Array") +DEF(BigUint64Array, "BigUint64Array") +DEF(Float16Array, "Float16Array") +DEF(Float32Array, "Float32Array") +DEF(Float64Array, "Float64Array") +DEF(DataView, "DataView") +DEF(BigInt, "BigInt") +DEF(WeakRef, "WeakRef") +DEF(FinalizationRegistry, "FinalizationRegistry") +DEF(Map, "Map") +DEF(Set, "Set") /* Map + 1 */ +DEF(WeakMap, "WeakMap") /* Map + 2 */ +DEF(WeakSet, "WeakSet") /* Map + 3 */ +DEF(Iterator, "Iterator") +DEF(IteratorConcat, "Iterator Concat") +DEF(IteratorHelper, "Iterator Helper") +DEF(IteratorWrap, "Iterator Wrap") +DEF(Map_Iterator, "Map Iterator") +DEF(Set_Iterator, "Set Iterator") +DEF(Array_Iterator, "Array Iterator") +DEF(String_Iterator, "String Iterator") +DEF(RegExp_String_Iterator, "RegExp String Iterator") +DEF(Generator, "Generator") +DEF(Proxy, "Proxy") +DEF(Promise, "Promise") +DEF(PromiseResolveFunction, "PromiseResolveFunction") +DEF(PromiseRejectFunction, "PromiseRejectFunction") +DEF(AsyncFunction, "AsyncFunction") +DEF(AsyncFunctionResolve, "AsyncFunctionResolve") +DEF(AsyncFunctionReject, "AsyncFunctionReject") +DEF(AsyncGeneratorFunction, "AsyncGeneratorFunction") +DEF(AsyncGenerator, "AsyncGenerator") +DEF(EvalError, "EvalError") +DEF(RangeError, "RangeError") +DEF(ReferenceError, "ReferenceError") +DEF(SyntaxError, "SyntaxError") +DEF(TypeError, "TypeError") +DEF(URIError, "URIError") +DEF(InternalError, "InternalError") +DEF(DOMException, "DOMException") +DEF(CallSite, "CallSite") +/* private symbols */ +DEF(Private_brand, "") +/* symbols */ +DEF(Symbol_toPrimitive, "Symbol.toPrimitive") +DEF(Symbol_iterator, "Symbol.iterator") +DEF(Symbol_match, "Symbol.match") +DEF(Symbol_matchAll, "Symbol.matchAll") +DEF(Symbol_replace, "Symbol.replace") +DEF(Symbol_search, "Symbol.search") +DEF(Symbol_split, "Symbol.split") +DEF(Symbol_toStringTag, "Symbol.toStringTag") +DEF(Symbol_isConcatSpreadable, "Symbol.isConcatSpreadable") +DEF(Symbol_hasInstance, "Symbol.hasInstance") +DEF(Symbol_species, "Symbol.species") +DEF(Symbol_unscopables, "Symbol.unscopables") +DEF(Symbol_asyncIterator, "Symbol.asyncIterator") + +#endif /* DEF */ + +#undef DEF + JS_ATOM_END, +}; +#define JS_ATOM_LAST_KEYWORD JS_ATOM_super +#define JS_ATOM_LAST_STRICT_KEYWORD JS_ATOM_yield + +static const char js_atom_init[] = +#define DEF(name, str) str "\0" +/* + * QuickJS atom definitions + * + * Copyright (c) 2017-2018 Fabrice Bellard + * Copyright (c) 2017-2018 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef DEF + +/* Note: first atoms are considered as keywords in the parser */ +DEF(null, "null") /* must be first */ +DEF(false, "false") +DEF(true, "true") +DEF(if, "if") +DEF(else, "else") +DEF(return, "return") +DEF(var, "var") +DEF(this, "this") +DEF(delete, "delete") +DEF(void, "void") +DEF(typeof, "typeof") +DEF(new, "new") +DEF(in, "in") +DEF(instanceof, "instanceof") +DEF(do, "do") +DEF(while, "while") +DEF(for, "for") +DEF(break, "break") +DEF(continue, "continue") +DEF(switch, "switch") +DEF(case, "case") +DEF(default, "default") +DEF(throw, "throw") +DEF(try, "try") +DEF(catch, "catch") +DEF(finally, "finally") +DEF(function, "function") +DEF(debugger, "debugger") +DEF(with, "with") +/* FutureReservedWord */ +DEF(class, "class") +DEF(const, "const") +DEF(enum, "enum") +DEF(export, "export") +DEF(extends, "extends") +DEF(import, "import") +DEF(super, "super") +/* FutureReservedWords when parsing strict mode code */ +DEF(implements, "implements") +DEF(interface, "interface") +DEF(let, "let") +DEF(package, "package") +DEF(private, "private") +DEF(protected, "protected") +DEF(public, "public") +DEF(static, "static") +DEF(yield, "yield") +DEF(await, "await") + +/* empty string */ +DEF(empty_string, "") +/* identifiers */ +DEF(keys, "keys") +DEF(size, "size") +DEF(length, "length") +DEF(message, "message") +DEF(cause, "cause") +DEF(errors, "errors") +DEF(stack, "stack") +DEF(name, "name") +DEF(toString, "toString") +DEF(toLocaleString, "toLocaleString") +DEF(valueOf, "valueOf") +DEF(eval, "eval") +DEF(prototype, "prototype") +DEF(constructor, "constructor") +DEF(configurable, "configurable") +DEF(writable, "writable") +DEF(enumerable, "enumerable") +DEF(value, "value") +DEF(get, "get") +DEF(set, "set") +DEF(of, "of") +DEF(__proto__, "__proto__") +DEF(undefined, "undefined") +DEF(number, "number") +DEF(boolean, "boolean") +DEF(string, "string") +DEF(object, "object") +DEF(symbol, "symbol") +DEF(integer, "integer") +DEF(unknown, "unknown") +DEF(arguments, "arguments") +DEF(callee, "callee") +DEF(caller, "caller") +DEF(_eval_, "") +DEF(_ret_, "") +DEF(_var_, "") +DEF(_arg_var_, "") +DEF(_with_, "") +DEF(lastIndex, "lastIndex") +DEF(target, "target") +DEF(index, "index") +DEF(input, "input") +DEF(defineProperties, "defineProperties") +DEF(apply, "apply") +DEF(join, "join") +DEF(concat, "concat") +DEF(split, "split") +DEF(construct, "construct") +DEF(getPrototypeOf, "getPrototypeOf") +DEF(setPrototypeOf, "setPrototypeOf") +DEF(isExtensible, "isExtensible") +DEF(preventExtensions, "preventExtensions") +DEF(has, "has") +DEF(deleteProperty, "deleteProperty") +DEF(defineProperty, "defineProperty") +DEF(getOwnPropertyDescriptor, "getOwnPropertyDescriptor") +DEF(ownKeys, "ownKeys") +DEF(add, "add") +DEF(done, "done") +DEF(next, "next") +DEF(values, "values") +DEF(source, "source") +DEF(flags, "flags") +DEF(global, "global") +DEF(unicode, "unicode") +DEF(raw, "raw") +DEF(new_target, "new.target") +DEF(this_active_func, "this.active_func") +DEF(home_object, "") +DEF(computed_field, "") +DEF(static_computed_field, "") /* must come after computed_fields */ +DEF(class_fields_init, "") +DEF(brand, "") +DEF(hash_constructor, "#constructor") +DEF(as, "as") +DEF(from, "from") +DEF(fromAsync, "fromAsync") +DEF(meta, "meta") +DEF(_default_, "*default*") +DEF(_star_, "*") +DEF(Module, "Module") +DEF(then, "then") +DEF(resolve, "resolve") +DEF(reject, "reject") +DEF(promise, "promise") +DEF(proxy, "proxy") +DEF(revoke, "revoke") +DEF(async, "async") +DEF(exec, "exec") +DEF(groups, "groups") +DEF(indices, "indices") +DEF(status, "status") +DEF(reason, "reason") +DEF(globalThis, "globalThis") +DEF(bigint, "bigint") +DEF(not_equal, "not-equal") +DEF(timed_out, "timed-out") +DEF(ok, "ok") +DEF(toJSON, "toJSON") +DEF(maxByteLength, "maxByteLength") +DEF(zip, "zip") +DEF(zipKeyed, "zipKeyed") +/* class names */ +DEF(Object, "Object") +DEF(Array, "Array") +DEF(Error, "Error") +DEF(Number, "Number") +DEF(String, "String") +DEF(Boolean, "Boolean") +DEF(Symbol, "Symbol") +DEF(Arguments, "Arguments") +DEF(Math, "Math") +DEF(JSON, "JSON") +DEF(Date, "Date") +DEF(Function, "Function") +DEF(GeneratorFunction, "GeneratorFunction") +DEF(ForInIterator, "ForInIterator") +DEF(RegExp, "RegExp") +DEF(ArrayBuffer, "ArrayBuffer") +DEF(SharedArrayBuffer, "SharedArrayBuffer") +/* must keep same order as class IDs for typed arrays */ +DEF(Uint8ClampedArray, "Uint8ClampedArray") +DEF(Int8Array, "Int8Array") +DEF(Uint8Array, "Uint8Array") +DEF(Int16Array, "Int16Array") +DEF(Uint16Array, "Uint16Array") +DEF(Int32Array, "Int32Array") +DEF(Uint32Array, "Uint32Array") +DEF(BigInt64Array, "BigInt64Array") +DEF(BigUint64Array, "BigUint64Array") +DEF(Float16Array, "Float16Array") +DEF(Float32Array, "Float32Array") +DEF(Float64Array, "Float64Array") +DEF(DataView, "DataView") +DEF(BigInt, "BigInt") +DEF(WeakRef, "WeakRef") +DEF(FinalizationRegistry, "FinalizationRegistry") +DEF(Map, "Map") +DEF(Set, "Set") /* Map + 1 */ +DEF(WeakMap, "WeakMap") /* Map + 2 */ +DEF(WeakSet, "WeakSet") /* Map + 3 */ +DEF(Iterator, "Iterator") +DEF(IteratorConcat, "Iterator Concat") +DEF(IteratorHelper, "Iterator Helper") +DEF(IteratorWrap, "Iterator Wrap") +DEF(Map_Iterator, "Map Iterator") +DEF(Set_Iterator, "Set Iterator") +DEF(Array_Iterator, "Array Iterator") +DEF(String_Iterator, "String Iterator") +DEF(RegExp_String_Iterator, "RegExp String Iterator") +DEF(Generator, "Generator") +DEF(Proxy, "Proxy") +DEF(Promise, "Promise") +DEF(PromiseResolveFunction, "PromiseResolveFunction") +DEF(PromiseRejectFunction, "PromiseRejectFunction") +DEF(AsyncFunction, "AsyncFunction") +DEF(AsyncFunctionResolve, "AsyncFunctionResolve") +DEF(AsyncFunctionReject, "AsyncFunctionReject") +DEF(AsyncGeneratorFunction, "AsyncGeneratorFunction") +DEF(AsyncGenerator, "AsyncGenerator") +DEF(EvalError, "EvalError") +DEF(RangeError, "RangeError") +DEF(ReferenceError, "ReferenceError") +DEF(SyntaxError, "SyntaxError") +DEF(TypeError, "TypeError") +DEF(URIError, "URIError") +DEF(InternalError, "InternalError") +DEF(DOMException, "DOMException") +DEF(CallSite, "CallSite") +/* private symbols */ +DEF(Private_brand, "") +/* symbols */ +DEF(Symbol_toPrimitive, "Symbol.toPrimitive") +DEF(Symbol_iterator, "Symbol.iterator") +DEF(Symbol_match, "Symbol.match") +DEF(Symbol_matchAll, "Symbol.matchAll") +DEF(Symbol_replace, "Symbol.replace") +DEF(Symbol_search, "Symbol.search") +DEF(Symbol_split, "Symbol.split") +DEF(Symbol_toStringTag, "Symbol.toStringTag") +DEF(Symbol_isConcatSpreadable, "Symbol.isConcatSpreadable") +DEF(Symbol_hasInstance, "Symbol.hasInstance") +DEF(Symbol_species, "Symbol.species") +DEF(Symbol_unscopables, "Symbol.unscopables") +DEF(Symbol_asyncIterator, "Symbol.asyncIterator") + +#endif /* DEF */ + +#undef DEF +; + +typedef enum OPCodeFormat { +#define FMT(f) OP_FMT_ ## f, +#define DEF(id, size, n_pop, n_push, f) +/* + * QuickJS opcode definitions + * + * Copyright (c) 2017-2018 Fabrice Bellard + * Copyright (c) 2017-2018 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef FMT +FMT(none) +FMT(none_int) +FMT(none_loc) +FMT(none_arg) +FMT(none_var_ref) +FMT(u8) +FMT(i8) +FMT(loc8) +FMT(const8) +FMT(label8) +FMT(u16) +FMT(i16) +FMT(label16) +FMT(npop) +FMT(npopx) +FMT(npop_u16) +FMT(loc) +FMT(arg) +FMT(var_ref) +FMT(u32) +FMT(u32x2) +FMT(i32) +FMT(const) +FMT(label) +FMT(atom) +FMT(atom_u8) +FMT(atom_u16) +FMT(atom_label_u8) +FMT(atom_label_u16) +FMT(label_u16) +#undef FMT +#endif /* FMT */ + +#ifdef DEF + +#ifndef def +#define def(id, size, n_pop, n_push, f) DEF(id, size, n_pop, n_push, f) +#endif + +DEF(invalid, 1, 0, 0, none) /* never emitted */ + +/* push values */ +DEF( push_i32, 5, 0, 1, i32) +DEF( push_const, 5, 0, 1, const) +DEF( fclosure, 5, 0, 1, const) /* must follow push_const */ +DEF(push_atom_value, 5, 0, 1, atom) +DEF( private_symbol, 5, 0, 1, atom) +DEF( undefined, 1, 0, 1, none) +DEF( null, 1, 0, 1, none) +DEF( push_this, 1, 0, 1, none) /* only used at the start of a function */ +DEF( push_false, 1, 0, 1, none) +DEF( push_true, 1, 0, 1, none) +DEF( object, 1, 0, 1, none) +DEF( special_object, 2, 0, 1, u8) /* only used at the start of a function */ +DEF( rest, 3, 0, 1, u16) /* only used at the start of a function */ + +DEF( drop, 1, 1, 0, none) /* a -> */ +DEF( nip, 1, 2, 1, none) /* a b -> b */ +DEF( nip1, 1, 3, 2, none) /* a b c -> b c */ +DEF( dup, 1, 1, 2, none) /* a -> a a */ +DEF( dup1, 1, 2, 3, none) /* a b -> a a b */ +DEF( dup2, 1, 2, 4, none) /* a b -> a b a b */ +DEF( dup3, 1, 3, 6, none) /* a b c -> a b c a b c */ +DEF( insert2, 1, 2, 3, none) /* obj a -> a obj a (dup_x1) */ +DEF( insert3, 1, 3, 4, none) /* obj prop a -> a obj prop a (dup_x2) */ +DEF( insert4, 1, 4, 5, none) /* this obj prop a -> a this obj prop a */ +DEF( perm3, 1, 3, 3, none) /* obj a b -> a obj b */ +DEF( perm4, 1, 4, 4, none) /* obj prop a b -> a obj prop b */ +DEF( perm5, 1, 5, 5, none) /* this obj prop a b -> a this obj prop b */ +DEF( swap, 1, 2, 2, none) /* a b -> b a */ +DEF( swap2, 1, 4, 4, none) /* a b c d -> c d a b */ +DEF( rot3l, 1, 3, 3, none) /* x a b -> a b x */ +DEF( rot3r, 1, 3, 3, none) /* a b x -> x a b */ +DEF( rot4l, 1, 4, 4, none) /* x a b c -> a b c x */ +DEF( rot5l, 1, 5, 5, none) /* x a b c d -> a b c d x */ + +DEF(call_constructor, 3, 2, 1, npop) /* func new.target args -> ret. arguments are not counted in n_pop */ +DEF( call, 3, 1, 1, npop) /* arguments are not counted in n_pop */ +DEF( tail_call, 3, 1, 0, npop) /* arguments are not counted in n_pop */ +DEF( call_method, 3, 2, 1, npop) /* arguments are not counted in n_pop */ +DEF(tail_call_method, 3, 2, 0, npop) /* arguments are not counted in n_pop */ +DEF( array_from, 3, 0, 1, npop) /* arguments are not counted in n_pop */ +DEF( apply, 3, 3, 1, u16) +DEF( return, 1, 1, 0, none) +DEF( return_undef, 1, 0, 0, none) +DEF(check_ctor_return, 1, 1, 2, none) +DEF( check_ctor, 1, 0, 0, none) +DEF( init_ctor, 1, 0, 1, none) +DEF( check_brand, 1, 2, 2, none) /* this_obj func -> this_obj func */ +DEF( add_brand, 1, 2, 0, none) /* this_obj home_obj -> */ +DEF( return_async, 1, 1, 0, none) +DEF( throw, 1, 1, 0, none) +DEF( throw_error, 6, 0, 0, atom_u8) +DEF( eval, 5, 1, 1, npop_u16) /* func args... -> ret_val */ +DEF( apply_eval, 3, 2, 1, u16) /* func array -> ret_eval */ +DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a + bytecode string */ +DEF( get_super, 1, 1, 1, none) +DEF( import, 1, 2, 1, none) /* dynamic module import */ + +DEF( get_var_undef, 5, 0, 1, atom) /* push undefined if the variable does not exist */ +DEF( get_var, 5, 0, 1, atom) /* throw an exception if the variable does not exist */ +DEF( put_var, 5, 1, 0, atom) /* must come after get_var */ +DEF( put_var_init, 5, 1, 0, atom) /* must come after put_var. Used to initialize a global lexical variable */ + +DEF( get_ref_value, 1, 2, 3, none) +DEF( put_ref_value, 1, 3, 0, none) + +DEF( define_var, 6, 0, 0, atom_u8) +DEF(check_define_var, 6, 0, 0, atom_u8) +DEF( define_func, 6, 1, 0, atom_u8) + +// order matters, see IC counterparts +DEF( get_field, 5, 1, 1, atom) +DEF( get_field2, 5, 1, 2, atom) +DEF( put_field, 5, 2, 0, atom) + +DEF( get_private_field, 1, 2, 1, none) /* obj prop -> value */ +DEF( put_private_field, 1, 3, 0, none) /* obj value prop -> */ +DEF(define_private_field, 1, 3, 1, none) /* obj prop value -> obj */ +DEF( get_array_el, 1, 2, 1, none) +DEF( get_array_el2, 1, 2, 2, none) /* obj prop -> obj value */ +DEF( put_array_el, 1, 3, 0, none) +DEF(get_super_value, 1, 3, 1, none) /* this obj prop -> value */ +DEF(put_super_value, 1, 4, 0, none) /* this obj prop value -> */ +DEF( define_field, 5, 2, 1, atom) +DEF( set_name, 5, 1, 1, atom) +DEF(set_name_computed, 1, 2, 2, none) +DEF( set_proto, 1, 2, 1, none) +DEF(set_home_object, 1, 2, 2, none) +DEF(define_array_el, 1, 3, 2, none) +DEF( append, 1, 3, 2, none) /* append enumerated object, update length */ +DEF(copy_data_properties, 2, 3, 3, u8) +DEF( define_method, 6, 2, 1, atom_u8) +DEF(define_method_computed, 2, 3, 1, u8) /* must come after define_method */ +DEF( define_class, 6, 2, 2, atom_u8) /* parent ctor -> ctor proto */ +DEF( define_class_computed, 6, 3, 3, atom_u8) /* field_name parent ctor -> field_name ctor proto (class with computed name) */ + +DEF( get_loc, 3, 0, 1, loc) +DEF( put_loc, 3, 1, 0, loc) /* must come after get_loc */ +DEF( set_loc, 3, 1, 1, loc) /* must come after put_loc */ +DEF( get_arg, 3, 0, 1, arg) +DEF( put_arg, 3, 1, 0, arg) /* must come after get_arg */ +DEF( set_arg, 3, 1, 1, arg) /* must come after put_arg */ +DEF( get_var_ref, 3, 0, 1, var_ref) +DEF( put_var_ref, 3, 1, 0, var_ref) /* must come after get_var_ref */ +DEF( set_var_ref, 3, 1, 1, var_ref) /* must come after put_var_ref */ +DEF(set_loc_uninitialized, 3, 0, 0, loc) +DEF( get_loc_check, 3, 0, 1, loc) +DEF( put_loc_check, 3, 1, 0, loc) /* must come after get_loc_check */ +DEF( put_loc_check_init, 3, 1, 0, loc) +DEF(get_var_ref_check, 3, 0, 1, var_ref) +DEF(put_var_ref_check, 3, 1, 0, var_ref) /* must come after get_var_ref_check */ +DEF(put_var_ref_check_init, 3, 1, 0, var_ref) +DEF( close_loc, 3, 0, 0, loc) +DEF( if_false, 5, 1, 0, label) +DEF( if_true, 5, 1, 0, label) /* must come after if_false */ +DEF( goto, 5, 0, 0, label) /* must come after if_true */ +DEF( catch, 5, 0, 1, label) +DEF( gosub, 5, 0, 0, label) /* used to execute the finally block */ +DEF( ret, 1, 1, 0, none) /* used to return from the finally block */ +DEF( nip_catch, 1, 2, 1, none) /* catch ... a -> a */ + +DEF( to_object, 1, 1, 1, none) +//DEF( to_string, 1, 1, 1, none) +DEF( to_propkey, 1, 1, 1, none) +DEF( to_propkey2, 1, 2, 2, none) + +DEF( with_get_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF( with_put_var, 10, 2, 1, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF(with_delete_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF( with_make_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF( with_get_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF(with_get_ref_undef, 10, 1, 0, atom_label_u8) + +DEF( make_loc_ref, 7, 0, 2, atom_u16) +DEF( make_arg_ref, 7, 0, 2, atom_u16) +DEF(make_var_ref_ref, 7, 0, 2, atom_u16) +DEF( make_var_ref, 5, 0, 2, atom) + +DEF( for_in_start, 1, 1, 1, none) +DEF( for_of_start, 1, 1, 3, none) +DEF(for_await_of_start, 1, 1, 3, none) +DEF( for_in_next, 1, 1, 3, none) +DEF( for_of_next, 2, 3, 5, u8) +DEF(iterator_check_object, 1, 1, 1, none) +DEF(iterator_get_value_done, 1, 1, 2, none) +DEF( iterator_close, 1, 3, 0, none) +DEF( iterator_next, 1, 4, 4, none) +DEF( iterator_call, 2, 4, 5, u8) +DEF( initial_yield, 1, 0, 0, none) +DEF( yield, 1, 1, 2, none) +DEF( yield_star, 1, 1, 2, none) +DEF(async_yield_star, 1, 1, 2, none) +DEF( await, 1, 1, 1, none) + +/* arithmetic/logic operations */ +DEF( neg, 1, 1, 1, none) +DEF( plus, 1, 1, 1, none) +DEF( dec, 1, 1, 1, none) +DEF( inc, 1, 1, 1, none) +DEF( post_dec, 1, 1, 2, none) +DEF( post_inc, 1, 1, 2, none) +DEF( dec_loc, 2, 0, 0, loc8) +DEF( inc_loc, 2, 0, 0, loc8) +DEF( add_loc, 2, 1, 0, loc8) +DEF( not, 1, 1, 1, none) +DEF( lnot, 1, 1, 1, none) +DEF( typeof, 1, 1, 1, none) +DEF( delete, 1, 2, 1, none) +DEF( delete_var, 5, 0, 1, atom) + +/* warning: order matters (see js_parse_assign_expr) */ +DEF( mul, 1, 2, 1, none) +DEF( div, 1, 2, 1, none) +DEF( mod, 1, 2, 1, none) +DEF( add, 1, 2, 1, none) +DEF( sub, 1, 2, 1, none) +DEF( shl, 1, 2, 1, none) +DEF( sar, 1, 2, 1, none) +DEF( shr, 1, 2, 1, none) +DEF( and, 1, 2, 1, none) +DEF( xor, 1, 2, 1, none) +DEF( or, 1, 2, 1, none) +DEF( pow, 1, 2, 1, none) + +DEF( lt, 1, 2, 1, none) +DEF( lte, 1, 2, 1, none) +DEF( gt, 1, 2, 1, none) +DEF( gte, 1, 2, 1, none) +DEF( instanceof, 1, 2, 1, none) +DEF( in, 1, 2, 1, none) +DEF( eq, 1, 2, 1, none) +DEF( neq, 1, 2, 1, none) +DEF( strict_eq, 1, 2, 1, none) +DEF( strict_neq, 1, 2, 1, none) +DEF(is_undefined_or_null, 1, 1, 1, none) +DEF( private_in, 1, 2, 1, none) +DEF(push_bigint_i32, 5, 0, 1, i32) +/* must be the last non short and non temporary opcode */ +DEF( nop, 1, 0, 0, none) + +/* temporary opcodes: never emitted in the final bytecode */ + +def( enter_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ +def( leave_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ + +def( label, 5, 0, 0, label) /* emitted in phase 1, removed in phase 3 */ + +def(scope_get_var_undef, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_get_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_put_var, 7, 1, 0, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_delete_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_make_ref, 11, 0, 2, atom_label_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_get_ref, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_put_var_init, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_get_private_field, 7, 1, 1, atom_u16) /* obj -> value, emitted in phase 1, removed in phase 2 */ +def(scope_get_private_field2, 7, 1, 2, atom_u16) /* obj -> obj value, emitted in phase 1, removed in phase 2 */ +def(scope_put_private_field, 7, 2, 0, atom_u16) /* obj value ->, emitted in phase 1, removed in phase 2 */ +def(scope_in_private_field, 7, 1, 1, atom_u16) /* obj -> res emitted in phase 1, removed in phase 2 */ +def(get_field_opt_chain, 5, 1, 1, atom) /* emitted in phase 1, removed in phase 2 */ +def(get_array_el_opt_chain, 1, 2, 1, none) /* emitted in phase 1, removed in phase 2 */ +def( set_class_name, 5, 1, 1, u32) /* emitted in phase 1, removed in phase 2 */ + +def( source_loc, 9, 0, 0, u32x2) /* emitted in phase 1, removed in phase 3 */ + +DEF( push_minus1, 1, 0, 1, none_int) +DEF( push_0, 1, 0, 1, none_int) +DEF( push_1, 1, 0, 1, none_int) +DEF( push_2, 1, 0, 1, none_int) +DEF( push_3, 1, 0, 1, none_int) +DEF( push_4, 1, 0, 1, none_int) +DEF( push_5, 1, 0, 1, none_int) +DEF( push_6, 1, 0, 1, none_int) +DEF( push_7, 1, 0, 1, none_int) +DEF( push_i8, 2, 0, 1, i8) +DEF( push_i16, 3, 0, 1, i16) +DEF( push_const8, 2, 0, 1, const8) +DEF( fclosure8, 2, 0, 1, const8) /* must follow push_const8 */ +DEF(push_empty_string, 1, 0, 1, none) + +DEF( get_loc8, 2, 0, 1, loc8) +DEF( put_loc8, 2, 1, 0, loc8) +DEF( set_loc8, 2, 1, 1, loc8) + +DEF( get_loc0_loc1, 1, 0, 2, none_loc) +DEF( get_loc0, 1, 0, 1, none_loc) +DEF( get_loc1, 1, 0, 1, none_loc) +DEF( get_loc2, 1, 0, 1, none_loc) +DEF( get_loc3, 1, 0, 1, none_loc) +DEF( put_loc0, 1, 1, 0, none_loc) +DEF( put_loc1, 1, 1, 0, none_loc) +DEF( put_loc2, 1, 1, 0, none_loc) +DEF( put_loc3, 1, 1, 0, none_loc) +DEF( set_loc0, 1, 1, 1, none_loc) +DEF( set_loc1, 1, 1, 1, none_loc) +DEF( set_loc2, 1, 1, 1, none_loc) +DEF( set_loc3, 1, 1, 1, none_loc) +DEF( get_arg0, 1, 0, 1, none_arg) +DEF( get_arg1, 1, 0, 1, none_arg) +DEF( get_arg2, 1, 0, 1, none_arg) +DEF( get_arg3, 1, 0, 1, none_arg) +DEF( put_arg0, 1, 1, 0, none_arg) +DEF( put_arg1, 1, 1, 0, none_arg) +DEF( put_arg2, 1, 1, 0, none_arg) +DEF( put_arg3, 1, 1, 0, none_arg) +DEF( set_arg0, 1, 1, 1, none_arg) +DEF( set_arg1, 1, 1, 1, none_arg) +DEF( set_arg2, 1, 1, 1, none_arg) +DEF( set_arg3, 1, 1, 1, none_arg) +DEF( get_var_ref0, 1, 0, 1, none_var_ref) +DEF( get_var_ref1, 1, 0, 1, none_var_ref) +DEF( get_var_ref2, 1, 0, 1, none_var_ref) +DEF( get_var_ref3, 1, 0, 1, none_var_ref) +DEF( put_var_ref0, 1, 1, 0, none_var_ref) +DEF( put_var_ref1, 1, 1, 0, none_var_ref) +DEF( put_var_ref2, 1, 1, 0, none_var_ref) +DEF( put_var_ref3, 1, 1, 0, none_var_ref) +DEF( set_var_ref0, 1, 1, 1, none_var_ref) +DEF( set_var_ref1, 1, 1, 1, none_var_ref) +DEF( set_var_ref2, 1, 1, 1, none_var_ref) +DEF( set_var_ref3, 1, 1, 1, none_var_ref) + +DEF( get_length, 1, 1, 1, none) + +DEF( if_false8, 2, 1, 0, label8) +DEF( if_true8, 2, 1, 0, label8) /* must come after if_false8 */ +DEF( goto8, 2, 0, 0, label8) /* must come after if_true8 */ +DEF( goto16, 3, 0, 0, label16) + +DEF( call0, 1, 1, 1, npopx) +DEF( call1, 1, 1, 1, npopx) +DEF( call2, 1, 1, 1, npopx) +DEF( call3, 1, 1, 1, npopx) + +DEF( is_undefined, 1, 1, 1, none) +DEF( is_null, 1, 1, 1, none) +DEF(typeof_is_undefined, 1, 1, 1, none) +DEF( typeof_is_function, 1, 1, 1, none) + +#undef DEF +#undef def +#endif /* DEF */ + +#undef DEF +#undef FMT +} OPCodeFormat; + +typedef enum OPCodeEnum { +#define FMT(f) +#define DEF(id, size, n_pop, n_push, f) OP_ ## id, +#define def(id, size, n_pop, n_push, f) +/* + * QuickJS opcode definitions + * + * Copyright (c) 2017-2018 Fabrice Bellard + * Copyright (c) 2017-2018 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef FMT +FMT(none) +FMT(none_int) +FMT(none_loc) +FMT(none_arg) +FMT(none_var_ref) +FMT(u8) +FMT(i8) +FMT(loc8) +FMT(const8) +FMT(label8) +FMT(u16) +FMT(i16) +FMT(label16) +FMT(npop) +FMT(npopx) +FMT(npop_u16) +FMT(loc) +FMT(arg) +FMT(var_ref) +FMT(u32) +FMT(u32x2) +FMT(i32) +FMT(const) +FMT(label) +FMT(atom) +FMT(atom_u8) +FMT(atom_u16) +FMT(atom_label_u8) +FMT(atom_label_u16) +FMT(label_u16) +#undef FMT +#endif /* FMT */ + +#ifdef DEF + +#ifndef def +#define def(id, size, n_pop, n_push, f) DEF(id, size, n_pop, n_push, f) +#endif + +DEF(invalid, 1, 0, 0, none) /* never emitted */ + +/* push values */ +DEF( push_i32, 5, 0, 1, i32) +DEF( push_const, 5, 0, 1, const) +DEF( fclosure, 5, 0, 1, const) /* must follow push_const */ +DEF(push_atom_value, 5, 0, 1, atom) +DEF( private_symbol, 5, 0, 1, atom) +DEF( undefined, 1, 0, 1, none) +DEF( null, 1, 0, 1, none) +DEF( push_this, 1, 0, 1, none) /* only used at the start of a function */ +DEF( push_false, 1, 0, 1, none) +DEF( push_true, 1, 0, 1, none) +DEF( object, 1, 0, 1, none) +DEF( special_object, 2, 0, 1, u8) /* only used at the start of a function */ +DEF( rest, 3, 0, 1, u16) /* only used at the start of a function */ + +DEF( drop, 1, 1, 0, none) /* a -> */ +DEF( nip, 1, 2, 1, none) /* a b -> b */ +DEF( nip1, 1, 3, 2, none) /* a b c -> b c */ +DEF( dup, 1, 1, 2, none) /* a -> a a */ +DEF( dup1, 1, 2, 3, none) /* a b -> a a b */ +DEF( dup2, 1, 2, 4, none) /* a b -> a b a b */ +DEF( dup3, 1, 3, 6, none) /* a b c -> a b c a b c */ +DEF( insert2, 1, 2, 3, none) /* obj a -> a obj a (dup_x1) */ +DEF( insert3, 1, 3, 4, none) /* obj prop a -> a obj prop a (dup_x2) */ +DEF( insert4, 1, 4, 5, none) /* this obj prop a -> a this obj prop a */ +DEF( perm3, 1, 3, 3, none) /* obj a b -> a obj b */ +DEF( perm4, 1, 4, 4, none) /* obj prop a b -> a obj prop b */ +DEF( perm5, 1, 5, 5, none) /* this obj prop a b -> a this obj prop b */ +DEF( swap, 1, 2, 2, none) /* a b -> b a */ +DEF( swap2, 1, 4, 4, none) /* a b c d -> c d a b */ +DEF( rot3l, 1, 3, 3, none) /* x a b -> a b x */ +DEF( rot3r, 1, 3, 3, none) /* a b x -> x a b */ +DEF( rot4l, 1, 4, 4, none) /* x a b c -> a b c x */ +DEF( rot5l, 1, 5, 5, none) /* x a b c d -> a b c d x */ + +DEF(call_constructor, 3, 2, 1, npop) /* func new.target args -> ret. arguments are not counted in n_pop */ +DEF( call, 3, 1, 1, npop) /* arguments are not counted in n_pop */ +DEF( tail_call, 3, 1, 0, npop) /* arguments are not counted in n_pop */ +DEF( call_method, 3, 2, 1, npop) /* arguments are not counted in n_pop */ +DEF(tail_call_method, 3, 2, 0, npop) /* arguments are not counted in n_pop */ +DEF( array_from, 3, 0, 1, npop) /* arguments are not counted in n_pop */ +DEF( apply, 3, 3, 1, u16) +DEF( return, 1, 1, 0, none) +DEF( return_undef, 1, 0, 0, none) +DEF(check_ctor_return, 1, 1, 2, none) +DEF( check_ctor, 1, 0, 0, none) +DEF( init_ctor, 1, 0, 1, none) +DEF( check_brand, 1, 2, 2, none) /* this_obj func -> this_obj func */ +DEF( add_brand, 1, 2, 0, none) /* this_obj home_obj -> */ +DEF( return_async, 1, 1, 0, none) +DEF( throw, 1, 1, 0, none) +DEF( throw_error, 6, 0, 0, atom_u8) +DEF( eval, 5, 1, 1, npop_u16) /* func args... -> ret_val */ +DEF( apply_eval, 3, 2, 1, u16) /* func array -> ret_eval */ +DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a + bytecode string */ +DEF( get_super, 1, 1, 1, none) +DEF( import, 1, 2, 1, none) /* dynamic module import */ + +DEF( get_var_undef, 5, 0, 1, atom) /* push undefined if the variable does not exist */ +DEF( get_var, 5, 0, 1, atom) /* throw an exception if the variable does not exist */ +DEF( put_var, 5, 1, 0, atom) /* must come after get_var */ +DEF( put_var_init, 5, 1, 0, atom) /* must come after put_var. Used to initialize a global lexical variable */ + +DEF( get_ref_value, 1, 2, 3, none) +DEF( put_ref_value, 1, 3, 0, none) + +DEF( define_var, 6, 0, 0, atom_u8) +DEF(check_define_var, 6, 0, 0, atom_u8) +DEF( define_func, 6, 1, 0, atom_u8) + +// order matters, see IC counterparts +DEF( get_field, 5, 1, 1, atom) +DEF( get_field2, 5, 1, 2, atom) +DEF( put_field, 5, 2, 0, atom) + +DEF( get_private_field, 1, 2, 1, none) /* obj prop -> value */ +DEF( put_private_field, 1, 3, 0, none) /* obj value prop -> */ +DEF(define_private_field, 1, 3, 1, none) /* obj prop value -> obj */ +DEF( get_array_el, 1, 2, 1, none) +DEF( get_array_el2, 1, 2, 2, none) /* obj prop -> obj value */ +DEF( put_array_el, 1, 3, 0, none) +DEF(get_super_value, 1, 3, 1, none) /* this obj prop -> value */ +DEF(put_super_value, 1, 4, 0, none) /* this obj prop value -> */ +DEF( define_field, 5, 2, 1, atom) +DEF( set_name, 5, 1, 1, atom) +DEF(set_name_computed, 1, 2, 2, none) +DEF( set_proto, 1, 2, 1, none) +DEF(set_home_object, 1, 2, 2, none) +DEF(define_array_el, 1, 3, 2, none) +DEF( append, 1, 3, 2, none) /* append enumerated object, update length */ +DEF(copy_data_properties, 2, 3, 3, u8) +DEF( define_method, 6, 2, 1, atom_u8) +DEF(define_method_computed, 2, 3, 1, u8) /* must come after define_method */ +DEF( define_class, 6, 2, 2, atom_u8) /* parent ctor -> ctor proto */ +DEF( define_class_computed, 6, 3, 3, atom_u8) /* field_name parent ctor -> field_name ctor proto (class with computed name) */ + +DEF( get_loc, 3, 0, 1, loc) +DEF( put_loc, 3, 1, 0, loc) /* must come after get_loc */ +DEF( set_loc, 3, 1, 1, loc) /* must come after put_loc */ +DEF( get_arg, 3, 0, 1, arg) +DEF( put_arg, 3, 1, 0, arg) /* must come after get_arg */ +DEF( set_arg, 3, 1, 1, arg) /* must come after put_arg */ +DEF( get_var_ref, 3, 0, 1, var_ref) +DEF( put_var_ref, 3, 1, 0, var_ref) /* must come after get_var_ref */ +DEF( set_var_ref, 3, 1, 1, var_ref) /* must come after put_var_ref */ +DEF(set_loc_uninitialized, 3, 0, 0, loc) +DEF( get_loc_check, 3, 0, 1, loc) +DEF( put_loc_check, 3, 1, 0, loc) /* must come after get_loc_check */ +DEF( put_loc_check_init, 3, 1, 0, loc) +DEF(get_var_ref_check, 3, 0, 1, var_ref) +DEF(put_var_ref_check, 3, 1, 0, var_ref) /* must come after get_var_ref_check */ +DEF(put_var_ref_check_init, 3, 1, 0, var_ref) +DEF( close_loc, 3, 0, 0, loc) +DEF( if_false, 5, 1, 0, label) +DEF( if_true, 5, 1, 0, label) /* must come after if_false */ +DEF( goto, 5, 0, 0, label) /* must come after if_true */ +DEF( catch, 5, 0, 1, label) +DEF( gosub, 5, 0, 0, label) /* used to execute the finally block */ +DEF( ret, 1, 1, 0, none) /* used to return from the finally block */ +DEF( nip_catch, 1, 2, 1, none) /* catch ... a -> a */ + +DEF( to_object, 1, 1, 1, none) +//DEF( to_string, 1, 1, 1, none) +DEF( to_propkey, 1, 1, 1, none) +DEF( to_propkey2, 1, 2, 2, none) + +DEF( with_get_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF( with_put_var, 10, 2, 1, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF(with_delete_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF( with_make_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF( with_get_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF(with_get_ref_undef, 10, 1, 0, atom_label_u8) + +DEF( make_loc_ref, 7, 0, 2, atom_u16) +DEF( make_arg_ref, 7, 0, 2, atom_u16) +DEF(make_var_ref_ref, 7, 0, 2, atom_u16) +DEF( make_var_ref, 5, 0, 2, atom) + +DEF( for_in_start, 1, 1, 1, none) +DEF( for_of_start, 1, 1, 3, none) +DEF(for_await_of_start, 1, 1, 3, none) +DEF( for_in_next, 1, 1, 3, none) +DEF( for_of_next, 2, 3, 5, u8) +DEF(iterator_check_object, 1, 1, 1, none) +DEF(iterator_get_value_done, 1, 1, 2, none) +DEF( iterator_close, 1, 3, 0, none) +DEF( iterator_next, 1, 4, 4, none) +DEF( iterator_call, 2, 4, 5, u8) +DEF( initial_yield, 1, 0, 0, none) +DEF( yield, 1, 1, 2, none) +DEF( yield_star, 1, 1, 2, none) +DEF(async_yield_star, 1, 1, 2, none) +DEF( await, 1, 1, 1, none) + +/* arithmetic/logic operations */ +DEF( neg, 1, 1, 1, none) +DEF( plus, 1, 1, 1, none) +DEF( dec, 1, 1, 1, none) +DEF( inc, 1, 1, 1, none) +DEF( post_dec, 1, 1, 2, none) +DEF( post_inc, 1, 1, 2, none) +DEF( dec_loc, 2, 0, 0, loc8) +DEF( inc_loc, 2, 0, 0, loc8) +DEF( add_loc, 2, 1, 0, loc8) +DEF( not, 1, 1, 1, none) +DEF( lnot, 1, 1, 1, none) +DEF( typeof, 1, 1, 1, none) +DEF( delete, 1, 2, 1, none) +DEF( delete_var, 5, 0, 1, atom) + +/* warning: order matters (see js_parse_assign_expr) */ +DEF( mul, 1, 2, 1, none) +DEF( div, 1, 2, 1, none) +DEF( mod, 1, 2, 1, none) +DEF( add, 1, 2, 1, none) +DEF( sub, 1, 2, 1, none) +DEF( shl, 1, 2, 1, none) +DEF( sar, 1, 2, 1, none) +DEF( shr, 1, 2, 1, none) +DEF( and, 1, 2, 1, none) +DEF( xor, 1, 2, 1, none) +DEF( or, 1, 2, 1, none) +DEF( pow, 1, 2, 1, none) + +DEF( lt, 1, 2, 1, none) +DEF( lte, 1, 2, 1, none) +DEF( gt, 1, 2, 1, none) +DEF( gte, 1, 2, 1, none) +DEF( instanceof, 1, 2, 1, none) +DEF( in, 1, 2, 1, none) +DEF( eq, 1, 2, 1, none) +DEF( neq, 1, 2, 1, none) +DEF( strict_eq, 1, 2, 1, none) +DEF( strict_neq, 1, 2, 1, none) +DEF(is_undefined_or_null, 1, 1, 1, none) +DEF( private_in, 1, 2, 1, none) +DEF(push_bigint_i32, 5, 0, 1, i32) +/* must be the last non short and non temporary opcode */ +DEF( nop, 1, 0, 0, none) + +/* temporary opcodes: never emitted in the final bytecode */ + +def( enter_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ +def( leave_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ + +def( label, 5, 0, 0, label) /* emitted in phase 1, removed in phase 3 */ + +def(scope_get_var_undef, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_get_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_put_var, 7, 1, 0, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_delete_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_make_ref, 11, 0, 2, atom_label_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_get_ref, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_put_var_init, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_get_private_field, 7, 1, 1, atom_u16) /* obj -> value, emitted in phase 1, removed in phase 2 */ +def(scope_get_private_field2, 7, 1, 2, atom_u16) /* obj -> obj value, emitted in phase 1, removed in phase 2 */ +def(scope_put_private_field, 7, 2, 0, atom_u16) /* obj value ->, emitted in phase 1, removed in phase 2 */ +def(scope_in_private_field, 7, 1, 1, atom_u16) /* obj -> res emitted in phase 1, removed in phase 2 */ +def(get_field_opt_chain, 5, 1, 1, atom) /* emitted in phase 1, removed in phase 2 */ +def(get_array_el_opt_chain, 1, 2, 1, none) /* emitted in phase 1, removed in phase 2 */ +def( set_class_name, 5, 1, 1, u32) /* emitted in phase 1, removed in phase 2 */ + +def( source_loc, 9, 0, 0, u32x2) /* emitted in phase 1, removed in phase 3 */ + +DEF( push_minus1, 1, 0, 1, none_int) +DEF( push_0, 1, 0, 1, none_int) +DEF( push_1, 1, 0, 1, none_int) +DEF( push_2, 1, 0, 1, none_int) +DEF( push_3, 1, 0, 1, none_int) +DEF( push_4, 1, 0, 1, none_int) +DEF( push_5, 1, 0, 1, none_int) +DEF( push_6, 1, 0, 1, none_int) +DEF( push_7, 1, 0, 1, none_int) +DEF( push_i8, 2, 0, 1, i8) +DEF( push_i16, 3, 0, 1, i16) +DEF( push_const8, 2, 0, 1, const8) +DEF( fclosure8, 2, 0, 1, const8) /* must follow push_const8 */ +DEF(push_empty_string, 1, 0, 1, none) + +DEF( get_loc8, 2, 0, 1, loc8) +DEF( put_loc8, 2, 1, 0, loc8) +DEF( set_loc8, 2, 1, 1, loc8) + +DEF( get_loc0_loc1, 1, 0, 2, none_loc) +DEF( get_loc0, 1, 0, 1, none_loc) +DEF( get_loc1, 1, 0, 1, none_loc) +DEF( get_loc2, 1, 0, 1, none_loc) +DEF( get_loc3, 1, 0, 1, none_loc) +DEF( put_loc0, 1, 1, 0, none_loc) +DEF( put_loc1, 1, 1, 0, none_loc) +DEF( put_loc2, 1, 1, 0, none_loc) +DEF( put_loc3, 1, 1, 0, none_loc) +DEF( set_loc0, 1, 1, 1, none_loc) +DEF( set_loc1, 1, 1, 1, none_loc) +DEF( set_loc2, 1, 1, 1, none_loc) +DEF( set_loc3, 1, 1, 1, none_loc) +DEF( get_arg0, 1, 0, 1, none_arg) +DEF( get_arg1, 1, 0, 1, none_arg) +DEF( get_arg2, 1, 0, 1, none_arg) +DEF( get_arg3, 1, 0, 1, none_arg) +DEF( put_arg0, 1, 1, 0, none_arg) +DEF( put_arg1, 1, 1, 0, none_arg) +DEF( put_arg2, 1, 1, 0, none_arg) +DEF( put_arg3, 1, 1, 0, none_arg) +DEF( set_arg0, 1, 1, 1, none_arg) +DEF( set_arg1, 1, 1, 1, none_arg) +DEF( set_arg2, 1, 1, 1, none_arg) +DEF( set_arg3, 1, 1, 1, none_arg) +DEF( get_var_ref0, 1, 0, 1, none_var_ref) +DEF( get_var_ref1, 1, 0, 1, none_var_ref) +DEF( get_var_ref2, 1, 0, 1, none_var_ref) +DEF( get_var_ref3, 1, 0, 1, none_var_ref) +DEF( put_var_ref0, 1, 1, 0, none_var_ref) +DEF( put_var_ref1, 1, 1, 0, none_var_ref) +DEF( put_var_ref2, 1, 1, 0, none_var_ref) +DEF( put_var_ref3, 1, 1, 0, none_var_ref) +DEF( set_var_ref0, 1, 1, 1, none_var_ref) +DEF( set_var_ref1, 1, 1, 1, none_var_ref) +DEF( set_var_ref2, 1, 1, 1, none_var_ref) +DEF( set_var_ref3, 1, 1, 1, none_var_ref) + +DEF( get_length, 1, 1, 1, none) + +DEF( if_false8, 2, 1, 0, label8) +DEF( if_true8, 2, 1, 0, label8) /* must come after if_false8 */ +DEF( goto8, 2, 0, 0, label8) /* must come after if_true8 */ +DEF( goto16, 3, 0, 0, label16) + +DEF( call0, 1, 1, 1, npopx) +DEF( call1, 1, 1, 1, npopx) +DEF( call2, 1, 1, 1, npopx) +DEF( call3, 1, 1, 1, npopx) + +DEF( is_undefined, 1, 1, 1, none) +DEF( is_null, 1, 1, 1, none) +DEF(typeof_is_undefined, 1, 1, 1, none) +DEF( typeof_is_function, 1, 1, 1, none) + +#undef DEF +#undef def +#endif /* DEF */ + +#undef def +#undef DEF +#undef FMT + OP_COUNT, /* excluding temporary opcodes */ + /* temporary opcodes : overlap with the short opcodes */ + OP_TEMP_START = OP_nop + 1, + OP___dummy = OP_TEMP_START - 1, +#define FMT(f) +#define DEF(id, size, n_pop, n_push, f) +#define def(id, size, n_pop, n_push, f) OP_ ## id, +/* + * QuickJS opcode definitions + * + * Copyright (c) 2017-2018 Fabrice Bellard + * Copyright (c) 2017-2018 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef FMT +FMT(none) +FMT(none_int) +FMT(none_loc) +FMT(none_arg) +FMT(none_var_ref) +FMT(u8) +FMT(i8) +FMT(loc8) +FMT(const8) +FMT(label8) +FMT(u16) +FMT(i16) +FMT(label16) +FMT(npop) +FMT(npopx) +FMT(npop_u16) +FMT(loc) +FMT(arg) +FMT(var_ref) +FMT(u32) +FMT(u32x2) +FMT(i32) +FMT(const) +FMT(label) +FMT(atom) +FMT(atom_u8) +FMT(atom_u16) +FMT(atom_label_u8) +FMT(atom_label_u16) +FMT(label_u16) +#undef FMT +#endif /* FMT */ + +#ifdef DEF + +#ifndef def +#define def(id, size, n_pop, n_push, f) DEF(id, size, n_pop, n_push, f) +#endif + +DEF(invalid, 1, 0, 0, none) /* never emitted */ + +/* push values */ +DEF( push_i32, 5, 0, 1, i32) +DEF( push_const, 5, 0, 1, const) +DEF( fclosure, 5, 0, 1, const) /* must follow push_const */ +DEF(push_atom_value, 5, 0, 1, atom) +DEF( private_symbol, 5, 0, 1, atom) +DEF( undefined, 1, 0, 1, none) +DEF( null, 1, 0, 1, none) +DEF( push_this, 1, 0, 1, none) /* only used at the start of a function */ +DEF( push_false, 1, 0, 1, none) +DEF( push_true, 1, 0, 1, none) +DEF( object, 1, 0, 1, none) +DEF( special_object, 2, 0, 1, u8) /* only used at the start of a function */ +DEF( rest, 3, 0, 1, u16) /* only used at the start of a function */ + +DEF( drop, 1, 1, 0, none) /* a -> */ +DEF( nip, 1, 2, 1, none) /* a b -> b */ +DEF( nip1, 1, 3, 2, none) /* a b c -> b c */ +DEF( dup, 1, 1, 2, none) /* a -> a a */ +DEF( dup1, 1, 2, 3, none) /* a b -> a a b */ +DEF( dup2, 1, 2, 4, none) /* a b -> a b a b */ +DEF( dup3, 1, 3, 6, none) /* a b c -> a b c a b c */ +DEF( insert2, 1, 2, 3, none) /* obj a -> a obj a (dup_x1) */ +DEF( insert3, 1, 3, 4, none) /* obj prop a -> a obj prop a (dup_x2) */ +DEF( insert4, 1, 4, 5, none) /* this obj prop a -> a this obj prop a */ +DEF( perm3, 1, 3, 3, none) /* obj a b -> a obj b */ +DEF( perm4, 1, 4, 4, none) /* obj prop a b -> a obj prop b */ +DEF( perm5, 1, 5, 5, none) /* this obj prop a b -> a this obj prop b */ +DEF( swap, 1, 2, 2, none) /* a b -> b a */ +DEF( swap2, 1, 4, 4, none) /* a b c d -> c d a b */ +DEF( rot3l, 1, 3, 3, none) /* x a b -> a b x */ +DEF( rot3r, 1, 3, 3, none) /* a b x -> x a b */ +DEF( rot4l, 1, 4, 4, none) /* x a b c -> a b c x */ +DEF( rot5l, 1, 5, 5, none) /* x a b c d -> a b c d x */ + +DEF(call_constructor, 3, 2, 1, npop) /* func new.target args -> ret. arguments are not counted in n_pop */ +DEF( call, 3, 1, 1, npop) /* arguments are not counted in n_pop */ +DEF( tail_call, 3, 1, 0, npop) /* arguments are not counted in n_pop */ +DEF( call_method, 3, 2, 1, npop) /* arguments are not counted in n_pop */ +DEF(tail_call_method, 3, 2, 0, npop) /* arguments are not counted in n_pop */ +DEF( array_from, 3, 0, 1, npop) /* arguments are not counted in n_pop */ +DEF( apply, 3, 3, 1, u16) +DEF( return, 1, 1, 0, none) +DEF( return_undef, 1, 0, 0, none) +DEF(check_ctor_return, 1, 1, 2, none) +DEF( check_ctor, 1, 0, 0, none) +DEF( init_ctor, 1, 0, 1, none) +DEF( check_brand, 1, 2, 2, none) /* this_obj func -> this_obj func */ +DEF( add_brand, 1, 2, 0, none) /* this_obj home_obj -> */ +DEF( return_async, 1, 1, 0, none) +DEF( throw, 1, 1, 0, none) +DEF( throw_error, 6, 0, 0, atom_u8) +DEF( eval, 5, 1, 1, npop_u16) /* func args... -> ret_val */ +DEF( apply_eval, 3, 2, 1, u16) /* func array -> ret_eval */ +DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a + bytecode string */ +DEF( get_super, 1, 1, 1, none) +DEF( import, 1, 2, 1, none) /* dynamic module import */ + +DEF( get_var_undef, 5, 0, 1, atom) /* push undefined if the variable does not exist */ +DEF( get_var, 5, 0, 1, atom) /* throw an exception if the variable does not exist */ +DEF( put_var, 5, 1, 0, atom) /* must come after get_var */ +DEF( put_var_init, 5, 1, 0, atom) /* must come after put_var. Used to initialize a global lexical variable */ + +DEF( get_ref_value, 1, 2, 3, none) +DEF( put_ref_value, 1, 3, 0, none) + +DEF( define_var, 6, 0, 0, atom_u8) +DEF(check_define_var, 6, 0, 0, atom_u8) +DEF( define_func, 6, 1, 0, atom_u8) + +// order matters, see IC counterparts +DEF( get_field, 5, 1, 1, atom) +DEF( get_field2, 5, 1, 2, atom) +DEF( put_field, 5, 2, 0, atom) + +DEF( get_private_field, 1, 2, 1, none) /* obj prop -> value */ +DEF( put_private_field, 1, 3, 0, none) /* obj value prop -> */ +DEF(define_private_field, 1, 3, 1, none) /* obj prop value -> obj */ +DEF( get_array_el, 1, 2, 1, none) +DEF( get_array_el2, 1, 2, 2, none) /* obj prop -> obj value */ +DEF( put_array_el, 1, 3, 0, none) +DEF(get_super_value, 1, 3, 1, none) /* this obj prop -> value */ +DEF(put_super_value, 1, 4, 0, none) /* this obj prop value -> */ +DEF( define_field, 5, 2, 1, atom) +DEF( set_name, 5, 1, 1, atom) +DEF(set_name_computed, 1, 2, 2, none) +DEF( set_proto, 1, 2, 1, none) +DEF(set_home_object, 1, 2, 2, none) +DEF(define_array_el, 1, 3, 2, none) +DEF( append, 1, 3, 2, none) /* append enumerated object, update length */ +DEF(copy_data_properties, 2, 3, 3, u8) +DEF( define_method, 6, 2, 1, atom_u8) +DEF(define_method_computed, 2, 3, 1, u8) /* must come after define_method */ +DEF( define_class, 6, 2, 2, atom_u8) /* parent ctor -> ctor proto */ +DEF( define_class_computed, 6, 3, 3, atom_u8) /* field_name parent ctor -> field_name ctor proto (class with computed name) */ + +DEF( get_loc, 3, 0, 1, loc) +DEF( put_loc, 3, 1, 0, loc) /* must come after get_loc */ +DEF( set_loc, 3, 1, 1, loc) /* must come after put_loc */ +DEF( get_arg, 3, 0, 1, arg) +DEF( put_arg, 3, 1, 0, arg) /* must come after get_arg */ +DEF( set_arg, 3, 1, 1, arg) /* must come after put_arg */ +DEF( get_var_ref, 3, 0, 1, var_ref) +DEF( put_var_ref, 3, 1, 0, var_ref) /* must come after get_var_ref */ +DEF( set_var_ref, 3, 1, 1, var_ref) /* must come after put_var_ref */ +DEF(set_loc_uninitialized, 3, 0, 0, loc) +DEF( get_loc_check, 3, 0, 1, loc) +DEF( put_loc_check, 3, 1, 0, loc) /* must come after get_loc_check */ +DEF( put_loc_check_init, 3, 1, 0, loc) +DEF(get_var_ref_check, 3, 0, 1, var_ref) +DEF(put_var_ref_check, 3, 1, 0, var_ref) /* must come after get_var_ref_check */ +DEF(put_var_ref_check_init, 3, 1, 0, var_ref) +DEF( close_loc, 3, 0, 0, loc) +DEF( if_false, 5, 1, 0, label) +DEF( if_true, 5, 1, 0, label) /* must come after if_false */ +DEF( goto, 5, 0, 0, label) /* must come after if_true */ +DEF( catch, 5, 0, 1, label) +DEF( gosub, 5, 0, 0, label) /* used to execute the finally block */ +DEF( ret, 1, 1, 0, none) /* used to return from the finally block */ +DEF( nip_catch, 1, 2, 1, none) /* catch ... a -> a */ + +DEF( to_object, 1, 1, 1, none) +//DEF( to_string, 1, 1, 1, none) +DEF( to_propkey, 1, 1, 1, none) +DEF( to_propkey2, 1, 2, 2, none) + +DEF( with_get_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF( with_put_var, 10, 2, 1, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF(with_delete_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF( with_make_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF( with_get_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF(with_get_ref_undef, 10, 1, 0, atom_label_u8) + +DEF( make_loc_ref, 7, 0, 2, atom_u16) +DEF( make_arg_ref, 7, 0, 2, atom_u16) +DEF(make_var_ref_ref, 7, 0, 2, atom_u16) +DEF( make_var_ref, 5, 0, 2, atom) + +DEF( for_in_start, 1, 1, 1, none) +DEF( for_of_start, 1, 1, 3, none) +DEF(for_await_of_start, 1, 1, 3, none) +DEF( for_in_next, 1, 1, 3, none) +DEF( for_of_next, 2, 3, 5, u8) +DEF(iterator_check_object, 1, 1, 1, none) +DEF(iterator_get_value_done, 1, 1, 2, none) +DEF( iterator_close, 1, 3, 0, none) +DEF( iterator_next, 1, 4, 4, none) +DEF( iterator_call, 2, 4, 5, u8) +DEF( initial_yield, 1, 0, 0, none) +DEF( yield, 1, 1, 2, none) +DEF( yield_star, 1, 1, 2, none) +DEF(async_yield_star, 1, 1, 2, none) +DEF( await, 1, 1, 1, none) + +/* arithmetic/logic operations */ +DEF( neg, 1, 1, 1, none) +DEF( plus, 1, 1, 1, none) +DEF( dec, 1, 1, 1, none) +DEF( inc, 1, 1, 1, none) +DEF( post_dec, 1, 1, 2, none) +DEF( post_inc, 1, 1, 2, none) +DEF( dec_loc, 2, 0, 0, loc8) +DEF( inc_loc, 2, 0, 0, loc8) +DEF( add_loc, 2, 1, 0, loc8) +DEF( not, 1, 1, 1, none) +DEF( lnot, 1, 1, 1, none) +DEF( typeof, 1, 1, 1, none) +DEF( delete, 1, 2, 1, none) +DEF( delete_var, 5, 0, 1, atom) + +/* warning: order matters (see js_parse_assign_expr) */ +DEF( mul, 1, 2, 1, none) +DEF( div, 1, 2, 1, none) +DEF( mod, 1, 2, 1, none) +DEF( add, 1, 2, 1, none) +DEF( sub, 1, 2, 1, none) +DEF( shl, 1, 2, 1, none) +DEF( sar, 1, 2, 1, none) +DEF( shr, 1, 2, 1, none) +DEF( and, 1, 2, 1, none) +DEF( xor, 1, 2, 1, none) +DEF( or, 1, 2, 1, none) +DEF( pow, 1, 2, 1, none) + +DEF( lt, 1, 2, 1, none) +DEF( lte, 1, 2, 1, none) +DEF( gt, 1, 2, 1, none) +DEF( gte, 1, 2, 1, none) +DEF( instanceof, 1, 2, 1, none) +DEF( in, 1, 2, 1, none) +DEF( eq, 1, 2, 1, none) +DEF( neq, 1, 2, 1, none) +DEF( strict_eq, 1, 2, 1, none) +DEF( strict_neq, 1, 2, 1, none) +DEF(is_undefined_or_null, 1, 1, 1, none) +DEF( private_in, 1, 2, 1, none) +DEF(push_bigint_i32, 5, 0, 1, i32) +/* must be the last non short and non temporary opcode */ +DEF( nop, 1, 0, 0, none) + +/* temporary opcodes: never emitted in the final bytecode */ + +def( enter_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ +def( leave_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ + +def( label, 5, 0, 0, label) /* emitted in phase 1, removed in phase 3 */ + +def(scope_get_var_undef, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_get_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_put_var, 7, 1, 0, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_delete_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_make_ref, 11, 0, 2, atom_label_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_get_ref, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_put_var_init, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_get_private_field, 7, 1, 1, atom_u16) /* obj -> value, emitted in phase 1, removed in phase 2 */ +def(scope_get_private_field2, 7, 1, 2, atom_u16) /* obj -> obj value, emitted in phase 1, removed in phase 2 */ +def(scope_put_private_field, 7, 2, 0, atom_u16) /* obj value ->, emitted in phase 1, removed in phase 2 */ +def(scope_in_private_field, 7, 1, 1, atom_u16) /* obj -> res emitted in phase 1, removed in phase 2 */ +def(get_field_opt_chain, 5, 1, 1, atom) /* emitted in phase 1, removed in phase 2 */ +def(get_array_el_opt_chain, 1, 2, 1, none) /* emitted in phase 1, removed in phase 2 */ +def( set_class_name, 5, 1, 1, u32) /* emitted in phase 1, removed in phase 2 */ + +def( source_loc, 9, 0, 0, u32x2) /* emitted in phase 1, removed in phase 3 */ + +DEF( push_minus1, 1, 0, 1, none_int) +DEF( push_0, 1, 0, 1, none_int) +DEF( push_1, 1, 0, 1, none_int) +DEF( push_2, 1, 0, 1, none_int) +DEF( push_3, 1, 0, 1, none_int) +DEF( push_4, 1, 0, 1, none_int) +DEF( push_5, 1, 0, 1, none_int) +DEF( push_6, 1, 0, 1, none_int) +DEF( push_7, 1, 0, 1, none_int) +DEF( push_i8, 2, 0, 1, i8) +DEF( push_i16, 3, 0, 1, i16) +DEF( push_const8, 2, 0, 1, const8) +DEF( fclosure8, 2, 0, 1, const8) /* must follow push_const8 */ +DEF(push_empty_string, 1, 0, 1, none) + +DEF( get_loc8, 2, 0, 1, loc8) +DEF( put_loc8, 2, 1, 0, loc8) +DEF( set_loc8, 2, 1, 1, loc8) + +DEF( get_loc0_loc1, 1, 0, 2, none_loc) +DEF( get_loc0, 1, 0, 1, none_loc) +DEF( get_loc1, 1, 0, 1, none_loc) +DEF( get_loc2, 1, 0, 1, none_loc) +DEF( get_loc3, 1, 0, 1, none_loc) +DEF( put_loc0, 1, 1, 0, none_loc) +DEF( put_loc1, 1, 1, 0, none_loc) +DEF( put_loc2, 1, 1, 0, none_loc) +DEF( put_loc3, 1, 1, 0, none_loc) +DEF( set_loc0, 1, 1, 1, none_loc) +DEF( set_loc1, 1, 1, 1, none_loc) +DEF( set_loc2, 1, 1, 1, none_loc) +DEF( set_loc3, 1, 1, 1, none_loc) +DEF( get_arg0, 1, 0, 1, none_arg) +DEF( get_arg1, 1, 0, 1, none_arg) +DEF( get_arg2, 1, 0, 1, none_arg) +DEF( get_arg3, 1, 0, 1, none_arg) +DEF( put_arg0, 1, 1, 0, none_arg) +DEF( put_arg1, 1, 1, 0, none_arg) +DEF( put_arg2, 1, 1, 0, none_arg) +DEF( put_arg3, 1, 1, 0, none_arg) +DEF( set_arg0, 1, 1, 1, none_arg) +DEF( set_arg1, 1, 1, 1, none_arg) +DEF( set_arg2, 1, 1, 1, none_arg) +DEF( set_arg3, 1, 1, 1, none_arg) +DEF( get_var_ref0, 1, 0, 1, none_var_ref) +DEF( get_var_ref1, 1, 0, 1, none_var_ref) +DEF( get_var_ref2, 1, 0, 1, none_var_ref) +DEF( get_var_ref3, 1, 0, 1, none_var_ref) +DEF( put_var_ref0, 1, 1, 0, none_var_ref) +DEF( put_var_ref1, 1, 1, 0, none_var_ref) +DEF( put_var_ref2, 1, 1, 0, none_var_ref) +DEF( put_var_ref3, 1, 1, 0, none_var_ref) +DEF( set_var_ref0, 1, 1, 1, none_var_ref) +DEF( set_var_ref1, 1, 1, 1, none_var_ref) +DEF( set_var_ref2, 1, 1, 1, none_var_ref) +DEF( set_var_ref3, 1, 1, 1, none_var_ref) + +DEF( get_length, 1, 1, 1, none) + +DEF( if_false8, 2, 1, 0, label8) +DEF( if_true8, 2, 1, 0, label8) /* must come after if_false8 */ +DEF( goto8, 2, 0, 0, label8) /* must come after if_true8 */ +DEF( goto16, 3, 0, 0, label16) + +DEF( call0, 1, 1, 1, npopx) +DEF( call1, 1, 1, 1, npopx) +DEF( call2, 1, 1, 1, npopx) +DEF( call3, 1, 1, 1, npopx) + +DEF( is_undefined, 1, 1, 1, none) +DEF( is_null, 1, 1, 1, none) +DEF(typeof_is_undefined, 1, 1, 1, none) +DEF( typeof_is_function, 1, 1, 1, none) + +#undef DEF +#undef def +#endif /* DEF */ + +#undef def +#undef DEF +#undef FMT + OP_TEMP_END, +} OPCodeEnum; + +static int JS_InitAtoms(JSRuntime *rt); +static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len, + int atom_type); +static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p); +static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b); +static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags); +static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags); +static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, JSValueConst new_target, + int argc, JSValueConst *argv, int flags); +static JSValue JS_CallConstructorInternal(JSContext *ctx, + JSValueConst func_obj, + JSValueConst new_target, + int argc, JSValueConst *argv, int flags); +static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj, + int argc, JSValueConst *argv); +static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom, + int argc, JSValueConst *argv); +static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, + JSValue val, bool is_array_ctor); +static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj, + JSValueConst val, int flags, int scope_idx); +static __maybe_unused void JS_DumpString(JSRuntime *rt, JSString *p); +static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt); +static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p); +static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p); +static __maybe_unused void JS_DumpValue(JSRuntime *rt, JSValueConst val); +static __maybe_unused void JS_DumpAtoms(JSRuntime *rt); +static __maybe_unused void JS_DumpShapes(JSRuntime *rt); + +static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic); +static void js_array_finalizer(JSRuntime *rt, JSValueConst val); +static void js_array_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_mapped_arguments_finalizer(JSRuntime *rt, JSValueConst val); +static void js_mapped_arguments_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_object_data_finalizer(JSRuntime *rt, JSValueConst val); +static void js_object_data_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_c_function_finalizer(JSRuntime *rt, JSValueConst val); +static void js_c_function_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_bytecode_function_finalizer(JSRuntime *rt, JSValueConst val); +static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_bound_function_finalizer(JSRuntime *rt, JSValueConst val); +static void js_bound_function_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValueConst val); +static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_regexp_finalizer(JSRuntime *rt, JSValueConst val); +static void js_array_buffer_finalizer(JSRuntime *rt, JSValueConst val); +static void js_typed_array_finalizer(JSRuntime *rt, JSValueConst val); +static void js_typed_array_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_proxy_finalizer(JSRuntime *rt, JSValueConst val); +static void js_proxy_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_map_finalizer(JSRuntime *rt, JSValueConst val); +static void js_map_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_map_iterator_finalizer(JSRuntime *rt, JSValueConst val); +static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_array_iterator_finalizer(JSRuntime *rt, JSValueConst val); +static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_iterator_concat_finalizer(JSRuntime *rt, JSValueConst val); +static void js_iterator_concat_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_iterator_helper_finalizer(JSRuntime *rt, JSValueConst val); +static void js_iterator_helper_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_iterator_wrap_finalizer(JSRuntime *rt, JSValueConst val); +static void js_iterator_wrap_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_regexp_string_iterator_finalizer(JSRuntime *rt, + JSValueConst val); +static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_generator_finalizer(JSRuntime *rt, JSValueConst val); +static void js_generator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_promise_finalizer(JSRuntime *rt, JSValueConst val); +static void js_promise_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValueConst val); +static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); + +#define HINT_STRING 0 +#define HINT_NUMBER 1 +#define HINT_NONE 2 +#define HINT_FORCE_ORDINARY (1 << 4) // don't try Symbol.toPrimitive +static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint); +static JSValue JS_ToStringFree(JSContext *ctx, JSValue val); +static int JS_ToBoolFree(JSContext *ctx, JSValue val); +static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val); +static int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val); +static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val); +static JSValue JS_ToPropertyKeyInternal(JSContext *ctx, JSValueConst val, + int flags); +static JSValue js_new_string8_len(JSContext *ctx, const char *buf, int len); +static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern, + JSValueConst flags); +static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor, + JSValue pattern, JSValue bc); +static void gc_decref(JSRuntime *rt); +static int JS_NewClass1(JSRuntime *rt, JSClassID class_id, + const JSClassDef *class_def, JSAtom name); +static JSValue js_array_push(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int unshift); +static JSValue js_array_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv); +static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv, int magic); +static JSValue js_object_defineProperty(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic); + +typedef enum JSStrictEqModeEnum { + JS_EQ_STRICT, + JS_EQ_SAME_VALUE, + JS_EQ_SAME_VALUE_ZERO, +} JSStrictEqModeEnum; + +static bool js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, + JSStrictEqModeEnum eq_mode); +static bool js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2); +static bool js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2); +static bool js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2); +static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val); +static JSProperty *add_property(JSContext *ctx, + JSObject *p, JSAtom prop, int prop_flags); +static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags); +static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val); +static JSValue JS_ThrowStackOverflow(JSContext *ctx); +static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx); +static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj); +static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj, + JSValueConst proto_val, bool throw_flag); +static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj); +static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj); +static int js_proxy_isArray(JSContext *ctx, JSValueConst obj); +static int JS_CreateProperty(JSContext *ctx, JSObject *p, + JSAtom prop, JSValueConst val, + JSValueConst getter, JSValueConst setter, + int flags); +static int js_string_memcmp(JSString *p1, JSString *p2, int len); +static void reset_weak_ref(JSRuntime *rt, JSWeakRefRecord **first_weak_ref); +static bool is_valid_weakref_target(JSValueConst val); +static void insert_weakref_record(JSValueConst target, + struct JSWeakRefRecord *wr); +static JSValue js_array_buffer_constructor3(JSContext *ctx, + JSValueConst new_target, + uint64_t len, uint64_t *max_len, + JSClassID class_id, + uint8_t *buf, + JSFreeArrayBufferDataFunc *free_func, + void *opaque, bool alloc_flag); +static void js_array_buffer_free(JSRuntime *rt, void *opaque, void *ptr); +static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj); +static bool array_buffer_is_resizable(const JSArrayBuffer *abuf); +static JSValue js_typed_array_constructor(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv, + int classid); +static JSValue js_typed_array_constructor_ta(JSContext *ctx, + JSValueConst new_target, + JSValueConst src_obj, + int classid, uint32_t len); +static bool is_typed_array(JSClassID class_id); +static bool typed_array_is_immutable(JSObject *p); +static bool typed_array_is_oob(JSObject *p); +static uint32_t typed_array_length(JSObject *p); +static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx); +static JSValue JS_ThrowTypeErrorImmutableArrayBuffer(JSContext *ctx); +static JSValue JS_ThrowTypeErrorArrayBufferOOB(JSContext *ctx); +static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx, + bool is_arg); +static JSVarRef *js_create_var_ref(JSContext *ctx, bool is_gc_object); +static JSValue js_call_generator_function(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, + int flags); +static void js_async_function_resolve_finalizer(JSRuntime *rt, + JSValueConst val); +static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, + const char *input, size_t input_len, + const char *filename, int line, int flags, int scope_idx); +static void js_free_module_def(JSContext *ctx, JSModuleDef *m); +static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m, + JS_MarkFunc *mark_func); +static JSValue js_import_meta(JSContext *ctx); +static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier, + JSValueConst options); +static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref); +static JSValue js_new_promise_capability(JSContext *ctx, + JSValue *resolving_funcs, + JSValueConst ctor); +static __exception int perform_promise_then(JSContext *ctx, + JSValueConst promise, + JSValueConst *resolve_reject, + JSValueConst *cap_resolving_funcs); +static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic); +static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +static JSValue js_promise_resolve_thenable_job(JSContext *ctx, + int argc, JSValueConst *argv); +static bool js_string_eq(JSString *p1, JSString *p2); +static int js_string_compare(JSString *p1, JSString *p2); +static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, + JSValue prop, JSValue val, int flags); +static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val); +static bool JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val); +static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val); +static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc, + JSObject *p, JSAtom prop); +static JSValue JS_GetOwnPropertyNames2(JSContext *ctx, JSValueConst obj1, + int flags, int kind); +static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc); +static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s, + JS_MarkFunc *mark_func); +static void JS_AddIntrinsicBasicObjects(JSContext *ctx); +static void js_free_shape(JSRuntime *rt, JSShape *sh); +static void js_free_shape_null(JSRuntime *rt, JSShape *sh); +static int js_shape_prepare_update(JSContext *ctx, JSObject *p, + JSShapeProperty **pprs); +static int init_shape_hash(JSRuntime *rt); +static __exception int js_get_length32(JSContext *ctx, uint32_t *pres, + JSValueConst obj); +static __exception int js_get_length64(JSContext *ctx, int64_t *pres, + JSValueConst obj); +static __exception int js_set_length64(JSContext *ctx, JSValueConst obj, + int64_t len); +static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len); +static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen, + JSValueConst array_arg); +static JSValue js_create_array(JSContext *ctx, int len, JSValueConst *tab); +static bool js_get_fast_array(JSContext *ctx, JSValue obj, + JSValue **arrpp, uint32_t *countp); +static int expand_fast_array(JSContext *ctx, JSObject *p, uint32_t new_len); +static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx, + JSValue sync_iter); +static void js_c_function_data_finalizer(JSRuntime *rt, JSValueConst val); +static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static JSValue js_call_c_function_data(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_val, + int argc, JSValueConst *argv, int flags); +static void js_c_closure_finalizer(JSRuntime *rt, JSValueConst val); +static JSValue js_call_c_closure(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_val, + int argc, JSValueConst *argv, int flags); +static JSAtom JS_ValueToAtomInternal(JSContext *ctx, JSValueConst val, + int flags); +static JSAtom js_symbol_to_atom(JSContext *ctx, JSValueConst val); +static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h, + JSGCObjectTypeEnum type); +static void remove_gc_object(JSGCObjectHeader *h); +static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s); +static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); +static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, + void *opaque); +static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p, + JSAtom atom, void *opaque); + +static void js_set_uncatchable_error(JSContext *ctx, JSValueConst val, + bool flag); + +static JSValue js_new_callsite(JSContext *ctx, JSCallSiteData *csd); +static void js_new_callsite_data(JSContext *ctx, JSCallSiteData *csd, JSStackFrame *sf); +static void js_new_callsite_data2(JSContext *ctx, JSCallSiteData *csd, const char *filename, int line_num, int col_num); +static void _JS_AddIntrinsicCallSite(JSContext *ctx); + +static void JS_SetOpaqueInternal(JSValueConst obj, void *opaque); + +static const JSClassExoticMethods js_arguments_exotic_methods; +static const JSClassExoticMethods js_string_exotic_methods; +static const JSClassExoticMethods js_proxy_exotic_methods; +static const JSClassExoticMethods js_module_ns_exotic_methods; + +static inline bool double_is_int32(double d) +{ + uint64_t u, e; + JSFloat64Union t; + + t.d = d; + u = t.u64; + + e = ((u >> 52) & 0x7FF) - 1023; + if (e > 30) { + // accept 0, INT32_MIN, reject too large, too small, nan, inf, -0 + return !u || (u == 0xc1e0000000000000); + } else { + // shift out sign, exponent and whole part bits + // value is fractional if remaining low bits are non-zero + return !(u << 12 << e); + } +} + +static JSValue js_float64(double d) +{ + return __JS_NewFloat64(d); +} + +static int compare_u32(uint32_t a, uint32_t b) +{ + return -(a < b) + (b < a); // -1, 0 or 1 +} + +static JSValue js_int32(int32_t v) +{ + return JS_MKVAL(JS_TAG_INT, v); +} + +static JSValue js_uint32(uint32_t v) +{ + if (v <= INT32_MAX) + return js_int32(v); + else + return js_float64(v); +} + +static JSValue js_int64(int64_t v) +{ + if (v >= INT32_MIN && v <= INT32_MAX) + return js_int32(v); + else + return js_float64(v); +} + +static JSValue js_number(double d) +{ + if (double_is_int32(d)) + return js_int32((int32_t)d); + else + return js_float64(d); +} + +JSValue JS_NewNumber(JSContext *ctx, double d) +{ + return js_number(d); +} + +static JSValue js_bool(bool v) +{ + return JS_MKVAL(JS_TAG_BOOL, (v != 0)); +} + +static JSValue js_dup(JSValueConst v) +{ + if (JS_VALUE_HAS_REF_COUNT(v)) { + JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); + p->ref_count++; + } + return unsafe_unconst(v); +} + +JSValue JS_DupValue(JSContext *ctx, JSValueConst v) +{ + return js_dup(v); +} + +JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst v) +{ + return js_dup(v); +} + +static void js_trigger_gc(JSRuntime *rt, size_t size) +{ + bool force_gc; +#ifdef FORCE_GC_AT_MALLOC + force_gc = true; +#else + force_gc = ((rt->malloc_state.malloc_size + size) > + rt->malloc_gc_threshold); +#endif + if (force_gc) { +#ifdef ENABLE_DUMPS // JS_DUMP_GC + if (check_dump_flag(rt, JS_DUMP_GC)) { + printf("GC: size=%zd\n", rt->malloc_state.malloc_size); + } +#endif + JS_RunGC(rt); + rt->malloc_gc_threshold = rt->malloc_state.malloc_size + + (rt->malloc_state.malloc_size >> 1); + } +} + +static size_t js_malloc_usable_size_unknown(const void *ptr) +{ + return 0; +} + +void *js_calloc_rt(JSRuntime *rt, size_t count, size_t size) +{ + void *ptr; + JSMallocState *s; + + /* Do not allocate zero bytes: behavior is platform dependent */ + assert(count != 0 && size != 0); + + if (size > 0) + if (unlikely(count != (count * size) / size)) + return NULL; + + s = &rt->malloc_state; + /* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */ + if (unlikely(s->malloc_size + (count * size) > s->malloc_limit - 1)) + return NULL; + + ptr = rt->mf.js_calloc(s->opaque, count, size); + if (!ptr) + return NULL; + + s->malloc_count++; + s->malloc_size += rt->mf.js_malloc_usable_size(ptr) + MALLOC_OVERHEAD; + return ptr; +} + +void *js_malloc_rt(JSRuntime *rt, size_t size) +{ + void *ptr; + JSMallocState *s; + + /* Do not allocate zero bytes: behavior is platform dependent */ + assert(size != 0); + + s = &rt->malloc_state; + /* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */ + if (unlikely(s->malloc_size + size > s->malloc_limit - 1)) + return NULL; + + ptr = rt->mf.js_malloc(s->opaque, size); + if (!ptr) + return NULL; + + s->malloc_count++; + s->malloc_size += rt->mf.js_malloc_usable_size(ptr) + MALLOC_OVERHEAD; + return ptr; +} + +void js_free_rt(JSRuntime *rt, void *ptr) +{ + JSMallocState *s; + + if (!ptr) + return; + + s = &rt->malloc_state; + s->malloc_count--; + s->malloc_size -= rt->mf.js_malloc_usable_size(ptr) + MALLOC_OVERHEAD; + rt->mf.js_free(s->opaque, ptr); +} + +void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size) +{ + size_t old_size; + JSMallocState *s; + + if (!ptr) { + if (size == 0) + return NULL; + return js_malloc_rt(rt, size); + } + if (unlikely(size == 0)) { + js_free_rt(rt, ptr); + return NULL; + } + old_size = rt->mf.js_malloc_usable_size(ptr); + s = &rt->malloc_state; + /* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */ + if (s->malloc_size + size - old_size > s->malloc_limit - 1) + return NULL; + + ptr = rt->mf.js_realloc(s->opaque, ptr, size); + if (!ptr) + return NULL; + + s->malloc_size += rt->mf.js_malloc_usable_size(ptr) - old_size; + return ptr; +} + +size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr) +{ + return rt->mf.js_malloc_usable_size(ptr); +} + +/** + * This used to be implemented as malloc + memset, but using calloc + * yields better performance in initial, bursty allocations, something useful + * for QuickJS. + * + * More information: https://github.com/quickjs-ng/quickjs/pull/519 + */ +void *js_mallocz_rt(JSRuntime *rt, size_t size) +{ + return js_calloc_rt(rt, 1, size); +} + +/* Throw out of memory in case of error */ +void *js_calloc(JSContext *ctx, size_t count, size_t size) +{ + void *ptr; + ptr = js_calloc_rt(ctx->rt, count, size); + if (unlikely(!ptr)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + return ptr; +} + +/* Throw out of memory in case of error */ +void *js_malloc(JSContext *ctx, size_t size) +{ + void *ptr; + ptr = js_malloc_rt(ctx->rt, size); + if (unlikely(!ptr)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + return ptr; +} + +/* Throw out of memory in case of error */ +void *js_mallocz(JSContext *ctx, size_t size) +{ + void *ptr; + ptr = js_mallocz_rt(ctx->rt, size); + if (unlikely(!ptr)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + return ptr; +} + +void js_free(JSContext *ctx, void *ptr) +{ + js_free_rt(ctx->rt, ptr); +} + +/* Throw out of memory in case of error */ +void *js_realloc(JSContext *ctx, void *ptr, size_t size) +{ + void *ret; + ret = js_realloc_rt(ctx->rt, ptr, size); + if (unlikely(!ret && size != 0)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + return ret; +} + +/* store extra allocated size in *pslack if successful */ +void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack) +{ + void *ret; + ret = js_realloc_rt(ctx->rt, ptr, size); + if (unlikely(!ret && size != 0)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + if (pslack) { + size_t new_size = js_malloc_usable_size_rt(ctx->rt, ret); + *pslack = (new_size > size) ? new_size - size : 0; + } + return ret; +} + +size_t js_malloc_usable_size(JSContext *ctx, const void *ptr) +{ + return js_malloc_usable_size_rt(ctx->rt, ptr); +} + +/* Throw out of memory exception in case of error */ +char *js_strndup(JSContext *ctx, const char *s, size_t n) +{ + char *ptr; + ptr = js_malloc(ctx, n + 1); + if (ptr) { + memcpy(ptr, s, n); + ptr[n] = '\0'; + } + return ptr; +} + +char *js_strdup(JSContext *ctx, const char *str) +{ + return js_strndup(ctx, str, strlen(str)); +} + +static no_inline int js_realloc_array(JSContext *ctx, void **parray, + int elem_size, int *psize, int req_size) +{ + int new_size; + size_t slack; + void *new_array; + /* XXX: potential arithmetic overflow */ + new_size = max_int(req_size, *psize * 3 / 2); + new_array = js_realloc2(ctx, *parray, new_size * elem_size, &slack); + if (!new_array) + return -1; + new_size += slack / elem_size; + *psize = new_size; + *parray = new_array; + return 0; +} + +/* resize the array and update its size if req_size > *psize */ +static inline int js_resize_array(JSContext *ctx, void **parray, int elem_size, + int *psize, int req_size) +{ + if (unlikely(req_size > *psize)) + return js_realloc_array(ctx, parray, elem_size, psize, req_size); + else + return 0; +} + +static void *js_dbuf_realloc(void *ctx, void *ptr, size_t size) +{ + return js_realloc(ctx, ptr, size); +} + +static inline void js_dbuf_init(JSContext *ctx, DynBuf *s) +{ + dbuf_init2(s, ctx, js_dbuf_realloc); +} + +static inline int is_digit(int c) { + return c >= '0' && c <= '9'; +} + +static inline int string_get(JSString *p, int idx) { + return p->is_wide_char ? str16(p)[idx] : str8(p)[idx]; +} + +typedef struct JSClassShortDef { + JSAtom class_name; + JSClassFinalizer *finalizer; + JSClassGCMark *gc_mark; +} JSClassShortDef; + +static JSClassShortDef const js_std_class_def[] = { + { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_OBJECT */ + { JS_ATOM_Array, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARRAY */ + { JS_ATOM_Error, NULL, NULL }, /* JS_CLASS_ERROR */ + { JS_ATOM_Number, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_NUMBER */ + { JS_ATOM_String, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_STRING */ + { JS_ATOM_Boolean, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BOOLEAN */ + { JS_ATOM_Symbol, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_SYMBOL */ + { JS_ATOM_Arguments, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARGUMENTS */ + { JS_ATOM_Arguments, js_mapped_arguments_finalizer, js_mapped_arguments_mark }, /* JS_CLASS_MAPPED_ARGUMENTS */ + { JS_ATOM_Date, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_DATE */ + { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_MODULE_NS */ + { JS_ATOM_Function, js_c_function_finalizer, js_c_function_mark }, /* JS_CLASS_C_FUNCTION */ + { JS_ATOM_Function, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_BYTECODE_FUNCTION */ + { JS_ATOM_Function, js_bound_function_finalizer, js_bound_function_mark }, /* JS_CLASS_BOUND_FUNCTION */ + { JS_ATOM_Function, js_c_function_data_finalizer, js_c_function_data_mark }, /* JS_CLASS_C_FUNCTION_DATA */ + { JS_ATOM_Function, js_c_closure_finalizer, NULL}, /* JS_CLASS_C_CLOSURE */ + { JS_ATOM_GeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_GENERATOR_FUNCTION */ + { JS_ATOM_ForInIterator, js_for_in_iterator_finalizer, js_for_in_iterator_mark }, /* JS_CLASS_FOR_IN_ITERATOR */ + { JS_ATOM_RegExp, js_regexp_finalizer, NULL }, /* JS_CLASS_REGEXP */ + { JS_ATOM_ArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_ARRAY_BUFFER */ + { JS_ATOM_SharedArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_SHARED_ARRAY_BUFFER */ + { JS_ATOM_Uint8ClampedArray, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8C_ARRAY */ + { JS_ATOM_Int8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT8_ARRAY */ + { JS_ATOM_Uint8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8_ARRAY */ + { JS_ATOM_Int16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT16_ARRAY */ + { JS_ATOM_Uint16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT16_ARRAY */ + { JS_ATOM_Int32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT32_ARRAY */ + { JS_ATOM_Uint32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT32_ARRAY */ + { JS_ATOM_BigInt64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_INT64_ARRAY */ + { JS_ATOM_BigUint64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_UINT64_ARRAY */ + { JS_ATOM_Float16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT16_ARRAY */ + { JS_ATOM_Float32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT32_ARRAY */ + { JS_ATOM_Float64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT64_ARRAY */ + { JS_ATOM_DataView, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_DATAVIEW */ + { JS_ATOM_BigInt, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_INT */ + { JS_ATOM_Map, js_map_finalizer, js_map_mark }, /* JS_CLASS_MAP */ + { JS_ATOM_Set, js_map_finalizer, js_map_mark }, /* JS_CLASS_SET */ + { JS_ATOM_WeakMap, js_map_finalizer, NULL }, /* JS_CLASS_WEAKMAP */ + { JS_ATOM_WeakSet, js_map_finalizer, NULL }, /* JS_CLASS_WEAKSET */ + { JS_ATOM_Iterator, NULL, NULL }, /* JS_CLASS_ITERATOR */ + { JS_ATOM_IteratorConcat, js_iterator_concat_finalizer, js_iterator_concat_mark }, /* JS_CLASS_ITERATOR_CONCAT */ + { JS_ATOM_IteratorHelper, js_iterator_helper_finalizer, js_iterator_helper_mark }, /* JS_CLASS_ITERATOR_HELPER */ + { JS_ATOM_IteratorWrap, js_iterator_wrap_finalizer, js_iterator_wrap_mark }, /* JS_CLASS_ITERATOR_WRAP */ + { JS_ATOM_Map_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_MAP_ITERATOR */ + { JS_ATOM_Set_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_SET_ITERATOR */ + { JS_ATOM_Array_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_ARRAY_ITERATOR */ + { JS_ATOM_String_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_STRING_ITERATOR */ + { JS_ATOM_RegExp_String_Iterator, js_regexp_string_iterator_finalizer, js_regexp_string_iterator_mark }, /* JS_CLASS_REGEXP_STRING_ITERATOR */ + { JS_ATOM_Generator, js_generator_finalizer, js_generator_mark }, /* JS_CLASS_GENERATOR */ +}; + +static int init_class_range(JSRuntime *rt, JSClassShortDef const *tab, + int start, int count) +{ + JSClassDef cm_s, *cm = &cm_s; + int i, class_id; + + for(i = 0; i < count; i++) { + class_id = i + start; + memset(cm, 0, sizeof(*cm)); + cm->finalizer = tab[i].finalizer; + cm->gc_mark = tab[i].gc_mark; + if (JS_NewClass1(rt, class_id, cm, tab[i].class_name) < 0) + return -1; + } + return 0; +} + +/* Uses code from LLVM project. */ +static inline uintptr_t js_get_stack_pointer(void) +{ +#if defined(__clang__) || defined(__GNUC__) + return (uintptr_t)__builtin_frame_address(0); +#elif defined(_MSC_VER) + return (uintptr_t)_AddressOfReturnAddress(); +#else + char CharOnStack = 0; + // The volatile store here is intended to escape the local variable, to + // prevent the compiler from optimizing CharOnStack into anything other + // than a char on the stack. + // + // Tested on: MSVC 2015 - 2019, GCC 4.9 - 9, Clang 3.2 - 9, ICC 13 - 19. + char *volatile Ptr = &CharOnStack; + return (uintptr_t) Ptr; +#endif +} + +static inline bool js_check_stack_overflow(JSRuntime *rt, size_t alloca_size) +{ + uintptr_t sp; + sp = js_get_stack_pointer() - alloca_size; + return unlikely(sp < rt->stack_limit); +} + +JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque) +{ + JSRuntime *rt; + JSMallocState ms; + + memset(&ms, 0, sizeof(ms)); + ms.opaque = opaque; + ms.malloc_limit = 0; + + rt = mf->js_calloc(opaque, 1, sizeof(JSRuntime)); + if (!rt) + return NULL; + rt->mf = *mf; + if (!rt->mf.js_malloc_usable_size) { + /* use dummy function if none provided */ + rt->mf.js_malloc_usable_size = js_malloc_usable_size_unknown; + } + /* Inline what js_malloc_rt does since we cannot use it here. */ + ms.malloc_count++; + ms.malloc_size += rt->mf.js_malloc_usable_size(rt) + MALLOC_OVERHEAD; + rt->malloc_state = ms; + rt->malloc_gc_threshold = 256 * 1024; + + init_list_head(&rt->context_list); + init_list_head(&rt->gc_obj_list); + init_list_head(&rt->gc_zero_ref_count_list); + rt->gc_phase = JS_GC_PHASE_NONE; + +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + init_list_head(&rt->string_list); +#endif + init_list_head(&rt->job_list); + + if (JS_InitAtoms(rt)) + goto fail; + + /* create the object, array and function classes */ + if (init_class_range(rt, js_std_class_def, JS_CLASS_OBJECT, + countof(js_std_class_def)) < 0) + goto fail; + rt->class_array[JS_CLASS_ARGUMENTS].exotic = &js_arguments_exotic_methods; + rt->class_array[JS_CLASS_MAPPED_ARGUMENTS].exotic = &js_arguments_exotic_methods; + rt->class_array[JS_CLASS_STRING].exotic = &js_string_exotic_methods; + rt->class_array[JS_CLASS_MODULE_NS].exotic = &js_module_ns_exotic_methods; + + rt->class_array[JS_CLASS_C_FUNCTION].call = js_call_c_function; + rt->class_array[JS_CLASS_C_FUNCTION_DATA].call = js_call_c_function_data; + rt->class_array[JS_CLASS_C_CLOSURE].call = js_call_c_closure; + rt->class_array[JS_CLASS_BOUND_FUNCTION].call = js_call_bound_function; + rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_call_generator_function; + if (init_shape_hash(rt)) + goto fail; + + rt->js_class_id_alloc = JS_CLASS_INIT_COUNT; + + rt->stack_size = JS_DEFAULT_STACK_SIZE; +#ifdef __wasi__ + rt->stack_size = 0; +#endif + + JS_UpdateStackTop(rt); + + rt->current_exception = JS_UNINITIALIZED; + + return rt; + fail: + JS_FreeRuntime(rt); + return NULL; +} + +void *JS_GetRuntimeOpaque(JSRuntime *rt) +{ + return rt->user_opaque; +} + +void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque) +{ + rt->user_opaque = opaque; +} + +int JS_AddRuntimeFinalizer(JSRuntime *rt, JSRuntimeFinalizer *finalizer, + void *arg) +{ + JSRuntimeFinalizerState *fs = js_malloc_rt(rt, sizeof(*fs)); + if (!fs) + return -1; + fs->next = rt->finalizers; + fs->finalizer = finalizer; + fs->arg = arg; + rt->finalizers = fs; + return 0; +} + +static void *js_def_calloc(void *opaque, size_t count, size_t size) +{ + return calloc(count, size); +} + +static void *js_def_malloc(void *opaque, size_t size) +{ + return malloc(size); +} + +static void js_def_free(void *opaque, void *ptr) +{ + free(ptr); +} + +static void *js_def_realloc(void *opaque, void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +static const JSMallocFunctions def_malloc_funcs = { + js_def_calloc, + js_def_malloc, + js_def_free, + js_def_realloc, + js__malloc_usable_size +}; + +JSRuntime *JS_NewRuntime(void) +{ + return JS_NewRuntime2(&def_malloc_funcs, NULL); +} + +void JS_SetMemoryLimit(JSRuntime *rt, size_t limit) +{ + rt->malloc_state.malloc_limit = limit; +} + +void JS_SetDumpFlags(JSRuntime *rt, uint64_t flags) +{ +#ifdef ENABLE_DUMPS + rt->dump_flags = flags; +#endif +} + +uint64_t JS_GetDumpFlags(JSRuntime *rt) +{ +#ifdef ENABLE_DUMPS + return rt->dump_flags; +#else + return 0; +#endif +} + +size_t JS_GetGCThreshold(JSRuntime *rt) { + return rt->malloc_gc_threshold; +} + +/* use -1 to disable automatic GC */ +void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold) +{ + rt->malloc_gc_threshold = gc_threshold; +} + +#define malloc(s) malloc_is_forbidden(s) +#define free(p) free_is_forbidden(p) +#define realloc(p,s) realloc_is_forbidden(p,s) + +void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque) +{ + rt->interrupt_handler = cb; + rt->interrupt_opaque = opaque; +} + +void JS_SetCanBlock(JSRuntime *rt, bool can_block) +{ + rt->can_block = can_block; +} + +void JS_SetSharedArrayBufferFunctions(JSRuntime *rt, + const JSSharedArrayBufferFunctions *sf) +{ + rt->sab_funcs = *sf; +} + +/* return 0 if OK, < 0 if exception */ +int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func, + int argc, JSValueConst *argv) +{ + JSRuntime *rt = ctx->rt; + JSJobEntry *e; + int i; + + assert(!rt->in_free); + + e = js_malloc(ctx, sizeof(*e) + argc * sizeof(JSValue)); + if (!e) + return -1; + e->ctx = ctx; + e->job_func = job_func; + e->argc = argc; + for(i = 0; i < argc; i++) { + e->argv[i] = js_dup(argv[i]); + } + list_add_tail(&e->link, &rt->job_list); + return 0; +} + +bool JS_IsJobPending(JSRuntime *rt) +{ + return !list_empty(&rt->job_list); +} + +/* return < 0 if exception, 0 if no job pending, 1 if a job was + executed successfully. the context of the job is stored in '*pctx' */ +int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx) +{ + JSContext *ctx; + JSJobEntry *e; + JSValue res; + int i, ret; + + if (list_empty(&rt->job_list)) { + *pctx = NULL; + return 0; + } + + /* get the first pending job and execute it */ + e = list_entry(rt->job_list.next, JSJobEntry, link); + list_del(&e->link); + ctx = e->ctx; + res = e->job_func(e->ctx, e->argc, vc(e->argv)); + for(i = 0; i < e->argc; i++) + JS_FreeValue(ctx, e->argv[i]); + if (JS_IsException(res)) + ret = -1; + else + ret = 1; + JS_FreeValue(ctx, res); + js_free(ctx, e); + *pctx = ctx; + return ret; +} + +static inline uint32_t atom_get_free(const JSAtomStruct *p) +{ + return (uintptr_t)p >> 1; +} + +static inline bool atom_is_free(const JSAtomStruct *p) +{ + return (uintptr_t)p & 1; +} + +static inline JSAtomStruct *atom_set_free(uint32_t v) +{ + return (JSAtomStruct *)(((uintptr_t)v << 1) | 1); +} + +/* Note: the string contents are uninitialized */ +static JSString *js_alloc_string_rt(JSRuntime *rt, int max_len, int is_wide_char) +{ + JSString *str; + str = js_malloc_rt(rt, sizeof(JSString) + (max_len << is_wide_char) + 1 - is_wide_char); + if (unlikely(!str)) + return NULL; + str->header.ref_count = 1; + str->is_wide_char = is_wide_char; + str->len = max_len; + str->kind = JS_STRING_KIND_NORMAL; + str->atom_type = 0; + str->hash = 0; /* optional but costless */ + str->hash_next = 0; /* optional */ +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + list_add_tail(&str->link, &rt->string_list); +#endif + return str; +} + +static JSString *js_alloc_string(JSContext *ctx, int max_len, int is_wide_char) +{ + JSString *p; + p = js_alloc_string_rt(ctx->rt, max_len, is_wide_char); + if (unlikely(!p)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + return p; +} + +static inline void js_free_string0(JSRuntime *rt, JSString *str); + +/* same as JS_FreeValueRT() but faster */ +static inline void js_free_string(JSRuntime *rt, JSString *str) +{ + if (--str->header.ref_count <= 0) + js_free_string0(rt, str); +} + +static inline void js_free_string0(JSRuntime *rt, JSString *str) +{ + JSStringSlice *slice; + + if (str->atom_type) { + JS_FreeAtomStruct(rt, str); + } else { +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + list_del(&str->link); +#endif + switch (str->kind) { + case JS_STRING_KIND_SLICE: + slice = (void *)&str[1]; + js_free_string(rt, slice->parent); // safe, recurses only 1 level + break; + case JS_STRING_KIND_INDIRECT: + js_free_rt(rt, strv(str)); + break; + } + js_free_rt(rt, str); + } +} + +void JS_SetRuntimeInfo(JSRuntime *rt, const char *s) +{ + if (rt) + rt->rt_info = s; +} + +void JS_FreeRuntime(JSRuntime *rt) +{ + struct list_head *el, *el1; + int i; + + rt->in_free = true; + JS_FreeValueRT(rt, rt->current_exception); + + list_for_each_safe(el, el1, &rt->job_list) { + JSJobEntry *e = list_entry(el, JSJobEntry, link); + for(i = 0; i < e->argc; i++) + JS_FreeValueRT(rt, e->argv[i]); + js_free_rt(rt, e); + } + init_list_head(&rt->job_list); + + JS_RunGC(rt); + +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + /* leaking objects */ + if (check_dump_flag(rt, JS_DUMP_LEAKS)) { + bool header_done; + JSGCObjectHeader *p; + int count; + + /* remove the internal refcounts to display only the object + referenced externally */ + list_for_each(el, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + p->mark = 0; + } + gc_decref(rt); + + header_done = false; + list_for_each(el, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + if (p->ref_count != 0) { + if (!header_done) { + printf("Object leaks:\n"); + JS_DumpObjectHeader(rt); + header_done = true; + } + JS_DumpGCObject(rt, p); + } + } + + count = 0; + list_for_each(el, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + if (p->ref_count == 0) { + count++; + } + } + if (count != 0) + printf("Secondary object leaks: %d\n", count); + } +#endif + + assert(list_empty(&rt->gc_obj_list)); + + /* free the classes */ + for(i = 0; i < rt->class_count; i++) { + JSClass *cl = &rt->class_array[i]; + if (cl->class_id != 0) { + JS_FreeAtomRT(rt, cl->class_name); + } + } + js_free_rt(rt, rt->class_array); + +#ifdef ENABLE_DUMPS // JS_DUMP_ATOM_LEAKS + /* only the atoms defined in JS_InitAtoms() should be left */ + if (check_dump_flag(rt, JS_DUMP_ATOM_LEAKS)) { + bool header_done = false; + + for(i = 0; i < rt->atom_size; i++) { + JSAtomStruct *p = rt->atom_array[i]; + if (!atom_is_free(p) /* && p->str*/) { + if (i >= JS_ATOM_END || p->header.ref_count != 1) { + if (!header_done) { + header_done = true; + if (rt->rt_info) { + printf("%s:1: atom leakage:", rt->rt_info); + } else { + printf("Atom leaks:\n" + " %6s %6s %s\n", + "ID", "REFCNT", "NAME"); + } + } + if (rt->rt_info) { + printf(" "); + } else { + printf(" %6u %6u ", i, p->header.ref_count); + } + switch (p->atom_type) { + case JS_ATOM_TYPE_STRING: + JS_DumpString(rt, p); + break; + case JS_ATOM_TYPE_GLOBAL_SYMBOL: + printf("Symbol.for("); + JS_DumpString(rt, p); + printf(")"); + break; + case JS_ATOM_TYPE_SYMBOL: + if (p->hash == JS_ATOM_HASH_SYMBOL) { + printf("Symbol("); + JS_DumpString(rt, p); + printf(")"); + } else { + printf("Private("); + JS_DumpString(rt, p); + printf(")"); + } + break; + } + if (rt->rt_info) { + printf(":%u", p->header.ref_count); + } else { + printf("\n"); + } + } + } + } + if (rt->rt_info && header_done) + printf("\n"); + } +#endif + + /* free the atoms */ + for(i = 0; i < rt->atom_size; i++) { + JSAtomStruct *p = rt->atom_array[i]; + if (!atom_is_free(p)) { +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + list_del(&p->link); +#endif + js_free_rt(rt, p); + } + } + js_free_rt(rt, rt->atom_array); + js_free_rt(rt, rt->atom_hash); + js_free_rt(rt, rt->shape_hash); +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + if (check_dump_flag(rt, JS_DUMP_LEAKS) && !list_empty(&rt->string_list)) { + if (rt->rt_info) { + printf("%s:1: string leakage:", rt->rt_info); + } else { + printf("String leaks:\n" + " %6s %s\n", + "REFCNT", "VALUE"); + } + list_for_each_safe(el, el1, &rt->string_list) { + JSString *str = list_entry(el, JSString, link); + if (rt->rt_info) { + printf(" "); + } else { + printf(" %6u ", str->header.ref_count); + } + JS_DumpString(rt, str); + if (rt->rt_info) { + printf(":%u", str->header.ref_count); + } else { + printf("\n"); + } + list_del(&str->link); + js_free_rt(rt, str); + } + if (rt->rt_info) + printf("\n"); + } +#endif + + while (rt->finalizers) { + JSRuntimeFinalizerState *fs = rt->finalizers; + rt->finalizers = fs->next; + fs->finalizer(rt, fs->arg); + js_free_rt(rt, fs); + } + +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + if (check_dump_flag(rt, JS_DUMP_LEAKS)) { + JSMallocState *s = &rt->malloc_state; + if (s->malloc_count > 1) { + if (rt->rt_info) + printf("%s:1: ", rt->rt_info); + printf("Memory leak: %zd bytes lost in %zd block%s\n", + s->malloc_size - sizeof(JSRuntime), + s->malloc_count - 1, &"s"[s->malloc_count == 2]); + } + } +#endif + + { + JSMallocState *ms = &rt->malloc_state; + rt->mf.js_free(ms->opaque, rt); + } +} + +JSContext *JS_NewContextRaw(JSRuntime *rt) +{ + JSContext *ctx; + int i; + + ctx = js_mallocz_rt(rt, sizeof(JSContext)); + if (!ctx) + return NULL; + ctx->header.ref_count = 1; + add_gc_object(rt, &ctx->header, JS_GC_OBJ_TYPE_JS_CONTEXT); + + ctx->class_proto = js_malloc_rt(rt, sizeof(ctx->class_proto[0]) * + rt->class_count); + if (!ctx->class_proto) { + js_free_rt(rt, ctx); + return NULL; + } + ctx->rt = rt; + list_add_tail(&ctx->link, &rt->context_list); + for(i = 0; i < rt->class_count; i++) + ctx->class_proto[i] = JS_NULL; + ctx->array_ctor = JS_NULL; + ctx->iterator_ctor = JS_NULL; + ctx->iterator_ctor_getset = JS_NULL; + ctx->regexp_ctor = JS_NULL; + ctx->promise_ctor = JS_NULL; + ctx->error_ctor = JS_NULL; + ctx->error_back_trace = JS_UNDEFINED; + ctx->error_prepare_stack = JS_UNDEFINED; + ctx->error_stack_trace_limit = js_int32(10); + init_list_head(&ctx->loaded_modules); + + JS_AddIntrinsicBasicObjects(ctx); + return ctx; +} + +JSContext *JS_NewContext(JSRuntime *rt) +{ + JSContext *ctx; + + ctx = JS_NewContextRaw(rt); + if (!ctx) + return NULL; + + JS_AddIntrinsicBaseObjects(ctx); + JS_AddIntrinsicDate(ctx); + JS_AddIntrinsicEval(ctx); + JS_AddIntrinsicRegExp(ctx); + JS_AddIntrinsicJSON(ctx); + JS_AddIntrinsicProxy(ctx); + JS_AddIntrinsicMapSet(ctx); + JS_AddIntrinsicTypedArrays(ctx); + JS_AddIntrinsicPromise(ctx); + JS_AddIntrinsicBigInt(ctx); + JS_AddIntrinsicWeakRef(ctx); + JS_AddIntrinsicDOMException(ctx); + + JS_AddPerformance(ctx); + + return ctx; +} + +void *JS_GetContextOpaque(JSContext *ctx) +{ + return ctx->user_opaque; +} + +void JS_SetContextOpaque(JSContext *ctx, void *opaque) +{ + ctx->user_opaque = opaque; +} + +/* set the new value and free the old value after (freeing the value + can reallocate the object data) */ +static inline void set_value(JSContext *ctx, JSValue *pval, JSValue new_val) +{ + JSValue old_val; + old_val = *pval; + *pval = new_val; + JS_FreeValue(ctx, old_val); +} + +void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj) +{ + assert(class_id < ctx->rt->class_count); + set_value(ctx, &ctx->class_proto[class_id], obj); +} + +JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id) +{ + assert(class_id < ctx->rt->class_count); + return js_dup(ctx->class_proto[class_id]); +} + +JSValue JS_GetFunctionProto(JSContext *ctx) +{ + return js_dup(ctx->function_proto); +} + +typedef enum JSFreeModuleEnum { + JS_FREE_MODULE_ALL, + JS_FREE_MODULE_NOT_RESOLVED, +} JSFreeModuleEnum; + +/* XXX: would be more efficient with separate module lists */ +static void js_free_modules(JSContext *ctx, JSFreeModuleEnum flag) +{ + struct list_head *el, *el1; + list_for_each_safe(el, el1, &ctx->loaded_modules) { + JSModuleDef *m = list_entry(el, JSModuleDef, link); + if (flag == JS_FREE_MODULE_ALL || + (flag == JS_FREE_MODULE_NOT_RESOLVED && !m->resolved)) { + js_free_module_def(ctx, m); + } + } +} + +JSContext *JS_DupContext(JSContext *ctx) +{ + ctx->header.ref_count++; + return ctx; +} + +/* used by the GC */ +static void JS_MarkContext(JSRuntime *rt, JSContext *ctx, + JS_MarkFunc *mark_func) +{ + int i; + struct list_head *el; + + /* modules are not seen by the GC, so we directly mark the objects + referenced by each module */ + list_for_each(el, &ctx->loaded_modules) { + JSModuleDef *m = list_entry(el, JSModuleDef, link); + js_mark_module_def(rt, m, mark_func); + } + + JS_MarkValue(rt, ctx->global_obj, mark_func); + JS_MarkValue(rt, ctx->global_var_obj, mark_func); + + JS_MarkValue(rt, ctx->throw_type_error, mark_func); + JS_MarkValue(rt, ctx->eval_obj, mark_func); + + JS_MarkValue(rt, ctx->array_proto_values, mark_func); + for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) { + JS_MarkValue(rt, ctx->native_error_proto[i], mark_func); + } + JS_MarkValue(rt, ctx->error_ctor, mark_func); + JS_MarkValue(rt, ctx->error_back_trace, mark_func); + JS_MarkValue(rt, ctx->error_prepare_stack, mark_func); + JS_MarkValue(rt, ctx->error_stack_trace_limit, mark_func); + for(i = 0; i < rt->class_count; i++) { + JS_MarkValue(rt, ctx->class_proto[i], mark_func); + } + JS_MarkValue(rt, ctx->iterator_ctor, mark_func); + JS_MarkValue(rt, ctx->iterator_ctor_getset, mark_func); + JS_MarkValue(rt, ctx->async_iterator_proto, mark_func); + JS_MarkValue(rt, ctx->promise_ctor, mark_func); + JS_MarkValue(rt, ctx->array_ctor, mark_func); + JS_MarkValue(rt, ctx->regexp_ctor, mark_func); + JS_MarkValue(rt, ctx->function_ctor, mark_func); + JS_MarkValue(rt, ctx->function_proto, mark_func); + + if (ctx->array_shape) + mark_func(rt, &ctx->array_shape->header); + + if (ctx->arguments_shape) + mark_func(rt, &ctx->arguments_shape->header); + + if (ctx->mapped_arguments_shape) + mark_func(rt, &ctx->mapped_arguments_shape->header); + + if (ctx->regexp_shape) + mark_func(rt, &ctx->regexp_shape->header); + + if (ctx->regexp_result_shape) + mark_func(rt, &ctx->regexp_result_shape->header); +} + +void JS_FreeContext(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + int i; + + if (--ctx->header.ref_count > 0) + return; + assert(ctx->header.ref_count == 0); + +#ifdef ENABLE_DUMPS // JS_DUMP_ATOMS + if (check_dump_flag(rt, JS_DUMP_ATOMS)) + JS_DumpAtoms(ctx->rt); +#endif +#ifdef ENABLE_DUMPS // JS_DUMP_SHAPES + if (check_dump_flag(rt, JS_DUMP_SHAPES)) + JS_DumpShapes(ctx->rt); +#endif +#ifdef ENABLE_DUMPS // JS_DUMP_OBJECTS + if (check_dump_flag(rt, JS_DUMP_OBJECTS)) { + struct list_head *el; + JSGCObjectHeader *p; + printf("JSObjects: {\n"); + JS_DumpObjectHeader(ctx->rt); + list_for_each(el, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + JS_DumpGCObject(rt, p); + } + printf("}\n"); + } +#endif +#ifdef ENABLE_DUMPS // JS_DUMP_MEM + if (check_dump_flag(rt, JS_DUMP_MEM)) { + JSMemoryUsage stats; + JS_ComputeMemoryUsage(rt, &stats); + JS_DumpMemoryUsage(stdout, &stats, rt); + } +#endif + + js_free_modules(ctx, JS_FREE_MODULE_ALL); + + JS_FreeValue(ctx, ctx->global_obj); + JS_FreeValue(ctx, ctx->global_var_obj); + + JS_FreeValue(ctx, ctx->throw_type_error); + JS_FreeValue(ctx, ctx->eval_obj); + + JS_FreeValue(ctx, ctx->array_proto_values); + for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) { + JS_FreeValue(ctx, ctx->native_error_proto[i]); + } + JS_FreeValue(ctx, ctx->error_ctor); + JS_FreeValue(ctx, ctx->error_back_trace); + JS_FreeValue(ctx, ctx->error_prepare_stack); + JS_FreeValue(ctx, ctx->error_stack_trace_limit); + for(i = 0; i < rt->class_count; i++) { + JS_FreeValue(ctx, ctx->class_proto[i]); + } + js_free_rt(rt, ctx->class_proto); + JS_FreeValue(ctx, ctx->iterator_ctor); + JS_FreeValue(ctx, ctx->iterator_ctor_getset); + JS_FreeValue(ctx, ctx->async_iterator_proto); + JS_FreeValue(ctx, ctx->promise_ctor); + JS_FreeValue(ctx, ctx->array_ctor); + JS_FreeValue(ctx, ctx->regexp_ctor); + JS_FreeValue(ctx, ctx->function_ctor); + JS_FreeValue(ctx, ctx->function_proto); + + js_free_shape_null(ctx->rt, ctx->array_shape); + js_free_shape_null(ctx->rt, ctx->arguments_shape); + js_free_shape_null(ctx->rt, ctx->mapped_arguments_shape); + js_free_shape_null(ctx->rt, ctx->regexp_shape); + js_free_shape_null(ctx->rt, ctx->regexp_result_shape); + + list_del(&ctx->link); + remove_gc_object(&ctx->header); + js_free_rt(ctx->rt, ctx); +} + +JSRuntime *JS_GetRuntime(JSContext *ctx) +{ + return ctx->rt; +} + +static void update_stack_limit(JSRuntime *rt) +{ +#if defined(__wasi__) + rt->stack_limit = 0; /* no limit */ +#else + if (rt->stack_size == 0) { + rt->stack_limit = 0; /* no limit */ + } else { + rt->stack_limit = rt->stack_top - rt->stack_size; + } +#endif +} + +void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size) +{ + rt->stack_size = stack_size; + update_stack_limit(rt); +} + +void JS_UpdateStackTop(JSRuntime *rt) +{ + rt->stack_top = js_get_stack_pointer(); + update_stack_limit(rt); +} + +static inline bool is_strict_mode(JSContext *ctx) +{ + JSStackFrame *sf = ctx->rt->current_stack_frame; + return sf && sf->is_strict_mode; +} + +/* JSAtom support */ + +#define JS_ATOM_TAG_INT (1U << 31) +#define JS_ATOM_MAX_INT (JS_ATOM_TAG_INT - 1) +#define JS_ATOM_MAX ((1U << 30) - 1) + +/* return the max count from the hash size */ +#define JS_ATOM_COUNT_RESIZE(n) ((n) * 2) + +static inline bool __JS_AtomIsConst(JSAtom v) +{ + return (int32_t)v < JS_ATOM_END; +} + +static inline bool __JS_AtomIsTaggedInt(JSAtom v) +{ + return (v & JS_ATOM_TAG_INT) != 0; +} + +static inline JSAtom __JS_AtomFromUInt32(uint32_t v) +{ + return v | JS_ATOM_TAG_INT; +} + +static inline uint32_t __JS_AtomToUInt32(JSAtom atom) +{ + return atom & ~JS_ATOM_TAG_INT; +} + +static inline int is_num(int c) +{ + return c >= '0' && c <= '9'; +} + +/* return true if the string is a number n with 0 <= n <= 2^32-1 */ +static inline bool is_num_string(uint32_t *pval, JSString *p) +{ + uint32_t n; + uint64_t n64; + int c, i, len; + + len = p->len; + if (len == 0 || len > 10) + return false; + c = string_get(p, 0); + if (is_num(c)) { + if (c == '0') { + if (len != 1) + return false; + n = 0; + } else { + n = c - '0'; + for(i = 1; i < len; i++) { + c = string_get(p, i); + if (!is_num(c)) + return false; + n64 = (uint64_t)n * 10 + (c - '0'); + if ((n64 >> 32) != 0) + return false; + n = n64; + } + } + *pval = n; + return true; + } else { + return false; + } +} + +/* XXX: could use faster version ? */ +static inline uint32_t hash_string8(const uint8_t *str, size_t len, uint32_t h) +{ + size_t i; + + for(i = 0; i < len; i++) + h = h * 263 + str[i]; + return h; +} + +static inline uint32_t hash_string16(const uint16_t *str, + size_t len, uint32_t h) +{ + size_t i; + + for(i = 0; i < len; i++) + h = h * 263 + str[i]; + return h; +} + +static uint32_t hash_string(JSString *str, uint32_t h) +{ + if (str->is_wide_char) + h = hash_string16(str16(str), str->len, h); + else + h = hash_string8(str8(str), str->len, h); + return h; +} + +static uint32_t hash_string_rope(JSValueConst val, uint32_t h) +{ + if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) { + return hash_string(JS_VALUE_GET_STRING(val), h); + } else { + JSStringRope *r = JS_VALUE_GET_STRING_ROPE(val); + h = hash_string_rope(r->left, h); + return hash_string_rope(r->right, h); + } +} + +static __maybe_unused void JS_DumpString(JSRuntime *rt, JSString *p) +{ + int i, c, sep; + + if (p == NULL) { + printf(""); + return; + } + if (p->header.ref_count != 1) + printf("%d", p->header.ref_count); + if (p->is_wide_char) + putchar('L'); + sep = '\"'; + putchar(sep); + for(i = 0; i < p->len; i++) { + c = string_get(p, i); + if (c == sep || c == '\\') { + putchar('\\'); + putchar(c); + } else if (c >= ' ' && c <= 126) { + putchar(c); + } else if (c == '\n') { + putchar('\\'); + putchar('n'); + } else { + printf("\\u%04x", c); + } + } + putchar(sep); +} + +static __maybe_unused void JS_DumpAtoms(JSRuntime *rt) +{ + JSAtomStruct *p; + int h, i; + /* This only dumps hashed atoms, not JS_ATOM_TYPE_SYMBOL atoms */ + printf("JSAtom count=%d size=%d hash_size=%d:\n", + rt->atom_count, rt->atom_size, rt->atom_hash_size); + printf("JSAtom hash table: {\n"); + for(i = 0; i < rt->atom_hash_size; i++) { + h = rt->atom_hash[i]; + if (h) { + printf(" %d:", i); + while (h) { + p = rt->atom_array[h]; + printf(" "); + JS_DumpString(rt, p); + h = p->hash_next; + } + printf("\n"); + } + } + printf("}\n"); + printf("JSAtom table: {\n"); + for(i = 0; i < rt->atom_size; i++) { + p = rt->atom_array[i]; + if (!atom_is_free(p)) { + printf(" %d: { %d %08x ", i, p->atom_type, p->hash); + if (!(p->len == 0 && p->is_wide_char != 0)) + JS_DumpString(rt, p); + printf(" %d }\n", p->hash_next); + } + } + printf("}\n"); +} + +static int JS_ResizeAtomHash(JSRuntime *rt, int new_hash_size) +{ + JSAtomStruct *p; + uint32_t new_hash_mask, h, i, hash_next1, j, *new_hash; + + assert((new_hash_size & (new_hash_size - 1)) == 0); /* power of two */ + new_hash_mask = new_hash_size - 1; + new_hash = js_mallocz_rt(rt, sizeof(rt->atom_hash[0]) * new_hash_size); + if (!new_hash) + return -1; + for(i = 0; i < rt->atom_hash_size; i++) { + h = rt->atom_hash[i]; + while (h != 0) { + p = rt->atom_array[h]; + hash_next1 = p->hash_next; + /* add in new hash table */ + j = p->hash & new_hash_mask; + p->hash_next = new_hash[j]; + new_hash[j] = h; + h = hash_next1; + } + } + js_free_rt(rt, rt->atom_hash); + rt->atom_hash = new_hash; + rt->atom_hash_size = new_hash_size; + rt->atom_count_resize = JS_ATOM_COUNT_RESIZE(new_hash_size); + // JS_DumpAtoms(rt); + return 0; +} + +static int JS_InitAtoms(JSRuntime *rt) +{ + int i, len, atom_type; + const char *p; + + rt->atom_hash_size = 0; + rt->atom_hash = NULL; + rt->atom_count = 0; + rt->atom_size = 0; + rt->atom_free_index = 0; + if (JS_ResizeAtomHash(rt, 256)) /* there are at least 195 predefined atoms */ + return -1; + + p = js_atom_init; + for(i = 1; i < JS_ATOM_END; i++) { + if (i == JS_ATOM_Private_brand) + atom_type = JS_ATOM_TYPE_PRIVATE; + else if (i >= JS_ATOM_Symbol_toPrimitive) + atom_type = JS_ATOM_TYPE_SYMBOL; + else + atom_type = JS_ATOM_TYPE_STRING; + len = strlen(p); + if (__JS_NewAtomInit(rt, p, len, atom_type) == JS_ATOM_NULL) + return -1; + p = p + len + 1; + } + return 0; +} + +JSAtom JS_DupAtomRT(JSRuntime *rt, JSAtom v) +{ + JSAtomStruct *p; + + if (!__JS_AtomIsConst(v)) { + p = rt->atom_array[v]; + p->header.ref_count++; + } + return v; +} + +JSAtom JS_DupAtom(JSContext *ctx, JSAtom v) +{ + JSRuntime *rt; + JSAtomStruct *p; + + if (!__JS_AtomIsConst(v)) { + rt = ctx->rt; + p = rt->atom_array[v]; + p->header.ref_count++; + } + return v; +} + +static JSAtomKindEnum JS_AtomGetKind(JSContext *ctx, JSAtom v) +{ + JSRuntime *rt; + JSAtomStruct *p; + + rt = ctx->rt; + if (__JS_AtomIsTaggedInt(v)) + return JS_ATOM_KIND_STRING; + p = rt->atom_array[v]; + switch(p->atom_type) { + case JS_ATOM_TYPE_STRING: + return JS_ATOM_KIND_STRING; + case JS_ATOM_TYPE_GLOBAL_SYMBOL: + return JS_ATOM_KIND_SYMBOL; + case JS_ATOM_TYPE_SYMBOL: + switch(p->hash) { + case JS_ATOM_HASH_SYMBOL: + return JS_ATOM_KIND_SYMBOL; + case JS_ATOM_HASH_PRIVATE: + return JS_ATOM_KIND_PRIVATE; + default: + abort(); + } + default: + abort(); + } + return (JSAtomKindEnum){-1}; // pacify compiler +} + +static JSAtom js_get_atom_index(JSRuntime *rt, JSAtomStruct *p) +{ + uint32_t i = p->hash_next; /* atom_index */ + if (p->atom_type != JS_ATOM_TYPE_SYMBOL) { + JSAtomStruct *p1; + + i = rt->atom_hash[p->hash & (rt->atom_hash_size - 1)]; + p1 = rt->atom_array[i]; + while (p1 != p) { + assert(i != 0); + i = p1->hash_next; + p1 = rt->atom_array[i]; + } + } + return i; +} + +/* string case (internal). Return JS_ATOM_NULL if error. 'str' is + freed. */ +static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type) +{ + uint32_t h, h1, i; + JSAtomStruct *p; + int len; + + if (atom_type < JS_ATOM_TYPE_SYMBOL) { + /* str is not NULL */ + if (str->atom_type == atom_type) { + /* str is the atom, return its index */ + i = js_get_atom_index(rt, str); + /* reduce string refcount and increase atom's unless constant */ + if (__JS_AtomIsConst(i)) + str->header.ref_count--; + return i; + } + /* try and locate an already registered atom */ + len = str->len; + h = hash_string(str, atom_type); + h &= JS_ATOM_HASH_MASK; + h1 = h & (rt->atom_hash_size - 1); + i = rt->atom_hash[h1]; + while (i != 0) { + p = rt->atom_array[i]; + if (p->hash == h && + p->atom_type == atom_type && + p->len == len && + js_string_memcmp(p, str, len) == 0) { + if (!__JS_AtomIsConst(i)) + p->header.ref_count++; + goto done; + } + i = p->hash_next; + } + } else { + h1 = 0; /* avoid warning */ + if (atom_type == JS_ATOM_TYPE_SYMBOL) { + h = JS_ATOM_HASH_SYMBOL; + } else { + h = JS_ATOM_HASH_PRIVATE; + atom_type = JS_ATOM_TYPE_SYMBOL; + } + } + + if (rt->atom_free_index == 0) { + /* allow new atom entries */ + uint32_t new_size, start; + JSAtomStruct **new_array; + + /* alloc new with size progression 3/2: + 4 6 9 13 19 28 42 63 94 141 211 316 474 711 1066 1599 2398 3597 5395 8092 + preallocating space for predefined atoms (at least 195). + */ + new_size = max_int(211, rt->atom_size * 3 / 2); + if (new_size > JS_ATOM_MAX) + goto fail; + /* XXX: should use realloc2 to use slack space */ + new_array = js_realloc_rt(rt, rt->atom_array, sizeof(*new_array) * new_size); + if (!new_array) + goto fail; + /* Note: the atom 0 is not used */ + start = rt->atom_size; + if (start == 0) { + /* JS_ATOM_NULL entry */ + p = js_mallocz_rt(rt, sizeof(JSAtomStruct)); + if (!p) { + js_free_rt(rt, new_array); + goto fail; + } + p->header.ref_count = 1; /* not refcounted */ + p->atom_type = JS_ATOM_TYPE_SYMBOL; +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + list_add_tail(&p->link, &rt->string_list); +#endif + new_array[0] = p; + rt->atom_count++; + start = 1; + } + rt->atom_size = new_size; + rt->atom_array = new_array; + rt->atom_free_index = start; + for(i = start; i < new_size; i++) { + uint32_t next; + if (i == (new_size - 1)) + next = 0; + else + next = i + 1; + rt->atom_array[i] = atom_set_free(next); + } + } + + if (str) { + if (str->atom_type == 0) { + p = str; + p->atom_type = atom_type; + } else { + p = js_malloc_rt(rt, sizeof(JSString) + + (str->len << str->is_wide_char) + + 1 - str->is_wide_char); + if (unlikely(!p)) + goto fail; + p->header.ref_count = 1; + p->is_wide_char = str->is_wide_char; + p->len = str->len; + p->kind = JS_STRING_KIND_NORMAL; +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + list_add_tail(&p->link, &rt->string_list); +#endif + memcpy(str8(p), str8(str), (str->len << str->is_wide_char) + + 1 - str->is_wide_char); + js_free_string(rt, str); + } + } else { + p = js_malloc_rt(rt, sizeof(JSAtomStruct)); /* empty wide string */ + if (!p) + return JS_ATOM_NULL; + p->header.ref_count = 1; + p->is_wide_char = 1; /* Hack to represent NULL as a JSString */ + p->len = 0; + p->kind = JS_STRING_KIND_NORMAL; +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + list_add_tail(&p->link, &rt->string_list); +#endif + } + + /* use an already free entry */ + i = rt->atom_free_index; + rt->atom_free_index = atom_get_free(rt->atom_array[i]); + rt->atom_array[i] = p; + + p->hash = h; + p->hash_next = i; /* atom_index */ + p->atom_type = atom_type; + p->first_weak_ref = NULL; + + rt->atom_count++; + + if (atom_type != JS_ATOM_TYPE_SYMBOL) { + p->hash_next = rt->atom_hash[h1]; + rt->atom_hash[h1] = i; + if (unlikely(rt->atom_count >= rt->atom_count_resize)) + JS_ResizeAtomHash(rt, rt->atom_hash_size * 2); + } + + // JS_DumpAtoms(rt); + return i; + + fail: + i = JS_ATOM_NULL; + done: + if (str) + js_free_string(rt, str); + return i; +} + +// XXX: `str` must be pure ASCII. No UTF-8 encoded strings +// XXX: `str` must not be the string representation of a small integer +static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len, + int atom_type) +{ + JSString *p; + p = js_alloc_string_rt(rt, len, 0); + if (!p) + return JS_ATOM_NULL; + memcpy(str8(p), str, len); + str8(p)[len] = '\0'; + return __JS_NewAtom(rt, p, atom_type); +} + +// XXX: `str` must be raw 8-bit contents. No UTF-8 encoded strings +static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len, + int atom_type) +{ + uint32_t h, h1, i; + JSAtomStruct *p; + + h = hash_string8((const uint8_t *)str, len, JS_ATOM_TYPE_STRING); + h &= JS_ATOM_HASH_MASK; + h1 = h & (rt->atom_hash_size - 1); + i = rt->atom_hash[h1]; + while (i != 0) { + p = rt->atom_array[i]; + if (p->hash == h && + p->atom_type == JS_ATOM_TYPE_STRING && + p->len == len && + p->is_wide_char == 0 && + memcmp(str8(p), str, len) == 0) { + if (!__JS_AtomIsConst(i)) + p->header.ref_count++; + return i; + } + i = p->hash_next; + } + return JS_ATOM_NULL; +} + +static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p) +{ + uint32_t i = p->hash_next; /* atom_index */ + if (p->atom_type != JS_ATOM_TYPE_SYMBOL) { + JSAtomStruct *p0, *p1; + uint32_t h0; + + h0 = p->hash & (rt->atom_hash_size - 1); + i = rt->atom_hash[h0]; + p1 = rt->atom_array[i]; + if (p1 == p) { + rt->atom_hash[h0] = p1->hash_next; + } else { + for(;;) { + assert(i != 0); + p0 = p1; + i = p1->hash_next; + p1 = rt->atom_array[i]; + if (p1 == p) { + p0->hash_next = p1->hash_next; + break; + } + } + } + } + /* insert in free atom list */ + rt->atom_array[i] = atom_set_free(rt->atom_free_index); + rt->atom_free_index = i; + if (unlikely(p->first_weak_ref)) { + reset_weak_ref(rt, &p->first_weak_ref); + } + /* free the string structure */ +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + list_del(&p->link); +#endif + js_free_rt(rt, p); + rt->atom_count--; + assert(rt->atom_count >= 0); +} + +static void __JS_FreeAtom(JSRuntime *rt, uint32_t i) +{ + JSAtomStruct *p; + + p = rt->atom_array[i]; + if (--p->header.ref_count > 0) + return; + JS_FreeAtomStruct(rt, p); +} + +/* Warning: 'p' is freed */ +static JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p) +{ + JSRuntime *rt = ctx->rt; + uint32_t n; + if (is_num_string(&n, p)) { + if (n <= JS_ATOM_MAX_INT) { + js_free_string(rt, p); + return __JS_AtomFromUInt32(n); + } + } + /* XXX: should generate an exception */ + return __JS_NewAtom(rt, p, JS_ATOM_TYPE_STRING); +} + +/* `str` may be pure ASCII or UTF-8 encoded */ +JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len) +{ + JSValue val; + + if (len == 0 || !is_digit(*str)) { + // TODO(chqrlie): this does not work if `str` has UTF-8 encoded contents + // bug example: `({ "\u00c3\u00a9": 1 }).\u00e9` evaluates to `1`. + JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING); + if (atom) + return atom; + } + val = JS_NewStringLen(ctx, str, len); + if (JS_IsException(val)) + return JS_ATOM_NULL; + return JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(val)); +} + +/* `str` may be pure ASCII or UTF-8 encoded */ +JSAtom JS_NewAtom(JSContext *ctx, const char *str) +{ + return JS_NewAtomLen(ctx, str, strlen(str)); +} + +JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n) +{ + if (n <= JS_ATOM_MAX_INT) { + return __JS_AtomFromUInt32(n); + } else { + char buf[16]; + size_t len = u32toa(buf, n); + JSValue val = js_new_string8_len(ctx, buf, len); + if (JS_IsException(val)) + return JS_ATOM_NULL; + return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val), + JS_ATOM_TYPE_STRING); + } +} + +static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n) +{ + if ((uint64_t)n <= JS_ATOM_MAX_INT) { + return __JS_AtomFromUInt32((uint32_t)n); + } else { + char buf[24]; + size_t len = i64toa(buf, n); + JSValue val = js_new_string8_len(ctx, buf, len); + if (JS_IsException(val)) + return JS_ATOM_NULL; + return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val), + JS_ATOM_TYPE_STRING); + } +} + +/* 'p' is freed */ +static JSValue JS_NewSymbolInternal(JSContext *ctx, JSString *p, int atom_type) +{ + JSRuntime *rt = ctx->rt; + JSAtom atom; + atom = __JS_NewAtom(rt, p, atom_type); + if (atom == JS_ATOM_NULL) + return JS_ThrowOutOfMemory(ctx); + return JS_MKPTR(JS_TAG_SYMBOL, rt->atom_array[atom]); +} + +/* descr must be a non-numeric string atom */ +static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr, + int atom_type) +{ + JSRuntime *rt = ctx->rt; + JSString *p; + + assert(!__JS_AtomIsTaggedInt(descr)); + assert(descr < rt->atom_size); + p = rt->atom_array[descr]; + js_dup(JS_MKPTR(JS_TAG_STRING, p)); + return JS_NewSymbolInternal(ctx, p, atom_type); +} + +/* `description` may be pure ASCII or UTF-8 encoded */ +JSValue JS_NewSymbol(JSContext *ctx, const char *description, bool is_global) +{ + JSAtom atom = JS_NewAtom(ctx, description); + if (atom == JS_ATOM_NULL) + return JS_EXCEPTION; + return JS_NewSymbolFromAtom(ctx, atom, is_global ? JS_ATOM_TYPE_GLOBAL_SYMBOL : JS_ATOM_TYPE_SYMBOL); +} + +#define ATOM_GET_STR_BUF_SIZE 64 + +static const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size, + JSAtom atom) +{ + if (__JS_AtomIsTaggedInt(atom)) { + snprintf(buf, buf_size, "%u", __JS_AtomToUInt32(atom)); + } else if (atom == JS_ATOM_NULL) { + snprintf(buf, buf_size, ""); + } else if (atom >= rt->atom_size) { + assert(atom < rt->atom_size); + snprintf(buf, buf_size, "", atom); + } else { + JSAtomStruct *p = rt->atom_array[atom]; + *buf = '\0'; + if (atom_is_free(p)) { + snprintf(buf, buf_size, "", atom); + } else if (p != NULL) { + JSString *str = p; + if (str->is_wide_char) { + /* encode surrogates correctly */ + utf8_encode_buf16(buf, buf_size, str16(str), str->len); + } else { + utf8_encode_buf8(buf, buf_size, str8(str), str->len); + } + } + } + return buf; +} + +static const char *JS_AtomGetStr(JSContext *ctx, char *buf, int buf_size, JSAtom atom) +{ + return JS_AtomGetStrRT(ctx->rt, buf, buf_size, atom); +} + +static JSValue __JS_AtomToValue(JSContext *ctx, JSAtom atom, bool force_string) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + + if (__JS_AtomIsTaggedInt(atom)) { + size_t len = u32toa(buf, __JS_AtomToUInt32(atom)); + return js_new_string8_len(ctx, buf, len); + } else { + JSRuntime *rt = ctx->rt; + JSAtomStruct *p; + assert(atom < rt->atom_size); + p = rt->atom_array[atom]; + if (p->atom_type == JS_ATOM_TYPE_STRING) { + goto ret_string; + } else if (force_string) { + if (p->len == 0 && p->is_wide_char != 0) { + /* no description string */ + p = rt->atom_array[JS_ATOM_empty_string]; + } + ret_string: + return js_dup(JS_MKPTR(JS_TAG_STRING, p)); + } else { + return js_dup(JS_MKPTR(JS_TAG_SYMBOL, p)); + } + } +} + +JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom) +{ + return __JS_AtomToValue(ctx, atom, false); +} + +JSValue JS_AtomToString(JSContext *ctx, JSAtom atom) +{ + return __JS_AtomToValue(ctx, atom, true); +} + +/* return true if the atom is an array index (i.e. 0 <= index <= + 2^32-2 and return its value */ +static bool JS_AtomIsArrayIndex(JSContext *ctx, uint32_t *pval, JSAtom atom) +{ + if (__JS_AtomIsTaggedInt(atom)) { + *pval = __JS_AtomToUInt32(atom); + return true; + } else { + JSRuntime *rt = ctx->rt; + JSAtomStruct *p; + uint32_t val; + + assert(atom < rt->atom_size); + p = rt->atom_array[atom]; + if (p->atom_type == JS_ATOM_TYPE_STRING && + is_num_string(&val, p) && val != -1) { + *pval = val; + return true; + } else { + *pval = 0; + return false; + } + } +} + +/* This test must be fast if atom is not a numeric index (e.g. a + method name). Return JS_UNDEFINED if not a numeric + index. JS_EXCEPTION can also be returned. */ +static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom) +{ + JSRuntime *rt = ctx->rt; + JSAtomStruct *p1; + JSString *p; + int c, len, ret; + JSValue num, str; + + if (__JS_AtomIsTaggedInt(atom)) + return js_int32(__JS_AtomToUInt32(atom)); + assert(atom < rt->atom_size); + p1 = rt->atom_array[atom]; + if (p1->atom_type != JS_ATOM_TYPE_STRING) + return JS_UNDEFINED; + p = p1; + len = p->len; + if (p->is_wide_char) { + const uint16_t *r = str16(p), *r_end = str16(p) + len; + if (r >= r_end) + return JS_UNDEFINED; + c = *r; + if (c == '-') { + if (r >= r_end) + return JS_UNDEFINED; + r++; + c = *r; + /* -0 case is specific */ + if (c == '0' && len == 2) + goto minus_zero; + } + /* XXX: should test NaN, but the tests do not check it */ + if (!is_num(c)) { + /* XXX: String should be normalized, therefore 8-bit only */ + const uint16_t nfinity16[7] = { 'n', 'f', 'i', 'n', 'i', 't', 'y' }; + if (!(c =='I' && (r_end - r) == 8 && + !memcmp(r + 1, nfinity16, sizeof(nfinity16)))) + return JS_UNDEFINED; + } + } else { + const uint8_t *r = str8(p), *r_end = str8(p) + len; + if (r >= r_end) + return JS_UNDEFINED; + c = *r; + if (c == '-') { + if (r >= r_end) + return JS_UNDEFINED; + r++; + c = *r; + /* -0 case is specific */ + if (c == '0' && len == 2) { + minus_zero: + return js_float64(-0.0); + } + } + if (!is_num(c)) { + if (!(c =='I' && (r_end - r) == 8 && + !memcmp(r + 1, "nfinity", 7))) + return JS_UNDEFINED; + } + } + /* this is ECMA CanonicalNumericIndexString primitive */ + num = JS_ToNumber(ctx, JS_MKPTR(JS_TAG_STRING, p)); + if (JS_IsException(num)) + return num; + str = JS_ToString(ctx, num); + if (JS_IsException(str)) { + JS_FreeValue(ctx, num); + return str; + } + ret = js_string_eq(p, JS_VALUE_GET_STRING(str)); + JS_FreeValue(ctx, str); + if (ret) { + return num; + } else { + JS_FreeValue(ctx, num); + return JS_UNDEFINED; + } +} + +/* return -1 if exception or true/false */ +static int JS_AtomIsNumericIndex(JSContext *ctx, JSAtom atom) +{ + JSValue num; + num = JS_AtomIsNumericIndex1(ctx, atom); + if (likely(JS_IsUndefined(num))) + return false; + if (JS_IsException(num)) + return -1; + JS_FreeValue(ctx, num); + return true; +} + +void JS_FreeAtom(JSContext *ctx, JSAtom v) +{ + if (!__JS_AtomIsConst(v)) + __JS_FreeAtom(ctx->rt, v); +} + +void JS_FreeAtomRT(JSRuntime *rt, JSAtom v) +{ + if (!__JS_AtomIsConst(v)) + __JS_FreeAtom(rt, v); +} + +/* return true if 'v' is a symbol with a string description */ +static bool JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v) +{ + JSRuntime *rt; + JSAtomStruct *p; + + rt = ctx->rt; + if (__JS_AtomIsTaggedInt(v)) + return false; + p = rt->atom_array[v]; + return (((p->atom_type == JS_ATOM_TYPE_SYMBOL && + p->hash == JS_ATOM_HASH_SYMBOL) || + p->atom_type == JS_ATOM_TYPE_GLOBAL_SYMBOL) && + !(p->len == 0 && p->is_wide_char != 0)); +} + +static __maybe_unused void print_atom(JSContext *ctx, JSAtom atom) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + const char *p; + int i; + + /* XXX: should handle embedded null characters */ + /* XXX: should move encoding code to JS_AtomGetStr */ + p = JS_AtomGetStr(ctx, buf, sizeof(buf), atom); + for (i = 0; p[i]; i++) { + int c = (unsigned char)p[i]; + if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c == '_' || c == '$') || (c >= '0' && c <= '9' && i > 0))) + break; + } + if (i > 0 && p[i] == '\0') { + printf("%s", p); + } else { + putchar('"'); + printf("%.*s", i, p); + for (; p[i]; i++) { + int c = (unsigned char)p[i]; + if (c == '\"' || c == '\\') { + putchar('\\'); + putchar(c); + } else if (c >= ' ' && c <= 126) { + putchar(c); + } else if (c == '\n') { + putchar('\\'); + putchar('n'); + } else { + printf("\\u%04x", c); + } + } + putchar('\"'); + } +} + +/* free with JS_FreeCString() */ +const char *JS_AtomToCStringLen(JSContext *ctx, size_t *plen, JSAtom atom) +{ + JSValue str; + const char *cstr; + + str = JS_AtomToString(ctx, atom); + if (JS_IsException(str)) { + if (plen) + *plen = 0; + return NULL; + } + cstr = JS_ToCStringLen(ctx, plen, str); + JS_FreeValue(ctx, str); + return cstr; +} + +#ifndef QJS_DISABLE_PARSER + +/* return a string atom containing name concatenated with str1 */ +/* `str1` may be pure ASCII or UTF-8 encoded */ +// TODO(chqrlie): use string concatenation instead of UTF-8 conversion +static JSAtom js_atom_concat_str(JSContext *ctx, JSAtom name, const char *str1) +{ + JSValue str; + JSAtom atom; + const char *cstr; + char *cstr2; + size_t len, len1; + + str = JS_AtomToString(ctx, name); + if (JS_IsException(str)) + return JS_ATOM_NULL; + cstr = JS_ToCStringLen(ctx, &len, str); + if (!cstr) + goto fail; + len1 = strlen(str1); + cstr2 = js_malloc(ctx, len + len1 + 1); + if (!cstr2) + goto fail; + memcpy(cstr2, cstr, len); + memcpy(cstr2 + len, str1, len1); + cstr2[len + len1] = '\0'; + atom = JS_NewAtomLen(ctx, cstr2, len + len1); + js_free(ctx, cstr2); + JS_FreeCString(ctx, cstr); + JS_FreeValue(ctx, str); + return atom; + fail: + JS_FreeCString(ctx, cstr); + JS_FreeValue(ctx, str); + return JS_ATOM_NULL; +} + +static JSAtom js_atom_concat_num(JSContext *ctx, JSAtom name, uint32_t n) +{ + char buf[16]; + size_t len; + + len = u32toa(buf, n); + buf[len] = '\0'; + return js_atom_concat_str(ctx, name, buf); +} + +#endif // QJS_DISABLE_PARSER + +static inline bool JS_IsEmptyString(JSValueConst v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_STRING && JS_VALUE_GET_STRING(v)->len == 0; +} + +/* JSClass support */ + +/* a new class ID is allocated if *pclass_id == 0, otherwise *pclass_id is left unchanged */ +JSClassID JS_NewClassID(JSRuntime *rt, JSClassID *pclass_id) +{ + JSClassID class_id = *pclass_id; + if (class_id == 0) { + class_id = rt->js_class_id_alloc++; + *pclass_id = class_id; + } + return class_id; +} + +JSClassID JS_GetClassID(JSValueConst v) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(v) != JS_TAG_OBJECT) + return JS_INVALID_CLASS_ID; + p = JS_VALUE_GET_OBJ(v); + return p->class_id; +} + +bool JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id) +{ + return (class_id < rt->class_count && + rt->class_array[class_id].class_id != 0); +} + +JSAtom JS_GetClassName(JSRuntime *rt, JSClassID class_id) +{ + if (JS_IsRegisteredClass(rt, class_id)) { + return JS_DupAtomRT(rt, rt->class_array[class_id].class_id); + } else { + return JS_ATOM_NULL; + } +} + +/* create a new object internal class. Return -1 if error, 0 if + OK. The finalizer can be NULL if none is needed. */ +static int JS_NewClass1(JSRuntime *rt, JSClassID class_id, + const JSClassDef *class_def, JSAtom name) +{ + int new_size, i; + JSClass *cl, *new_class_array; + struct list_head *el; + + if (class_id >= (1 << 16)) + return -1; + if (class_id < rt->class_count && + rt->class_array[class_id].class_id != 0) + return -1; + + if (class_id >= rt->class_count) { + new_size = max_int(JS_CLASS_INIT_COUNT, + max_int(class_id + 1, rt->class_count * 3 / 2)); + + /* reallocate the context class prototype array, if any */ + list_for_each(el, &rt->context_list) { + JSContext *ctx = list_entry(el, JSContext, link); + JSValue *new_tab; + new_tab = js_realloc_rt(rt, ctx->class_proto, + sizeof(ctx->class_proto[0]) * new_size); + if (!new_tab) + return -1; + for(i = rt->class_count; i < new_size; i++) + new_tab[i] = JS_NULL; + ctx->class_proto = new_tab; + } + /* reallocate the class array */ + new_class_array = js_realloc_rt(rt, rt->class_array, + sizeof(JSClass) * new_size); + if (!new_class_array) + return -1; + memset(new_class_array + rt->class_count, 0, + (new_size - rt->class_count) * sizeof(JSClass)); + rt->class_array = new_class_array; + rt->class_count = new_size; + } + cl = &rt->class_array[class_id]; + cl->class_id = class_id; + cl->class_name = JS_DupAtomRT(rt, name); + cl->finalizer = class_def->finalizer; + cl->gc_mark = class_def->gc_mark; + cl->call = class_def->call; + cl->exotic = class_def->exotic; + return 0; +} + +int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def) +{ + int ret, len; + JSAtom name; + + // XXX: class_def->class_name must be raw 8-bit contents. No UTF-8 encoded strings + len = strlen(class_def->class_name); + name = __JS_FindAtom(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING); + if (name == JS_ATOM_NULL) { + name = __JS_NewAtomInit(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING); + if (name == JS_ATOM_NULL) + return -1; + } + ret = JS_NewClass1(rt, class_id, class_def, name); + JS_FreeAtomRT(rt, name); + return ret; +} + +static inline JSValue js_empty_string(JSRuntime *rt) +{ + JSAtomStruct *p = rt->atom_array[JS_ATOM_empty_string]; + return js_dup(JS_MKPTR(JS_TAG_STRING, p)); +} + +// XXX: `buf` contains raw 8-bit data, no UTF-8 decoding is performed +// XXX: no special case for len == 0 +static JSValue js_new_string8_len(JSContext *ctx, const char *buf, int len) +{ + JSString *str; + str = js_alloc_string(ctx, len, 0); + if (!str) + return JS_EXCEPTION; + memcpy(str8(str), buf, len); + str8(str)[len] = '\0'; + return JS_MKPTR(JS_TAG_STRING, str); +} + +// XXX: `buf` contains raw 8-bit data, no UTF-8 decoding is performed +// XXX: no special case for the empty string +static inline JSValue js_new_string8(JSContext *ctx, const char *str) +{ + return js_new_string8_len(ctx, str, strlen(str)); +} + +static JSValue js_new_string16_len(JSContext *ctx, const uint16_t *buf, int len) +{ + JSString *str; + str = js_alloc_string(ctx, len, 1); + if (!str) + return JS_EXCEPTION; + memcpy(str16(str), buf, len * 2); + return JS_MKPTR(JS_TAG_STRING, str); +} + +static JSValue js_new_string_char(JSContext *ctx, uint16_t c) +{ + if (c < 0x100) { + char ch8 = c; + return js_new_string8_len(ctx, &ch8, 1); + } else { + uint16_t ch16 = c; + return js_new_string16_len(ctx, &ch16, 1); + } +} + +static JSValue js_sub_string(JSContext *ctx, JSString *p, int start, int end) +{ + JSStringSlice *slice; + JSString *q; + int len; + + len = end - start; + if (start == 0 && end == p->len) { + return js_dup(JS_MKPTR(JS_TAG_STRING, p)); + } + if (len <= 0) { + return js_empty_string(ctx->rt); + } + if (len > (JS_STRING_SLICE_LEN_MAX >> p->is_wide_char)) { + if (p->kind == JS_STRING_KIND_SLICE) { + slice = (void *)&p[1]; + p = slice->parent; + start += slice->start >> p->is_wide_char; // bytes -> chars + } + // allocate as 16 bit wide string to avoid wastage; + // js_alloc_string allocates 1 byte extra for 8 bit strings; + q = js_alloc_string(ctx, sizeof(*slice)/2, /*is_wide_char*/true); + if (!q) + return JS_EXCEPTION; + q->is_wide_char = p->is_wide_char; + q->kind = JS_STRING_KIND_SLICE; + q->len = len; + slice = (void *)&q[1]; + slice->parent = p; + slice->start = start << p->is_wide_char; // chars -> bytes + p->header.ref_count++; + return JS_MKPTR(JS_TAG_STRING, q); + } + if (p->is_wide_char) { + JSString *str; + int i; + uint16_t c = 0; + for (i = start; i < end; i++) { + c |= str16(p)[i]; + } + if (c > 0xFF) + return js_new_string16_len(ctx, str16(p) + start, len); + + str = js_alloc_string(ctx, len, 0); + if (!str) + return JS_EXCEPTION; + for (i = 0; i < len; i++) { + str8(str)[i] = str16(p)[start + i]; + } + str8(str)[len] = '\0'; + return JS_MKPTR(JS_TAG_STRING, str); + } else { + return js_new_string8_len(ctx, (const char *)(str8(p) + start), len); + } +} + +typedef struct StringBuffer { + JSContext *ctx; + JSString *str; + int len; + int size; + int is_wide_char; + int error_status; +} StringBuffer; + +/* It is valid to call string_buffer_end() and all string_buffer functions even + if string_buffer_init() or another string_buffer function returns an error. + If the error_status is set, string_buffer_end() returns JS_EXCEPTION. + */ +static int string_buffer_init2(JSContext *ctx, StringBuffer *s, int size, + int is_wide) +{ + s->ctx = ctx; + s->size = size; + s->len = 0; + s->is_wide_char = is_wide; + s->error_status = 0; + s->str = js_alloc_string(ctx, size, is_wide); + if (unlikely(!s->str)) { + s->size = 0; + return s->error_status = -1; + } +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + /* the StringBuffer may reallocate the JSString, only link it at the end */ + list_del(&s->str->link); +#endif + return 0; +} + +static inline int string_buffer_init(JSContext *ctx, StringBuffer *s, int size) +{ + return string_buffer_init2(ctx, s, size, 0); +} + +static void string_buffer_free(StringBuffer *s) +{ + js_free(s->ctx, s->str); + s->str = NULL; +} + +static int string_buffer_set_error(StringBuffer *s) +{ + js_free(s->ctx, s->str); + s->str = NULL; + s->size = 0; + s->len = 0; + return s->error_status = -1; +} + +static no_inline int string_buffer_widen(StringBuffer *s, int size) +{ + JSString *str; + size_t slack; + int i; + + if (s->error_status) + return -1; + + str = js_realloc2(s->ctx, s->str, sizeof(JSString) + (size << 1), &slack); + if (!str) + return string_buffer_set_error(s); + size += slack >> 1; + for(i = s->len; i-- > 0;) { + str16(str)[i] = str8(str)[i]; + } + s->is_wide_char = 1; + s->size = size; + s->str = str; + return 0; +} + +static no_inline int string_buffer_realloc(StringBuffer *s, int new_len, int c) +{ + JSString *new_str; + int new_size; + size_t new_size_bytes, slack; + + if (s->error_status) + return -1; + + if (new_len > JS_STRING_LEN_MAX) { + JS_ThrowRangeError(s->ctx, "invalid string length"); + return string_buffer_set_error(s); + } + new_size = min_int(max_int(new_len, s->size * 3 / 2), JS_STRING_LEN_MAX); + if (!s->is_wide_char && c >= 0x100) { + return string_buffer_widen(s, new_size); + } + new_size_bytes = sizeof(JSString) + (new_size << s->is_wide_char) + 1 - s->is_wide_char; + new_str = js_realloc2(s->ctx, s->str, new_size_bytes, &slack); + if (!new_str) + return string_buffer_set_error(s); + new_size = min_int(new_size + (slack >> s->is_wide_char), JS_STRING_LEN_MAX); + s->size = new_size; + s->str = new_str; + return 0; +} + +static no_inline int string_buffer_putc16_slow(StringBuffer *s, uint32_t c) +{ + if (unlikely(s->len >= s->size)) { + if (string_buffer_realloc(s, s->len + 1, c)) + return -1; + } + if (s->is_wide_char) { + str16(s->str)[s->len++] = c; + } else if (c < 0x100) { + str8(s->str)[s->len++] = c; + } else { + if (string_buffer_widen(s, s->size)) + return -1; + str16(s->str)[s->len++] = c; + } + return 0; +} + +/* 0 <= c <= 0xff */ +static int string_buffer_putc8(StringBuffer *s, uint32_t c) +{ + if (unlikely(s->len >= s->size)) { + if (string_buffer_realloc(s, s->len + 1, c)) + return -1; + } + if (s->is_wide_char) { + str16(s->str)[s->len++] = c; + } else { + str8(s->str)[s->len++] = c; + } + return 0; +} + +/* 0 <= c <= 0xffff */ +static int string_buffer_putc16(StringBuffer *s, uint32_t c) +{ + if (likely(s->len < s->size)) { + if (s->is_wide_char) { + str16(s->str)[s->len++] = c; + return 0; + } else if (c < 0x100) { + str8(s->str)[s->len++] = c; + return 0; + } + } + return string_buffer_putc16_slow(s, c); +} + +/* 0 <= c <= 0x10ffff */ +static no_inline int string_buffer_putc_slow(StringBuffer *s, uint32_t c) +{ + if (c >= 0x10000) { + /* surrogate pair */ + if (string_buffer_putc16(s, get_hi_surrogate(c))) + return -1; + c = get_lo_surrogate(c); + } + return string_buffer_putc16(s, c); +} + +/* 0 <= c <= 0x10ffff */ +static inline int string_buffer_putc(StringBuffer *s, uint32_t c) +{ + if (likely(s->len < s->size)) { + if (s->is_wide_char) { + if (c < 0x10000) { + str16(s->str)[s->len++] = c; + return 0; + } else if (s->len + 1 < s->size) { + /* surrogate pair */ + str16(s->str)[s->len++] = get_hi_surrogate(c); + str16(s->str)[s->len++] = get_lo_surrogate(c); + return 0; + } + } else if (c < 0x100) { + str8(s->str)[s->len++] = c; + return 0; + } + } + return string_buffer_putc_slow(s, c); +} + +static int string_getc(JSString *p, int *pidx) +{ + int idx, c, c1; + idx = *pidx; + if (p->is_wide_char) { + c = str16(p)[idx++]; + if (is_hi_surrogate(c) && idx < p->len) { + c1 = str16(p)[idx]; + if (is_lo_surrogate(c1)) { + c = from_surrogate(c, c1); + idx++; + } + } + } else { + c = str8(p)[idx++]; + } + *pidx = idx; + return c; +} + +static int string_buffer_write8(StringBuffer *s, const uint8_t *p, int len) +{ + int i; + + if (s->len + len > s->size) { + if (string_buffer_realloc(s, s->len + len, 0)) + return -1; + } + if (s->is_wide_char) { + for (i = 0; i < len; i++) { + str16(s->str)[s->len + i] = p[i]; + } + s->len += len; + } else { + memcpy(&str8(s->str)[s->len], p, len); + s->len += len; + } + return 0; +} + +static int string_buffer_write16(StringBuffer *s, const uint16_t *p, int len) +{ + int c = 0, i; + + for (i = 0; i < len; i++) { + c |= p[i]; + } + if (s->len + len > s->size) { + if (string_buffer_realloc(s, s->len + len, c)) + return -1; + } else if (!s->is_wide_char && c >= 0x100) { + if (string_buffer_widen(s, s->size)) + return -1; + } + if (s->is_wide_char) { + memcpy(&str16(s->str)[s->len], p, len << 1); + s->len += len; + } else { + for (i = 0; i < len; i++) { + str8(s->str)[s->len + i] = p[i]; + } + s->len += len; + } + return 0; +} + +/* appending an ASCII string */ +static int string_buffer_puts8(StringBuffer *s, const char *str) +{ + return string_buffer_write8(s, (const uint8_t *)str, strlen(str)); +} + +static int string_buffer_concat(StringBuffer *s, JSString *p, + uint32_t from, uint32_t to) +{ + if (to <= from) + return 0; + if (p->is_wide_char) + return string_buffer_write16(s, str16(p) + from, to - from); + else + return string_buffer_write8(s, str8(p) + from, to - from); +} + +static int string_buffer_concat_value(StringBuffer *s, JSValueConst v) +{ + JSString *p; + JSValue v1; + int res; + int tag; + + if (s->error_status) { + /* prevent exception overload */ + return -1; + } + tag = JS_VALUE_GET_TAG(v); + if (tag == JS_TAG_STRING_ROPE) { + /* recursively concatenate rope children */ + JSStringRope *r = JS_VALUE_GET_STRING_ROPE(v); + if (string_buffer_concat_value(s, r->left)) + return -1; + return string_buffer_concat_value(s, r->right); + } + if (unlikely(tag != JS_TAG_STRING)) { + v1 = JS_ToString(s->ctx, v); + if (JS_IsException(v1)) + return string_buffer_set_error(s); + p = JS_VALUE_GET_STRING(v1); + res = string_buffer_concat(s, p, 0, p->len); + JS_FreeValue(s->ctx, v1); + return res; + } + p = JS_VALUE_GET_STRING(v); + return string_buffer_concat(s, p, 0, p->len); +} + +static int string_buffer_concat_value_free(StringBuffer *s, JSValue v) +{ + JSString *p; + int res; + int tag; + + if (s->error_status) { + /* prevent exception overload */ + JS_FreeValue(s->ctx, v); + return -1; + } + tag = JS_VALUE_GET_TAG(v); + if (tag == JS_TAG_STRING_ROPE) { + /* concatenate rope (don't free since concat_value doesn't free) */ + res = string_buffer_concat_value(s, v); + JS_FreeValue(s->ctx, v); + return res; + } + if (unlikely(tag != JS_TAG_STRING)) { + v = JS_ToStringFree(s->ctx, v); + if (JS_IsException(v)) + return string_buffer_set_error(s); + } + p = JS_VALUE_GET_STRING(v); + res = string_buffer_concat(s, p, 0, p->len); + JS_FreeValue(s->ctx, v); + return res; +} + +static int string_buffer_fill(StringBuffer *s, int c, int count) +{ + /* XXX: optimize */ + if (s->len + count > s->size) { + if (string_buffer_realloc(s, s->len + count, c)) + return -1; + } + while (count-- > 0) { + if (string_buffer_putc16(s, c)) + return -1; + } + return 0; +} + +static JSValue string_buffer_end(StringBuffer *s) +{ + JSString *str; + str = s->str; + if (s->error_status) + return JS_EXCEPTION; + if (s->len == 0) { + js_free(s->ctx, str); + s->str = NULL; + return js_empty_string(s->ctx->rt); + } + if (s->len < s->size) { + /* smaller size so js_realloc should not fail, but OK if it does */ + /* XXX: should add some slack to avoid unnecessary calls */ + /* XXX: might need to use malloc+free to ensure smaller size */ + str = js_realloc_rt(s->ctx->rt, str, sizeof(JSString) + + (s->len << s->is_wide_char) + 1 - s->is_wide_char); + if (str == NULL) + str = s->str; + s->str = str; + } + if (!s->is_wide_char) + str8(str)[s->len] = 0; +#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS + list_add_tail(&str->link, &s->ctx->rt->string_list); +#endif + str->is_wide_char = s->is_wide_char; + str->len = s->len; + s->str = NULL; + return JS_MKPTR(JS_TAG_STRING, str); +} + +/* create a string from a UTF-8 buffer */ +JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len) +{ + JSString *str; + size_t len; + int kind; + + if (buf_len <= 0) { + return js_empty_string(ctx->rt); + } + /* Compute string kind and length: 7-bit, 8-bit, 16-bit, 16-bit UTF-16 */ + kind = utf8_scan(buf, buf_len, &len); + if (len > JS_STRING_LEN_MAX) + return JS_ThrowRangeError(ctx, "invalid string length"); + + switch (kind) { + case UTF8_PLAIN_ASCII: + str = js_alloc_string(ctx, len, 0); + if (!str) + return JS_EXCEPTION; + memcpy(str8(str), buf, len); + str8(str)[len] = '\0'; + break; + case UTF8_NON_ASCII: + /* buf contains non-ASCII code-points, but limited to 8-bit values */ + str = js_alloc_string(ctx, len, 0); + if (!str) + return JS_EXCEPTION; + utf8_decode_buf8(str8(str), len + 1, buf, buf_len); + break; + default: + // This causes a potential problem in JS_ThrowError if message is invalid + //if (kind & UTF8_HAS_ERRORS) + // return JS_ThrowRangeError(ctx, "invalid UTF-8 sequence"); + str = js_alloc_string(ctx, len, 1); + if (!str) + return JS_EXCEPTION; + utf8_decode_buf16(str16(str), len, buf, buf_len); + break; + } + return JS_MKPTR(JS_TAG_STRING, str); +} + +JSValue JS_NewStringUTF16(JSContext *ctx, const uint16_t *buf, size_t len) +{ + JSString *str; + + if (!len) + return js_empty_string(ctx->rt); + str = js_alloc_string(ctx, len, 1); + if (!str) + return JS_EXCEPTION; + memcpy(str16(str), buf, len * sizeof(*buf)); + return JS_MKPTR(JS_TAG_STRING, str); +} + +static JSValue JS_ConcatString3(JSContext *ctx, const char *str1, + JSValue str2, const char *str3) +{ + StringBuffer b_s, *b = &b_s; + int len1, len3; + JSString *p; + + if (unlikely(JS_VALUE_GET_TAG(str2) != JS_TAG_STRING)) { + str2 = JS_ToStringFree(ctx, str2); + if (JS_IsException(str2)) + goto fail; + } + p = JS_VALUE_GET_STRING(str2); + len1 = strlen(str1); + len3 = strlen(str3); + + if (string_buffer_init2(ctx, b, len1 + p->len + len3, p->is_wide_char)) + goto fail; + + string_buffer_write8(b, (const uint8_t *)str1, len1); + string_buffer_concat(b, p, 0, p->len); + string_buffer_write8(b, (const uint8_t *)str3, len3); + + JS_FreeValue(ctx, str2); + return string_buffer_end(b); + + fail: + JS_FreeValue(ctx, str2); + return JS_EXCEPTION; +} + +/* `str` may be pure ASCII or UTF-8 encoded */ +JSValue JS_NewAtomString(JSContext *ctx, const char *str) +{ + JSAtom atom = JS_NewAtom(ctx, str); + if (atom == JS_ATOM_NULL) + return JS_EXCEPTION; + JSValue val = JS_AtomToString(ctx, atom); + JS_FreeAtom(ctx, atom); + return val; +} + +static JSValue js_force_tostring(JSContext *ctx, JSValueConst val1) +{ + JSObject *p; + JSValue val; + + if (JS_VALUE_GET_TAG(val1) == JS_TAG_STRING) + return js_dup(val1); + val = JS_ToString(ctx, val1); + if (!JS_IsException(val)) + return val; + // Stringification can fail when there is an exception pending, + // e.g. a stack overflow InternalError. Special-case exception + // objects to make debugging easier, look up the .message property + // and stringify that. + if (JS_VALUE_GET_TAG(val1) != JS_TAG_OBJECT) + return JS_EXCEPTION; + p = JS_VALUE_GET_OBJ(val1); + if (p->class_id != JS_CLASS_ERROR) + return JS_EXCEPTION; + val = JS_GetProperty(ctx, val1, JS_ATOM_message); + if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + return val; +} + +/* return (NULL, 0) if exception. */ +/* return pointer into a JSString with a live ref_count */ +/* cesu8 determines if non-BMP1 codepoints are encoded as 1 or 2 utf-8 sequences */ +const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, + bool cesu8) +{ + JSValue val; + JSString *str, *str_new; + int pos, len, c, c1; + uint8_t *q; + + val = js_force_tostring(ctx, val1); + if (JS_IsException(val)) + goto fail; + str = JS_VALUE_GET_STRING(val); + len = str->len; + if (!str->is_wide_char) { + const uint8_t *src = str8(str); + int count; + + /* count the number of non-ASCII characters */ + /* Scanning the whole string is required for ASCII strings, + and computing the number of non-ASCII bytes is less expensive + than testing each byte, hence this method is faster for ASCII + strings, which is the most common case. + */ + count = 0; + for (pos = 0; pos < len; pos++) { + count += src[pos] >> 7; + } + if (count == 0 && str->kind == JS_STRING_KIND_NORMAL) { + if (plen) + *plen = len; + return (const char *)src; + } + str_new = js_alloc_string(ctx, len + count, 0); + if (!str_new) + goto fail; + q = str8(str_new); + for (pos = 0; pos < len; pos++) { + c = src[pos]; + if (c < 0x80) { + *q++ = c; + } else { + *q++ = (c >> 6) | 0xc0; + *q++ = (c & 0x3f) | 0x80; + } + } + } else { + const uint16_t *src = str16(str); + /* Allocate 3 bytes per 16 bit code point. Surrogate pairs may + produce 4 bytes but use 2 code points. + */ + str_new = js_alloc_string(ctx, len * 3, 0); + if (!str_new) + goto fail; + q = str8(str_new); + pos = 0; + while (pos < len) { + c = src[pos++]; + if (c < 0x80) { + *q++ = c; + } else { + if (is_hi_surrogate(c)) { + if (pos < len && !cesu8) { + c1 = src[pos]; + if (is_lo_surrogate(c1)) { + pos++; + c = from_surrogate(c, c1); + } else { + /* Keep unmatched surrogate code points */ + /* c = 0xfffd; */ /* error */ + } + } else { + /* Keep unmatched surrogate code points */ + /* c = 0xfffd; */ /* error */ + } + } + q += utf8_encode(q, c); + } + } + } + + *q = '\0'; + str_new->len = q - str8(str_new); + JS_FreeValue(ctx, val); + if (plen) + *plen = str_new->len; + return (const char *)str8(str_new); +fail: + if (plen) + *plen = 0; + return NULL; +} + +const uint16_t *JS_ToCStringLenUTF16(JSContext *ctx, size_t *plen, + JSValueConst val1) +{ + JSString *p, *q; + uint32_t i; + JSValue v; + + v = js_force_tostring(ctx, val1); + if (JS_IsException(v)) + goto fail; + p = JS_VALUE_GET_STRING(v); + if (!p->is_wide_char) { + q = js_alloc_string(ctx, p->len, /*is_wide_char*/true); + if (!q) + goto fail; + for (i = 0; i < p->len; i++) + str16(q)[i] = str8(p)[i]; + JS_FreeValue(ctx, v); + p = q; + } + if (plen) + *plen = p->len; + return str16(p); +fail: + JS_FreeValue(ctx, v); + if (plen) + *plen = 0; + return NULL; +} + +static void js_free_cstring(JSRuntime *rt, const void *ptr) +{ + if (!ptr) + return; + /* purposely removing constness */ + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, (JSString *)ptr - 1)); +} + +void JS_FreeCString(JSContext *ctx, const char *ptr) +{ + return js_free_cstring(ctx->rt, ptr); +} + +void JS_FreeCStringRT(JSRuntime *rt, const char *ptr) +{ + return js_free_cstring(rt, ptr); +} + +void JS_FreeCStringUTF16(JSContext *ctx, const uint16_t *ptr) +{ + return js_free_cstring(ctx->rt, ptr); +} + +void JS_FreeCStringRT_UTF16(JSRuntime *rt, const uint16_t *ptr) +{ + return js_free_cstring(rt, ptr); +} + +static int memcmp16_8(const uint16_t *src1, const uint8_t *src2, int len) +{ + int c, i; + for(i = 0; i < len; i++) { + c = src1[i] - src2[i]; + if (c != 0) + return c; + } + return 0; +} + +static int memcmp16(const uint16_t *src1, const uint16_t *src2, int len) +{ + int c, i; + for(i = 0; i < len; i++) { + c = src1[i] - src2[i]; + if (c != 0) + return c; + } + return 0; +} + +static int js_string_memcmp(JSString *p1, JSString *p2, int len) +{ + int res; + + if (likely(!p1->is_wide_char)) { + if (likely(!p2->is_wide_char)) + res = memcmp(str8(p1), str8(p2), len); + else + res = -memcmp16_8(str16(p2), str8(p1), len); + } else { + if (!p2->is_wide_char) + res = memcmp16_8(str16(p1), str8(p2), len); + else + res = memcmp16(str16(p1), str16(p2), len); + } + return res; +} + +static bool js_string_eq(JSString *p1, JSString *p2) { + if (p1->len != p2->len) + return false; + return js_string_memcmp(p1, p2, p1->len) == 0; +} + +/* return < 0, 0 or > 0 */ +static int js_string_compare(JSString *p1, JSString *p2) +{ + int res, len; + len = min_int(p1->len, p2->len); + res = js_string_memcmp(p1, p2, len); + if (res == 0) + res = compare_u32(p1->len, p2->len); + return res; +} + +/* Rope string support functions */ + +static inline bool tag_is_string(int tag) +{ + return tag == JS_TAG_STRING || tag == JS_TAG_STRING_ROPE; +} + +static uint32_t string_rope_get_len(JSValueConst val) +{ + if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) + return JS_VALUE_GET_STRING(val)->len; + else + return JS_VALUE_GET_STRING_ROPE(val)->len; +} + +static int string_rope_get(JSValueConst val, uint32_t idx) +{ + if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) { + return string_get(JS_VALUE_GET_STRING(val), idx); + } else { + JSStringRope *r = JS_VALUE_GET_STRING_ROPE(val); + uint32_t len; + if (JS_VALUE_GET_TAG(r->left) == JS_TAG_STRING) + len = JS_VALUE_GET_STRING(r->left)->len; + else + len = JS_VALUE_GET_STRING_ROPE(r->left)->len; + if (idx < len) + return string_rope_get(r->left, idx); + else + return string_rope_get(r->right, idx - len); + } +} + +typedef struct { + JSValueConst stack[JS_STRING_ROPE_MAX_DEPTH]; + int stack_len; +} JSStringRopeIter; + +static void string_rope_iter_init(JSStringRopeIter *s, JSValueConst val) +{ + s->stack_len = 0; + s->stack[s->stack_len++] = val; +} + +/* iterate thru a rope and return the strings in order */ +static JSString *string_rope_iter_next(JSStringRopeIter *s) +{ + JSValueConst val; + JSStringRope *r; + + if (s->stack_len == 0) + return NULL; + val = s->stack[--s->stack_len]; + for(;;) { + if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) + return JS_VALUE_GET_STRING(val); + r = JS_VALUE_GET_STRING_ROPE(val); + assert(s->stack_len < JS_STRING_ROPE_MAX_DEPTH); + s->stack[s->stack_len++] = r->right; + val = r->left; + } +} + +/* compare two string values with position offsets */ +static int js_string_memcmp_pos(JSString *p1, uint32_t pos1, + JSString *p2, uint32_t pos2, uint32_t len) +{ + int res; + + if (likely(!p1->is_wide_char)) { + if (likely(!p2->is_wide_char)) + res = memcmp(str8(p1) + pos1, str8(p2) + pos2, len); + else + res = -memcmp16_8(str16(p2) + pos2, str8(p1) + pos1, len); + } else { + if (!p2->is_wide_char) + res = memcmp16_8(str16(p1) + pos1, str8(p2) + pos2, len); + else + res = memcmp16(str16(p1) + pos1, str16(p2) + pos2, len); + } + return res; +} + +static int js_string_rope_compare(JSValueConst op1, + JSValueConst op2, bool eq_only) +{ + uint32_t len1, len2, len, pos1, pos2, l; + int res; + JSStringRopeIter it1, it2; + JSString *p1, *p2; + + len1 = string_rope_get_len(op1); + len2 = string_rope_get_len(op2); + /* no need to go further for equality test if different length */ + if (eq_only && len1 != len2) + return 1; + len = min_uint32(len1, len2); + string_rope_iter_init(&it1, op1); + string_rope_iter_init(&it2, op2); + p1 = string_rope_iter_next(&it1); + p2 = string_rope_iter_next(&it2); + pos1 = 0; + pos2 = 0; + while (len != 0) { + l = min_uint32(p1->len - pos1, p2->len - pos2); + l = min_uint32(l, len); + res = js_string_memcmp_pos(p1, pos1, p2, pos2, l); + if (res != 0) + return res; + len -= l; + pos1 += l; + if (pos1 >= p1->len) { + p1 = string_rope_iter_next(&it1); + pos1 = 0; + } + pos2 += l; + if (pos2 >= p2->len) { + p2 = string_rope_iter_next(&it2); + pos2 = 0; + } + } + + if (len1 == len2) + res = 0; + else if (len1 < len2) + res = -1; + else + res = 1; + return res; +} + +/* forward declaration */ +static int string_buffer_concat_value(StringBuffer *s, JSValueConst v); +static JSValue js_rebalance_string_rope(JSContext *ctx, JSValueConst rope); + +/* op1 and op2 must be strings or string ropes */ +static JSValue js_new_string_rope(JSContext *ctx, JSValue op1, JSValue op2) +{ + uint32_t len; + int is_wide_char, depth; + JSStringRope *r; + JSValue res; + + if (JS_VALUE_GET_TAG(op1) == JS_TAG_STRING) { + JSString *p1 = JS_VALUE_GET_STRING(op1); + len = p1->len; + is_wide_char = p1->is_wide_char; + depth = 0; + } else { + JSStringRope *r1 = JS_VALUE_GET_STRING_ROPE(op1); + len = r1->len; + is_wide_char = r1->is_wide_char; + depth = r1->depth; + } + + if (JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) { + JSString *p2 = JS_VALUE_GET_STRING(op2); + len += p2->len; + is_wide_char |= p2->is_wide_char; + } else { + JSStringRope *r2 = JS_VALUE_GET_STRING_ROPE(op2); + len += r2->len; + is_wide_char |= r2->is_wide_char; + depth = max_int(depth, r2->depth); + } + if (len > JS_STRING_LEN_MAX) { + JS_ThrowInternalError(ctx, "string too long"); + goto fail; + } + r = js_malloc(ctx, sizeof(*r)); + if (!r) + goto fail; + r->header.ref_count = 1; + r->len = len; + r->is_wide_char = is_wide_char; + r->depth = depth + 1; + r->left = op1; + r->right = op2; + res = JS_MKPTR(JS_TAG_STRING_ROPE, r); + if (r->depth > JS_STRING_ROPE_MAX_DEPTH) { + JSValue res2; +#ifdef DUMP_ROPE_REBALANCE + printf("rebalance: initial depth=%d\n", r->depth); +#endif + res2 = js_rebalance_string_rope(ctx, res); +#ifdef DUMP_ROPE_REBALANCE + if (JS_VALUE_GET_TAG(res2) == JS_TAG_STRING_ROPE) + printf("rebalance: final depth=%d\n", JS_VALUE_GET_STRING_ROPE(res2)->depth); +#endif + JS_FreeValue(ctx, res); + return res2; + } else { + return res; + } + fail: + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return JS_EXCEPTION; +} + +#define ROPE_N_BUCKETS 44 + +/* Fibonacci numbers starting from F_2 */ +static const uint32_t rope_bucket_len[ROPE_N_BUCKETS] = { + 1, 2, 3, 5, + 8, 13, 21, 34, + 55, 89, 144, 233, + 377, 610, 987, 1597, + 2584, 4181, 6765, 10946, + 17711, 28657, 46368, 75025, + 121393, 196418, 317811, 514229, + 832040, 1346269, 2178309, 3524578, + 5702887, 9227465, 14930352, 24157817, + 39088169, 63245986, 102334155, 165580141, + 267914296, 433494437, 701408733, 1134903170, /* > JS_STRING_LEN_MAX */ +}; + +static int js_rebalance_string_rope_rec(JSContext *ctx, JSValue *buckets, + JSValueConst val) +{ + if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) { + JSString *p = JS_VALUE_GET_STRING(val); + uint32_t len, i; + JSValue a, b; + + len = p->len; + if (len == 0) + return 0; /* nothing to do */ + /* find the bucket i so that rope_bucket_len[i] <= len < + rope_bucket_len[i + 1] and concatenate the ropes in the + buckets before */ + a = JS_NULL; + i = 0; + while (len >= rope_bucket_len[i + 1]) { + b = buckets[i]; + if (!JS_IsNull(b)) { + buckets[i] = JS_NULL; + if (JS_IsNull(a)) { + a = b; + } else { + a = js_new_string_rope(ctx, b, a); + if (JS_IsException(a)) + return -1; + } + } + i++; + } + if (!JS_IsNull(a)) { + a = js_new_string_rope(ctx, a, js_dup(val)); + if (JS_IsException(a)) + return -1; + } else { + a = js_dup(val); + } + while (!JS_IsNull(buckets[i])) { + a = js_new_string_rope(ctx, buckets[i], a); + buckets[i] = JS_NULL; + if (JS_IsException(a)) + return -1; + i++; + } + buckets[i] = a; + } else { + JSStringRope *r = JS_VALUE_GET_STRING_ROPE(val); + if (js_rebalance_string_rope_rec(ctx, buckets, r->left)) + return -1; + if (js_rebalance_string_rope_rec(ctx, buckets, r->right)) + return -1; + } + return 0; +} + +/* Return a new rope which is balanced. Algorithm from "Ropes: an + Alternative to Strings", Hans-J. Boehm, Russ Atkinson and Michael + Plass. */ +static JSValue js_rebalance_string_rope(JSContext *ctx, JSValueConst rope) +{ + JSValue buckets[ROPE_N_BUCKETS], a, b; + int i; + + for(i = 0; i < ROPE_N_BUCKETS; i++) + buckets[i] = JS_NULL; + if (js_rebalance_string_rope_rec(ctx, buckets, rope)) + goto fail; + a = JS_NULL; + for(i = 0; i < ROPE_N_BUCKETS; i++) { + b = buckets[i]; + if (!JS_IsNull(b)) { + buckets[i] = JS_NULL; + if (JS_IsNull(a)) { + a = b; + } else { + a = js_new_string_rope(ctx, b, a); + if (JS_IsException(a)) + goto fail; + } + } + } + /* fail safe */ + if (JS_IsNull(a)) + return JS_AtomToString(ctx, JS_ATOM_empty_string); + else + return a; + fail: + for(i = 0; i < ROPE_N_BUCKETS; i++) { + JS_FreeValue(ctx, buckets[i]); + } + return JS_EXCEPTION; +} + +/* 'rope' must be a rope. return a string and modify the rope so that + it won't need to be linearized again. */ +static JSValue js_linearize_string_rope(JSContext *ctx, JSValueConst rope) +{ + StringBuffer b_s, *b = &b_s; + JSStringRope *r; + JSValue ret; + + r = JS_VALUE_GET_STRING_ROPE(rope); + + /* check whether it is already linearized */ + if (JS_VALUE_GET_TAG(r->right) == JS_TAG_STRING && + JS_VALUE_GET_STRING(r->right)->len == 0) { + ret = js_dup(r->left); + return ret; + } + if (string_buffer_init2(ctx, b, r->len, r->is_wide_char)) + goto fail; + if (string_buffer_concat_value(b, rope)) + goto fail; + ret = string_buffer_end(b); + if (r->header.ref_count > 1) { + /* update the rope so that it won't need to be linearized again */ + JS_FreeValue(ctx, r->left); + JS_FreeValue(ctx, r->right); + r->left = js_dup(ret); + r->right = JS_AtomToString(ctx, JS_ATOM_empty_string); + } + return ret; + fail: + return JS_EXCEPTION; +} + +/* flat string concatenation - used by rope when concatenating short strings */ +static JSValue JS_ConcatString2(JSContext *ctx, JSValue op1, JSValue op2); + +static void copy_str16(uint16_t *dst, JSString *p, int offset, int len) +{ + if (p->is_wide_char) { + memcpy(dst, str16(p) + offset, len * 2); + } else { + const uint8_t *src1 = str8(p) + offset; + int i; + + for(i = 0; i < len; i++) + dst[i] = src1[i]; + } +} + +static JSValue JS_ConcatString1(JSContext *ctx, JSString *p1, JSString *p2) +{ + JSString *p; + uint32_t len; + int is_wide_char; + + len = p1->len + p2->len; + if (len > JS_STRING_LEN_MAX) + return JS_ThrowRangeError(ctx, "invalid string length"); + is_wide_char = p1->is_wide_char | p2->is_wide_char; + p = js_alloc_string(ctx, len, is_wide_char); + if (!p) + return JS_EXCEPTION; + if (!is_wide_char) { + memcpy(str8(p), str8(p1), p1->len); + memcpy(str8(p) + p1->len, str8(p2), p2->len); + str8(p)[len] = '\0'; + } else { + copy_str16(str16(p), p1, 0, p1->len); + copy_str16(str16(p) + p1->len, p2, 0, p2->len); + } + return JS_MKPTR(JS_TAG_STRING, p); +} + +/* flat string concatenation - op1 and op2 must be JS_TAG_STRING */ +static JSValue JS_ConcatString2(JSContext *ctx, JSValue op1, JSValue op2) +{ + JSValue ret; + JSString *p1, *p2; + + p1 = JS_VALUE_GET_STRING(op1); + p2 = JS_VALUE_GET_STRING(op2); + + /* XXX: could also check if p1 is empty */ + if (p2->len == 0) { + goto ret_op1; + } + if (p1->header.ref_count == 1 && p1->is_wide_char == p2->is_wide_char + && js_malloc_usable_size(ctx, p1) >= sizeof(*p1) + ((p1->len + p2->len) << p2->is_wide_char) + 1 - p1->is_wide_char) { + /* Concatenate in place in available space at the end of p1 */ + if (p1->is_wide_char) { + memcpy(str16(p1) + p1->len, str16(p2), p2->len << 1); + p1->len += p2->len; + } else { + memcpy(str8(p1) + p1->len, str8(p2), p2->len); + p1->len += p2->len; + str8(p1)[p1->len] = '\0'; + } + ret_op1: + JS_FreeValue(ctx, op2); + return op1; + } + ret = JS_ConcatString1(ctx, p1, p2); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return ret; +} + +/* op1 and op2 are converted to strings. For convenience, op1 or op2 = + JS_EXCEPTION are accepted and return JS_EXCEPTION. */ +static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2) +{ + JSString *p1, *p2; + + if (unlikely(!tag_is_string(JS_VALUE_GET_TAG(op1)))) { + op1 = JS_ToStringFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + return JS_EXCEPTION; + } + } + if (unlikely(!tag_is_string(JS_VALUE_GET_TAG(op2)))) { + op2 = JS_ToStringFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + return JS_EXCEPTION; + } + } + + /* normal concatenation for short strings */ + if (JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) { + p2 = JS_VALUE_GET_STRING(op2); + if (p2->len == 0) { + JS_FreeValue(ctx, op2); + return op1; + } + if (p2->len <= JS_STRING_ROPE_SHORT_LEN) { + if (JS_VALUE_GET_TAG(op1) == JS_TAG_STRING) { + p1 = JS_VALUE_GET_STRING(op1); + if (p1->len <= JS_STRING_ROPE_SHORT2_LEN) { + return JS_ConcatString2(ctx, op1, op2); + } else { + return js_new_string_rope(ctx, op1, op2); + } + } else { + JSStringRope *r1; + r1 = JS_VALUE_GET_STRING_ROPE(op1); + if (JS_VALUE_GET_TAG(r1->right) == JS_TAG_STRING && + JS_VALUE_GET_STRING(r1->right)->len <= JS_STRING_ROPE_SHORT_LEN) { + JSValue val, ret; + val = JS_ConcatString2(ctx, js_dup(r1->right), op2); + if (JS_IsException(val)) { + JS_FreeValue(ctx, op1); + return JS_EXCEPTION; + } + ret = js_new_string_rope(ctx, js_dup(r1->left), val); + JS_FreeValue(ctx, op1); + return ret; + } + } + } + } else if (JS_VALUE_GET_TAG(op1) == JS_TAG_STRING) { + JSStringRope *r2; + p1 = JS_VALUE_GET_STRING(op1); + if (p1->len == 0) { + JS_FreeValue(ctx, op1); + return op2; + } + r2 = JS_VALUE_GET_STRING_ROPE(op2); + if (JS_VALUE_GET_TAG(r2->left) == JS_TAG_STRING && + JS_VALUE_GET_STRING(r2->left)->len <= JS_STRING_ROPE_SHORT_LEN) { + JSValue val, ret; + val = JS_ConcatString2(ctx, op1, js_dup(r2->left)); + if (JS_IsException(val)) { + JS_FreeValue(ctx, op2); + return JS_EXCEPTION; + } + ret = js_new_string_rope(ctx, val, js_dup(r2->right)); + JS_FreeValue(ctx, op2); + return ret; + } + } + return js_new_string_rope(ctx, op1, op2); +} + +/* Shape support */ + +static inline size_t get_shape_size(size_t hash_size, size_t prop_size) +{ + return hash_size * sizeof(uint32_t) + sizeof(JSShape) + + prop_size * sizeof(JSShapeProperty); +} + +static inline JSShape *get_shape_from_alloc(void *sh_alloc, size_t hash_size) +{ + return (JSShape *)(void *)((uint32_t *)sh_alloc + hash_size); +} + +static inline uint32_t *prop_hash_end(JSShape *sh) +{ + return (uint32_t *)sh; +} + +static inline void *get_alloc_from_shape(JSShape *sh) +{ + return prop_hash_end(sh) - ((intptr_t)sh->prop_hash_mask + 1); +} + +static inline JSShapeProperty *get_shape_prop(JSShape *sh) +{ + return sh->prop; +} + +static int init_shape_hash(JSRuntime *rt) +{ + rt->shape_hash_bits = 6; /* 64 shapes */ + rt->shape_hash_size = 1 << rt->shape_hash_bits; + rt->shape_hash_count = 0; + rt->shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) * + rt->shape_hash_size); + if (!rt->shape_hash) + return -1; + return 0; +} + +/* same magic hash multiplier as the Linux kernel */ +static uint32_t shape_hash(uint32_t h, uint32_t val) +{ + return (h + val) * 0x9e370001; +} + +/* truncate the shape hash to 'hash_bits' bits */ +static uint32_t get_shape_hash(uint32_t h, int hash_bits) +{ + return h >> (32 - hash_bits); +} + +static uint32_t shape_initial_hash(JSObject *proto) +{ + uint32_t h; + h = shape_hash(1, (uintptr_t)proto); + if (sizeof(proto) > 4) + h = shape_hash(h, (uint64_t)(uintptr_t)proto >> 32); + return h; +} + +static int resize_shape_hash(JSRuntime *rt, int new_shape_hash_bits) +{ + int new_shape_hash_size, i; + uint32_t h; + JSShape **new_shape_hash, *sh, *sh_next; + + new_shape_hash_size = 1 << new_shape_hash_bits; + new_shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) * + new_shape_hash_size); + if (!new_shape_hash) + return -1; + for(i = 0; i < rt->shape_hash_size; i++) { + for(sh = rt->shape_hash[i]; sh != NULL; sh = sh_next) { + sh_next = sh->shape_hash_next; + h = get_shape_hash(sh->hash, new_shape_hash_bits); + sh->shape_hash_next = new_shape_hash[h]; + new_shape_hash[h] = sh; + } + } + js_free_rt(rt, rt->shape_hash); + rt->shape_hash_bits = new_shape_hash_bits; + rt->shape_hash_size = new_shape_hash_size; + rt->shape_hash = new_shape_hash; + return 0; +} + +static void js_shape_hash_link(JSRuntime *rt, JSShape *sh) +{ + uint32_t h; + h = get_shape_hash(sh->hash, rt->shape_hash_bits); + sh->shape_hash_next = rt->shape_hash[h]; + rt->shape_hash[h] = sh; + rt->shape_hash_count++; +} + +static void js_shape_hash_unlink(JSRuntime *rt, JSShape *sh) +{ + uint32_t h; + JSShape **psh; + + h = get_shape_hash(sh->hash, rt->shape_hash_bits); + psh = &rt->shape_hash[h]; + while (*psh != sh) + psh = &(*psh)->shape_hash_next; + *psh = sh->shape_hash_next; + rt->shape_hash_count--; +} + +/* create a new empty shape with prototype 'proto' */ +static no_inline JSShape *js_new_shape2(JSContext *ctx, JSObject *proto, + int hash_size, int prop_size) +{ + JSRuntime *rt = ctx->rt; + void *sh_alloc; + JSShape *sh; + + /* resize the shape hash table if necessary */ + if (2 * (rt->shape_hash_count + 1) > rt->shape_hash_size) { + resize_shape_hash(rt, rt->shape_hash_bits + 1); + } + + sh_alloc = js_malloc(ctx, get_shape_size(hash_size, prop_size)); + if (!sh_alloc) + return NULL; + sh = get_shape_from_alloc(sh_alloc, hash_size); + sh->header.ref_count = 1; + add_gc_object(rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE); + if (proto) + js_dup(JS_MKPTR(JS_TAG_OBJECT, proto)); + sh->proto = proto; + memset(prop_hash_end(sh) - hash_size, 0, sizeof(prop_hash_end(sh)[0]) * + hash_size); + sh->prop_hash_mask = hash_size - 1; + sh->prop_size = prop_size; + sh->prop_count = 0; + sh->deleted_prop_count = 0; + + /* insert in the hash table */ + sh->hash = shape_initial_hash(proto); + sh->is_hashed = true; + js_shape_hash_link(ctx->rt, sh); + return sh; +} + +static JSShape *js_new_shape(JSContext *ctx, JSObject *proto) +{ + return js_new_shape2(ctx, proto, JS_PROP_INITIAL_HASH_SIZE, + JS_PROP_INITIAL_SIZE); +} + +/* The shape is cloned. The new shape is not inserted in the shape + hash table */ +static JSShape *js_clone_shape(JSContext *ctx, JSShape *sh1) +{ + JSShape *sh; + void *sh_alloc, *sh_alloc1; + size_t size; + JSShapeProperty *pr; + uint32_t i, hash_size; + + hash_size = sh1->prop_hash_mask + 1; + size = get_shape_size(hash_size, sh1->prop_size); + sh_alloc = js_malloc(ctx, size); + if (!sh_alloc) + return NULL; + sh_alloc1 = get_alloc_from_shape(sh1); + memcpy(sh_alloc, sh_alloc1, size); + sh = get_shape_from_alloc(sh_alloc, hash_size); + sh->header.ref_count = 1; + add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE); + sh->is_hashed = false; + if (sh->proto) { + js_dup(JS_MKPTR(JS_TAG_OBJECT, sh->proto)); + } + for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) { + JS_DupAtom(ctx, pr->atom); + } + return sh; +} + +static JSShape *js_dup_shape(JSShape *sh) +{ + sh->header.ref_count++; + return sh; +} + +static void js_free_shape0(JSRuntime *rt, JSShape *sh) +{ + uint32_t i; + JSShapeProperty *pr; + + assert(sh->header.ref_count == 0); + if (sh->is_hashed) + js_shape_hash_unlink(rt, sh); + if (sh->proto != NULL) { + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, sh->proto)); + } + pr = get_shape_prop(sh); + for(i = 0; i < sh->prop_count; i++) { + JS_FreeAtomRT(rt, pr->atom); + pr++; + } + remove_gc_object(&sh->header); + js_free_rt(rt, get_alloc_from_shape(sh)); +} + +static void js_free_shape(JSRuntime *rt, JSShape *sh) +{ + if (unlikely(--sh->header.ref_count <= 0)) { + js_free_shape0(rt, sh); + } +} + +static void js_free_shape_null(JSRuntime *rt, JSShape *sh) +{ + if (sh) + js_free_shape(rt, sh); +} + +/* make space to hold at least 'count' properties */ +static no_inline int resize_properties(JSContext *ctx, JSShape **psh, + JSObject *p, uint32_t count) +{ + JSShape *sh; + uint32_t new_size, new_hash_size, new_hash_mask, i; + JSShapeProperty *pr; + void *sh_alloc; + intptr_t h; + + sh = *psh; + new_size = max_int(count, sh->prop_size * 3 / 2); + /* Reallocate prop array first to avoid crash or size inconsistency + in case of memory allocation failure */ + if (p) { + JSProperty *new_prop; + new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size); + if (unlikely(!new_prop)) + return -1; + p->prop = new_prop; + } + new_hash_size = sh->prop_hash_mask + 1; + while (new_hash_size < new_size) + new_hash_size = 2 * new_hash_size; + if (new_hash_size != (sh->prop_hash_mask + 1)) { + JSShape *old_sh; + /* resize the hash table and the properties */ + old_sh = sh; + sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size)); + if (!sh_alloc) + return -1; + sh = get_shape_from_alloc(sh_alloc, new_hash_size); + list_del(&old_sh->header.link); + /* copy all the fields and the properties */ + memcpy(sh, old_sh, + sizeof(JSShape) + sizeof(sh->prop[0]) * old_sh->prop_count); + list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); + new_hash_mask = new_hash_size - 1; + sh->prop_hash_mask = new_hash_mask; + memset(prop_hash_end(sh) - new_hash_size, 0, + sizeof(prop_hash_end(sh)[0]) * new_hash_size); + for(i = 0, pr = sh->prop; i < sh->prop_count; i++, pr++) { + if (pr->atom != JS_ATOM_NULL) { + h = ((uintptr_t)pr->atom & new_hash_mask); + pr->hash_next = prop_hash_end(sh)[-h - 1]; + prop_hash_end(sh)[-h - 1] = i + 1; + } + } + js_free(ctx, get_alloc_from_shape(old_sh)); + } else { + /* only resize the properties */ + list_del(&sh->header.link); + sh_alloc = js_realloc(ctx, get_alloc_from_shape(sh), + get_shape_size(new_hash_size, new_size)); + if (unlikely(!sh_alloc)) { + /* insert again in the GC list */ + list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); + return -1; + } + sh = get_shape_from_alloc(sh_alloc, new_hash_size); + list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); + } + *psh = sh; + sh->prop_size = new_size; + return 0; +} + +/* remove the deleted properties. */ +static int compact_properties(JSContext *ctx, JSObject *p) +{ + JSShape *sh, *old_sh; + void *sh_alloc; + intptr_t h; + uint32_t new_hash_size, i, j, new_hash_mask, new_size; + JSShapeProperty *old_pr, *pr; + JSProperty *prop, *new_prop; + + sh = p->shape; + assert(!sh->is_hashed); + + new_size = max_int(JS_PROP_INITIAL_SIZE, + sh->prop_count - sh->deleted_prop_count); + assert(new_size <= sh->prop_size); + + new_hash_size = sh->prop_hash_mask + 1; + while ((new_hash_size / 2) >= new_size) + new_hash_size = new_hash_size / 2; + new_hash_mask = new_hash_size - 1; + + /* resize the hash table and the properties */ + old_sh = sh; + sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size)); + if (!sh_alloc) + return -1; + sh = get_shape_from_alloc(sh_alloc, new_hash_size); + list_del(&old_sh->header.link); + memcpy(sh, old_sh, sizeof(JSShape)); + list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); + + memset(prop_hash_end(sh) - new_hash_size, 0, + sizeof(prop_hash_end(sh)[0]) * new_hash_size); + + j = 0; + old_pr = old_sh->prop; + pr = sh->prop; + prop = p->prop; + for(i = 0; i < sh->prop_count; i++) { + if (old_pr->atom != JS_ATOM_NULL) { + pr->atom = old_pr->atom; + pr->flags = old_pr->flags; + h = ((uintptr_t)old_pr->atom & new_hash_mask); + pr->hash_next = prop_hash_end(sh)[-h - 1]; + prop_hash_end(sh)[-h - 1] = j + 1; + prop[j] = prop[i]; + j++; + pr++; + } + old_pr++; + } + assert(j == (sh->prop_count - sh->deleted_prop_count)); + sh->prop_hash_mask = new_hash_mask; + sh->prop_size = new_size; + sh->deleted_prop_count = 0; + sh->prop_count = j; + + p->shape = sh; + js_free(ctx, get_alloc_from_shape(old_sh)); + + /* reduce the size of the object properties */ + new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size); + if (new_prop) + p->prop = new_prop; + return 0; +} + +static int add_shape_property(JSContext *ctx, JSShape **psh, + JSObject *p, JSAtom atom, int prop_flags) +{ + JSRuntime *rt = ctx->rt; + JSShape *sh = *psh; + JSShapeProperty *pr, *prop; + uint32_t hash_mask, new_shape_hash = 0; + intptr_t h; + + /* update the shape hash */ + if (sh->is_hashed) { + js_shape_hash_unlink(rt, sh); + new_shape_hash = shape_hash(shape_hash(sh->hash, atom), prop_flags); + } + + if (unlikely(sh->prop_count >= sh->prop_size)) { + if (resize_properties(ctx, psh, p, sh->prop_count + 1)) { + /* in case of error, reinsert in the hash table. + sh is still valid if resize_properties() failed */ + if (sh->is_hashed) + js_shape_hash_link(rt, sh); + return -1; + } + sh = *psh; + } + if (sh->is_hashed) { + sh->hash = new_shape_hash; + js_shape_hash_link(rt, sh); + } + /* Initialize the new shape property. + The object property at p->prop[sh->prop_count] is uninitialized */ + prop = get_shape_prop(sh); + pr = &prop[sh->prop_count++]; + pr->atom = JS_DupAtom(ctx, atom); + pr->flags = prop_flags; + /* add in hash table */ + hash_mask = sh->prop_hash_mask; + h = atom & hash_mask; + pr->hash_next = prop_hash_end(sh)[-h - 1]; + prop_hash_end(sh)[-h - 1] = sh->prop_count; + return 0; +} + +/* find a hashed empty shape matching the prototype. Return NULL if + not found */ +static JSShape *find_hashed_shape_proto(JSRuntime *rt, JSObject *proto) +{ + JSShape *sh1; + uint32_t h, h1; + + h = shape_initial_hash(proto); + h1 = get_shape_hash(h, rt->shape_hash_bits); + for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) { + if (sh1->hash == h && + sh1->proto == proto && + sh1->prop_count == 0) { + return sh1; + } + } + return NULL; +} + +/* find a hashed shape matching sh + (prop, prop_flags). Return NULL if + not found */ +static JSShape *find_hashed_shape_prop(JSRuntime *rt, JSShape *sh, + JSAtom atom, int prop_flags) +{ + JSShape *sh1; + uint32_t h, h1, i, n; + + h = sh->hash; + h = shape_hash(h, atom); + h = shape_hash(h, prop_flags); + h1 = get_shape_hash(h, rt->shape_hash_bits); + for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) { + /* we test the hash first so that the rest is done only if the + shapes really match */ + if (sh1->hash == h && + sh1->proto == sh->proto && + sh1->prop_count == ((n = sh->prop_count) + 1)) { + for(i = 0; i < n; i++) { + if (unlikely(sh1->prop[i].atom != sh->prop[i].atom) || + unlikely(sh1->prop[i].flags != sh->prop[i].flags)) + goto next; + } + if (unlikely(sh1->prop[n].atom != atom) || + unlikely(sh1->prop[n].flags != prop_flags)) + goto next; + return sh1; + } + next: ; + } + return NULL; +} + +static __maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh) +{ + char atom_buf[ATOM_GET_STR_BUF_SIZE]; + int j; + + /* XXX: should output readable class prototype */ + printf("%5d %3d%c %14p %5d %5d", i, + sh->header.ref_count, " *"[sh->is_hashed], + (void *)sh->proto, sh->prop_size, sh->prop_count); + for(j = 0; j < sh->prop_count; j++) { + printf(" %s", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), + sh->prop[j].atom)); + } + printf("\n"); +} + +static __maybe_unused void JS_DumpShapes(JSRuntime *rt) +{ + int i; + JSShape *sh; + struct list_head *el; + JSObject *p; + JSGCObjectHeader *gp; + + printf("JSShapes: {\n"); + printf("%5s %4s %14s %5s %5s %s\n", "SLOT", "REFS", "PROTO", "SIZE", "COUNT", "PROPS"); + for(i = 0; i < rt->shape_hash_size; i++) { + for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) { + JS_DumpShape(rt, i, sh); + assert(sh->is_hashed); + } + } + /* dump non-hashed shapes */ + list_for_each(el, &rt->gc_obj_list) { + gp = list_entry(el, JSGCObjectHeader, link); + if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { + p = (JSObject *)gp; + if (!p->shape->is_hashed) { + JS_DumpShape(rt, -1, p->shape); + } + } + } + printf("}\n"); +} + +/* 'props[]' is used to initialized the object properties. The number + of elements depends on the shape. */ +static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID class_id, + JSProperty *props) +{ + JSObject *p; + int i; + + js_trigger_gc(ctx->rt, sizeof(JSObject)); + p = js_malloc(ctx, sizeof(JSObject)); + if (unlikely(!p)) + goto fail; + p->class_id = class_id; + p->extensible = true; + p->free_mark = 0; + p->is_exotic = 0; + p->fast_array = 0; + p->is_constructor = 0; + p->is_uncatchable_error = 0; + p->tmp_mark = 0; + p->is_HTMLDDA = 0; + p->is_prototype = 0; + p->first_weak_ref = NULL; + p->u.opaque = NULL; + p->shape = sh; + p->prop = js_malloc(ctx, sizeof(JSProperty) * sh->prop_size); + if (unlikely(!p->prop)) { + js_free(ctx, p); + fail: + if (props) { + JSShapeProperty *prs = get_shape_prop(sh); + for(i = 0; i < sh->prop_count; i++) { + free_property(ctx->rt, &props[i], prs->flags); + prs++; + } + } + js_free_shape(ctx->rt, sh); + return JS_EXCEPTION; + } + + switch(class_id) { + case JS_CLASS_OBJECT: + break; + case JS_CLASS_ARRAY: + { + JSProperty *pr; + p->is_exotic = 1; + p->fast_array = 1; + p->u.array.u.values = NULL; + p->u.array.count = 0; + p->u.array.u1.size = 0; + if (!props) { + /* XXX: remove */ + /* the length property is always the first one */ + if (likely(sh == ctx->array_shape)) { + pr = &p->prop[0]; + } else { + /* only used for the first array */ + /* cannot fail */ + pr = add_property(ctx, p, JS_ATOM_length, + JS_PROP_WRITABLE | JS_PROP_LENGTH); + } + pr->u.value = js_int32(0); + } + } + break; + case JS_CLASS_C_FUNCTION: + p->prop[0].u.value = JS_UNDEFINED; + break; + case JS_CLASS_ARGUMENTS: + case JS_CLASS_MAPPED_ARGUMENTS: + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_INT8_ARRAY: + case JS_CLASS_UINT8_ARRAY: + case JS_CLASS_INT16_ARRAY: + case JS_CLASS_UINT16_ARRAY: + case JS_CLASS_INT32_ARRAY: + case JS_CLASS_UINT32_ARRAY: + case JS_CLASS_BIG_INT64_ARRAY: + case JS_CLASS_BIG_UINT64_ARRAY: + case JS_CLASS_FLOAT16_ARRAY: + case JS_CLASS_FLOAT32_ARRAY: + case JS_CLASS_FLOAT64_ARRAY: + p->is_exotic = 1; + p->fast_array = 1; + p->u.array.u.ptr = NULL; + p->u.array.count = 0; + break; + case JS_CLASS_DATAVIEW: + p->u.array.u.ptr = NULL; + p->u.array.count = 0; + break; + case JS_CLASS_NUMBER: + case JS_CLASS_STRING: + case JS_CLASS_BOOLEAN: + case JS_CLASS_SYMBOL: + case JS_CLASS_DATE: + case JS_CLASS_BIG_INT: + p->u.object_data = JS_UNDEFINED; + goto set_exotic; + case JS_CLASS_REGEXP: + p->u.regexp.pattern = NULL; + p->u.regexp.bytecode = NULL; + goto set_exotic; + default: + set_exotic: + if (ctx->rt->class_array[class_id].exotic) { + p->is_exotic = 1; + } + break; + } + p->header.ref_count = 1; + add_gc_object(ctx->rt, &p->header, JS_GC_OBJ_TYPE_JS_OBJECT); + if (props) { + for(i = 0; i < sh->prop_count; i++) + p->prop[i] = props[i]; + } + return JS_MKPTR(JS_TAG_OBJECT, p); +} + +static JSObject *get_proto_obj(JSValueConst proto_val) +{ + if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) + return NULL; + else + return JS_VALUE_GET_OBJ(proto_val); +} + +/* WARNING: proto must be an object or JS_NULL */ +JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto_val, + JSClassID class_id) +{ + JSShape *sh; + JSObject *proto; + + proto = get_proto_obj(proto_val); + sh = find_hashed_shape_proto(ctx->rt, proto); + if (likely(sh)) { + sh = js_dup_shape(sh); + } else { + sh = js_new_shape(ctx, proto); + if (!sh) + return JS_EXCEPTION; + } + return JS_NewObjectFromShape(ctx, sh, class_id, NULL); +} + +static int JS_SetObjectData(JSContext *ctx, JSValueConst obj, JSValue val) +{ + JSObject *p; + + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + p = JS_VALUE_GET_OBJ(obj); + switch(p->class_id) { + case JS_CLASS_NUMBER: + case JS_CLASS_STRING: + case JS_CLASS_BOOLEAN: + case JS_CLASS_SYMBOL: + case JS_CLASS_DATE: + case JS_CLASS_BIG_INT: + JS_FreeValue(ctx, p->u.object_data); + p->u.object_data = val; /* for JS_CLASS_STRING, 'val' must + be JS_TAG_STRING (and not a rope) */ + return 0; + } + } + JS_FreeValue(ctx, val); + if (!JS_IsException(obj)) + JS_ThrowTypeError(ctx, "invalid object type"); + return -1; +} + +JSValue JS_NewObjectClass(JSContext *ctx, JSClassID class_id) +{ + return JS_NewObjectProtoClass(ctx, ctx->class_proto[class_id], class_id); +} + +JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto) +{ + return JS_NewObjectProtoClass(ctx, proto, JS_CLASS_OBJECT); +} + +JSValue JS_NewObjectFrom(JSContext *ctx, int count, const JSAtom *props, + const JSValue *values) +{ + JSShapeProperty *pr; + uint32_t *hash; + JSRuntime *rt; + JSObject *p; + JSShape *sh; + JSValue obj; + JSAtom atom; + intptr_t h; + int i; + + rt = ctx->rt; + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + return JS_EXCEPTION; + if (count > 0) { + p = JS_VALUE_GET_OBJ(obj); + sh = p->shape; + assert(sh->is_hashed); + assert(sh->header.ref_count == 1); + js_shape_hash_unlink(rt, sh); + if (resize_properties(ctx, &sh, p, count)) { + js_shape_hash_link(rt, sh); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + p->shape = sh; + for (i = 0; i < count; i++) { + atom = props[i]; + pr = &sh->prop[i]; + sh->hash = shape_hash(shape_hash(sh->hash, atom), JS_PROP_C_W_E); + h = atom & sh->prop_hash_mask; + hash = &prop_hash_end(sh)[-h - 1]; + pr->hash_next = *hash; + *hash = i + 1; + pr->atom = JS_DupAtom(ctx, atom); + pr->flags = JS_PROP_C_W_E; + p->prop[i].u.value = values[i]; + } + js_shape_hash_link(rt, sh); + sh->prop_count = count; + } + return obj; +} + +JSValue JS_NewObjectFromStr(JSContext *ctx, int count, const char **props, + const JSValue *values) +{ + JSAtom atoms_s[16], *atoms = atoms_s; + JSValue ret; + int i; + + i = 0; + ret = JS_EXCEPTION; + if (count < 1) + goto out; + if (count > (int)countof(atoms_s)) { + atoms = js_malloc(ctx, count * sizeof(*atoms)); + if (!atoms) + return JS_EXCEPTION; + } + for (i = 0; i < count; i++) { + atoms[i] = JS_NewAtom(ctx, props[i]); + if (atoms[i] == JS_ATOM_NULL) + goto out; + } + ret = JS_NewObjectFrom(ctx, count, atoms, values); +out: + while (i-- > 0) + JS_FreeAtom(ctx, atoms[i]); + if (atoms != atoms_s) + js_free(ctx, atoms); + return ret; +} + +JSValue JS_NewArray(JSContext *ctx) +{ + return JS_NewObjectFromShape(ctx, js_dup_shape(ctx->array_shape), + JS_CLASS_ARRAY, NULL); +} + +// note: takes ownership of |values|, unlike js_create_array +JSValue JS_NewArrayFrom(JSContext *ctx, int count, const JSValue *values) +{ + JSObject *p; + JSValue obj; + int i; + + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + goto exception; + if (count > 0) { + p = JS_VALUE_GET_OBJ(obj); + if (expand_fast_array(ctx, p, count)) { + JS_FreeValue(ctx, obj); + goto exception; + } + p->u.array.count = count; + p->prop[0].u.value = js_int32(count); + memcpy(p->u.array.u.values, values, count * sizeof(*values)); + } + return obj; +exception: + for (i = 0; i < count; i++) + JS_FreeValue(ctx, values[i]); + return JS_EXCEPTION; +} + +JSValue JS_NewObject(JSContext *ctx) +{ + /* inline JS_NewObjectClass(ctx, JS_CLASS_OBJECT); */ + return JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_OBJECT); +} + +static void js_function_set_properties(JSContext *ctx, JSValue func_obj, + JSAtom name, int len) +{ + /* ES6 feature non compatible with ES5.1: length is configurable */ + JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, js_int32(len), + JS_PROP_CONFIGURABLE); + JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, + JS_AtomToString(ctx, name), JS_PROP_CONFIGURABLE); +} + +static bool js_class_has_bytecode(JSClassID class_id) +{ + return (class_id == JS_CLASS_BYTECODE_FUNCTION || + class_id == JS_CLASS_GENERATOR_FUNCTION || + class_id == JS_CLASS_ASYNC_FUNCTION || + class_id == JS_CLASS_ASYNC_GENERATOR_FUNCTION); +} + +/* return NULL without exception if not a function or no bytecode */ +static JSFunctionBytecode *JS_GetFunctionBytecode(JSValueConst val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return NULL; + p = JS_VALUE_GET_OBJ(val); + if (!js_class_has_bytecode(p->class_id)) + return NULL; + return p->u.func.function_bytecode; +} + +static void js_method_set_home_object(JSContext *ctx, JSValue func_obj, + JSValue home_obj) +{ + JSObject *p, *p1; + JSFunctionBytecode *b; + + if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) + return; + p = JS_VALUE_GET_OBJ(func_obj); + if (!js_class_has_bytecode(p->class_id)) + return; + b = p->u.func.function_bytecode; + if (b->need_home_object) { + p1 = p->u.func.home_object; + if (p1) { + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); + } + if (JS_VALUE_GET_TAG(home_obj) == JS_TAG_OBJECT) + p1 = JS_VALUE_GET_OBJ(js_dup(home_obj)); + else + p1 = NULL; + p->u.func.home_object = p1; + } +} + +static JSValue js_get_function_name(JSContext *ctx, JSAtom name) +{ + JSValue name_str; + + name_str = JS_AtomToString(ctx, name); + if (JS_AtomSymbolHasDescription(ctx, name)) { + name_str = JS_ConcatString3(ctx, "[", name_str, "]"); + } + return name_str; +} + +/* Modify the name of a method according to the atom and + 'flags'. 'flags' is a bitmask of JS_PROP_HAS_GET and + JS_PROP_HAS_SET. Also set the home object of the method. + Return < 0 if exception. */ +static int js_method_set_properties(JSContext *ctx, JSValue func_obj, + JSAtom name, int flags, JSValue home_obj) +{ + JSValue name_str; + + name_str = js_get_function_name(ctx, name); + if (flags & JS_PROP_HAS_GET) { + name_str = JS_ConcatString3(ctx, "get ", name_str, ""); + } else if (flags & JS_PROP_HAS_SET) { + name_str = JS_ConcatString3(ctx, "set ", name_str, ""); + } + if (JS_IsException(name_str)) + return -1; + if (JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name_str, + JS_PROP_CONFIGURABLE) < 0) + return -1; + js_method_set_home_object(ctx, func_obj, home_obj); + return 0; +} + +/* Note: at least 'length' arguments will be readable in 'argv' */ +/* `name` may be NULL, pure ASCII or UTF-8 encoded */ +JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func, + const char *name, + int length, JSCFunctionEnum cproto, int magic, + JSValueConst proto_val) +{ + JSValue func_obj; + JSObject *p; + JSAtom name_atom; + + func_obj = JS_NewObjectProtoClass(ctx, proto_val, JS_CLASS_C_FUNCTION); + if (JS_IsException(func_obj)) + return func_obj; + p = JS_VALUE_GET_OBJ(func_obj); + p->u.cfunc.realm = JS_DupContext(ctx); + p->u.cfunc.c_function.generic = func; + p->u.cfunc.length = length; + p->u.cfunc.cproto = cproto; + p->u.cfunc.magic = magic; + p->is_constructor = (cproto == JS_CFUNC_constructor || + cproto == JS_CFUNC_constructor_magic || + cproto == JS_CFUNC_constructor_or_func || + cproto == JS_CFUNC_constructor_or_func_magic); + name_atom = JS_ATOM_empty_string; + if (name && *name) { + name_atom = JS_NewAtom(ctx, name); + if (name_atom == JS_ATOM_NULL) { + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; + } + } + js_function_set_properties(ctx, func_obj, name_atom, length); + JS_FreeAtom(ctx, name_atom); + return func_obj; +} + +/* Note: at least 'length' arguments will be readable in 'argv' */ +JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func, + const char *name, + int length, JSCFunctionEnum cproto, int magic) +{ + return JS_NewCFunction3(ctx, func, name, length, cproto, magic, + ctx->function_proto); +} + +typedef struct JSCFunctionDataRecord { + JSCFunctionData *func; + uint8_t length; + uint8_t data_len; + uint16_t magic; + JSValue data[]; +} JSCFunctionDataRecord; + +static void js_c_function_data_finalizer(JSRuntime *rt, JSValueConst val) +{ + JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA); + int i; + + if (s) { + for(i = 0; i < s->data_len; i++) { + JS_FreeValueRT(rt, s->data[i]); + } + js_free_rt(rt, s); + } +} + +static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA); + int i; + + if (s) { + for(i = 0; i < s->data_len; i++) { + JS_MarkValue(rt, s->data[i], mark_func); + } + } +} + +static JSValue js_call_c_function_data(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_val, + int argc, JSValueConst *argv, int flags) +{ + JSRuntime *rt = ctx->rt; + JSStackFrame sf_s, *sf = &sf_s, *prev_sf; + JSCFunctionDataRecord *s; + JSValueConst *arg_buf; + JSValue ret; + size_t stack_size; + int arg_count; + int i; + + s = JS_GetOpaque(func_obj, JS_CLASS_C_FUNCTION_DATA); + if (!s) + return JS_EXCEPTION; // can't really happen + arg_buf = argv; + arg_count = s->length; + if (unlikely(argc < arg_count)) { + stack_size = arg_count * sizeof(arg_buf[0]); + if (js_check_stack_overflow(rt, stack_size)) + return JS_ThrowStackOverflow(ctx); + arg_buf = alloca(stack_size); + for(i = 0; i < argc; i++) + arg_buf[i] = argv[i]; + for(i = argc; i < arg_count; i++) + arg_buf[i] = JS_UNDEFINED; + } + prev_sf = rt->current_stack_frame; + sf->prev_frame = prev_sf; + rt->current_stack_frame = sf; + // TODO(bnoordhuis) switch realms like js_call_c_function does + sf->is_strict_mode = false; + sf->cur_func = unsafe_unconst(func_obj); + sf->arg_count = argc; + ret = s->func(ctx, this_val, argc, arg_buf, s->magic, vc(s->data)); + rt->current_stack_frame = sf->prev_frame; + return ret; +} + +JSValue JS_NewCFunctionData2(JSContext *ctx, JSCFunctionData *func, + const char *name, + int length, int magic, int data_len, + JSValueConst *data) +{ + JSCFunctionDataRecord *s; + JSAtom name_atom; + JSValue func_obj; + int i; + + func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, + JS_CLASS_C_FUNCTION_DATA); + if (JS_IsException(func_obj)) + return func_obj; + s = js_malloc(ctx, sizeof(*s) + data_len * sizeof(JSValue)); + if (!s) { + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; + } + s->func = func; + s->length = length; + s->data_len = data_len; + s->magic = magic; + for(i = 0; i < data_len; i++) + s->data[i] = js_dup(data[i]); + JS_SetOpaqueInternal(func_obj, s); + name_atom = JS_ATOM_empty_string; + if (name && *name) { + name_atom = JS_NewAtom(ctx, name); + if (name_atom == JS_ATOM_NULL) { + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; + } + } + js_function_set_properties(ctx, func_obj, name_atom, length); + JS_FreeAtom(ctx, name_atom); + return func_obj; +} + +JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func, + int length, int magic, int data_len, + JSValueConst *data) +{ + return JS_NewCFunctionData2(ctx, func, NULL, length, magic, data_len, data); +} + +static JSContext *js_autoinit_get_realm(JSProperty *pr) +{ + return (JSContext *)(pr->u.init.realm_and_id & ~3); +} + +static JSAutoInitIDEnum js_autoinit_get_id(JSProperty *pr) +{ + return pr->u.init.realm_and_id & 3; +} + +static void js_autoinit_free(JSRuntime *rt, JSProperty *pr) +{ + JS_FreeContext(js_autoinit_get_realm(pr)); +} + +static void js_autoinit_mark(JSRuntime *rt, JSProperty *pr, + JS_MarkFunc *mark_func) +{ + mark_func(rt, &js_autoinit_get_realm(pr)->header); +} + +typedef struct JSCClosureRecord { + JSCClosure *func; + uint16_t length; + uint16_t magic; + void *opaque; + void (*opaque_finalize)(void *opaque); +} JSCClosureRecord; + +static void js_c_closure_finalizer(JSRuntime *rt, JSValueConst val) +{ + JSCClosureRecord *s = JS_GetOpaque(val, JS_CLASS_C_CLOSURE); + + if (s) { + if (s->opaque_finalize) + s->opaque_finalize(s->opaque); + + js_free_rt(rt, s); + } +} + +static JSValue js_call_c_closure(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_val, + int argc, JSValueConst *argv, int flags) +{ + JSRuntime *rt = ctx->rt; + JSStackFrame sf_s, *sf = &sf_s, *prev_sf; + JSCClosureRecord *s = JS_GetOpaque(func_obj, JS_CLASS_C_CLOSURE); + JSValueConst *arg_buf; + JSValue ret; + int arg_count; + int i; + size_t stack_size; + + arg_buf = argv; + arg_count = s->length; + if (unlikely(argc < arg_count)) { + stack_size = arg_count * sizeof(arg_buf[0]); + if (js_check_stack_overflow(rt, stack_size)) + return JS_ThrowStackOverflow(ctx); + arg_buf = alloca(stack_size); + for (i = 0; i < argc; i++) + arg_buf[i] = argv[i]; + for (i = argc; i < arg_count; i++) + arg_buf[i] = JS_UNDEFINED; + } + + prev_sf = rt->current_stack_frame; + sf->prev_frame = prev_sf; + rt->current_stack_frame = sf; + // TODO(bnoordhuis) switch realms like js_call_c_function does + sf->is_strict_mode = false; + sf->cur_func = unsafe_unconst(func_obj); + sf->arg_count = argc; + ret = s->func(ctx, this_val, argc, arg_buf, s->magic, s->opaque); + rt->current_stack_frame = sf->prev_frame; + + return ret; +} + +JSValue JS_NewCClosure(JSContext *ctx, JSCClosure *func, const char *name, + JSCClosureFinalizerFunc *opaque_finalize, + int length, int magic, void *opaque) +{ + JSCClosureRecord *s; + JSAtom name_atom; + JSValue func_obj; + + func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, + JS_CLASS_C_CLOSURE); + if (JS_IsException(func_obj)) + return func_obj; + s = js_malloc(ctx, sizeof(*s)); + if (!s) { + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; + } + s->func = func; + s->length = length; + s->magic = magic; + s->opaque = opaque; + s->opaque_finalize = opaque_finalize; + JS_SetOpaqueInternal(func_obj, s); + name_atom = JS_ATOM_empty_string; + if (name && *name) { + name_atom = JS_NewAtom(ctx, name); + if (name_atom == JS_ATOM_NULL) { + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; + } + } + js_function_set_properties(ctx, func_obj, name_atom, length); + JS_FreeAtom(ctx, name_atom); + return func_obj; +} + +static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags) +{ + if (unlikely(prop_flags & JS_PROP_TMASK)) { + if ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + if (pr->u.getset.getter) + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); + if (pr->u.getset.setter) + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); + } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + free_var_ref(rt, pr->u.var_ref); + } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + js_autoinit_free(rt, pr); + } + } else { + JS_FreeValueRT(rt, pr->u.value); + } +} + +static inline JSShapeProperty *find_own_property1(JSObject *p, JSAtom atom) +{ + JSShape *sh; + JSShapeProperty *pr, *prop; + intptr_t h; + sh = p->shape; + h = (uintptr_t)atom & sh->prop_hash_mask; + h = prop_hash_end(sh)[-h - 1]; + prop = get_shape_prop(sh); + while (h) { + pr = &prop[h - 1]; + if (likely(pr->atom == atom)) { + return pr; + } + h = pr->hash_next; + } + return NULL; +} + +static inline JSShapeProperty *find_own_property(JSProperty **ppr, + JSObject *p, + JSAtom atom) +{ + JSShape *sh; + JSShapeProperty *pr, *prop; + intptr_t h; + sh = p->shape; + h = (uintptr_t)atom & sh->prop_hash_mask; + h = prop_hash_end(sh)[-h - 1]; + prop = get_shape_prop(sh); + while (h) { + pr = &prop[h - 1]; + if (likely(pr->atom == atom)) { + *ppr = &p->prop[h - 1]; + /* the compiler should be able to assume that pr != NULL here */ + return pr; + } + h = pr->hash_next; + } + *ppr = NULL; + return NULL; +} + +static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref) +{ + if (var_ref) { + assert(var_ref->header.ref_count > 0); + if (--var_ref->header.ref_count == 0) { + if (var_ref->is_detached) { + JS_FreeValueRT(rt, var_ref->value); + remove_gc_object(&var_ref->header); + } else { + JSStackFrame *sf = var_ref->stack_frame; + assert(sf->var_refs[var_ref->var_ref_idx] == var_ref); + sf->var_refs[var_ref->var_ref_idx] = NULL; + } + js_free_rt(rt, var_ref); + } + } +} + +static void js_array_finalizer(JSRuntime *rt, JSValueConst val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + int i; + + for(i = 0; i < p->u.array.count; i++) { + JS_FreeValueRT(rt, p->u.array.u.values[i]); + } + js_free_rt(rt, p->u.array.u.values); +} + +static void js_array_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + int i; + + for(i = 0; i < p->u.array.count; i++) { + JS_MarkValue(rt, p->u.array.u.values[i], mark_func); + } +} + +static void js_object_data_finalizer(JSRuntime *rt, JSValueConst val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JS_FreeValueRT(rt, p->u.object_data); + p->u.object_data = JS_UNDEFINED; +} + +static void js_object_data_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JS_MarkValue(rt, p->u.object_data, mark_func); +} + +static void js_c_function_finalizer(JSRuntime *rt, JSValueConst val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + + if (p->u.cfunc.realm) + JS_FreeContext(p->u.cfunc.realm); +} + +static void js_c_function_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + + if (p->u.cfunc.realm) + mark_func(rt, &p->u.cfunc.realm->header); +} + +static void js_bytecode_function_finalizer(JSRuntime *rt, JSValueConst val) +{ + JSObject *p1, *p = JS_VALUE_GET_OBJ(val); + JSFunctionBytecode *b; + JSVarRef **var_refs; + int i; + + p1 = p->u.func.home_object; + if (p1) { + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, p1)); + } + b = p->u.func.function_bytecode; + if (b) { + var_refs = p->u.func.var_refs; + if (var_refs) { + for(i = 0; i < b->closure_var_count; i++) + free_var_ref(rt, var_refs[i]); + js_free_rt(rt, var_refs); + } + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b)); + } +} + +static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSVarRef **var_refs = p->u.func.var_refs; + JSFunctionBytecode *b = p->u.func.function_bytecode; + int i; + + if (p->u.func.home_object) { + JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object), + mark_func); + } + if (b) { + if (var_refs) { + for(i = 0; i < b->closure_var_count; i++) { + JSVarRef *var_ref = var_refs[i]; + if (var_ref && var_ref->is_detached) { + mark_func(rt, &var_ref->header); + } + } + } + /* must mark the function bytecode because template objects may be + part of a cycle */ + JS_MarkValue(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b), mark_func); + } +} + +static void js_bound_function_finalizer(JSRuntime *rt, JSValueConst val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSBoundFunction *bf = p->u.bound_function; + int i; + + JS_FreeValueRT(rt, bf->func_obj); + JS_FreeValueRT(rt, bf->this_val); + for(i = 0; i < bf->argc; i++) { + JS_FreeValueRT(rt, bf->argv[i]); + } + js_free_rt(rt, bf); +} + +static void js_bound_function_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSBoundFunction *bf = p->u.bound_function; + int i; + + JS_MarkValue(rt, bf->func_obj, mark_func); + JS_MarkValue(rt, bf->this_val, mark_func); + for(i = 0; i < bf->argc; i++) + JS_MarkValue(rt, bf->argv[i], mark_func); +} + +static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValueConst val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSForInIterator *it = p->u.for_in_iterator; + JS_FreeValueRT(rt, it->obj); + js_free_rt(rt, it); +} + +static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSForInIterator *it = p->u.for_in_iterator; + JS_MarkValue(rt, it->obj, mark_func); +} + +static void free_object(JSRuntime *rt, JSObject *p) +{ + int i; + JSClassFinalizer *finalizer; + JSShape *sh; + JSShapeProperty *pr; + + p->free_mark = 1; /* used to tell the object is invalid when + freeing cycles */ + /* free all the fields */ + sh = p->shape; + pr = get_shape_prop(sh); + for(i = 0; i < sh->prop_count; i++) { + free_property(rt, &p->prop[i], pr->flags); + pr++; + } + js_free_rt(rt, p->prop); + /* as an optimization we destroy the shape immediately without + putting it in gc_zero_ref_count_list */ + js_free_shape(rt, sh); + + /* fail safe */ + p->shape = NULL; + p->prop = NULL; + + if (unlikely(p->first_weak_ref)) { + reset_weak_ref(rt, &p->first_weak_ref); + } + + finalizer = rt->class_array[p->class_id].finalizer; + if (finalizer) + (*finalizer)(rt, JS_MKPTR(JS_TAG_OBJECT, p)); + + /* fail safe */ + p->class_id = 0; + p->u.opaque = NULL; + p->u.func.var_refs = NULL; + p->u.func.home_object = NULL; + + remove_gc_object(&p->header); + if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && p->header.ref_count != 0) { + list_add_tail(&p->header.link, &rt->gc_zero_ref_count_list); + } else { + js_free_rt(rt, p); + } +} + +static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp) +{ + switch(gp->gc_obj_type) { + case JS_GC_OBJ_TYPE_JS_OBJECT: + free_object(rt, (JSObject *)gp); + break; + case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: + free_function_bytecode(rt, (JSFunctionBytecode *)gp); + break; + default: + abort(); + } +} + +static void free_zero_refcount(JSRuntime *rt) +{ + struct list_head *el; + JSGCObjectHeader *p; + + rt->gc_phase = JS_GC_PHASE_DECREF; + for(;;) { + el = rt->gc_zero_ref_count_list.next; + if (el == &rt->gc_zero_ref_count_list) + break; + p = list_entry(el, JSGCObjectHeader, link); + assert(p->ref_count == 0); + free_gc_object(rt, p); + } + rt->gc_phase = JS_GC_PHASE_NONE; +} + +/* called with the ref_count of 'v' reaches zero. */ +static void js_free_value_rt(JSRuntime *rt, JSValue v) +{ + uint32_t tag = JS_VALUE_GET_TAG(v); + +#ifdef ENABLE_DUMPS // JS_DUMP_FREE + if (check_dump_flag(rt, JS_DUMP_FREE)) { + /* Prevent invalid object access during GC */ + if ((rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) + || (tag != JS_TAG_OBJECT && tag != JS_TAG_FUNCTION_BYTECODE)) { + printf("Freeing "); + if (tag == JS_TAG_OBJECT) { + JS_DumpObject(rt, JS_VALUE_GET_OBJ(v)); + } else { + JS_DumpValue(rt, v); + printf("\n"); + } + } + } +#endif + + switch(tag) { + case JS_TAG_STRING: + js_free_string0(rt, JS_VALUE_GET_STRING(v)); + break; + case JS_TAG_STRING_ROPE: + { + JSStringRope *p = JS_VALUE_GET_STRING_ROPE(v); + JS_FreeValueRT(rt, p->left); + JS_FreeValueRT(rt, p->right); + js_free_rt(rt, p); + } + break; + case JS_TAG_OBJECT: + case JS_TAG_FUNCTION_BYTECODE: + { + JSGCObjectHeader *p = JS_VALUE_GET_PTR(v); + if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) { + list_del(&p->link); + list_add(&p->link, &rt->gc_zero_ref_count_list); + if (rt->gc_phase == JS_GC_PHASE_NONE) { + free_zero_refcount(rt); + } + } + } + break; + case JS_TAG_MODULE: + abort(); /* never freed here */ + break; + case JS_TAG_BIG_INT: + { + JSBigInt *p = JS_VALUE_GET_PTR(v); + js_free_rt(rt, p); + } + break; + case JS_TAG_SYMBOL: + { + JSAtomStruct *p = JS_VALUE_GET_PTR(v); + JS_FreeAtomStruct(rt, p); + } + break; + default: + printf("js_free_value_rt: unknown tag=%d\n", tag); + abort(); + } +} + +void JS_FreeValueRT(JSRuntime *rt, JSValue v) +{ + if (JS_VALUE_HAS_REF_COUNT(v)) { + JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); + if (--p->ref_count <= 0) { + js_free_value_rt(rt, v); + } + } +} + +void JS_FreeValue(JSContext *ctx, JSValue v) +{ + JS_FreeValueRT(ctx->rt, v); +} + +/* garbage collection */ + +static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h, + JSGCObjectTypeEnum type) +{ + h->mark = 0; + h->gc_obj_type = type; + list_add_tail(&h->link, &rt->gc_obj_list); +} + +static void remove_gc_object(JSGCObjectHeader *h) +{ + list_del(&h->link); +} + +void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) +{ + if (JS_VALUE_HAS_REF_COUNT(val)) { + switch(JS_VALUE_GET_TAG(val)) { + case JS_TAG_OBJECT: + case JS_TAG_FUNCTION_BYTECODE: + mark_func(rt, JS_VALUE_GET_PTR(val)); + break; + default: + break; + } + } +} + +static void mark_weak_map_value(JSRuntime *rt, JSWeakRefRecord *first_weak_ref, JS_MarkFunc *mark_func) { + JSWeakRefRecord *wr; + JSMapRecord *mr; + JSMapState *s; + + for (wr = first_weak_ref; wr != NULL; wr = wr->next_weak_ref) { + if (wr->kind == JS_WEAK_REF_KIND_MAP) { + mr = wr->u.map_record; + s = mr->map; + assert(s->is_weak); + assert(!mr->empty); /* no iterator on WeakMap/WeakSet */ + JS_MarkValue(rt, mr->value, mark_func); + } + } +} + +static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp, + JS_MarkFunc *mark_func) +{ + switch(gp->gc_obj_type) { + case JS_GC_OBJ_TYPE_JS_OBJECT: + { + JSObject *p = (JSObject *)gp; + JSShapeProperty *prs; + JSShape *sh; + int i; + sh = p->shape; + mark_func(rt, &sh->header); + /* mark all the fields */ + prs = get_shape_prop(sh); + for(i = 0; i < sh->prop_count; i++) { + JSProperty *pr = &p->prop[i]; + if (prs->atom != JS_ATOM_NULL) { + if (prs->flags & JS_PROP_TMASK) { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + if (pr->u.getset.getter) + mark_func(rt, &pr->u.getset.getter->header); + if (pr->u.getset.setter) + mark_func(rt, &pr->u.getset.setter->header); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + if (pr->u.var_ref->is_detached) { + /* Note: the tag does not matter + provided it is a GC object */ + mark_func(rt, &pr->u.var_ref->header); + } + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + js_autoinit_mark(rt, pr, mark_func); + } + } else { + JS_MarkValue(rt, pr->u.value, mark_func); + } + } + prs++; + } + + if (unlikely(p->first_weak_ref)) { + mark_weak_map_value(rt, p->first_weak_ref, mark_func); + } + + if (p->class_id != JS_CLASS_OBJECT) { + JSClassGCMark *gc_mark; + gc_mark = rt->class_array[p->class_id].gc_mark; + if (gc_mark) + gc_mark(rt, JS_MKPTR(JS_TAG_OBJECT, p), mark_func); + } + } + break; + case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: + /* the template objects can be part of a cycle */ + { + JSFunctionBytecode *b = (JSFunctionBytecode *)gp; + int i; + for(i = 0; i < b->cpool_count; i++) { + JS_MarkValue(rt, b->cpool[i], mark_func); + } + if (b->realm) + mark_func(rt, &b->realm->header); + } + break; + case JS_GC_OBJ_TYPE_VAR_REF: + { + JSVarRef *var_ref = (JSVarRef *)gp; + /* only detached variable referenced are taken into account */ + assert(var_ref->is_detached); + JS_MarkValue(rt, *var_ref->pvalue, mark_func); + } + break; + case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: + { + JSAsyncFunctionData *s = (JSAsyncFunctionData *)gp; + if (s->is_active) + async_func_mark(rt, &s->func_state, mark_func); + JS_MarkValue(rt, s->resolving_funcs[0], mark_func); + JS_MarkValue(rt, s->resolving_funcs[1], mark_func); + } + break; + case JS_GC_OBJ_TYPE_SHAPE: + { + JSShape *sh = (JSShape *)gp; + if (sh->proto != NULL) { + mark_func(rt, &sh->proto->header); + } + } + break; + case JS_GC_OBJ_TYPE_JS_CONTEXT: + { + JSContext *ctx = (JSContext *)gp; + JS_MarkContext(rt, ctx, mark_func); + } + break; + default: + abort(); + } +} + +static void gc_decref_child(JSRuntime *rt, JSGCObjectHeader *p) +{ + assert(p->ref_count > 0); + p->ref_count--; + if (p->ref_count == 0 && p->mark == 1) { + list_del(&p->link); + list_add_tail(&p->link, &rt->tmp_obj_list); + } +} + +static void gc_decref(JSRuntime *rt) +{ + struct list_head *el, *el1; + JSGCObjectHeader *p; + + init_list_head(&rt->tmp_obj_list); + + /* decrement the refcount of all the children of all the GC + objects and move the GC objects with zero refcount to + tmp_obj_list */ + list_for_each_safe(el, el1, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + assert(p->mark == 0); + mark_children(rt, p, gc_decref_child); + p->mark = 1; + if (p->ref_count == 0) { + list_del(&p->link); + list_add_tail(&p->link, &rt->tmp_obj_list); + } + } +} + +static void gc_scan_incref_child(JSRuntime *rt, JSGCObjectHeader *p) +{ + p->ref_count++; + if (p->ref_count == 1) { + /* ref_count was 0: remove from tmp_obj_list and add at the + end of gc_obj_list */ + list_del(&p->link); + list_add_tail(&p->link, &rt->gc_obj_list); + p->mark = 0; /* reset the mark for the next GC call */ + } +} + +static void gc_scan_incref_child2(JSRuntime *rt, JSGCObjectHeader *p) +{ + p->ref_count++; +} + +static void gc_scan(JSRuntime *rt) +{ + struct list_head *el; + JSGCObjectHeader *p; + + /* keep the objects with a refcount > 0 and their children. */ + list_for_each(el, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + assert(p->ref_count > 0); + p->mark = 0; /* reset the mark for the next GC call */ + mark_children(rt, p, gc_scan_incref_child); + } + + /* restore the refcount of the objects to be deleted. */ + list_for_each(el, &rt->tmp_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + mark_children(rt, p, gc_scan_incref_child2); + } +} + +static void gc_free_cycles(JSRuntime *rt) +{ + struct list_head *el, *el1; + JSGCObjectHeader *p; +#ifdef ENABLE_DUMPS // JS_DUMP_GC_FREE + bool header_done = false; +#endif + + rt->gc_phase = JS_GC_PHASE_REMOVE_CYCLES; + + for(;;) { + el = rt->tmp_obj_list.next; + if (el == &rt->tmp_obj_list) + break; + p = list_entry(el, JSGCObjectHeader, link); + /* Only need to free the GC object associated with JS + values. The rest will be automatically removed because they + must be referenced by them. */ + switch(p->gc_obj_type) { + case JS_GC_OBJ_TYPE_JS_OBJECT: + case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: +#ifdef ENABLE_DUMPS // JS_DUMP_GC_FREE + if (check_dump_flag(rt, JS_DUMP_GC_FREE)) { + if (!header_done) { + printf("Freeing cycles:\n"); + JS_DumpObjectHeader(rt); + header_done = true; + } + JS_DumpGCObject(rt, p); + } +#endif + free_gc_object(rt, p); + break; + default: + list_del(&p->link); + list_add_tail(&p->link, &rt->gc_zero_ref_count_list); + break; + } + } + rt->gc_phase = JS_GC_PHASE_NONE; + + list_for_each_safe(el, el1, &rt->gc_zero_ref_count_list) { + p = list_entry(el, JSGCObjectHeader, link); + assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT || + p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); + js_free_rt(rt, p); + } + + init_list_head(&rt->gc_zero_ref_count_list); +} + +void JS_RunGC(JSRuntime *rt) +{ + /* decrement the reference of the children of each object. mark = + 1 after this pass. */ + gc_decref(rt); + + /* keep the GC objects with a non zero refcount and their childs */ + gc_scan(rt); + + /* free the GC objects in a cycle */ + gc_free_cycles(rt); +} + +/* Return false if not an object or if the object has already been + freed (zombie objects are visible in finalizers when freeing + cycles). */ +bool JS_IsLiveObject(JSRuntime *rt, JSValueConst obj) +{ + JSObject *p; + if (!JS_IsObject(obj)) + return false; + p = JS_VALUE_GET_OBJ(obj); + return !p->free_mark; +} + +/* Compute memory used by various object types */ +/* XXX: poor man's approach to handling multiply referenced objects */ +typedef struct JSMemoryUsage_helper { + double memory_used_count; + double str_count; + double str_size; + int64_t js_func_count; + double js_func_size; + int64_t js_func_code_size; + int64_t js_func_pc2line_count; + int64_t js_func_pc2line_size; +} JSMemoryUsage_helper; + +static void compute_value_size(JSValue val, JSMemoryUsage_helper *hp); + +static void compute_jsstring_size(JSString *str, JSMemoryUsage_helper *hp) +{ + if (!str->atom_type) { /* atoms are handled separately */ + double s_ref_count = str->header.ref_count; + hp->str_count += 1 / s_ref_count; + hp->str_size += ((sizeof(*str) + (str->len << str->is_wide_char) + + 1 - str->is_wide_char) / s_ref_count); + } +} + +static void compute_bytecode_size(JSFunctionBytecode *b, JSMemoryUsage_helper *hp) +{ + int memory_used_count, js_func_size, i; + + memory_used_count = 0; + js_func_size = sizeof(*b); + if (b->vardefs) { + js_func_size += (b->arg_count + b->var_count) * sizeof(*b->vardefs); + } + if (b->cpool) { + js_func_size += b->cpool_count * sizeof(*b->cpool); + for (i = 0; i < b->cpool_count; i++) { + JSValue val = b->cpool[i]; + compute_value_size(val, hp); + } + } + if (b->closure_var) { + js_func_size += b->closure_var_count * sizeof(*b->closure_var); + } + if (b->byte_code_buf) { + hp->js_func_code_size += b->byte_code_len; + } + memory_used_count++; + js_func_size += b->source_len + 1; + if (b->pc2line_len) { + memory_used_count++; + hp->js_func_pc2line_count += 1; + hp->js_func_pc2line_size += b->pc2line_len; + } + hp->js_func_size += js_func_size; + hp->js_func_count += 1; + hp->memory_used_count += memory_used_count; +} + +static void compute_value_size(JSValue val, JSMemoryUsage_helper *hp) +{ + switch(JS_VALUE_GET_TAG(val)) { + case JS_TAG_STRING: + compute_jsstring_size(JS_VALUE_GET_STRING(val), hp); + break; + case JS_TAG_BIG_INT: + /* should track JSBigInt usage */ + break; + } +} + +void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s) +{ + struct list_head *el, *el1; + int i; + JSMemoryUsage_helper mem = { 0 }, *hp = &mem; + + memset(s, 0, sizeof(*s)); + s->malloc_count = rt->malloc_state.malloc_count; + s->malloc_size = rt->malloc_state.malloc_size; + s->malloc_limit = rt->malloc_state.malloc_limit; + + s->memory_used_count = 2; /* rt + rt->class_array */ + s->memory_used_size = sizeof(JSRuntime) + sizeof(JSClass) * rt->class_count; + + list_for_each(el, &rt->context_list) { + JSContext *ctx = list_entry(el, JSContext, link); + JSShape *sh = ctx->array_shape; + s->memory_used_count += 2; /* ctx + ctx->class_proto */ + s->memory_used_size += sizeof(JSContext) + + sizeof(JSValue) * rt->class_count; + s->binary_object_count += ctx->binary_object_count; + s->binary_object_size += ctx->binary_object_size; + + /* the hashed shapes are counted separately */ + if (sh && !sh->is_hashed) { + int hash_size = sh->prop_hash_mask + 1; + s->shape_count++; + s->shape_size += get_shape_size(hash_size, sh->prop_size); + } + list_for_each(el1, &ctx->loaded_modules) { + JSModuleDef *m = list_entry(el1, JSModuleDef, link); + s->memory_used_count += 1; + s->memory_used_size += sizeof(*m); + if (m->req_module_entries) { + s->memory_used_count += 1; + s->memory_used_size += m->req_module_entries_count * sizeof(*m->req_module_entries); + } + if (m->export_entries) { + s->memory_used_count += 1; + s->memory_used_size += m->export_entries_count * sizeof(*m->export_entries); + for (i = 0; i < m->export_entries_count; i++) { + JSExportEntry *me = &m->export_entries[i]; + if (me->export_type == JS_EXPORT_TYPE_LOCAL && me->u.local.var_ref) { + /* potential multiple count */ + s->memory_used_count += 1; + compute_value_size(me->u.local.var_ref->value, hp); + } + } + } + if (m->star_export_entries) { + s->memory_used_count += 1; + s->memory_used_size += m->star_export_entries_count * sizeof(*m->star_export_entries); + } + if (m->import_entries) { + s->memory_used_count += 1; + s->memory_used_size += m->import_entries_count * sizeof(*m->import_entries); + } + compute_value_size(m->module_ns, hp); + compute_value_size(m->func_obj, hp); + } + } + + list_for_each(el, &rt->gc_obj_list) { + JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link); + JSObject *p; + JSShape *sh; + JSShapeProperty *prs; + + /* XXX: could count the other GC object types too */ + if (gp->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE) { + compute_bytecode_size((JSFunctionBytecode *)gp, hp); + continue; + } else if (gp->gc_obj_type != JS_GC_OBJ_TYPE_JS_OBJECT) { + continue; + } + p = (JSObject *)gp; + sh = p->shape; + s->obj_count++; + if (p->prop) { + s->memory_used_count++; + s->prop_size += sh->prop_size * sizeof(*p->prop); + s->prop_count += sh->prop_count; + prs = get_shape_prop(sh); + for(i = 0; i < sh->prop_count; i++) { + JSProperty *pr = &p->prop[i]; + if (prs->atom != JS_ATOM_NULL && !(prs->flags & JS_PROP_TMASK)) { + compute_value_size(pr->u.value, hp); + } + prs++; + } + } + /* the hashed shapes are counted separately */ + if (!sh->is_hashed) { + int hash_size = sh->prop_hash_mask + 1; + s->shape_count++; + s->shape_size += get_shape_size(hash_size, sh->prop_size); + } + + switch(p->class_id) { + case JS_CLASS_ARRAY: /* u.array | length */ + case JS_CLASS_ARGUMENTS: /* u.array | length */ + s->array_count++; + if (p->fast_array) { + s->fast_array_count++; + if (p->u.array.u.values) { + s->memory_used_count++; + s->memory_used_size += p->u.array.count * + sizeof(*p->u.array.u.values); + s->fast_array_elements += p->u.array.count; + for (i = 0; i < p->u.array.count; i++) { + compute_value_size(p->u.array.u.values[i], hp); + } + } + } + break; + case JS_CLASS_NUMBER: /* u.object_data */ + case JS_CLASS_STRING: /* u.object_data */ + case JS_CLASS_BOOLEAN: /* u.object_data */ + case JS_CLASS_SYMBOL: /* u.object_data */ + case JS_CLASS_DATE: /* u.object_data */ + case JS_CLASS_BIG_INT: /* u.object_data */ + compute_value_size(p->u.object_data, hp); + break; + case JS_CLASS_C_FUNCTION: /* u.cfunc */ + s->c_func_count++; + break; + case JS_CLASS_BYTECODE_FUNCTION: /* u.func */ + { + JSFunctionBytecode *b = p->u.func.function_bytecode; + JSVarRef **var_refs = p->u.func.var_refs; + /* home_object: object will be accounted for in list scan */ + if (var_refs) { + s->memory_used_count++; + s->js_func_size += b->closure_var_count * sizeof(*var_refs); + for (i = 0; i < b->closure_var_count; i++) { + if (var_refs[i]) { + double ref_count = var_refs[i]->header.ref_count; + s->memory_used_count += 1 / ref_count; + s->js_func_size += sizeof(*var_refs[i]) / ref_count; + /* handle non object closed values */ + if (var_refs[i]->pvalue == &var_refs[i]->value) { + /* potential multiple count */ + compute_value_size(var_refs[i]->value, hp); + } + } + } + } + } + break; + case JS_CLASS_BOUND_FUNCTION: /* u.bound_function */ + { + JSBoundFunction *bf = p->u.bound_function; + /* func_obj and this_val are objects */ + for (i = 0; i < bf->argc; i++) { + compute_value_size(bf->argv[i], hp); + } + s->memory_used_count += 1; + s->memory_used_size += sizeof(*bf) + bf->argc * sizeof(*bf->argv); + } + break; + case JS_CLASS_C_FUNCTION_DATA: /* u.c_function_data_record */ + { + JSCFunctionDataRecord *fd = p->u.c_function_data_record; + if (fd) { + for (i = 0; i < fd->data_len; i++) { + compute_value_size(fd->data[i], hp); + } + s->memory_used_count += 1; + s->memory_used_size += sizeof(*fd) + fd->data_len * sizeof(*fd->data); + } + } + break; + case JS_CLASS_C_CLOSURE: /* u.c_closure_record */ + { + JSCClosureRecord *c = p->u.c_closure_record; + if (c) { + s->memory_used_count += 1; + s->memory_used_size += sizeof(*c); + } + } + break; + case JS_CLASS_REGEXP: /* u.regexp */ + compute_jsstring_size(p->u.regexp.pattern, hp); + compute_jsstring_size(p->u.regexp.bytecode, hp); + break; + + case JS_CLASS_FOR_IN_ITERATOR: /* u.for_in_iterator */ + { + JSForInIterator *it = p->u.for_in_iterator; + if (it) { + compute_value_size(it->obj, hp); + s->memory_used_count += 1; + s->memory_used_size += sizeof(*it); + } + } + break; + case JS_CLASS_ARRAY_BUFFER: /* u.array_buffer */ + case JS_CLASS_SHARED_ARRAY_BUFFER: /* u.array_buffer */ + { + JSArrayBuffer *abuf = p->u.array_buffer; + if (abuf) { + s->memory_used_count += 1; + s->memory_used_size += sizeof(*abuf); + if (abuf->data) { + s->memory_used_count += 1; + s->memory_used_size += abuf->byte_length; + } + } + } + break; + case JS_CLASS_GENERATOR: /* u.generator_data */ + case JS_CLASS_UINT8C_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_INT8_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_UINT8_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_INT16_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_UINT16_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_INT32_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_UINT32_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_BIG_INT64_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_BIG_UINT64_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_FLOAT16_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_FLOAT32_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_FLOAT64_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_DATAVIEW: /* u.typed_array */ + case JS_CLASS_MAP: /* u.map_state */ + case JS_CLASS_SET: /* u.map_state */ + case JS_CLASS_WEAKMAP: /* u.map_state */ + case JS_CLASS_WEAKSET: /* u.map_state */ + case JS_CLASS_MAP_ITERATOR: /* u.map_iterator_data */ + case JS_CLASS_SET_ITERATOR: /* u.map_iterator_data */ + case JS_CLASS_ARRAY_ITERATOR: /* u.array_iterator_data */ + case JS_CLASS_STRING_ITERATOR: /* u.array_iterator_data */ + case JS_CLASS_PROXY: /* u.proxy_data */ + case JS_CLASS_PROMISE: /* u.promise_data */ + case JS_CLASS_PROMISE_RESOLVE_FUNCTION: /* u.promise_function_data */ + case JS_CLASS_PROMISE_REJECT_FUNCTION: /* u.promise_function_data */ + case JS_CLASS_ASYNC_FUNCTION_RESOLVE: /* u.async_function_data */ + case JS_CLASS_ASYNC_FUNCTION_REJECT: /* u.async_function_data */ + case JS_CLASS_ASYNC_FROM_SYNC_ITERATOR: /* u.async_from_sync_iterator_data */ + case JS_CLASS_ASYNC_GENERATOR: /* u.async_generator_data */ + /* TODO */ + default: + /* XXX: class definition should have an opaque block size */ + if (p->u.opaque) { + s->memory_used_count += 1; + } + break; + } + } + s->obj_size += s->obj_count * sizeof(JSObject); + + /* hashed shapes */ + s->memory_used_count++; /* rt->shape_hash */ + s->memory_used_size += sizeof(rt->shape_hash[0]) * rt->shape_hash_size; + for(i = 0; i < rt->shape_hash_size; i++) { + JSShape *sh; + for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) { + int hash_size = sh->prop_hash_mask + 1; + s->shape_count++; + s->shape_size += get_shape_size(hash_size, sh->prop_size); + } + } + + /* atoms */ + s->memory_used_count += 2; /* rt->atom_array, rt->atom_hash */ + s->atom_count = rt->atom_count; + s->atom_size = sizeof(rt->atom_array[0]) * rt->atom_size + + sizeof(rt->atom_hash[0]) * rt->atom_hash_size; + for(i = 0; i < rt->atom_size; i++) { + JSAtomStruct *p = rt->atom_array[i]; + if (!atom_is_free(p)) { + s->atom_size += (sizeof(*p) + (p->len << p->is_wide_char) + + 1 - p->is_wide_char); + } + } + s->str_count = round(mem.str_count); + s->str_size = round(mem.str_size); + s->js_func_count = mem.js_func_count; + s->js_func_size = round(mem.js_func_size); + s->js_func_code_size = mem.js_func_code_size; + s->js_func_pc2line_count = mem.js_func_pc2line_count; + s->js_func_pc2line_size = mem.js_func_pc2line_size; + s->memory_used_count += round(mem.memory_used_count) + + s->atom_count + s->str_count + + s->obj_count + s->shape_count + + s->js_func_count + s->js_func_pc2line_count; + s->memory_used_size += s->atom_size + s->str_size + + s->obj_size + s->prop_size + s->shape_size + + s->js_func_size + s->js_func_code_size + s->js_func_pc2line_size; +} + +void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt) +{ + fprintf(fp, "QuickJS-ng memory usage -- %s version, %d-bit, %s Endian, malloc limit: %"PRId64"\n\n", + JS_GetVersion(), (int)sizeof(void *) * 8, is_be() ? "Big" : "Little", s->malloc_limit); + if (rt) { + static const struct { + const char *name; + size_t size; + } object_types[] = { + { "JSRuntime", sizeof(JSRuntime) }, + { "JSContext", sizeof(JSContext) }, + { "JSObject", sizeof(JSObject) }, + { "JSString", sizeof(JSString) }, + { "JSFunctionBytecode", sizeof(JSFunctionBytecode) }, + }; + int i, usage_size_ok = 0; + for(i = 0; i < countof(object_types); i++) { + unsigned int size = object_types[i].size; + void *p = js_malloc_rt(rt, size); + if (p) { + unsigned int size1 = js_malloc_usable_size_rt(rt, p); + if (size1 >= size) { + usage_size_ok = 1; + fprintf(fp, " %3u + %-2u %s\n", + size, size1 - size, object_types[i].name); + } + js_free_rt(rt, p); + } + } + if (!usage_size_ok) { + fprintf(fp, " malloc_usable_size unavailable\n"); + } + { + int obj_classes[JS_CLASS_INIT_COUNT + 1] = { 0 }; + int class_id; + struct list_head *el; + list_for_each(el, &rt->gc_obj_list) { + JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link); + JSObject *p; + if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { + p = (JSObject *)gp; + obj_classes[min_uint32(p->class_id, JS_CLASS_INIT_COUNT)]++; + } + } + fprintf(fp, "\n" "JSObject classes\n"); + if (obj_classes[0]) + fprintf(fp, " %5d %2.0d %s\n", obj_classes[0], 0, "none"); + for (class_id = 1; class_id < JS_CLASS_INIT_COUNT; class_id++) { + if (obj_classes[class_id] && class_id < rt->class_count) { + char buf[ATOM_GET_STR_BUF_SIZE]; + fprintf(fp, " %5d %2.0d %s\n", obj_classes[class_id], class_id, + JS_AtomGetStrRT(rt, buf, sizeof(buf), rt->class_array[class_id].class_name)); + } + } + if (obj_classes[JS_CLASS_INIT_COUNT]) + fprintf(fp, " %5d %2.0d %s\n", obj_classes[JS_CLASS_INIT_COUNT], 0, "other"); + } + fprintf(fp, "\n"); + } + fprintf(fp, "%-20s %8s %8s\n", "NAME", "COUNT", "SIZE"); + + if (s->malloc_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per block)\n", + "memory allocated", s->malloc_count, s->malloc_size, + (double)s->malloc_size / s->malloc_count); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%d overhead, %0.1f average slack)\n", + "memory used", s->memory_used_count, s->memory_used_size, + MALLOC_OVERHEAD, ((double)(s->malloc_size - s->memory_used_size) / + s->memory_used_count)); + } + if (s->atom_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per atom)\n", + "atoms", s->atom_count, s->atom_size, + (double)s->atom_size / s->atom_count); + } + if (s->str_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per string)\n", + "strings", s->str_count, s->str_size, + (double)s->str_size / s->str_count); + } + if (s->obj_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n", + "objects", s->obj_count, s->obj_size, + (double)s->obj_size / s->obj_count); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n", + " properties", s->prop_count, s->prop_size, + (double)s->prop_count / s->obj_count); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per shape)\n", + " shapes", s->shape_count, s->shape_size, + (double)s->shape_size / s->shape_count); + } + if (s->js_func_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n", + "bytecode functions", s->js_func_count, s->js_func_size); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n", + " bytecode", s->js_func_count, s->js_func_code_size, + (double)s->js_func_code_size / s->js_func_count); + if (s->js_func_pc2line_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n", + " pc2line", s->js_func_pc2line_count, + s->js_func_pc2line_size, + (double)s->js_func_pc2line_size / s->js_func_pc2line_count); + } + } + if (s->c_func_count) { + fprintf(fp, "%-20s %8"PRId64"\n", "C functions", s->c_func_count); + } + if (s->array_count) { + fprintf(fp, "%-20s %8"PRId64"\n", "arrays", s->array_count); + if (s->fast_array_count) { + fprintf(fp, "%-20s %8"PRId64"\n", " fast arrays", s->fast_array_count); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per fast array)\n", + " elements", s->fast_array_elements, + s->fast_array_elements * (int)sizeof(JSValue), + (double)s->fast_array_elements / s->fast_array_count); + } + } + if (s->binary_object_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n", + "binary objects", s->binary_object_count, s->binary_object_size); + } +} + +JSValue JS_GetGlobalObject(JSContext *ctx) +{ + return js_dup(ctx->global_obj); +} + +/* WARNING: obj is freed */ +JSValue JS_Throw(JSContext *ctx, JSValue obj) +{ + JSRuntime *rt = ctx->rt; + JS_FreeValue(ctx, rt->current_exception); + rt->current_exception = obj; + return JS_EXCEPTION; +} + +/* return the pending exception (cannot be called twice). */ +JSValue JS_GetException(JSContext *ctx) +{ + JSValue val; + JSRuntime *rt = ctx->rt; + val = rt->current_exception; + rt->current_exception = JS_UNINITIALIZED; + return val; +} + +bool JS_HasException(JSContext *ctx) +{ + return !JS_IsUninitialized(ctx->rt->current_exception); +} + +static void dbuf_put_leb128(DynBuf *s, uint32_t v) +{ + uint32_t a; + for(;;) { + a = v & 0x7f; + v >>= 7; + if (v != 0) { + dbuf_putc(s, a | 0x80); + } else { + dbuf_putc(s, a); + break; + } + } +} + +static void dbuf_put_sleb128(DynBuf *s, int32_t v1) +{ + uint32_t v = v1; + dbuf_put_leb128(s, (2 * v) ^ -(v >> 31)); +} + +static int get_leb128(uint32_t *pval, const uint8_t *buf, + const uint8_t *buf_end) +{ + const uint8_t *ptr = buf; + uint32_t v, a, i; + v = 0; + for(i = 0; i < 5; i++) { + if (unlikely(ptr >= buf_end)) + break; + a = *ptr++; + v |= (a & 0x7f) << (i * 7); + if (!(a & 0x80)) { + *pval = v; + return ptr - buf; + } + } + *pval = 0; + return -1; +} + +static int get_sleb128(int32_t *pval, const uint8_t *buf, + const uint8_t *buf_end) +{ + int ret; + uint32_t val; + ret = get_leb128(&val, buf, buf_end); + if (ret < 0) { + *pval = 0; + return -1; + } + *pval = (val >> 1) ^ -(val & 1); + return ret; +} + +static int find_line_num(JSContext *ctx, JSFunctionBytecode *b, + uint32_t pc_value, int *col) +{ + const uint8_t *p_end, *p; + int new_line_num, new_col_num, line_num, col_num, pc, v, ret; + unsigned int op; + + *col = 1; + p = b->pc2line_buf; + if (!p) + goto fail; + p_end = p + b->pc2line_len; + pc = 0; + line_num = b->line_num; + col_num = b->col_num; + while (p < p_end) { + op = *p++; + if (op == 0) { + uint32_t val; + ret = get_leb128(&val, p, p_end); + if (ret < 0) + goto fail; + pc += val; + p += ret; + ret = get_sleb128(&v, p, p_end); + if (ret < 0) + goto fail; + p += ret; + new_line_num = line_num + v; + } else { + op -= PC2LINE_OP_FIRST; + pc += (op / PC2LINE_RANGE); + new_line_num = line_num + (op % PC2LINE_RANGE) + PC2LINE_BASE; + } + ret = get_sleb128(&v, p, p_end); + if (ret < 0) + goto fail; + p += ret; + new_col_num = col_num + v; + if (pc_value < pc) + break; + line_num = new_line_num; + col_num = new_col_num; + } + *col = col_num; + return line_num; +fail: + /* should never happen */ + return b->line_num; +} + +/* in order to avoid executing arbitrary code during the stack trace + generation, we only look at simple 'name' properties containing a + string. */ +static const char *get_func_name(JSContext *ctx, JSValueConst func) +{ + JSProperty *pr; + JSShapeProperty *prs; + JSValue val; + + if (JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT) + return NULL; + prs = find_own_property(&pr, JS_VALUE_GET_OBJ(func), JS_ATOM_name); + if (!prs) + return NULL; + if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL) + return NULL; + val = pr->u.value; + if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING) + return NULL; + return JS_ToCString(ctx, val); +} + +/* Note: it is important that no exception is returned by this function */ +static bool can_add_backtrace(JSValueConst obj) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return false; + p = JS_VALUE_GET_OBJ(obj); + if (p->class_id != JS_CLASS_ERROR && p->class_id != JS_CLASS_DOM_EXCEPTION) + return false; + if (find_own_property1(p, JS_ATOM_stack)) + return false; + return true; +} + +#define JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL (1 << 0) +/* only taken into account if filename is provided */ +#define JS_BACKTRACE_FLAG_SINGLE_LEVEL (1 << 1) +#define JS_BACKTRACE_FLAG_FILTER_FUNC (1 << 2) + +/* if filename != NULL, an additional level is added with the filename + and line number information (used for parse error). */ +static void build_backtrace(JSContext *ctx, JSValueConst error_val, + JSValueConst filter_func, const char *filename, + int line_num, int col_num, int backtrace_flags) +{ + JSStackFrame *sf, *sf_start; + JSValue stack, prepare, saved_exception; + DynBuf dbuf; + const char *func_name_str; + const char *str1; + JSObject *p; + JSFunctionBytecode *b; + bool backtrace_barrier, has_prepare, has_filter_func; + JSRuntime *rt; + JSCallSiteData csd[64]; + uint32_t i; + double d; + int stack_trace_limit; + + rt = ctx->rt; + if (rt->in_build_stack_trace) + return; + rt->in_build_stack_trace = true; + + // Save exception because conversion to double may fail. + saved_exception = JS_GetException(ctx); + + // Extract stack trace limit. + // Ignore error since it sets d to NAN anyway. + // coverity[check_return] + JS_ToFloat64(ctx, &d, ctx->error_stack_trace_limit); + if (isnan(d) || d < 0.0) + stack_trace_limit = 0; + else if (d > INT32_MAX) + stack_trace_limit = INT32_MAX; + else + stack_trace_limit = fabs(d); + + // Restore current exception. + JS_Throw(ctx, saved_exception); + saved_exception = JS_UNINITIALIZED; + + stack_trace_limit = min_int(stack_trace_limit, countof(csd)); + stack_trace_limit = max_int(stack_trace_limit, 0); + has_prepare = false; + has_filter_func = backtrace_flags & JS_BACKTRACE_FLAG_FILTER_FUNC; + i = 0; + + if (!JS_IsNull(ctx->error_ctor)) { + prepare = js_dup(ctx->error_prepare_stack); + has_prepare = JS_IsFunction(ctx, prepare); + } + + if (has_prepare) { + saved_exception = JS_GetException(ctx); + if (stack_trace_limit == 0) + goto done; + if (filename) + js_new_callsite_data2(ctx, &csd[i++], filename, line_num, col_num); + } else { + js_dbuf_init(ctx, &dbuf); + if (stack_trace_limit == 0) + goto done; + if (filename) { + i++; + dbuf_printf(&dbuf, " at %s", filename); + if (line_num != -1) + dbuf_printf(&dbuf, ":%d:%d", line_num, col_num); + dbuf_putc(&dbuf, '\n'); + } + } + + if (filename && (backtrace_flags & JS_BACKTRACE_FLAG_SINGLE_LEVEL)) + goto done; + + sf_start = rt->current_stack_frame; + + /* Find the frame we want to start from. Note that when a filter is used the filter + function will be the first, but we also specify we want to skip the first one. */ + if (has_filter_func) { + for (sf = sf_start; sf != NULL && i < stack_trace_limit; sf = sf->prev_frame) { + if (js_same_value(ctx, sf->cur_func, filter_func)) { + sf_start = sf; + break; + } + } + } + + for (sf = sf_start; sf != NULL && i < stack_trace_limit; sf = sf->prev_frame) { + if (backtrace_flags & JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL) { + backtrace_flags &= ~JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL; + continue; + } + + p = JS_VALUE_GET_OBJ(sf->cur_func); + b = NULL; + backtrace_barrier = false; + + if (js_class_has_bytecode(p->class_id)) { + b = p->u.func.function_bytecode; + backtrace_barrier = b->backtrace_barrier; + } + + if (has_prepare) { + js_new_callsite_data(ctx, &csd[i], sf); + } else { + /* func_name_str is UTF-8 encoded if needed */ + func_name_str = get_func_name(ctx, sf->cur_func); + if (!func_name_str || func_name_str[0] == '\0') + str1 = ""; + else + str1 = func_name_str; + dbuf_printf(&dbuf, " at %s", str1); + JS_FreeCString(ctx, func_name_str); + + if (b && sf->cur_pc) { + const char *atom_str; + int line_num1, col_num1; + uint32_t pc; + + pc = sf->cur_pc - b->byte_code_buf - 1; + line_num1 = find_line_num(ctx, b, pc, &col_num1); + atom_str = b->filename ? JS_AtomToCString(ctx, b->filename) : NULL; + dbuf_printf(&dbuf, " (%s", atom_str ? atom_str : ""); + JS_FreeCString(ctx, atom_str); + if (line_num1 != -1) + dbuf_printf(&dbuf, ":%d:%d", line_num1, col_num1); + dbuf_putc(&dbuf, ')'); + } else if (b) { + // FIXME(bnoordhuis) Missing `sf->cur_pc = pc` in bytecode + // handler in JS_CallInternal. Almost never user observable + // except with intercepting JS proxies that throw exceptions. + dbuf_printf(&dbuf, " (missing)"); + } else { + dbuf_printf(&dbuf, " (native)"); + } + dbuf_putc(&dbuf, '\n'); + } + i++; + + /* stop backtrace if JS_EVAL_FLAG_BACKTRACE_BARRIER was used */ + if (backtrace_barrier) + break; + } + done: + if (has_prepare) { + int j = 0, k; + stack = JS_NewArray(ctx); + if (JS_IsException(stack)) { + stack = JS_NULL; + } else { + for (; j < i; j++) { + JSValue v = js_new_callsite(ctx, &csd[j]); + if (JS_IsException(v)) + break; + if (JS_DefinePropertyValueUint32(ctx, stack, j, v, JS_PROP_C_W_E) < 0) { + JS_FreeValue(ctx, v); + break; + } + } + } + // Clear the csd's we didn't use in case of error. + for (k = j; k < i; k++) { + JS_FreeValue(ctx, csd[k].filename); + JS_FreeValue(ctx, csd[k].func); + JS_FreeValue(ctx, csd[k].func_name); + } + JSValueConst args[] = { + error_val, + stack, + }; + JSValue stack2 = JS_Call(ctx, prepare, ctx->error_ctor, countof(args), args); + JS_FreeValue(ctx, stack); + if (JS_IsException(stack2)) + stack = JS_NULL; + else + stack = stack2; + JS_FreeValue(ctx, prepare); + JS_Throw(ctx, saved_exception); + } else { + if (dbuf_error(&dbuf)) + stack = JS_NULL; + else + stack = JS_NewStringLen(ctx, (char *)dbuf.buf, dbuf.size); + dbuf_free(&dbuf); + } + + if (JS_IsUndefined(ctx->error_back_trace)) + ctx->error_back_trace = js_dup(stack); + if (has_filter_func || can_add_backtrace(error_val)) { + JS_DefinePropertyValue(ctx, error_val, JS_ATOM_stack, stack, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + } else { + JS_FreeValue(ctx, stack); + } + + rt->in_build_stack_trace = false; +} + +JSValue JS_NewError(JSContext *ctx) +{ + JSValue obj = JS_NewObjectClass(ctx, JS_CLASS_ERROR); + if (JS_IsException(obj)) + return JS_EXCEPTION; + build_backtrace(ctx, obj, JS_UNDEFINED, NULL, 0, 0, 0); + return obj; +} + +static JSValue JS_MakeError2(JSContext *ctx, JSErrorEnum error_num, + bool add_backtrace, const char *message) +{ + JSValue obj, msg; + + if (error_num == JS_PLAIN_ERROR) { + obj = JS_NewObjectClass(ctx, JS_CLASS_ERROR); + } else { + obj = JS_NewObjectProtoClass(ctx, ctx->native_error_proto[error_num], + JS_CLASS_ERROR); + } + if (JS_IsException(obj)) + return JS_EXCEPTION; + msg = JS_NewString(ctx, message); + if (JS_IsException(msg)) + msg = JS_NewString(ctx, "Invalid error message"); + if (!JS_IsException(msg)) { + JS_DefinePropertyValue(ctx, obj, JS_ATOM_message, msg, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + } + if (add_backtrace) + build_backtrace(ctx, obj, JS_UNDEFINED, NULL, 0, 0, 0); + return obj; +} + +static JSValue JS_PRINTF_FORMAT_ATTR(4, 0) +JS_MakeError(JSContext *ctx, JSErrorEnum error_num, bool add_backtrace, + JS_PRINTF_FORMAT const char *fmt, va_list ap) +{ + char buf[256]; + + vsnprintf(buf, sizeof(buf), fmt, ap); + return JS_MakeError2(ctx, error_num, add_backtrace, buf); +} + +/* fmt and arguments may be pure ASCII or UTF-8 encoded contents */ +static JSValue JS_PRINTF_FORMAT_ATTR(4, 0) +JS_ThrowError2(JSContext *ctx, JSErrorEnum error_num, bool add_backtrace, + JS_PRINTF_FORMAT const char *fmt, va_list ap) +{ + JSValue obj; + + obj = JS_MakeError(ctx, error_num, add_backtrace, fmt, ap); + if (unlikely(JS_IsException(obj))) { + /* out of memory: throw JS_NULL to avoid recursing */ + obj = JS_NULL; + } + return JS_Throw(ctx, obj); +} + +static JSValue JS_PRINTF_FORMAT_ATTR(3, 0) +JS_ThrowError(JSContext *ctx, JSErrorEnum error_num, + JS_PRINTF_FORMAT const char *fmt, va_list ap) +{ + JSRuntime *rt = ctx->rt; + JSStackFrame *sf; + bool add_backtrace; + + /* the backtrace is added later if called from a bytecode function */ + sf = rt->current_stack_frame; + add_backtrace = !rt->in_out_of_memory && + (!sf || (JS_GetFunctionBytecode(sf->cur_func) == NULL)); + return JS_ThrowError2(ctx, error_num, add_backtrace, fmt, ap); +} + +#define JS_ERROR_MAP(X) \ + X(Internal, INTERNAL) \ + X(Plain, PLAIN) \ + X(Range, RANGE) \ + X(Reference, REFERENCE) \ + X(Syntax, SYNTAX) \ + X(Type, TYPE) \ + +#define X(lc, uc) \ + JSValue JS_PRINTF_FORMAT_ATTR(2, 3) \ + JS_New##lc##Error(JSContext *ctx, \ + JS_PRINTF_FORMAT const char *fmt, ...) \ + { \ + JSValue val; \ + va_list ap; \ + \ + va_start(ap, fmt); \ + val = JS_MakeError(ctx, JS_##uc##_ERROR, \ + /*add_backtrace*/true, fmt, ap); \ + va_end(ap); \ + return val; \ + } \ + JSValue JS_PRINTF_FORMAT_ATTR(2, 3) \ + JS_Throw##lc##Error(JSContext *ctx, \ + JS_PRINTF_FORMAT const char *fmt, ...) \ + { \ + JSValue val; \ + va_list ap; \ + \ + va_start(ap, fmt); \ + val = JS_ThrowError(ctx, JS_##uc##_ERROR, fmt, ap); \ + va_end(ap); \ + return val; \ + } \ + +JS_ERROR_MAP(X) + +#undef X +#undef JS_ERROR_MAP + +static int JS_PRINTF_FORMAT_ATTR(3, 4) JS_ThrowTypeErrorOrFalse(JSContext *ctx, int flags, JS_PRINTF_FORMAT const char *fmt, ...) +{ + va_list ap; + + if ((flags & JS_PROP_THROW) || + ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { + va_start(ap, fmt); + JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap); + va_end(ap); + return -1; + } else { + return false; + } +} + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif // __GNUC__ +static JSValue JS_ThrowTypeErrorAtom(JSContext *ctx, const char *fmt, JSAtom atom) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + JS_AtomGetStr(ctx, buf, sizeof(buf), atom); + return JS_ThrowTypeError(ctx, fmt, buf); +} + +static JSValue JS_ThrowSyntaxErrorAtom(JSContext *ctx, const char *fmt, JSAtom atom) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + JS_AtomGetStr(ctx, buf, sizeof(buf), atom); + return JS_ThrowSyntaxError(ctx, fmt, buf); +} +#ifdef __GNUC__ +#pragma GCC diagnostic pop // ignored "-Wformat-nonliteral" +#endif // __GNUC__ + +static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom) +{ + if ((flags & JS_PROP_THROW) || + ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { + JS_ThrowTypeErrorAtom(ctx, "'%s' is read-only", atom); + return -1; + } else { + return false; + } +} + +JSValue JS_ThrowOutOfMemory(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + if (!rt->in_out_of_memory) { + rt->in_out_of_memory = true; + JS_ThrowInternalError(ctx, "out of memory"); + rt->in_out_of_memory = false; + } + return JS_EXCEPTION; +} + +static JSValue JS_ThrowStackOverflow(JSContext *ctx) +{ + return JS_ThrowRangeError(ctx, "Maximum call stack size exceeded"); +} + +static JSValue JS_ThrowTypeErrorNotAConstructor(JSContext *ctx, + JSValueConst func_obj) +{ + JSObject *p; + JSAtom name; + + if (JS_TAG_OBJECT != JS_VALUE_GET_TAG(func_obj)) + goto fini; + p = JS_VALUE_GET_OBJ(func_obj); + if (!js_class_has_bytecode(p->class_id)) + goto fini; + name = p->u.func.function_bytecode->func_name; + if (name == JS_ATOM_NULL) + goto fini; + return JS_ThrowTypeErrorAtom(ctx, "%s is not a constructor", name); +fini: + return JS_ThrowTypeError(ctx, "not a constructor"); +} + +static JSValue JS_ThrowTypeErrorNotAFunction(JSContext *ctx) +{ + return JS_ThrowTypeError(ctx, "not a function"); +} + +static JSValue JS_ThrowTypeErrorNotAnObject(JSContext *ctx) +{ + return JS_ThrowTypeError(ctx, "not an object"); +} + +static JSValue JS_ThrowTypeErrorNotASymbol(JSContext *ctx) +{ + return JS_ThrowTypeError(ctx, "not a symbol"); +} + +static JSValue JS_ThrowReferenceErrorNotDefined(JSContext *ctx, JSAtom name) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + return JS_ThrowReferenceError(ctx, "%s is not defined", + JS_AtomGetStr(ctx, buf, sizeof(buf), name)); +} + +static JSValue JS_ThrowReferenceErrorUninitialized(JSContext *ctx, JSAtom name) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + return JS_ThrowReferenceError(ctx, "%s is not initialized", + name == JS_ATOM_NULL ? "lexical variable" : + JS_AtomGetStr(ctx, buf, sizeof(buf), name)); +} + +static JSValue JS_ThrowReferenceErrorUninitialized2(JSContext *ctx, + JSFunctionBytecode *b, + int idx, bool is_ref) +{ + JSAtom atom = JS_ATOM_NULL; + if (is_ref) { + atom = b->closure_var[idx].var_name; + } else { + /* not present if the function is stripped and contains no eval() */ + if (b->vardefs) + atom = b->vardefs[b->arg_count + idx].var_name; + } + return JS_ThrowReferenceErrorUninitialized(ctx, atom); +} + +static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id) +{ + JSRuntime *rt = ctx->rt; + JSAtom name; + name = rt->class_array[class_id].class_name; + return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name); +} + +static void JS_ThrowInterrupted(JSContext *ctx) +{ + JS_ThrowInternalError(ctx, "interrupted"); + JS_SetUncatchableError(ctx, ctx->rt->current_exception); +} + +static no_inline __exception int __js_poll_interrupts(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT; + if (rt->interrupt_handler) { + if (rt->interrupt_handler(rt, rt->interrupt_opaque)) { + JS_ThrowInterrupted(ctx); + return -1; + } + } + return 0; +} + +static inline __exception int js_poll_interrupts(JSContext *ctx) +{ + if (unlikely(--ctx->interrupt_counter <= 0)) { + return __js_poll_interrupts(ctx); + } else { + return 0; + } +} + +/* return -1 (exception) or true/false */ +static int JS_SetPrototypeInternal(JSContext *ctx, JSValueConst obj, + JSValueConst proto_val, bool throw_flag) +{ + JSObject *proto, *p, *p1; + JSShape *sh; + + if (throw_flag) { + if (JS_VALUE_GET_TAG(obj) == JS_TAG_NULL || + JS_VALUE_GET_TAG(obj) == JS_TAG_UNDEFINED) + goto not_obj; + } else { + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + goto not_obj; + } + p = JS_VALUE_GET_OBJ(obj); + if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) { + if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_NULL) { + not_obj: + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + proto = NULL; + } else { + proto = JS_VALUE_GET_OBJ(proto_val); + } + + if (throw_flag && JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return true; + + if (unlikely(p->class_id == JS_CLASS_PROXY)) + return js_proxy_setPrototypeOf(ctx, obj, proto_val, throw_flag); + sh = p->shape; + if (sh->proto == proto) + return true; + if (p == JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_OBJECT])) { + if (throw_flag) { + JS_ThrowTypeError(ctx, "'Immutable prototype object \'Object.prototype\' cannot have their prototype set'"); + return -1; + } + return false; + } + if (!p->extensible) { + if (throw_flag) { + JS_ThrowTypeError(ctx, "object is not extensible"); + return -1; + } else { + return false; + } + } + if (proto) { + /* check if there is a cycle */ + p1 = proto; + do { + if (p1 == p) { + if (throw_flag) { + JS_ThrowTypeError(ctx, "circular prototype chain"); + return -1; + } else { + return false; + } + } + /* Note: for Proxy objects, proto is NULL */ + p1 = p1->shape->proto; + } while (p1 != NULL); + js_dup(proto_val); + } + + if (js_shape_prepare_update(ctx, p, NULL)) + return -1; + sh = p->shape; + if (sh->proto) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto)); + sh->proto = proto; + if (proto) + proto->is_prototype = true; + if (p->is_prototype) { + /* track modification of Array.prototype */ + if (unlikely(p == JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_ARRAY]))) { + ctx->std_array_prototype = false; + } + } + return true; +} + +/* return -1 (exception) or true/false */ +int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val) +{ + return JS_SetPrototypeInternal(ctx, obj, proto_val, true); +} + +/* Only works for primitive types, otherwise return JS_NULL. */ +static JSValueConst JS_GetPrototypePrimitive(JSContext *ctx, JSValueConst val) +{ + JSValue ret; + switch(JS_VALUE_GET_NORM_TAG(val)) { + case JS_TAG_SHORT_BIG_INT: + case JS_TAG_BIG_INT: + ret = ctx->class_proto[JS_CLASS_BIG_INT]; + break; + case JS_TAG_INT: + case JS_TAG_FLOAT64: + ret = ctx->class_proto[JS_CLASS_NUMBER]; + break; + case JS_TAG_BOOL: + ret = ctx->class_proto[JS_CLASS_BOOLEAN]; + break; + case JS_TAG_STRING: + case JS_TAG_STRING_ROPE: + ret = ctx->class_proto[JS_CLASS_STRING]; + break; + case JS_TAG_SYMBOL: + ret = ctx->class_proto[JS_CLASS_SYMBOL]; + break; + case JS_TAG_OBJECT: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + default: + ret = JS_NULL; + break; + } + return ret; +} + +/* Return an Object, JS_NULL or JS_EXCEPTION in case of Proxy object. */ +JSValue JS_GetPrototype(JSContext *ctx, JSValueConst obj) +{ + JSValue val; + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject *p; + p = JS_VALUE_GET_OBJ(obj); + if (unlikely(p->class_id == JS_CLASS_PROXY)) { + val = js_proxy_getPrototypeOf(ctx, obj); + } else { + p = p->shape->proto; + if (!p) + val = JS_NULL; + else + val = js_dup(JS_MKPTR(JS_TAG_OBJECT, p)); + } + } else { + val = js_dup(JS_GetPrototypePrimitive(ctx, obj)); + } + return val; +} + +static JSValue JS_GetPrototypeFree(JSContext *ctx, JSValue obj) +{ + JSValue obj1; + obj1 = JS_GetPrototype(ctx, obj); + JS_FreeValue(ctx, obj); + return obj1; +} + +int JS_GetLength(JSContext *ctx, JSValueConst obj, int64_t *pres) { + return js_get_length64(ctx, pres, obj); +} + +int JS_SetLength(JSContext *ctx, JSValueConst obj, int64_t len) { + return js_set_length64(ctx, obj, len); +} + +/* return true, false or (-1) in case of exception */ +static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst val, + JSValueConst obj) +{ + JSValue obj_proto; + JSObject *proto; + const JSObject *p, *proto1; + int ret; + + if (!JS_IsFunction(ctx, obj)) + return false; + p = JS_VALUE_GET_OBJ(obj); + if (p->class_id == JS_CLASS_BOUND_FUNCTION) { + JSBoundFunction *s = p->u.bound_function; + return JS_IsInstanceOf(ctx, val, s->func_obj); + } + + /* Only explicitly boxed values are instances of constructors */ + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return false; + obj_proto = JS_GetProperty(ctx, obj, JS_ATOM_prototype); + if (JS_VALUE_GET_TAG(obj_proto) != JS_TAG_OBJECT) { + if (!JS_IsException(obj_proto)) + JS_ThrowTypeError(ctx, "operand 'prototype' property is not an object"); + ret = -1; + goto done; + } + proto = JS_VALUE_GET_OBJ(obj_proto); + p = JS_VALUE_GET_OBJ(val); + for(;;) { + proto1 = p->shape->proto; + if (!proto1) { + /* slow case if proxy in the prototype chain */ + if (unlikely(p->class_id == JS_CLASS_PROXY)) { + JSValue obj1; + obj1 = js_dup(JS_MKPTR(JS_TAG_OBJECT, (JSObject *)p)); + for(;;) { + obj1 = JS_GetPrototypeFree(ctx, obj1); + if (JS_IsException(obj1)) { + ret = -1; + break; + } + if (JS_IsNull(obj1)) { + ret = false; + break; + } + if (proto == JS_VALUE_GET_OBJ(obj1)) { + JS_FreeValue(ctx, obj1); + ret = true; + break; + } + /* must check for timeout to avoid infinite loop */ + if (js_poll_interrupts(ctx)) { + JS_FreeValue(ctx, obj1); + ret = -1; + break; + } + } + } else { + ret = false; + } + break; + } + p = proto1; + if (proto == p) { + ret = true; + break; + } + } +done: + JS_FreeValue(ctx, obj_proto); + return ret; +} + +/* return true, false or (-1) in case of exception */ +int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj) +{ + JSValue method; + + if (!JS_IsObject(obj)) + goto fail; + method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_hasInstance); + if (JS_IsException(method)) + return -1; + if (!JS_IsNull(method) && !JS_IsUndefined(method)) { + JSValue ret; + ret = JS_CallFree(ctx, method, obj, 1, &val); + return JS_ToBoolFree(ctx, ret); + } + + /* legacy case */ + if (!JS_IsFunction(ctx, obj)) { + fail: + JS_ThrowTypeError(ctx, "invalid 'instanceof' right operand"); + return -1; + } + return JS_OrdinaryIsInstanceOf(ctx, val, obj); +} + +/* File generated automatically by the QuickJS-ng compiler. */ + +#include + +const uint32_t qjsc_builtin_array_fromasync_size = 871; + +const uint8_t qjsc_builtin_array_fromasync[871] = { + 0x17, 0x0e, 0x01, 0x28, 0x53, 0x79, 0x6d, 0x62, + 0x6f, 0x6c, 0xb7, 0x61, 0x73, 0x79, 0x6e, 0x63, + 0x49, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x01, 0x2a, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0xb7, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x50, + 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x01, + 0x1e, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0xb7, + 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x01, 0x12, 0x61, 0x72, 0x72, 0x61, 0x79, 0x4c, + 0x69, 0x6b, 0x65, 0x01, 0x0a, 0x6d, 0x61, 0x70, + 0x46, 0x6e, 0x01, 0x0e, 0x74, 0x68, 0x69, 0x73, + 0x41, 0x72, 0x67, 0x01, 0x0c, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x01, 0x02, 0x69, 0x01, 0x1a, + 0x69, 0x73, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, + 0x75, 0x63, 0x74, 0x6f, 0x72, 0x01, 0x08, 0x73, + 0x79, 0x6e, 0x63, 0x01, 0x0c, 0x6d, 0x65, 0x74, + 0x68, 0x6f, 0x64, 0x01, 0x08, 0x69, 0x74, 0x65, + 0x72, 0x01, 0x1c, 0x6e, 0x6f, 0x74, 0x20, 0x61, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x01, 0x08, 0x63, 0x61, 0x6c, 0x6c, 0x0c, + 0x00, 0x02, 0x00, 0xa2, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x04, 0x01, 0xa4, 0x01, + 0x00, 0x00, 0x00, 0x0c, 0x43, 0x02, 0x01, 0x00, + 0x05, 0x00, 0x05, 0x01, 0x05, 0x00, 0x01, 0x03, + 0x05, 0xae, 0x02, 0x00, 0x01, 0x40, 0x03, 0xa4, + 0x03, 0x00, 0x01, 0x40, 0x00, 0xca, 0x03, 0x00, + 0x01, 0x40, 0x01, 0xcc, 0x03, 0x00, 0x01, 0x40, + 0x04, 0xce, 0x03, 0x00, 0x01, 0x40, 0x02, 0x0c, + 0x60, 0x02, 0x01, 0xf8, 0x01, 0x03, 0x0e, 0x01, + 0x06, 0x00, 0x05, 0x00, 0x86, 0x04, 0x11, 0xd0, + 0x03, 0x00, 0x01, 0x00, 0xd2, 0x03, 0x00, 0x01, + 0x00, 0xd4, 0x03, 0x00, 0x01, 0x00, 0xd0, 0x03, + 0x01, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x20, 0xd2, + 0x03, 0x01, 0x01, 0x20, 0xd4, 0x03, 0x01, 0x02, + 0x20, 0xd6, 0x03, 0x02, 0x00, 0x20, 0xd8, 0x03, + 0x02, 0x04, 0x20, 0xda, 0x03, 0x02, 0x05, 0x20, + 0xdc, 0x03, 0x02, 0x06, 0x20, 0xde, 0x03, 0x02, + 0x07, 0x20, 0x64, 0x06, 0x08, 0x20, 0x82, 0x01, + 0x07, 0x09, 0x20, 0xe0, 0x03, 0x0a, 0x08, 0x30, + 0x82, 0x01, 0x0d, 0x0b, 0x20, 0xd4, 0x01, 0x0d, + 0x0c, 0x20, 0x10, 0x00, 0x01, 0x00, 0xa4, 0x03, + 0x01, 0x01, 0xca, 0x03, 0x02, 0x01, 0xce, 0x03, + 0x04, 0x01, 0xae, 0x02, 0x00, 0x01, 0xcc, 0x03, + 0x03, 0x01, 0x08, 0xc2, 0x0d, 0x60, 0x02, 0x00, + 0x60, 0x01, 0x00, 0x60, 0x00, 0x00, 0xd1, 0xc9, + 0xd2, 0x11, 0xf2, 0xea, 0x08, 0x0e, 0x38, 0x46, + 0x00, 0x00, 0x00, 0xda, 0xca, 0xd3, 0x11, 0xf2, + 0xea, 0x08, 0x0e, 0x38, 0x46, 0x00, 0x00, 0x00, + 0xdb, 0xcb, 0x60, 0x07, 0x00, 0x60, 0x06, 0x00, + 0x60, 0x05, 0x00, 0x60, 0x04, 0x00, 0x60, 0x03, + 0x00, 0xd2, 0x38, 0x46, 0x00, 0x00, 0x00, 0xae, + 0xea, 0x16, 0xd2, 0x96, 0x04, 0x1b, 0x00, 0x00, + 0x00, 0xae, 0xea, 0x0c, 0xdd, 0x11, 0x04, 0xf1, + 0x00, 0x00, 0x00, 0x21, 0x01, 0x00, 0x30, 0x06, + 0xcc, 0xb4, 0xc2, 0x04, 0xc1, 0x0d, 0xf5, 0xc2, + 0x05, 0x09, 0xc2, 0x06, 0xd1, 0xde, 0x46, 0xc2, + 0x07, 0x61, 0x07, 0x00, 0x07, 0xab, 0xea, 0x0f, + 0x0a, 0x11, 0x62, 0x06, 0x00, 0x0e, 0xd1, 0xdf, + 0x46, 0x11, 0x62, 0x07, 0x00, 0x0e, 0x61, 0x07, + 0x00, 0x07, 0xab, 0x68, 0xa6, 0x00, 0x00, 0x00, + 0x60, 0x08, 0x00, 0x06, 0x11, 0xf2, 0xeb, 0x0c, + 0x6f, 0x41, 0x32, 0x00, 0x00, 0x00, 0xc2, 0x08, + 0x0e, 0xec, 0x05, 0x0e, 0xd1, 0xec, 0xf2, 0x61, + 0x08, 0x00, 0x8c, 0x11, 0xeb, 0x03, 0x0e, 0xb4, + 0x11, 0x62, 0x08, 0x00, 0x0e, 0x61, 0x05, 0x00, + 0xea, 0x0c, 0xc1, 0x0d, 0x11, 0x61, 0x08, 0x00, + 0x21, 0x01, 0x00, 0xec, 0x06, 0xe0, 0x61, 0x08, + 0x00, 0xef, 0x11, 0x62, 0x03, 0x00, 0x0e, 0x61, + 0x04, 0x00, 0x61, 0x08, 0x00, 0xa5, 0x68, 0x2a, + 0x01, 0x00, 0x00, 0x60, 0x09, 0x00, 0xd1, 0x61, + 0x04, 0x00, 0x46, 0xc2, 0x09, 0x61, 0x06, 0x00, + 0xea, 0x0a, 0x61, 0x09, 0x00, 0x8a, 0x11, 0x62, + 0x09, 0x00, 0x0e, 0xd2, 0xea, 0x17, 0xd2, 0x41, + 0xf2, 0x00, 0x00, 0x00, 0xd3, 0x61, 0x09, 0x00, + 0x61, 0x04, 0x00, 0x24, 0x03, 0x00, 0x8a, 0x11, + 0x62, 0x09, 0x00, 0x0e, 0x5d, 0x04, 0x00, 0x61, + 0x03, 0x00, 0x61, 0x04, 0x00, 0x90, 0x62, 0x04, + 0x00, 0x0b, 0x61, 0x09, 0x00, 0x4b, 0x41, 0x00, + 0x00, 0x00, 0x0a, 0x4b, 0x3e, 0x00, 0x00, 0x00, + 0x0a, 0x4b, 0x3f, 0x00, 0x00, 0x00, 0xf1, 0x0e, + 0xec, 0x9e, 0x60, 0x0a, 0x00, 0x61, 0x07, 0x00, + 0x41, 0xf2, 0x00, 0x00, 0x00, 0xd1, 0x24, 0x01, + 0x00, 0xc2, 0x0a, 0x61, 0x05, 0x00, 0xea, 0x09, + 0xc1, 0x0d, 0x11, 0x21, 0x00, 0x00, 0xec, 0x03, + 0xe0, 0xee, 0x11, 0x62, 0x03, 0x00, 0x0e, 0x6b, + 0x8c, 0x00, 0x00, 0x00, 0x60, 0x0c, 0x00, 0x60, + 0x0b, 0x00, 0x06, 0x11, 0xf2, 0xeb, 0x13, 0x6f, + 0x41, 0x41, 0x00, 0x00, 0x00, 0xc2, 0x0b, 0x41, + 0x6a, 0x00, 0x00, 0x00, 0xc2, 0x0c, 0x0e, 0xec, + 0x10, 0x0e, 0x61, 0x0a, 0x00, 0x41, 0x6b, 0x00, + 0x00, 0x00, 0x24, 0x00, 0x00, 0x8a, 0xec, 0xe0, + 0x61, 0x0c, 0x00, 0xeb, 0x4e, 0x61, 0x06, 0x00, + 0xea, 0x0a, 0x61, 0x0b, 0x00, 0x8a, 0x11, 0x62, + 0x0b, 0x00, 0x0e, 0xd2, 0xea, 0x17, 0xd2, 0x41, + 0xf2, 0x00, 0x00, 0x00, 0xd3, 0x61, 0x0b, 0x00, + 0x61, 0x04, 0x00, 0x24, 0x03, 0x00, 0x8a, 0x11, + 0x62, 0x0b, 0x00, 0x0e, 0x5d, 0x04, 0x00, 0x61, + 0x03, 0x00, 0x61, 0x04, 0x00, 0x90, 0x62, 0x04, + 0x00, 0x0b, 0x61, 0x0b, 0x00, 0x4b, 0x41, 0x00, + 0x00, 0x00, 0x0a, 0x4b, 0x3e, 0x00, 0x00, 0x00, + 0x0a, 0x4b, 0x3f, 0x00, 0x00, 0x00, 0xf1, 0x0e, + 0xec, 0x83, 0x0e, 0x06, 0x6c, 0x0d, 0x00, 0x00, + 0x00, 0x0e, 0xec, 0x1e, 0x6c, 0x05, 0x00, 0x00, + 0x00, 0x30, 0x61, 0x0a, 0x00, 0x40, 0x06, 0x00, + 0x00, 0x00, 0xea, 0x0d, 0x61, 0x0a, 0x00, 0x41, + 0x06, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x0e, + 0x6d, 0x61, 0x03, 0x00, 0x61, 0x04, 0x00, 0x42, + 0x32, 0x00, 0x00, 0x00, 0x61, 0x03, 0x00, 0x2f, + 0xbf, 0x00, 0x28, 0xbf, 0x00, 0xcd, 0x28, +}; + + +/* File generated automatically by the QuickJS-ng compiler. */ + +#include + +const uint32_t qjsc_builtin_iterator_zip_keyed_size = 2578; + +const uint8_t qjsc_builtin_iterator_zip_keyed[2578] = { + 0x17, 0x2b, 0x01, 0x1c, 0x49, 0x74, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x48, 0x65, 0x6c, 0x70, + 0x65, 0x72, 0x01, 0x08, 0x63, 0x61, 0x6c, 0x6c, + 0x01, 0x24, 0x68, 0x61, 0x73, 0x4f, 0x77, 0x6e, + 0x45, 0x6e, 0x75, 0x6d, 0x50, 0x72, 0x6f, 0x70, + 0x65, 0x72, 0x74, 0x79, 0x01, 0x24, 0x67, 0x65, + 0x74, 0x4f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x70, + 0x65, 0x72, 0x74, 0x79, 0x4b, 0x65, 0x79, 0x73, + 0x01, 0x1e, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, + 0xb7, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x01, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, + 0x01, 0x0a, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x01, + 0x10, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x61, 0x6c, + 0x6c, 0x01, 0x02, 0x76, 0x01, 0x02, 0x73, 0x01, + 0x08, 0x69, 0x74, 0x65, 0x72, 0x01, 0x0c, 0x6d, + 0x65, 0x74, 0x68, 0x6f, 0x64, 0x01, 0x02, 0x65, + 0x01, 0x0a, 0x69, 0x74, 0x65, 0x72, 0x73, 0x01, + 0x0a, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x01, 0x04, + 0x65, 0x78, 0x01, 0x02, 0x69, 0x01, 0x12, 0x69, + 0x74, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x73, + 0x01, 0x0e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x01, 0x08, 0x6d, 0x6f, 0x64, 0x65, 0x01, + 0x0e, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, + 0x01, 0x0a, 0x6e, 0x65, 0x78, 0x74, 0x73, 0x01, + 0x08, 0x70, 0x61, 0x64, 0x73, 0x01, 0x06, 0x6b, + 0x65, 0x79, 0x01, 0x06, 0x64, 0x65, 0x6c, 0x01, + 0x02, 0x6a, 0x01, 0x0a, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x01, 0x0a, 0x61, 0x6c, 0x69, 0x76, 0x65, + 0x01, 0x0a, 0x64, 0x6f, 0x6e, 0x65, 0x73, 0x01, + 0x0e, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, + 0x01, 0x0c, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x01, 0x1c, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, + 0x67, 0x20, 0x7a, 0x69, 0x70, 0x70, 0x65, 0x72, + 0x01, 0x06, 0x62, 0x75, 0x67, 0x01, 0x0e, 0x6c, + 0x6f, 0x6e, 0x67, 0x65, 0x73, 0x74, 0x01, 0x0c, + 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x01, 0x22, + 0x6d, 0x69, 0x73, 0x6d, 0x61, 0x74, 0x63, 0x68, + 0x65, 0x64, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, + 0x73, 0x01, 0x10, 0x73, 0x68, 0x6f, 0x72, 0x74, + 0x65, 0x73, 0x74, 0x01, 0x16, 0x62, 0x75, 0x67, + 0x3a, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x3d, + 0x01, 0x1a, 0x62, 0x61, 0x64, 0x20, 0x69, 0x74, + 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x01, + 0x16, 0x62, 0x61, 0x64, 0x20, 0x6f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x01, 0x10, 0x62, 0x61, + 0x64, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x01, 0x16, + 0x62, 0x61, 0x64, 0x20, 0x70, 0x61, 0x64, 0x64, + 0x69, 0x6e, 0x67, 0x01, 0x18, 0x62, 0x61, 0x64, + 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x0c, 0x00, 0x02, 0x00, 0xa2, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x04, 0x01, + 0xa4, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x43, 0x02, + 0x00, 0x00, 0x07, 0x03, 0x07, 0x01, 0x0a, 0x00, + 0x04, 0x0c, 0x0a, 0xca, 0x03, 0x00, 0x01, 0x40, + 0x09, 0xa8, 0x03, 0x00, 0x01, 0x40, 0x03, 0xa4, + 0x03, 0x00, 0x01, 0x40, 0x00, 0xcc, 0x03, 0x00, + 0x01, 0x40, 0x01, 0xce, 0x03, 0x00, 0x01, 0x40, + 0x07, 0xd0, 0x03, 0x00, 0x01, 0x40, 0x06, 0xd2, + 0x03, 0x00, 0x01, 0x40, 0x08, 0xd4, 0x03, 0x00, + 0x00, 0x40, 0x05, 0xd6, 0x03, 0x00, 0x01, 0x40, + 0x02, 0xd8, 0x03, 0x00, 0x02, 0x40, 0x04, 0x0c, + 0x43, 0x02, 0x00, 0xd4, 0x03, 0x02, 0x00, 0x02, + 0x03, 0x00, 0x01, 0x00, 0x17, 0x02, 0xda, 0x03, + 0x00, 0x01, 0x00, 0xdc, 0x03, 0x00, 0x01, 0x00, + 0xa4, 0x03, 0x02, 0x01, 0xd1, 0x96, 0x04, 0x4a, + 0x00, 0x00, 0x00, 0xad, 0xea, 0x07, 0xd1, 0x07, + 0xae, 0xea, 0x02, 0x29, 0xdd, 0x11, 0xd2, 0x21, + 0x01, 0x00, 0x30, 0x0c, 0x43, 0x02, 0x00, 0xd6, + 0x03, 0x01, 0x02, 0x01, 0x04, 0x00, 0x01, 0x00, + 0x2e, 0x03, 0xde, 0x03, 0x00, 0x01, 0x00, 0xe0, + 0x03, 0x02, 0x00, 0x20, 0xe2, 0x03, 0x05, 0x00, + 0x03, 0xcc, 0x03, 0x03, 0x01, 0x6b, 0x23, 0x00, + 0x00, 0x00, 0x60, 0x00, 0x00, 0xd1, 0x95, 0xea, + 0x04, 0x06, 0x6e, 0x28, 0xd1, 0x40, 0x06, 0x00, + 0x00, 0x00, 0xc9, 0x61, 0x00, 0x00, 0xea, 0x08, + 0xdd, 0xd1, 0x61, 0x00, 0x00, 0xf0, 0x0e, 0x0e, + 0x29, 0xca, 0x6b, 0x07, 0x00, 0x00, 0x00, 0xc6, + 0x6e, 0x28, 0x30, 0x0c, 0x43, 0x02, 0x00, 0xd8, + 0x03, 0x02, 0x04, 0x02, 0x03, 0x00, 0x01, 0x00, + 0x55, 0x06, 0xe4, 0x03, 0x00, 0x01, 0x00, 0xe6, + 0x03, 0x00, 0x01, 0x00, 0xe8, 0x03, 0x01, 0x00, + 0x20, 0xea, 0x03, 0x02, 0x01, 0x20, 0xde, 0x03, + 0x03, 0x02, 0x20, 0xe2, 0x03, 0x03, 0x03, 0x20, + 0xd6, 0x03, 0x01, 0x00, 0x60, 0x00, 0x00, 0x38, + 0x46, 0x00, 0x00, 0x00, 0xc9, 0x60, 0x01, 0x00, + 0xd2, 0xca, 0x61, 0x01, 0x00, 0x8f, 0x62, 0x01, + 0x00, 0xb4, 0xa7, 0xea, 0x39, 0x60, 0x03, 0x00, + 0x60, 0x02, 0x00, 0xd1, 0x61, 0x01, 0x00, 0x46, + 0xcb, 0xd1, 0x61, 0x01, 0x00, 0x1b, 0x11, 0xaf, + 0xeb, 0x04, 0x1b, 0x70, 0x1b, 0x1b, 0x38, 0x46, + 0x00, 0x00, 0x00, 0x1b, 0x70, 0x1b, 0x48, 0xdd, + 0x61, 0x02, 0x00, 0xef, 0xcc, 0x61, 0x00, 0x00, + 0x95, 0xea, 0xc8, 0x61, 0x03, 0x00, 0x11, 0x62, + 0x00, 0x00, 0x0e, 0xec, 0xbe, 0x61, 0x00, 0x00, + 0x28, 0x0c, 0x41, 0x02, 0x00, 0xaa, 0x02, 0x02, + 0x15, 0x01, 0x06, 0x08, 0x09, 0x02, 0xc9, 0x05, + 0x17, 0xec, 0x03, 0x00, 0x01, 0x00, 0xee, 0x03, + 0x00, 0x01, 0x00, 0xec, 0x03, 0x01, 0xff, 0xff, + 0xff, 0xff, 0x0f, 0x20, 0xee, 0x03, 0x01, 0x01, + 0x20, 0xf0, 0x03, 0x02, 0x00, 0x60, 0x04, 0xf2, + 0x03, 0x02, 0x03, 0x20, 0x60, 0x02, 0x04, 0x60, + 0x02, 0xe6, 0x03, 0x02, 0x05, 0x60, 0x01, 0xe4, + 0x03, 0x02, 0x06, 0x60, 0x03, 0xf4, 0x03, 0x02, + 0x07, 0x60, 0x06, 0xf6, 0x03, 0x02, 0x08, 0x60, + 0x05, 0xf8, 0x03, 0x09, 0x15, 0x20, 0xea, 0x03, + 0x0b, 0x15, 0x20, 0xfa, 0x03, 0x0c, 0x0b, 0x20, + 0xf8, 0x03, 0x0c, 0x0c, 0x20, 0xde, 0x03, 0x0e, + 0x0d, 0x20, 0xe0, 0x03, 0x10, 0x0e, 0x20, 0xfc, + 0x03, 0x14, 0x0d, 0x20, 0xea, 0x03, 0x19, 0x15, + 0x20, 0xea, 0x03, 0x1b, 0x15, 0x20, 0xe2, 0x03, + 0x1c, 0x15, 0x03, 0xfe, 0x03, 0x02, 0x09, 0x60, + 0x00, 0x80, 0x04, 0x02, 0x14, 0x60, 0x07, 0xa4, + 0x03, 0x02, 0x01, 0xa8, 0x03, 0x01, 0x01, 0xcc, + 0x03, 0x03, 0x01, 0xd8, 0x03, 0x02, 0x00, 0xd4, + 0x03, 0x00, 0x00, 0xd0, 0x03, 0x05, 0x01, 0xce, + 0x03, 0x04, 0x01, 0xd2, 0x03, 0x06, 0x01, 0xca, + 0x03, 0x00, 0x01, 0x0c, 0x42, 0x03, 0x00, 0x00, + 0x00, 0x09, 0x00, 0x05, 0x00, 0x0c, 0x00, 0xf7, + 0x04, 0x09, 0x82, 0x04, 0x01, 0x00, 0x20, 0xd8, + 0x01, 0x01, 0x01, 0x20, 0x84, 0x04, 0x01, 0x02, + 0x20, 0xea, 0x03, 0x03, 0x03, 0x20, 0xf8, 0x03, + 0x04, 0x04, 0x20, 0xde, 0x03, 0x04, 0x05, 0x20, + 0x86, 0x04, 0x04, 0x06, 0x20, 0xe2, 0x03, 0x09, + 0x07, 0x03, 0xe8, 0x03, 0x10, 0x07, 0x20, 0xfe, + 0x03, 0x13, 0x10, 0xa4, 0x03, 0x00, 0x02, 0xa8, + 0x03, 0x01, 0x02, 0xe6, 0x03, 0x05, 0x10, 0x60, + 0x04, 0x10, 0xe4, 0x03, 0x06, 0x10, 0xf0, 0x03, + 0x02, 0x10, 0xf6, 0x03, 0x08, 0x10, 0xcc, 0x03, + 0x02, 0x02, 0xf4, 0x03, 0x07, 0x10, 0x80, 0x04, + 0x14, 0x10, 0xd8, 0x03, 0x03, 0x02, 0x60, 0x02, + 0x00, 0x60, 0x01, 0x00, 0x60, 0x00, 0x00, 0x64, + 0x00, 0x00, 0x11, 0xb4, 0xad, 0xeb, 0x06, 0x11, + 0xb5, 0xad, 0xea, 0x09, 0xb6, 0x11, 0x65, 0x00, + 0x00, 0x0e, 0xec, 0x33, 0x11, 0xb6, 0xad, 0xea, + 0x0c, 0xde, 0x11, 0x04, 0x04, 0x01, 0x00, 0x00, + 0x21, 0x01, 0x00, 0x30, 0x11, 0xb7, 0xad, 0xea, + 0x13, 0x0b, 0x38, 0x46, 0x00, 0x00, 0x00, 0x4b, + 0x41, 0x00, 0x00, 0x00, 0x0a, 0x4b, 0x6a, 0x00, + 0x00, 0x00, 0x28, 0xdf, 0x11, 0x04, 0x05, 0x01, + 0x00, 0x00, 0x21, 0x01, 0x00, 0x30, 0x0e, 0xb4, + 0xc9, 0xb4, 0xca, 0x0c, 0x07, 0xcb, 0x60, 0x03, + 0x00, 0xb4, 0xcc, 0x61, 0x03, 0x00, 0x64, 0x03, + 0x00, 0xa5, 0x68, 0xdd, 0x01, 0x00, 0x00, 0x60, + 0x06, 0x00, 0x60, 0x05, 0x00, 0x60, 0x04, 0x00, + 0x64, 0x04, 0x00, 0x61, 0x03, 0x00, 0x46, 0xc2, + 0x04, 0x64, 0x05, 0x00, 0x61, 0x03, 0x00, 0x46, + 0xc2, 0x05, 0x61, 0x05, 0x00, 0x95, 0xea, 0x34, + 0x64, 0x06, 0x00, 0x04, 0x06, 0x01, 0x00, 0x00, + 0xae, 0xea, 0x0c, 0xdf, 0x11, 0x04, 0x05, 0x01, + 0x00, 0x00, 0x21, 0x01, 0x00, 0x30, 0x61, 0x02, + 0x00, 0x61, 0x04, 0x00, 0x1b, 0x11, 0xaf, 0xeb, + 0x04, 0x1b, 0x70, 0x1b, 0x1b, 0x64, 0x07, 0x00, + 0x61, 0x03, 0x00, 0x46, 0x1b, 0x70, 0x1b, 0x48, + 0xed, 0x7c, 0x01, 0x06, 0xc2, 0x06, 0x6b, 0x1a, + 0x00, 0x00, 0x00, 0x5d, 0x08, 0x00, 0x61, 0x05, + 0x00, 0x64, 0x09, 0x00, 0x61, 0x03, 0x00, 0x46, + 0xf0, 0x11, 0x62, 0x06, 0x00, 0x0e, 0x0e, 0xec, + 0x35, 0xc2, 0x07, 0x6b, 0x30, 0x00, 0x00, 0x00, + 0xb4, 0x11, 0x65, 0x0a, 0x00, 0x0e, 0x64, 0x05, + 0x00, 0x61, 0x03, 0x00, 0x1b, 0x11, 0xaf, 0xeb, + 0x04, 0x1b, 0x70, 0x1b, 0x1b, 0x38, 0x46, 0x00, + 0x00, 0x00, 0x1b, 0x70, 0x1b, 0x48, 0x5d, 0x0b, + 0x00, 0x64, 0x05, 0x00, 0x64, 0x03, 0x00, 0xf0, + 0x0e, 0xc1, 0x07, 0x30, 0x30, 0x61, 0x06, 0x00, + 0x40, 0x6a, 0x00, 0x00, 0x00, 0x95, 0xea, 0x4f, + 0x64, 0x06, 0x00, 0x04, 0x07, 0x01, 0x00, 0x00, + 0xad, 0xea, 0x1e, 0x61, 0x00, 0x00, 0xb4, 0xa7, + 0xea, 0x17, 0x5d, 0x0b, 0x00, 0x64, 0x05, 0x00, + 0x64, 0x03, 0x00, 0xf0, 0x0e, 0xde, 0x11, 0x04, + 0x08, 0x01, 0x00, 0x00, 0x21, 0x01, 0x00, 0x30, + 0x61, 0x02, 0x00, 0x61, 0x04, 0x00, 0x1b, 0x11, + 0xaf, 0xeb, 0x04, 0x1b, 0x70, 0x1b, 0x1b, 0x61, + 0x06, 0x00, 0x40, 0x41, 0x00, 0x00, 0x00, 0x1b, + 0x70, 0x1b, 0x48, 0x61, 0x01, 0x00, 0x90, 0x62, + 0x01, 0x00, 0x0e, 0xed, 0xd1, 0x00, 0x64, 0x0a, + 0x00, 0x8f, 0x65, 0x0a, 0x00, 0x0e, 0x61, 0x00, + 0x00, 0x90, 0x62, 0x00, 0x00, 0x0e, 0x64, 0x05, + 0x00, 0x61, 0x03, 0x00, 0x1b, 0x11, 0xaf, 0xeb, + 0x04, 0x1b, 0x70, 0x1b, 0x1b, 0x38, 0x46, 0x00, + 0x00, 0x00, 0x1b, 0x70, 0x1b, 0x48, 0x64, 0x06, + 0x00, 0x60, 0x08, 0x00, 0x11, 0x04, 0x09, 0x01, + 0x00, 0x00, 0xad, 0xea, 0x2e, 0x5d, 0x0b, 0x00, + 0x64, 0x05, 0x00, 0x64, 0x03, 0x00, 0xf0, 0xc2, + 0x08, 0x61, 0x08, 0x00, 0xea, 0x05, 0x61, 0x08, + 0x00, 0x30, 0xb7, 0x11, 0x65, 0x00, 0x00, 0x0e, + 0x0b, 0x38, 0x46, 0x00, 0x00, 0x00, 0x4b, 0x41, + 0x00, 0x00, 0x00, 0x0a, 0x4b, 0x6a, 0x00, 0x00, + 0x00, 0x28, 0x11, 0x04, 0x06, 0x01, 0x00, 0x00, + 0xad, 0xea, 0x3c, 0x64, 0x0a, 0x00, 0xb5, 0xa5, + 0xea, 0x19, 0xb7, 0x11, 0x65, 0x00, 0x00, 0x0e, + 0x0b, 0x38, 0x46, 0x00, 0x00, 0x00, 0x4b, 0x41, + 0x00, 0x00, 0x00, 0x0a, 0x4b, 0x6a, 0x00, 0x00, + 0x00, 0x28, 0x61, 0x02, 0x00, 0x61, 0x04, 0x00, + 0x1b, 0x11, 0xaf, 0xeb, 0x04, 0x1b, 0x70, 0x1b, + 0x1b, 0x64, 0x07, 0x00, 0x61, 0x03, 0x00, 0x46, + 0x1b, 0x70, 0x1b, 0x48, 0xec, 0x27, 0x11, 0x04, + 0x07, 0x01, 0x00, 0x00, 0xad, 0xea, 0x1e, 0x61, + 0x01, 0x00, 0xb4, 0xa7, 0xea, 0x17, 0x5d, 0x0b, + 0x00, 0x64, 0x05, 0x00, 0x64, 0x03, 0x00, 0xf0, + 0x0e, 0xde, 0x11, 0x04, 0x08, 0x01, 0x00, 0x00, + 0x21, 0x01, 0x00, 0x30, 0x0e, 0x61, 0x03, 0x00, + 0x90, 0x62, 0x03, 0x00, 0x0e, 0xed, 0x1d, 0xfe, + 0x61, 0x01, 0x00, 0xb4, 0xad, 0xea, 0x19, 0xb7, + 0x11, 0x65, 0x00, 0x00, 0x0e, 0x0b, 0x38, 0x46, + 0x00, 0x00, 0x00, 0x4b, 0x41, 0x00, 0x00, 0x00, + 0x0a, 0x4b, 0x6a, 0x00, 0x00, 0x00, 0x28, 0xb5, + 0x11, 0x65, 0x00, 0x00, 0x0e, 0x0b, 0x61, 0x02, + 0x00, 0x4b, 0x41, 0x00, 0x00, 0x00, 0x09, 0x4b, + 0x6a, 0x00, 0x00, 0x00, 0x28, 0x0c, 0x42, 0x03, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x06, + 0x00, 0x88, 0x01, 0x01, 0xe8, 0x03, 0x01, 0x00, + 0x20, 0xfe, 0x03, 0x13, 0x10, 0xa4, 0x03, 0x00, + 0x02, 0xa8, 0x03, 0x01, 0x02, 0xd8, 0x03, 0x03, + 0x02, 0xe4, 0x03, 0x06, 0x10, 0xe6, 0x03, 0x05, + 0x10, 0x60, 0x00, 0x00, 0x64, 0x00, 0x00, 0x11, + 0xb4, 0xad, 0xea, 0x09, 0xb7, 0x11, 0x65, 0x00, + 0x00, 0x0e, 0xec, 0x4b, 0x11, 0xb5, 0xad, 0xea, + 0x09, 0xb6, 0x11, 0x65, 0x00, 0x00, 0x0e, 0xec, + 0x3e, 0x11, 0xb6, 0xad, 0xea, 0x0c, 0xde, 0x11, + 0x04, 0x04, 0x01, 0x00, 0x00, 0x21, 0x01, 0x00, + 0x30, 0x11, 0xb7, 0xad, 0xea, 0x13, 0x0b, 0x38, + 0x46, 0x00, 0x00, 0x00, 0x4b, 0x41, 0x00, 0x00, + 0x00, 0x0a, 0x4b, 0x6a, 0x00, 0x00, 0x00, 0x28, + 0xdf, 0x11, 0x04, 0x0a, 0x01, 0x00, 0x00, 0x41, + 0x5d, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x24, + 0x01, 0x00, 0x21, 0x01, 0x00, 0x30, 0x0e, 0xe0, + 0x64, 0x04, 0x00, 0x64, 0x05, 0x00, 0xf0, 0xc9, + 0x61, 0x00, 0x00, 0xea, 0x05, 0x61, 0x00, 0x00, + 0x30, 0xb7, 0x11, 0x65, 0x00, 0x00, 0x0e, 0x0b, + 0x38, 0x46, 0x00, 0x00, 0x00, 0x4b, 0x41, 0x00, + 0x00, 0x00, 0x0a, 0x4b, 0x6a, 0x00, 0x00, 0x00, + 0x28, 0x60, 0x01, 0x00, 0x60, 0x00, 0x00, 0xd1, + 0xc9, 0xd2, 0x11, 0xf2, 0xea, 0x08, 0x0e, 0x38, + 0x46, 0x00, 0x00, 0x00, 0xda, 0xca, 0x60, 0x14, + 0x00, 0x60, 0x13, 0x00, 0x60, 0x08, 0x00, 0x60, + 0x07, 0x00, 0x60, 0x06, 0x00, 0x60, 0x05, 0x00, + 0x60, 0x04, 0x00, 0x60, 0x03, 0x00, 0x60, 0x02, + 0x00, 0x5d, 0x04, 0x00, 0xd1, 0x04, 0x0b, 0x01, + 0x00, 0x00, 0xf0, 0x0e, 0xd2, 0x38, 0x46, 0x00, + 0x00, 0x00, 0xad, 0xea, 0x06, 0x0c, 0x07, 0xd6, + 0xec, 0x0c, 0x5d, 0x04, 0x00, 0xd2, 0x04, 0x0c, + 0x01, 0x00, 0x00, 0xf0, 0x0e, 0xd2, 0x40, 0xf8, + 0x00, 0x00, 0x00, 0xcb, 0x61, 0x02, 0x00, 0x38, + 0x46, 0x00, 0x00, 0x00, 0xad, 0xea, 0x0b, 0x04, + 0x09, 0x01, 0x00, 0x00, 0x11, 0x62, 0x02, 0x00, + 0x0e, 0x61, 0x02, 0x00, 0x04, 0x07, 0x01, 0x00, + 0x00, 0xad, 0x11, 0xeb, 0x18, 0x0e, 0x61, 0x02, + 0x00, 0x04, 0x06, 0x01, 0x00, 0x00, 0xad, 0x11, + 0xeb, 0x0b, 0x0e, 0x61, 0x02, 0x00, 0x04, 0x09, + 0x01, 0x00, 0x00, 0xad, 0x95, 0xea, 0x0c, 0xdd, + 0x11, 0x04, 0x0d, 0x01, 0x00, 0x00, 0x21, 0x01, + 0x00, 0x30, 0x38, 0x46, 0x00, 0x00, 0x00, 0xcc, + 0x61, 0x02, 0x00, 0x04, 0x06, 0x01, 0x00, 0x00, + 0xad, 0xea, 0x24, 0xd2, 0x40, 0xf9, 0x00, 0x00, + 0x00, 0x11, 0x62, 0x03, 0x00, 0x0e, 0x61, 0x03, + 0x00, 0x38, 0x46, 0x00, 0x00, 0x00, 0xae, 0xea, + 0x0e, 0x5d, 0x04, 0x00, 0x61, 0x03, 0x00, 0x04, + 0x0e, 0x01, 0x00, 0x00, 0xf0, 0x0e, 0x26, 0x00, + 0x00, 0xc2, 0x04, 0xb4, 0xc2, 0x05, 0x26, 0x00, + 0x00, 0xc2, 0x06, 0x26, 0x00, 0x00, 0xc2, 0x07, + 0x26, 0x00, 0x00, 0xc2, 0x08, 0x60, 0x09, 0x00, + 0x5d, 0x05, 0x00, 0xd1, 0xef, 0x7d, 0xec, 0x1d, + 0xc2, 0x09, 0x61, 0x04, 0x00, 0x61, 0x05, 0x00, + 0x90, 0x62, 0x05, 0x00, 0x1b, 0x11, 0xaf, 0xeb, + 0x04, 0x1b, 0x70, 0x1b, 0x1b, 0x61, 0x09, 0x00, + 0x1b, 0x70, 0x1b, 0x48, 0x80, 0x00, 0xea, 0xe1, + 0x0e, 0x83, 0x6b, 0x7d, 0x01, 0x00, 0x00, 0x60, + 0x0a, 0x00, 0xb4, 0xc2, 0x0a, 0x61, 0x0a, 0x00, + 0x61, 0x05, 0x00, 0xa5, 0x68, 0xf0, 0x00, 0x00, + 0x00, 0x60, 0x0c, 0x00, 0x60, 0x0b, 0x00, 0x0a, + 0xc2, 0x0b, 0x61, 0x04, 0x00, 0x61, 0x0a, 0x00, + 0x46, 0xc2, 0x0c, 0x5d, 0x06, 0x00, 0xd1, 0x61, + 0x0c, 0x00, 0xf0, 0xea, 0x78, 0x60, 0x0d, 0x00, + 0xd1, 0x61, 0x0c, 0x00, 0x46, 0xc2, 0x0d, 0x61, + 0x0d, 0x00, 0x38, 0x46, 0x00, 0x00, 0x00, 0xae, + 0xea, 0x63, 0x60, 0x0e, 0x00, 0x5d, 0x04, 0x00, + 0x61, 0x0d, 0x00, 0x04, 0x0f, 0x01, 0x00, 0x00, + 0xf0, 0x0e, 0x61, 0x0d, 0x00, 0x5d, 0x07, 0x00, + 0x46, 0xc2, 0x0e, 0x61, 0x0e, 0x00, 0xea, 0x0e, + 0xdf, 0x61, 0x0d, 0x00, 0x61, 0x0e, 0x00, 0xf0, + 0x11, 0x62, 0x0d, 0x00, 0x0e, 0x61, 0x06, 0x00, + 0x61, 0x0a, 0x00, 0x1b, 0x11, 0xaf, 0xeb, 0x04, + 0x1b, 0x70, 0x1b, 0x1b, 0x61, 0x0d, 0x00, 0x1b, + 0x70, 0x1b, 0x48, 0x61, 0x07, 0x00, 0x61, 0x0a, + 0x00, 0x1b, 0x11, 0xaf, 0xeb, 0x04, 0x1b, 0x70, + 0x1b, 0x1b, 0x61, 0x0d, 0x00, 0x40, 0x6b, 0x00, + 0x00, 0x00, 0x1b, 0x70, 0x1b, 0x48, 0x09, 0x11, + 0x62, 0x0b, 0x00, 0x0e, 0x61, 0x0b, 0x00, 0xea, + 0x4a, 0x60, 0x0f, 0x00, 0x61, 0x0a, 0x00, 0xb5, + 0x9c, 0xc2, 0x0f, 0x61, 0x0f, 0x00, 0x61, 0x05, + 0x00, 0xa5, 0xea, 0x27, 0x61, 0x04, 0x00, 0x61, + 0x0f, 0x00, 0xb5, 0x9d, 0x1b, 0x11, 0xaf, 0xeb, + 0x04, 0x1b, 0x70, 0x1b, 0x1b, 0x61, 0x04, 0x00, + 0x61, 0x0f, 0x00, 0x46, 0x1b, 0x70, 0x1b, 0x48, + 0x61, 0x0f, 0x00, 0x90, 0x62, 0x0f, 0x00, 0x0e, + 0xec, 0xd2, 0x61, 0x05, 0x00, 0x8f, 0x62, 0x05, + 0x00, 0x0e, 0x61, 0x0a, 0x00, 0x8f, 0x62, 0x0a, + 0x00, 0x0e, 0x61, 0x0a, 0x00, 0x90, 0x62, 0x0a, + 0x00, 0x0e, 0xed, 0x0a, 0xff, 0x61, 0x02, 0x00, + 0x04, 0x06, 0x01, 0x00, 0x00, 0xad, 0xea, 0x6e, + 0x61, 0x03, 0x00, 0xea, 0x38, 0x60, 0x10, 0x00, + 0xb4, 0xc2, 0x10, 0x61, 0x10, 0x00, 0x61, 0x05, + 0x00, 0xa5, 0xea, 0x5a, 0x61, 0x08, 0x00, 0x61, + 0x10, 0x00, 0x1b, 0x11, 0xaf, 0xeb, 0x04, 0x1b, + 0x70, 0x1b, 0x1b, 0x61, 0x03, 0x00, 0x61, 0x04, + 0x00, 0x61, 0x10, 0x00, 0x46, 0x46, 0x1b, 0x70, + 0x1b, 0x48, 0x61, 0x10, 0x00, 0x90, 0x62, 0x10, + 0x00, 0x0e, 0xec, 0xd0, 0x60, 0x11, 0x00, 0xb4, + 0xc2, 0x11, 0x61, 0x11, 0x00, 0x61, 0x05, 0x00, + 0xa5, 0xea, 0x23, 0x61, 0x08, 0x00, 0x61, 0x11, + 0x00, 0x1b, 0x11, 0xaf, 0xeb, 0x04, 0x1b, 0x70, + 0x1b, 0x1b, 0x38, 0x46, 0x00, 0x00, 0x00, 0x1b, + 0x70, 0x1b, 0x48, 0x61, 0x11, 0x00, 0x90, 0x62, + 0x11, 0x00, 0x0e, 0xec, 0xd6, 0x0e, 0xec, 0x15, + 0xc2, 0x12, 0x6b, 0x10, 0x00, 0x00, 0x00, 0xe0, + 0x61, 0x06, 0x00, 0x61, 0x05, 0x00, 0xf0, 0x0e, + 0xc1, 0x12, 0x30, 0x30, 0xb4, 0xc2, 0x13, 0x61, + 0x05, 0x00, 0xc2, 0x14, 0x0b, 0x5d, 0x08, 0x00, + 0x4e, 0xbf, 0x00, 0x53, 0x6b, 0x00, 0x00, 0x00, + 0x04, 0xbf, 0x01, 0x53, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x28, 0xbf, 0x00, 0xc9, 0xbf, 0x01, 0xca, + 0xbf, 0x02, 0xcb, 0xbf, 0x03, 0x28, 0xbf, 0x00, + 0xcd, 0x28, +}; + + +/* File generated automatically by the QuickJS-ng compiler. */ + +#include + +const uint32_t qjsc_builtin_iterator_zip_size = 2617; + +const uint8_t qjsc_builtin_iterator_zip[2617] = { + 0x17, 0x2a, 0x01, 0x1c, 0x49, 0x74, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x48, 0x65, 0x6c, 0x70, + 0x65, 0x72, 0x01, 0x08, 0x63, 0x61, 0x6c, 0x6c, + 0x01, 0x1e, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, + 0xb7, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x01, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, + 0x01, 0x0a, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x01, + 0x10, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x61, 0x6c, + 0x6c, 0x01, 0x02, 0x76, 0x01, 0x02, 0x73, 0x01, + 0x08, 0x69, 0x74, 0x65, 0x72, 0x01, 0x0c, 0x6d, + 0x65, 0x74, 0x68, 0x6f, 0x64, 0x01, 0x02, 0x65, + 0x01, 0x0a, 0x69, 0x74, 0x65, 0x72, 0x73, 0x01, + 0x0a, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x01, 0x04, + 0x65, 0x78, 0x01, 0x02, 0x69, 0x01, 0x12, 0x69, + 0x74, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x73, + 0x01, 0x0e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x01, 0x08, 0x6d, 0x6f, 0x64, 0x65, 0x01, + 0x0e, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, + 0x01, 0x08, 0x70, 0x61, 0x64, 0x73, 0x01, 0x0a, + 0x6e, 0x65, 0x78, 0x74, 0x73, 0x01, 0x16, 0x70, + 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x69, 0x74, + 0x65, 0x72, 0x01, 0x1a, 0x69, 0x74, 0x65, 0x72, + 0x61, 0x62, 0x6c, 0x65, 0x73, 0x69, 0x74, 0x65, + 0x72, 0x01, 0x08, 0x69, 0x74, 0x65, 0x6d, 0x01, + 0x02, 0x74, 0x01, 0x0a, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x01, 0x0a, 0x61, 0x6c, 0x69, 0x76, 0x65, + 0x01, 0x0a, 0x64, 0x6f, 0x6e, 0x65, 0x73, 0x01, + 0x0e, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, + 0x01, 0x0c, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x01, 0x1c, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, + 0x67, 0x20, 0x7a, 0x69, 0x70, 0x70, 0x65, 0x72, + 0x01, 0x06, 0x62, 0x75, 0x67, 0x01, 0x0e, 0x6c, + 0x6f, 0x6e, 0x67, 0x65, 0x73, 0x74, 0x01, 0x0c, + 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x01, 0x22, + 0x6d, 0x69, 0x73, 0x6d, 0x61, 0x74, 0x63, 0x68, + 0x65, 0x64, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, + 0x73, 0x01, 0x10, 0x73, 0x68, 0x6f, 0x72, 0x74, + 0x65, 0x73, 0x74, 0x01, 0x16, 0x62, 0x75, 0x67, + 0x3a, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x3d, + 0x01, 0x1a, 0x62, 0x61, 0x64, 0x20, 0x69, 0x74, + 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x01, + 0x16, 0x62, 0x61, 0x64, 0x20, 0x6f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x01, 0x10, 0x62, 0x61, + 0x64, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x01, 0x16, + 0x62, 0x61, 0x64, 0x20, 0x70, 0x61, 0x64, 0x64, + 0x69, 0x6e, 0x67, 0x01, 0x18, 0x62, 0x61, 0x64, + 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x0c, 0x00, 0x02, 0x00, 0xa2, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x04, 0x01, + 0xa4, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x43, 0x02, + 0x00, 0x00, 0x05, 0x03, 0x05, 0x01, 0x08, 0x00, + 0x04, 0x0c, 0x08, 0xca, 0x03, 0x00, 0x01, 0x40, + 0x07, 0xa8, 0x03, 0x00, 0x01, 0x40, 0x03, 0xa4, + 0x03, 0x00, 0x01, 0x40, 0x00, 0xcc, 0x03, 0x00, + 0x01, 0x40, 0x01, 0xce, 0x03, 0x00, 0x01, 0x40, + 0x06, 0xd0, 0x03, 0x00, 0x00, 0x40, 0x05, 0xd2, + 0x03, 0x00, 0x01, 0x40, 0x02, 0xd4, 0x03, 0x00, + 0x02, 0x40, 0x04, 0x0c, 0x43, 0x02, 0x00, 0xd0, + 0x03, 0x02, 0x00, 0x02, 0x03, 0x00, 0x01, 0x00, + 0x17, 0x02, 0xd6, 0x03, 0x00, 0x01, 0x00, 0xd8, + 0x03, 0x00, 0x01, 0x00, 0xa4, 0x03, 0x02, 0x01, + 0xd1, 0x96, 0x04, 0x4a, 0x00, 0x00, 0x00, 0xad, + 0xea, 0x07, 0xd1, 0x07, 0xae, 0xea, 0x02, 0x29, + 0xdd, 0x11, 0xd2, 0x21, 0x01, 0x00, 0x30, 0x0c, + 0x43, 0x02, 0x00, 0xd2, 0x03, 0x01, 0x02, 0x01, + 0x04, 0x00, 0x01, 0x00, 0x2e, 0x03, 0xda, 0x03, + 0x00, 0x01, 0x00, 0xdc, 0x03, 0x02, 0x00, 0x20, + 0xde, 0x03, 0x05, 0x00, 0x03, 0xcc, 0x03, 0x03, + 0x01, 0x6b, 0x23, 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0xd1, 0x95, 0xea, 0x04, 0x06, 0x6e, 0x28, + 0xd1, 0x40, 0x06, 0x00, 0x00, 0x00, 0xc9, 0x61, + 0x00, 0x00, 0xea, 0x08, 0xdd, 0xd1, 0x61, 0x00, + 0x00, 0xf0, 0x0e, 0x0e, 0x29, 0xca, 0x6b, 0x07, + 0x00, 0x00, 0x00, 0xc6, 0x6e, 0x28, 0x30, 0x0c, + 0x43, 0x02, 0x00, 0xd4, 0x03, 0x02, 0x04, 0x02, + 0x03, 0x00, 0x01, 0x00, 0x55, 0x06, 0xe0, 0x03, + 0x00, 0x01, 0x00, 0xe2, 0x03, 0x00, 0x01, 0x00, + 0xe4, 0x03, 0x01, 0x00, 0x20, 0xe6, 0x03, 0x02, + 0x01, 0x20, 0xda, 0x03, 0x03, 0x02, 0x20, 0xde, + 0x03, 0x03, 0x03, 0x20, 0xd2, 0x03, 0x01, 0x00, + 0x60, 0x00, 0x00, 0x38, 0x46, 0x00, 0x00, 0x00, + 0xc9, 0x60, 0x01, 0x00, 0xd2, 0xca, 0x61, 0x01, + 0x00, 0x8f, 0x62, 0x01, 0x00, 0xb4, 0xa7, 0xea, + 0x39, 0x60, 0x03, 0x00, 0x60, 0x02, 0x00, 0xd1, + 0x61, 0x01, 0x00, 0x46, 0xcb, 0xd1, 0x61, 0x01, + 0x00, 0x1b, 0x11, 0xaf, 0xeb, 0x04, 0x1b, 0x70, + 0x1b, 0x1b, 0x38, 0x46, 0x00, 0x00, 0x00, 0x1b, + 0x70, 0x1b, 0x48, 0xdd, 0x61, 0x02, 0x00, 0xef, + 0xcc, 0x61, 0x00, 0x00, 0x95, 0xea, 0xc8, 0x61, + 0x03, 0x00, 0x11, 0x62, 0x00, 0x00, 0x0e, 0xec, + 0xbe, 0x61, 0x00, 0x00, 0x28, 0x0c, 0x41, 0x02, + 0x00, 0xa8, 0x02, 0x02, 0x1a, 0x01, 0x05, 0x07, + 0x08, 0x02, 0x8a, 0x06, 0x1c, 0xe8, 0x03, 0x00, + 0x01, 0x00, 0xea, 0x03, 0x00, 0x01, 0x00, 0xe8, + 0x03, 0x01, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x20, + 0xea, 0x03, 0x01, 0x01, 0x20, 0xec, 0x03, 0x02, + 0x00, 0x60, 0x03, 0xee, 0x03, 0x02, 0x03, 0x20, + 0xf0, 0x03, 0x02, 0x04, 0x60, 0x04, 0xe0, 0x03, + 0x02, 0x05, 0x60, 0x02, 0xf2, 0x03, 0x02, 0x06, + 0x60, 0x05, 0xe2, 0x03, 0x02, 0x07, 0x60, 0x01, + 0xf4, 0x03, 0x02, 0x08, 0x20, 0xf6, 0x03, 0x02, + 0x09, 0x20, 0xd6, 0x01, 0x09, 0x1a, 0x20, 0xf8, + 0x03, 0x0b, 0x0b, 0x20, 0xde, 0x03, 0x0d, 0x0f, + 0x03, 0xda, 0x03, 0x0b, 0x0c, 0x20, 0xdc, 0x03, + 0x0b, 0x0e, 0x20, 0xd6, 0x01, 0x13, 0x0b, 0x20, + 0xe6, 0x03, 0x13, 0x10, 0x20, 0xd4, 0x01, 0x13, + 0x11, 0x20, 0x82, 0x01, 0x15, 0x16, 0x20, 0xf8, + 0x03, 0x16, 0x13, 0x20, 0xde, 0x03, 0x17, 0x13, + 0x03, 0xfa, 0x03, 0x13, 0x12, 0x20, 0xe4, 0x03, + 0x1c, 0x16, 0x20, 0xde, 0x03, 0x1f, 0x1a, 0x03, + 0xfc, 0x03, 0x02, 0x0a, 0x60, 0x00, 0xfe, 0x03, + 0x02, 0x19, 0x60, 0x06, 0xa4, 0x03, 0x02, 0x01, + 0xa8, 0x03, 0x01, 0x01, 0xcc, 0x03, 0x03, 0x01, + 0xd4, 0x03, 0x02, 0x00, 0xd0, 0x03, 0x00, 0x00, + 0xce, 0x03, 0x04, 0x01, 0xd2, 0x03, 0x01, 0x00, + 0xca, 0x03, 0x00, 0x01, 0x0c, 0x42, 0x03, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x05, 0x00, 0x0b, 0x00, + 0xec, 0x04, 0x08, 0x80, 0x04, 0x01, 0x00, 0x20, + 0xd8, 0x01, 0x01, 0x01, 0x20, 0x82, 0x04, 0x01, + 0x02, 0x20, 0xe6, 0x03, 0x03, 0x03, 0x20, 0xda, + 0x03, 0x04, 0x04, 0x20, 0x84, 0x04, 0x04, 0x05, + 0x20, 0xde, 0x03, 0x09, 0x06, 0x03, 0xe4, 0x03, + 0x10, 0x06, 0x20, 0xfc, 0x03, 0x18, 0x10, 0xa4, + 0x03, 0x00, 0x02, 0xa8, 0x03, 0x01, 0x02, 0xe2, + 0x03, 0x07, 0x10, 0xe0, 0x03, 0x05, 0x10, 0xec, + 0x03, 0x02, 0x10, 0xf0, 0x03, 0x04, 0x10, 0xcc, + 0x03, 0x02, 0x02, 0xf2, 0x03, 0x06, 0x10, 0xfe, + 0x03, 0x19, 0x10, 0xd4, 0x03, 0x03, 0x02, 0x60, + 0x02, 0x00, 0x60, 0x01, 0x00, 0x60, 0x00, 0x00, + 0x64, 0x00, 0x00, 0x11, 0xb4, 0xad, 0xeb, 0x06, + 0x11, 0xb5, 0xad, 0xea, 0x09, 0xb6, 0x11, 0x65, + 0x00, 0x00, 0x0e, 0xec, 0x33, 0x11, 0xb6, 0xad, + 0xea, 0x0c, 0xde, 0x11, 0x04, 0x03, 0x01, 0x00, + 0x00, 0x21, 0x01, 0x00, 0x30, 0x11, 0xb7, 0xad, + 0xea, 0x13, 0x0b, 0x38, 0x46, 0x00, 0x00, 0x00, + 0x4b, 0x41, 0x00, 0x00, 0x00, 0x0a, 0x4b, 0x6a, + 0x00, 0x00, 0x00, 0x28, 0xdf, 0x11, 0x04, 0x04, + 0x01, 0x00, 0x00, 0x21, 0x01, 0x00, 0x30, 0x0e, + 0xb4, 0xc9, 0xb4, 0xca, 0x26, 0x00, 0x00, 0xcb, + 0x60, 0x03, 0x00, 0xb4, 0xcc, 0x61, 0x03, 0x00, + 0x64, 0x03, 0x00, 0xa5, 0x68, 0xd1, 0x01, 0x00, + 0x00, 0x60, 0x05, 0x00, 0x60, 0x04, 0x00, 0x64, + 0x04, 0x00, 0x61, 0x03, 0x00, 0x46, 0xc2, 0x04, + 0x61, 0x04, 0x00, 0x95, 0xea, 0x34, 0x64, 0x05, + 0x00, 0x04, 0x05, 0x01, 0x00, 0x00, 0xae, 0xea, + 0x0c, 0xdf, 0x11, 0x04, 0x04, 0x01, 0x00, 0x00, + 0x21, 0x01, 0x00, 0x30, 0x61, 0x02, 0x00, 0x61, + 0x03, 0x00, 0x1b, 0x11, 0xaf, 0xeb, 0x04, 0x1b, + 0x70, 0x1b, 0x1b, 0x64, 0x06, 0x00, 0x61, 0x03, + 0x00, 0x46, 0x1b, 0x70, 0x1b, 0x48, 0xed, 0x7c, + 0x01, 0x06, 0xc2, 0x05, 0x6b, 0x1a, 0x00, 0x00, + 0x00, 0x5d, 0x07, 0x00, 0x61, 0x04, 0x00, 0x64, + 0x08, 0x00, 0x61, 0x03, 0x00, 0x46, 0xf0, 0x11, + 0x62, 0x05, 0x00, 0x0e, 0x0e, 0xec, 0x35, 0xc2, + 0x06, 0x6b, 0x30, 0x00, 0x00, 0x00, 0xb4, 0x11, + 0x65, 0x09, 0x00, 0x0e, 0x64, 0x04, 0x00, 0x61, + 0x03, 0x00, 0x1b, 0x11, 0xaf, 0xeb, 0x04, 0x1b, + 0x70, 0x1b, 0x1b, 0x38, 0x46, 0x00, 0x00, 0x00, + 0x1b, 0x70, 0x1b, 0x48, 0x5d, 0x0a, 0x00, 0x64, + 0x04, 0x00, 0x64, 0x03, 0x00, 0xf0, 0x0e, 0xc1, + 0x06, 0x30, 0x30, 0x61, 0x05, 0x00, 0x40, 0x6a, + 0x00, 0x00, 0x00, 0x95, 0xea, 0x4f, 0x64, 0x05, + 0x00, 0x04, 0x06, 0x01, 0x00, 0x00, 0xad, 0xea, + 0x1e, 0x61, 0x00, 0x00, 0xb4, 0xa7, 0xea, 0x17, + 0x5d, 0x0a, 0x00, 0x64, 0x04, 0x00, 0x64, 0x03, + 0x00, 0xf0, 0x0e, 0xde, 0x11, 0x04, 0x07, 0x01, + 0x00, 0x00, 0x21, 0x01, 0x00, 0x30, 0x61, 0x02, + 0x00, 0x61, 0x03, 0x00, 0x1b, 0x11, 0xaf, 0xeb, + 0x04, 0x1b, 0x70, 0x1b, 0x1b, 0x61, 0x05, 0x00, + 0x40, 0x41, 0x00, 0x00, 0x00, 0x1b, 0x70, 0x1b, + 0x48, 0x61, 0x01, 0x00, 0x90, 0x62, 0x01, 0x00, + 0x0e, 0xed, 0xd1, 0x00, 0x64, 0x09, 0x00, 0x8f, + 0x65, 0x09, 0x00, 0x0e, 0x61, 0x00, 0x00, 0x90, + 0x62, 0x00, 0x00, 0x0e, 0x64, 0x04, 0x00, 0x61, + 0x03, 0x00, 0x1b, 0x11, 0xaf, 0xeb, 0x04, 0x1b, + 0x70, 0x1b, 0x1b, 0x38, 0x46, 0x00, 0x00, 0x00, + 0x1b, 0x70, 0x1b, 0x48, 0x64, 0x05, 0x00, 0x60, + 0x07, 0x00, 0x11, 0x04, 0x08, 0x01, 0x00, 0x00, + 0xad, 0xea, 0x2e, 0x5d, 0x0a, 0x00, 0x64, 0x04, + 0x00, 0x64, 0x03, 0x00, 0xf0, 0xc2, 0x07, 0x61, + 0x07, 0x00, 0xea, 0x05, 0x61, 0x07, 0x00, 0x30, + 0xb7, 0x11, 0x65, 0x00, 0x00, 0x0e, 0x0b, 0x38, + 0x46, 0x00, 0x00, 0x00, 0x4b, 0x41, 0x00, 0x00, + 0x00, 0x0a, 0x4b, 0x6a, 0x00, 0x00, 0x00, 0x28, + 0x11, 0x04, 0x05, 0x01, 0x00, 0x00, 0xad, 0xea, + 0x3c, 0x64, 0x09, 0x00, 0xb5, 0xa5, 0xea, 0x19, + 0xb7, 0x11, 0x65, 0x00, 0x00, 0x0e, 0x0b, 0x38, + 0x46, 0x00, 0x00, 0x00, 0x4b, 0x41, 0x00, 0x00, + 0x00, 0x0a, 0x4b, 0x6a, 0x00, 0x00, 0x00, 0x28, + 0x61, 0x02, 0x00, 0x61, 0x03, 0x00, 0x1b, 0x11, + 0xaf, 0xeb, 0x04, 0x1b, 0x70, 0x1b, 0x1b, 0x64, + 0x06, 0x00, 0x61, 0x03, 0x00, 0x46, 0x1b, 0x70, + 0x1b, 0x48, 0xec, 0x27, 0x11, 0x04, 0x06, 0x01, + 0x00, 0x00, 0xad, 0xea, 0x1e, 0x61, 0x01, 0x00, + 0xb4, 0xa7, 0xea, 0x17, 0x5d, 0x0a, 0x00, 0x64, + 0x04, 0x00, 0x64, 0x03, 0x00, 0xf0, 0x0e, 0xde, + 0x11, 0x04, 0x07, 0x01, 0x00, 0x00, 0x21, 0x01, + 0x00, 0x30, 0x0e, 0x61, 0x03, 0x00, 0x90, 0x62, + 0x03, 0x00, 0x0e, 0xed, 0x29, 0xfe, 0x61, 0x01, + 0x00, 0xb4, 0xad, 0xea, 0x19, 0xb7, 0x11, 0x65, + 0x00, 0x00, 0x0e, 0x0b, 0x38, 0x46, 0x00, 0x00, + 0x00, 0x4b, 0x41, 0x00, 0x00, 0x00, 0x0a, 0x4b, + 0x6a, 0x00, 0x00, 0x00, 0x28, 0xb5, 0x11, 0x65, + 0x00, 0x00, 0x0e, 0x0b, 0x61, 0x02, 0x00, 0x4b, + 0x41, 0x00, 0x00, 0x00, 0x09, 0x4b, 0x6a, 0x00, + 0x00, 0x00, 0x28, 0x0c, 0x42, 0x03, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x06, 0x00, 0x06, 0x00, 0x88, + 0x01, 0x01, 0xe4, 0x03, 0x01, 0x00, 0x20, 0xfc, + 0x03, 0x18, 0x10, 0xa4, 0x03, 0x00, 0x02, 0xa8, + 0x03, 0x01, 0x02, 0xd4, 0x03, 0x03, 0x02, 0xe0, + 0x03, 0x05, 0x10, 0xe2, 0x03, 0x07, 0x10, 0x60, + 0x00, 0x00, 0x64, 0x00, 0x00, 0x11, 0xb4, 0xad, + 0xea, 0x09, 0xb7, 0x11, 0x65, 0x00, 0x00, 0x0e, + 0xec, 0x4b, 0x11, 0xb5, 0xad, 0xea, 0x09, 0xb6, + 0x11, 0x65, 0x00, 0x00, 0x0e, 0xec, 0x3e, 0x11, + 0xb6, 0xad, 0xea, 0x0c, 0xde, 0x11, 0x04, 0x03, + 0x01, 0x00, 0x00, 0x21, 0x01, 0x00, 0x30, 0x11, + 0xb7, 0xad, 0xea, 0x13, 0x0b, 0x38, 0x46, 0x00, + 0x00, 0x00, 0x4b, 0x41, 0x00, 0x00, 0x00, 0x0a, + 0x4b, 0x6a, 0x00, 0x00, 0x00, 0x28, 0xdf, 0x11, + 0x04, 0x09, 0x01, 0x00, 0x00, 0x41, 0x5d, 0x00, + 0x00, 0x00, 0x64, 0x00, 0x00, 0x24, 0x01, 0x00, + 0x21, 0x01, 0x00, 0x30, 0x0e, 0xe0, 0x64, 0x04, + 0x00, 0x64, 0x05, 0x00, 0xf0, 0xc9, 0x61, 0x00, + 0x00, 0xea, 0x05, 0x61, 0x00, 0x00, 0x30, 0xb7, + 0x11, 0x65, 0x00, 0x00, 0x0e, 0x0b, 0x38, 0x46, + 0x00, 0x00, 0x00, 0x4b, 0x41, 0x00, 0x00, 0x00, + 0x0a, 0x4b, 0x6a, 0x00, 0x00, 0x00, 0x28, 0x60, + 0x01, 0x00, 0x60, 0x00, 0x00, 0xd1, 0xc9, 0xd2, + 0x11, 0xf2, 0xea, 0x08, 0x0e, 0x38, 0x46, 0x00, + 0x00, 0x00, 0xda, 0xca, 0x60, 0x19, 0x00, 0x60, + 0x18, 0x00, 0x60, 0x09, 0x00, 0x60, 0x08, 0x00, + 0x60, 0x07, 0x00, 0x60, 0x06, 0x00, 0x60, 0x05, + 0x00, 0x60, 0x04, 0x00, 0x60, 0x03, 0x00, 0x60, + 0x02, 0x00, 0x5d, 0x04, 0x00, 0xd1, 0x04, 0x0a, + 0x01, 0x00, 0x00, 0xf0, 0x0e, 0xd2, 0x38, 0x46, + 0x00, 0x00, 0x00, 0xad, 0xea, 0x06, 0x0c, 0x07, + 0xd6, 0xec, 0x0c, 0x5d, 0x04, 0x00, 0xd2, 0x04, + 0x0b, 0x01, 0x00, 0x00, 0xf0, 0x0e, 0xd2, 0x40, + 0xf6, 0x00, 0x00, 0x00, 0xcb, 0x61, 0x02, 0x00, + 0x38, 0x46, 0x00, 0x00, 0x00, 0xad, 0xea, 0x0b, + 0x04, 0x08, 0x01, 0x00, 0x00, 0x11, 0x62, 0x02, + 0x00, 0x0e, 0x61, 0x02, 0x00, 0x04, 0x06, 0x01, + 0x00, 0x00, 0xad, 0x11, 0xeb, 0x18, 0x0e, 0x61, + 0x02, 0x00, 0x04, 0x05, 0x01, 0x00, 0x00, 0xad, + 0x11, 0xeb, 0x0b, 0x0e, 0x61, 0x02, 0x00, 0x04, + 0x08, 0x01, 0x00, 0x00, 0xad, 0x95, 0xea, 0x0c, + 0xdd, 0x11, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x21, + 0x01, 0x00, 0x30, 0x38, 0x46, 0x00, 0x00, 0x00, + 0xcc, 0x61, 0x02, 0x00, 0x04, 0x05, 0x01, 0x00, + 0x00, 0xad, 0xea, 0x24, 0xd2, 0x40, 0xf7, 0x00, + 0x00, 0x00, 0x11, 0x62, 0x03, 0x00, 0x0e, 0x61, + 0x03, 0x00, 0x38, 0x46, 0x00, 0x00, 0x00, 0xae, + 0xea, 0x0e, 0x5d, 0x04, 0x00, 0x61, 0x03, 0x00, + 0x04, 0x0d, 0x01, 0x00, 0x00, 0xf0, 0x0e, 0x26, + 0x00, 0x00, 0xc2, 0x04, 0x26, 0x00, 0x00, 0xc2, + 0x05, 0x26, 0x00, 0x00, 0xc2, 0x06, 0xb4, 0xc2, + 0x07, 0x38, 0x46, 0x00, 0x00, 0x00, 0xc2, 0x08, + 0xd1, 0x5d, 0x05, 0x00, 0x47, 0x24, 0x00, 0x00, + 0xc2, 0x09, 0x6b, 0xcc, 0x01, 0x00, 0x00, 0x60, + 0x0a, 0x00, 0x61, 0x09, 0x00, 0x40, 0x6b, 0x00, + 0x00, 0x00, 0xc2, 0x0a, 0x60, 0x0e, 0x00, 0x60, + 0x0d, 0x00, 0x60, 0x0b, 0x00, 0x06, 0xc2, 0x0b, + 0x6b, 0x14, 0x00, 0x00, 0x00, 0xdf, 0x61, 0x09, + 0x00, 0x61, 0x0a, 0x00, 0xf0, 0x11, 0x62, 0x0b, + 0x00, 0x0e, 0x0e, 0xec, 0x16, 0xc2, 0x0c, 0x6b, + 0x11, 0x00, 0x00, 0x00, 0x38, 0x46, 0x00, 0x00, + 0x00, 0x11, 0x62, 0x09, 0x00, 0x0e, 0xc1, 0x0c, + 0x30, 0x30, 0x61, 0x0b, 0x00, 0x40, 0x6a, 0x00, + 0x00, 0x00, 0xeb, 0x6f, 0x61, 0x0b, 0x00, 0x40, + 0x41, 0x00, 0x00, 0x00, 0xc2, 0x0d, 0x5d, 0x04, + 0x00, 0x61, 0x0d, 0x00, 0x04, 0x0e, 0x01, 0x00, + 0x00, 0xf0, 0x0e, 0x61, 0x0d, 0x00, 0x5d, 0x05, + 0x00, 0x46, 0xc2, 0x0e, 0x61, 0x0e, 0x00, 0xea, + 0x0e, 0xdf, 0x61, 0x0d, 0x00, 0x61, 0x0e, 0x00, + 0xf0, 0x11, 0x62, 0x0d, 0x00, 0x0e, 0x61, 0x05, + 0x00, 0x61, 0x07, 0x00, 0x1b, 0x11, 0xaf, 0xeb, + 0x04, 0x1b, 0x70, 0x1b, 0x1b, 0x61, 0x0d, 0x00, + 0x1b, 0x70, 0x1b, 0x48, 0x61, 0x06, 0x00, 0x61, + 0x07, 0x00, 0x1b, 0x11, 0xaf, 0xeb, 0x04, 0x1b, + 0x70, 0x1b, 0x1b, 0x61, 0x0d, 0x00, 0x40, 0x6b, + 0x00, 0x00, 0x00, 0x1b, 0x70, 0x1b, 0x48, 0x61, + 0x07, 0x00, 0x90, 0x62, 0x07, 0x00, 0x0e, 0xed, + 0x54, 0xff, 0x38, 0x46, 0x00, 0x00, 0x00, 0x11, + 0x62, 0x09, 0x00, 0x0e, 0x61, 0x03, 0x00, 0x68, + 0xfc, 0x00, 0x00, 0x00, 0x60, 0x15, 0x00, 0x60, + 0x11, 0x00, 0x60, 0x10, 0x00, 0x60, 0x0f, 0x00, + 0x61, 0x03, 0x00, 0x5d, 0x05, 0x00, 0x47, 0x24, + 0x00, 0x00, 0x11, 0x62, 0x08, 0x00, 0x0e, 0x61, + 0x08, 0x00, 0x40, 0x6b, 0x00, 0x00, 0x00, 0xc2, + 0x0f, 0xb4, 0xc2, 0x10, 0x09, 0xc2, 0x11, 0x61, + 0x10, 0x00, 0x61, 0x07, 0x00, 0xa5, 0xea, 0x70, + 0x60, 0x12, 0x00, 0x06, 0xc2, 0x12, 0x6b, 0x2e, + 0x00, 0x00, 0x00, 0x60, 0x13, 0x00, 0xdf, 0x61, + 0x08, 0x00, 0x61, 0x0f, 0x00, 0xf0, 0xc2, 0x13, + 0x61, 0x13, 0x00, 0x40, 0x6a, 0x00, 0x00, 0x00, + 0x11, 0x62, 0x11, 0x00, 0x0e, 0x61, 0x13, 0x00, + 0x40, 0x41, 0x00, 0x00, 0x00, 0x11, 0x62, 0x12, + 0x00, 0x0e, 0x0e, 0xec, 0x16, 0xc2, 0x14, 0x6b, + 0x11, 0x00, 0x00, 0x00, 0x38, 0x46, 0x00, 0x00, + 0x00, 0x11, 0x62, 0x08, 0x00, 0x0e, 0xc1, 0x14, + 0x30, 0x30, 0x61, 0x11, 0x00, 0xeb, 0x21, 0x61, + 0x04, 0x00, 0x61, 0x10, 0x00, 0x1b, 0x11, 0xaf, + 0xeb, 0x04, 0x1b, 0x70, 0x1b, 0x1b, 0x61, 0x12, + 0x00, 0x1b, 0x70, 0x1b, 0x48, 0x61, 0x10, 0x00, + 0x90, 0x62, 0x10, 0x00, 0x0e, 0xec, 0x89, 0x61, + 0x08, 0x00, 0xc2, 0x15, 0x38, 0x46, 0x00, 0x00, + 0x00, 0x11, 0x62, 0x08, 0x00, 0x0e, 0x61, 0x11, + 0x00, 0x95, 0xea, 0x16, 0x60, 0x16, 0x00, 0x5d, + 0x06, 0x00, 0x61, 0x15, 0x00, 0xef, 0xc2, 0x16, + 0x61, 0x16, 0x00, 0xea, 0x05, 0x61, 0x16, 0x00, + 0x30, 0x61, 0x10, 0x00, 0x61, 0x07, 0x00, 0xa5, + 0xea, 0x23, 0x61, 0x04, 0x00, 0x61, 0x10, 0x00, + 0x1b, 0x11, 0xaf, 0xeb, 0x04, 0x1b, 0x70, 0x1b, + 0x1b, 0x38, 0x46, 0x00, 0x00, 0x00, 0x1b, 0x70, + 0x1b, 0x48, 0x61, 0x10, 0x00, 0x90, 0x62, 0x10, + 0x00, 0x0e, 0xec, 0xd6, 0x0e, 0xec, 0x25, 0xc2, + 0x17, 0x6b, 0x20, 0x00, 0x00, 0x00, 0xe0, 0x61, + 0x05, 0x00, 0x61, 0x07, 0x00, 0xf0, 0x0e, 0x5d, + 0x06, 0x00, 0x61, 0x09, 0x00, 0xef, 0x0e, 0x5d, + 0x06, 0x00, 0x61, 0x08, 0x00, 0xef, 0x0e, 0xc1, + 0x17, 0x30, 0x30, 0xb4, 0xc2, 0x18, 0x61, 0x07, + 0x00, 0xc2, 0x19, 0x0b, 0x5d, 0x07, 0x00, 0x4e, + 0xbf, 0x00, 0x53, 0x6b, 0x00, 0x00, 0x00, 0x04, + 0xbf, 0x01, 0x53, 0x06, 0x00, 0x00, 0x00, 0x04, + 0x28, 0xbf, 0x00, 0xc9, 0xbf, 0x01, 0xca, 0xbf, + 0x02, 0xcb, 0xbf, 0x03, 0x28, 0xbf, 0x00, 0xcd, + 0x28, +}; + + + +// like Function.prototype.call but monkey patch-proof +static JSValue js_call_function(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_Call(ctx, argv[1], argv[0], argc-2, argv+2); +} + +// returns enumerable and non-enumerable strings *and* symbols +static JSValue js_getOwnPropertyKeys(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int flags = JS_GPN_STRING_MASK|JS_GPN_SYMBOL_MASK; + return JS_GetOwnPropertyNames2(ctx, argv[0], flags, JS_ITERATOR_KIND_KEY); +} + +static JSValue js_hasOwnEnumProperty(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSPropertyDescriptor d; + JSObject *p; + JSAtom key; + int flags, res; + + if (JS_TAG_OBJECT != JS_VALUE_GET_TAG(argv[0])) + return JS_ThrowTypeErrorNotAnObject(ctx); + p = JS_VALUE_GET_OBJ(argv[0]); + key = JS_ValueToAtomInternal(ctx, argv[1], JS_TO_STRING_NO_SIDE_EFFECTS); + if (key == JS_ATOM_NULL) + return JS_EXCEPTION; + res = JS_GetOwnPropertyInternal(ctx, &d, p, key); + JS_FreeAtom(ctx, key); + if (res < 0) + return JS_EXCEPTION; + flags = 0; + if (res > 0) { + flags = d.flags; + js_free_desc(ctx, &d); + } + if (flags & JS_PROP_ENUMERABLE) + return JS_TRUE; + return JS_FALSE; +} + +// note: takes ownership of |argv| +static JSValue js_bytecode_eval(JSContext *ctx, const uint8_t *bytecode, + size_t len, int argc, JSValue *argv) +{ + JSValue obj, fun, result; + int i; + + obj = JS_ReadObject(ctx, bytecode, len, JS_READ_OBJ_BYTECODE); + if (JS_IsException(obj)) + return JS_EXCEPTION; + fun = JS_EvalFunction(ctx, obj); + if (JS_IsException(fun)) + return JS_EXCEPTION; + assert(JS_IsFunction(ctx, fun)); + result = JS_Call(ctx, fun, JS_UNDEFINED, argc, vc(argv)); + for (i = 0; i < argc; i++) + JS_FreeValue(ctx, argv[i]); + JS_FreeValue(ctx, fun); + if (JS_SetPrototypeInternal(ctx, result, ctx->function_proto, + /*throw_flag*/true) < 0) { + JS_FreeValue(ctx, result); + return JS_EXCEPTION; + } + return result; +} + +static JSValue js_bytecode_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, + void *opaque) +{ + switch ((uintptr_t)opaque) { + default: + abort(); + case JS_BUILTIN_ARRAY_FROMASYNC: + { + JSValue argv[] = { + JS_NewCFunction(ctx, js_array_constructor, "Array", 0), + JS_NewCFunctionMagic(ctx, js_error_constructor, "TypeError", + 1, JS_CFUNC_constructor_or_func_magic, + JS_TYPE_ERROR), + JS_AtomToValue(ctx, JS_ATOM_Symbol_asyncIterator), + JS_NewCFunctionMagic(ctx, js_object_defineProperty, + "Object.defineProperty", 3, + JS_CFUNC_generic_magic, 0), + JS_AtomToValue(ctx, JS_ATOM_Symbol_iterator), + }; + return js_bytecode_eval(ctx, qjsc_builtin_array_fromasync, + sizeof(qjsc_builtin_array_fromasync), + countof(argv), argv); + } + case JS_BUILTIN_ITERATOR_ZIP: + { + JSValue argv[] = { + js_dup(ctx->class_proto[JS_CLASS_ITERATOR_HELPER]), + JS_NewCFunctionMagic(ctx, js_error_constructor, "InternalError", + 1, JS_CFUNC_constructor_or_func_magic, + JS_INTERNAL_ERROR), + JS_NewCFunctionMagic(ctx, js_error_constructor, "TypeError", + 1, JS_CFUNC_constructor_or_func_magic, + JS_TYPE_ERROR), + JS_NewCFunction(ctx, js_call_function, "call", 2), + JS_AtomToValue(ctx, JS_ATOM_Symbol_iterator), + }; + JSValue result = js_bytecode_eval(ctx, qjsc_builtin_iterator_zip, + sizeof(qjsc_builtin_iterator_zip), + countof(argv), argv); + JS_SetConstructorBit(ctx, result, false); + return result; + } + case JS_BUILTIN_ITERATOR_ZIP_KEYED: + { + JSValue argv[] = { + js_dup(ctx->class_proto[JS_CLASS_ITERATOR_HELPER]), + JS_NewCFunctionMagic(ctx, js_error_constructor, "InternalError", + 1, JS_CFUNC_constructor_or_func_magic, + JS_INTERNAL_ERROR), + JS_NewCFunctionMagic(ctx, js_error_constructor, "TypeError", + 1, JS_CFUNC_constructor_or_func_magic, + JS_TYPE_ERROR), + JS_NewCFunction(ctx, js_call_function, "call", 2), + JS_NewCFunction(ctx, js_hasOwnEnumProperty, + "hasOwnEnumProperty", 2), + JS_NewCFunction(ctx, js_getOwnPropertyKeys, + "getOwnPropertyKeys", 1), + JS_AtomToValue(ctx, JS_ATOM_Symbol_iterator), + }; + JSValue result = js_bytecode_eval(ctx, qjsc_builtin_iterator_zip_keyed, + sizeof(qjsc_builtin_iterator_zip_keyed), + countof(argv), argv); + JS_SetConstructorBit(ctx, result, false); + return result; + } + } + return JS_UNDEFINED; +} + +/* return the value associated to the autoinit property or an exception */ +typedef JSValue JSAutoInitFunc(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); + +static JSAutoInitFunc *const js_autoinit_func_table[] = { + js_instantiate_prototype, /* JS_AUTOINIT_ID_PROTOTYPE */ + js_module_ns_autoinit, /* JS_AUTOINIT_ID_MODULE_NS */ + JS_InstantiateFunctionListItem2, /* JS_AUTOINIT_ID_PROP */ + js_bytecode_autoinit, /* JS_AUTOINIT_ID_BYTECODE */ +}; + +/* warning: 'prs' is reallocated after it */ +static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop, + JSProperty *pr, JSShapeProperty *prs) +{ + JSValue val; + JSContext *realm; + JSAutoInitFunc *func; + + if (js_shape_prepare_update(ctx, p, &prs)) + return -1; + + realm = js_autoinit_get_realm(pr); + func = js_autoinit_func_table[js_autoinit_get_id(pr)]; + /* 'func' shall not modify the object properties 'pr' */ + val = func(realm, p, prop, pr->u.init.opaque); + js_autoinit_free(ctx->rt, pr); + prs->flags &= ~JS_PROP_TMASK; + pr->u.value = JS_UNDEFINED; + if (JS_IsException(val)) + return -1; + pr->u.value = val; + return 0; +} + +static JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, + JSAtom prop, JSValueConst this_obj, + bool throw_ref_error) +{ + JSObject *p; + JSProperty *pr; + JSShapeProperty *prs; + uint32_t tag; + + tag = JS_VALUE_GET_TAG(obj); + if (unlikely(tag != JS_TAG_OBJECT)) { + switch(tag) { + case JS_TAG_NULL: + return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of null", prop); + case JS_TAG_UNDEFINED: + return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of undefined", prop); + case JS_TAG_EXCEPTION: + return JS_EXCEPTION; + case JS_TAG_STRING: + { + JSString *p1 = JS_VALUE_GET_STRING(obj); + if (__JS_AtomIsTaggedInt(prop)) { + uint32_t idx, ch; + idx = __JS_AtomToUInt32(prop); + if (idx < p1->len) { + ch = string_get(p1, idx); + return js_new_string_char(ctx, ch); + } + } else if (prop == JS_ATOM_length) { + return js_int32(p1->len); + } + } + break; + case JS_TAG_STRING_ROPE: + { + JSStringRope *r = JS_VALUE_GET_STRING_ROPE(obj); + if (__JS_AtomIsTaggedInt(prop)) { + uint32_t idx, ch; + idx = __JS_AtomToUInt32(prop); + if (idx < r->len) { + ch = string_rope_get(obj, idx); + return js_new_string_char(ctx, ch); + } + } else if (prop == JS_ATOM_length) { + return js_int32(r->len); + } + } + break; + default: + break; + } + /* cannot raise an exception */ + p = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj)); + if (!p) + return JS_UNDEFINED; + } else { + p = JS_VALUE_GET_OBJ(obj); + } + + for(;;) { + prs = find_own_property(&pr, p, prop); + if (prs) { + /* found */ + if (unlikely(prs->flags & JS_PROP_TMASK)) { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + if (unlikely(!pr->u.getset.getter)) { + return JS_UNDEFINED; + } else { + JSValue func = JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter); + /* Note: the field could be removed in the getter */ + func = js_dup(func); + return JS_CallFree(ctx, func, this_obj, 0, NULL); + } + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + JSValue val = *pr->u.var_ref->pvalue; + if (unlikely(JS_IsUninitialized(val))) + return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return js_dup(val); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry */ + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) + return JS_EXCEPTION; + continue; + } + } else { + return js_dup(pr->u.value); + } + } + if (unlikely(p->is_exotic)) { + /* exotic behaviors */ + if (p->fast_array) { + if (__JS_AtomIsTaggedInt(prop)) { + uint32_t idx = __JS_AtomToUInt32(prop); + if (idx < p->u.array.count) { + /* we avoid duplicating the code */ + return JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx); + } else if (is_typed_array(p->class_id)) { + return JS_UNDEFINED; + } + } else if (is_typed_array(p->class_id)) { + int ret; + ret = JS_AtomIsNumericIndex(ctx, prop); + if (ret != 0) { + if (ret < 0) + return JS_EXCEPTION; + return JS_UNDEFINED; + } + } + } else { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em) { + if (em->get_property) { + JSValue obj1, retval; + /* XXX: should pass throw_ref_error */ + /* Note: if 'p' is a prototype, it can be + freed in the called function */ + obj1 = js_dup(JS_MKPTR(JS_TAG_OBJECT, p)); + retval = em->get_property(ctx, obj1, prop, this_obj); + JS_FreeValue(ctx, obj1); + return retval; + } + if (em->get_own_property) { + JSPropertyDescriptor desc; + int ret; + JSValue obj1; + + /* Note: if 'p' is a prototype, it can be + freed in the called function */ + obj1 = js_dup(JS_MKPTR(JS_TAG_OBJECT, p)); + ret = em->get_own_property(ctx, &desc, obj1, prop); + JS_FreeValue(ctx, obj1); + if (ret < 0) + return JS_EXCEPTION; + if (ret) { + if (desc.flags & JS_PROP_GETSET) { + JS_FreeValue(ctx, desc.setter); + return JS_CallFree(ctx, desc.getter, this_obj, 0, NULL); + } else { + return desc.value; + } + } + } + } + } + } + p = p->shape->proto; + if (!p) + break; + } + if (unlikely(throw_ref_error)) { + return JS_ThrowReferenceErrorNotDefined(ctx, prop); + } else { + return JS_UNDEFINED; + } +} + +JSValue JS_GetProperty(JSContext *ctx, JSValueConst this_obj, JSAtom prop) +{ + return JS_GetPropertyInternal(ctx, this_obj, prop, this_obj, false); +} + +static JSValue JS_ThrowTypeErrorPrivateNotFound(JSContext *ctx, JSAtom atom) +{ + return JS_ThrowTypeErrorAtom(ctx, "private class field '%s' does not exist", + atom); +} + +/* Private fields can be added even on non extensible objects or + Proxies */ +static int JS_DefinePrivateField(JSContext *ctx, JSValueConst obj, + JSValue name, JSValue val) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + JSAtom prop; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { + JS_ThrowTypeErrorNotAnObject(ctx); + goto fail; + } + /* safety check */ + if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) { + JS_ThrowTypeErrorNotASymbol(ctx); + goto fail; + } + prop = js_symbol_to_atom(ctx, name); + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, prop); + if (prs) { + JS_ThrowTypeErrorAtom(ctx, "private class field '%s' already exists", + prop); + goto fail; + } + pr = add_property(ctx, p, prop, JS_PROP_C_W_E); + if (unlikely(!pr)) { + fail: + JS_FreeValue(ctx, val); + return -1; + } + pr->u.value = val; + return 0; +} + +static JSValue JS_GetPrivateField(JSContext *ctx, JSValueConst obj, + JSValueConst name) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + JSAtom prop; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + return JS_ThrowTypeErrorNotAnObject(ctx); + /* safety check */ + if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) + return JS_ThrowTypeErrorNotASymbol(ctx); + prop = js_symbol_to_atom(ctx, name); + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, prop); + if (!prs) { + JS_ThrowTypeErrorPrivateNotFound(ctx, prop); + return JS_EXCEPTION; + } + return js_dup(pr->u.value); +} + +static int JS_SetPrivateField(JSContext *ctx, JSValueConst obj, + JSValueConst name, JSValue val) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + JSAtom prop; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { + JS_ThrowTypeErrorNotAnObject(ctx); + goto fail; + } + /* safety check */ + if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) { + JS_ThrowTypeErrorNotASymbol(ctx); + goto fail; + } + prop = js_symbol_to_atom(ctx, name); + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, prop); + if (!prs) { + JS_ThrowTypeErrorPrivateNotFound(ctx, prop); + fail: + JS_FreeValue(ctx, val); + return -1; + } + set_value(ctx, &pr->u.value, val); + return 0; +} + +/* add a private brand field to 'home_obj' if not already present and + if obj is != null add a private brand to it */ +static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj) +{ + JSObject *p, *p1; + JSShapeProperty *prs; + JSProperty *pr; + JSValue brand; + JSAtom brand_atom; + + if (unlikely(JS_VALUE_GET_TAG(home_obj) != JS_TAG_OBJECT)) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + p = JS_VALUE_GET_OBJ(home_obj); + prs = find_own_property(&pr, p, JS_ATOM_Private_brand); + if (!prs) { + /* if the brand is not present, add it */ + brand = JS_NewSymbolFromAtom(ctx, JS_ATOM_brand, JS_ATOM_TYPE_PRIVATE); + if (JS_IsException(brand)) + return -1; + pr = add_property(ctx, p, JS_ATOM_Private_brand, JS_PROP_C_W_E); + if (!pr) { + JS_FreeValue(ctx, brand); + return -1; + } + pr->u.value = js_dup(brand); + } else { + brand = js_dup(pr->u.value); + } + brand_atom = js_symbol_to_atom(ctx, brand); + + if (JS_IsObject(obj)) { + p1 = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p1, brand_atom); + if (unlikely(prs)) { + JS_FreeAtom(ctx, brand_atom); + JS_ThrowTypeError(ctx, "private method is already present"); + return -1; + } + pr = add_property(ctx, p1, brand_atom, JS_PROP_C_W_E); + JS_FreeAtom(ctx, brand_atom); + if (!pr) + return -1; + pr->u.value = JS_UNDEFINED; + } else { + JS_FreeAtom(ctx, brand_atom); + } + + return 0; +} + +/* return a boolean telling if the brand of the home object of 'func' + is present on 'obj' or -1 in case of exception */ +static int JS_CheckBrand(JSContext *ctx, JSValue obj, JSValue func) +{ + JSObject *p, *p1, *home_obj; + JSShapeProperty *prs; + JSProperty *pr; + JSValue brand; + + /* get the home object of 'func' */ + if (unlikely(JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)) + goto not_obj; + p1 = JS_VALUE_GET_OBJ(func); + if (!js_class_has_bytecode(p1->class_id)) + goto not_obj; + home_obj = p1->u.func.home_object; + if (!home_obj) + goto not_obj; + prs = find_own_property(&pr, home_obj, JS_ATOM_Private_brand); + if (!prs) { + JS_ThrowTypeError(ctx, "expecting private field"); + return -1; + } + brand = pr->u.value; + /* safety check */ + if (unlikely(JS_VALUE_GET_TAG(brand) != JS_TAG_SYMBOL)) + goto not_obj; + + /* get the brand array of 'obj' */ + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { + not_obj: + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, brand)); + return (prs != NULL); +} + +static uint32_t js_string_obj_get_length(JSContext *ctx, JSValueConst obj) +{ + JSObject *p; + JSString *p1; + uint32_t len = 0; + + /* This is a class exotic method: obj class_id is JS_CLASS_STRING */ + p = JS_VALUE_GET_OBJ(obj); + if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) { + p1 = JS_VALUE_GET_STRING(p->u.object_data); + len = p1->len; + } + return len; +} + +static int num_keys_cmp(const void *p1, const void *p2, void *opaque) +{ + JSContext *ctx = opaque; + JSAtom atom1 = ((const JSPropertyEnum *)p1)->atom; + JSAtom atom2 = ((const JSPropertyEnum *)p2)->atom; + uint32_t v1, v2; + bool atom1_is_integer, atom2_is_integer; + + atom1_is_integer = JS_AtomIsArrayIndex(ctx, &v1, atom1); + atom2_is_integer = JS_AtomIsArrayIndex(ctx, &v2, atom2); + assert(atom1_is_integer && atom2_is_integer); + if (v1 < v2) + return -1; + else if (v1 == v2) + return 0; + else + return 1; +} + +static void js_free_prop_enum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len) +{ + uint32_t i; + if (tab) { + for(i = 0; i < len; i++) + JS_FreeAtom(ctx, tab[i].atom); + js_free(ctx, tab); + } +} + +/* return < 0 in case if exception, 0 if OK. ptab and its atoms must + be freed by the user. */ +static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, + JSPropertyEnum **ptab, + uint32_t *plen, + JSObject *p, int flags) +{ + int i, j; + JSShape *sh; + JSShapeProperty *prs; + JSPropertyEnum *tab_atom, *tab_exotic; + JSAtom atom; + uint32_t num_keys_count, str_keys_count, sym_keys_count, atom_count; + uint32_t num_index, str_index, sym_index, exotic_count, exotic_keys_count; + bool is_enumerable, num_sorted; + uint32_t num_key; + JSAtomKindEnum kind; + + /* clear pointer for consistency in case of failure */ + *ptab = NULL; + *plen = 0; + + /* compute the number of returned properties */ + num_keys_count = 0; + str_keys_count = 0; + sym_keys_count = 0; + exotic_keys_count = 0; + exotic_count = 0; + tab_exotic = NULL; + sh = p->shape; + for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { + atom = prs->atom; + if (atom != JS_ATOM_NULL) { + is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0); + kind = JS_AtomGetKind(ctx, atom); + if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && + ((flags >> kind) & 1) != 0) { + /* need to raise an exception in case of the module + name space (implicit GetOwnProperty) */ + if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) && + (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY))) { + JSVarRef *var_ref = p->prop[i].u.var_ref; + if (unlikely(JS_IsUninitialized(*var_ref->pvalue))) { + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + } + if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) { + num_keys_count++; + } else if (kind == JS_ATOM_KIND_STRING) { + str_keys_count++; + } else { + sym_keys_count++; + } + } + } + } + + if (p->is_exotic) { + if (p->fast_array) { + if (flags & JS_GPN_STRING_MASK) { + num_keys_count += p->u.array.count; + } + } else if (p->class_id == JS_CLASS_STRING) { + if (flags & JS_GPN_STRING_MASK) { + num_keys_count += js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + } + } else { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em && em->get_own_property_names) { + if (em->get_own_property_names(ctx, &tab_exotic, &exotic_count, + JS_MKPTR(JS_TAG_OBJECT, p))) + return -1; + for(i = 0; i < exotic_count; i++) { + atom = tab_exotic[i].atom; + kind = JS_AtomGetKind(ctx, atom); + if (((flags >> kind) & 1) != 0) { + is_enumerable = false; + if (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY)) { + JSPropertyDescriptor desc; + int res; + /* set the "is_enumerable" field if necessary */ + res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom); + if (res < 0) { + js_free_prop_enum(ctx, tab_exotic, exotic_count); + return -1; + } + if (res) { + is_enumerable = + ((desc.flags & JS_PROP_ENUMERABLE) != 0); + js_free_desc(ctx, &desc); + } + tab_exotic[i].is_enumerable = is_enumerable; + } + if (!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) { + exotic_keys_count++; + } + } + } + } + } + } + + /* fill them */ + + atom_count = num_keys_count + str_keys_count + sym_keys_count + exotic_keys_count; + /* avoid allocating 0 bytes */ + tab_atom = js_malloc(ctx, sizeof(tab_atom[0]) * max_int(atom_count, 1)); + if (!tab_atom) { + js_free_prop_enum(ctx, tab_exotic, exotic_count); + return -1; + } + + num_index = 0; + str_index = num_keys_count; + sym_index = str_index + str_keys_count; + + num_sorted = true; + sh = p->shape; + for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { + atom = prs->atom; + if (atom != JS_ATOM_NULL) { + is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0); + kind = JS_AtomGetKind(ctx, atom); + if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && + ((flags >> kind) & 1) != 0) { + if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) { + j = num_index++; + num_sorted = false; + } else if (kind == JS_ATOM_KIND_STRING) { + j = str_index++; + } else { + j = sym_index++; + } + tab_atom[j].atom = JS_DupAtom(ctx, atom); + tab_atom[j].is_enumerable = is_enumerable; + } + } + } + + if (p->is_exotic) { + int len; + if (p->fast_array) { + if (flags & JS_GPN_STRING_MASK) { + len = p->u.array.count; + goto add_array_keys; + } + } else if (p->class_id == JS_CLASS_STRING) { + if (flags & JS_GPN_STRING_MASK) { + len = js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + add_array_keys: + for(i = 0; i < len; i++) { + tab_atom[num_index].atom = __JS_AtomFromUInt32(i); + if (tab_atom[num_index].atom == JS_ATOM_NULL) { + js_free_prop_enum(ctx, tab_atom, num_index); + return -1; + } + tab_atom[num_index].is_enumerable = true; + num_index++; + } + } + } else { + /* Note: exotic keys are not reordered and comes after the object own properties. */ + for(i = 0; i < exotic_count; i++) { + atom = tab_exotic[i].atom; + is_enumerable = tab_exotic[i].is_enumerable; + kind = JS_AtomGetKind(ctx, atom); + if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && + ((flags >> kind) & 1) != 0) { + tab_atom[sym_index].atom = atom; + tab_atom[sym_index].is_enumerable = is_enumerable; + sym_index++; + } else { + JS_FreeAtom(ctx, atom); + } + } + js_free(ctx, tab_exotic); + } + } + + assert(num_index == num_keys_count); + assert(str_index == num_keys_count + str_keys_count); + assert(sym_index == atom_count); + + if (num_keys_count != 0 && !num_sorted) { + rqsort(tab_atom, num_keys_count, sizeof(tab_atom[0]), num_keys_cmp, + ctx); + } + *ptab = tab_atom; + *plen = atom_count; + return 0; +} + +int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, + uint32_t *plen, JSValueConst obj, int flags) +{ + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen, + JS_VALUE_GET_OBJ(obj), flags); +} + +/* Return -1 if exception, + false if the property does not exist, true if it exists. If true is + returned, the property descriptor 'desc' is filled present. */ +static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc, + JSObject *p, JSAtom prop) +{ + JSShapeProperty *prs; + JSProperty *pr; + +retry: + prs = find_own_property(&pr, p, prop); + if (prs) { + if (desc) { + desc->flags = prs->flags & JS_PROP_C_W_E; + desc->getter = JS_UNDEFINED; + desc->setter = JS_UNDEFINED; + desc->value = JS_UNDEFINED; + if (unlikely(prs->flags & JS_PROP_TMASK)) { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + desc->flags |= JS_PROP_GETSET; + if (pr->u.getset.getter) + desc->getter = js_dup(JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); + if (pr->u.getset.setter) + desc->setter = js_dup(JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + JSValue val = *pr->u.var_ref->pvalue; + if (unlikely(JS_IsUninitialized(val))) { + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + desc->value = js_dup(val); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry */ + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) + return -1; + goto retry; + } + } else { + desc->value = js_dup(pr->u.value); + } + } else { + /* for consistency, send the exception even if desc is NULL */ + if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF)) { + if (unlikely(JS_IsUninitialized(*pr->u.var_ref->pvalue))) { + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* nothing to do: delay instantiation until actual value and/or attributes are read */ + } + } + return true; + } + if (p->is_exotic) { + if (p->fast_array) { + /* specific case for fast arrays */ + if (__JS_AtomIsTaggedInt(prop)) { + uint32_t idx; + idx = __JS_AtomToUInt32(prop); + if (idx < p->u.array.count) { + if (desc) { + desc->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE | + JS_PROP_CONFIGURABLE; + if (is_typed_array(p->class_id) && typed_array_is_immutable(p)) { + desc->flags &= ~JS_PROP_WRITABLE; + desc->flags &= ~JS_PROP_CONFIGURABLE; + } + desc->getter = JS_UNDEFINED; + desc->setter = JS_UNDEFINED; + desc->value = JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx); + } + return true; + } + } + } else { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em && em->get_own_property) { + return em->get_own_property(ctx, desc, + JS_MKPTR(JS_TAG_OBJECT, p), prop); + } + } + } + return false; +} + +int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc, + JSValueConst obj, JSAtom prop) +{ + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + return JS_GetOwnPropertyInternal(ctx, desc, JS_VALUE_GET_OBJ(obj), prop); +} + +void JS_FreePropertyEnum(JSContext *ctx, JSPropertyEnum *tab, + uint32_t len) +{ + js_free_prop_enum(ctx, tab, len); +} + +/* return -1 if exception (Proxy object only) or true/false */ +int JS_IsExtensible(JSContext *ctx, JSValueConst obj) +{ + JSObject *p; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + return false; + p = JS_VALUE_GET_OBJ(obj); + if (unlikely(p->class_id == JS_CLASS_PROXY)) + return js_proxy_isExtensible(ctx, obj); + else + return p->extensible; +} + +/* return -1 if exception (Proxy object only) or true/false */ +int JS_PreventExtensions(JSContext *ctx, JSValueConst obj) +{ + JSObject *p; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + return false; + p = JS_VALUE_GET_OBJ(obj); + if (unlikely(p->class_id == JS_CLASS_PROXY)) + return js_proxy_preventExtensions(ctx, obj); + p->extensible = false; + return true; +} + +/* return -1 if exception otherwise true or false */ +int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop) +{ + JSObject *p; + int ret; + JSValue obj1; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + return false; + p = JS_VALUE_GET_OBJ(obj); + for(;;) { + if (p->is_exotic) { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em && em->has_property) { + /* has_property can free the prototype */ + obj1 = js_dup(JS_MKPTR(JS_TAG_OBJECT, p)); + ret = em->has_property(ctx, obj1, prop); + JS_FreeValue(ctx, obj1); + return ret; + } + } + /* JS_GetOwnPropertyInternal can free the prototype */ + js_dup(JS_MKPTR(JS_TAG_OBJECT, p)); + ret = JS_GetOwnPropertyInternal(ctx, NULL, p, prop); + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + if (ret != 0) + return ret; + if (is_typed_array(p->class_id)) { + ret = JS_AtomIsNumericIndex(ctx, prop); + if (ret != 0) { + if (ret < 0) + return -1; + return false; + } + } + p = p->shape->proto; + if (!p) + break; + } + return false; +} + +/* val must be a symbol */ +static JSAtom js_symbol_to_atom(JSContext *ctx, JSValueConst val) +{ + JSAtomStruct *p = JS_VALUE_GET_PTR(val); + return js_get_atom_index(ctx->rt, p); +} + +/* return JS_ATOM_NULL in case of exception */ +static JSAtom JS_ValueToAtomInternal(JSContext *ctx, JSValueConst val, + int flags) +{ + JSAtom atom; + uint32_t tag; + tag = JS_VALUE_GET_TAG(val); + if (tag == JS_TAG_INT && + (uint32_t)JS_VALUE_GET_INT(val) <= JS_ATOM_MAX_INT) { + /* fast path for integer values */ + atom = __JS_AtomFromUInt32(JS_VALUE_GET_INT(val)); + } else if (tag == JS_TAG_SYMBOL) { + JSAtomStruct *p = JS_VALUE_GET_PTR(val); + atom = JS_DupAtom(ctx, js_get_atom_index(ctx->rt, p)); + } else { + JSValue str; + str = JS_ToPropertyKeyInternal(ctx, val, flags); + if (JS_IsException(str)) + return JS_ATOM_NULL; + if (JS_VALUE_GET_TAG(str) == JS_TAG_SYMBOL) { + atom = js_symbol_to_atom(ctx, str); + } else { + atom = JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(str)); + } + } + return atom; +} + +JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val) +{ + return JS_ValueToAtomInternal(ctx, val, /*flags*/0); +} + +static bool js_get_fast_array_element(JSContext *ctx, JSObject *p, + uint32_t idx, JSValue *pval) +{ + switch(p->class_id) { + case JS_CLASS_ARRAY: + case JS_CLASS_ARGUMENTS: + if (unlikely(idx >= p->u.array.count)) return false; + *pval = js_dup(p->u.array.u.values[idx]); + return true; + case JS_CLASS_MAPPED_ARGUMENTS: + if (unlikely(idx >= p->u.array.count)) return false; + *pval = js_dup(*p->u.array.u.var_refs[idx]->pvalue); + return true; + case JS_CLASS_INT8_ARRAY: + if (unlikely(idx >= p->u.array.count)) return false; + *pval = js_int32(p->u.array.u.int8_ptr[idx]); + return true; + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_UINT8_ARRAY: + if (unlikely(idx >= p->u.array.count)) return false; + *pval = js_int32(p->u.array.u.uint8_ptr[idx]); + return true; + case JS_CLASS_INT16_ARRAY: + if (unlikely(idx >= p->u.array.count)) return false; + *pval = js_int32(p->u.array.u.int16_ptr[idx]); + return true; + case JS_CLASS_UINT16_ARRAY: + if (unlikely(idx >= p->u.array.count)) return false; + *pval = js_int32(p->u.array.u.uint16_ptr[idx]); + return true; + case JS_CLASS_INT32_ARRAY: + if (unlikely(idx >= p->u.array.count)) return false; + *pval = js_int32(p->u.array.u.int32_ptr[idx]); + return true; + case JS_CLASS_UINT32_ARRAY: + if (unlikely(idx >= p->u.array.count)) return false; + *pval = js_uint32(p->u.array.u.uint32_ptr[idx]); + return true; + case JS_CLASS_BIG_INT64_ARRAY: + if (unlikely(idx >= p->u.array.count)) return false; + *pval = JS_NewBigInt64(ctx, p->u.array.u.int64_ptr[idx]); + return true; + case JS_CLASS_BIG_UINT64_ARRAY: + if (unlikely(idx >= p->u.array.count)) return false; + *pval = JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]); + return true; + case JS_CLASS_FLOAT16_ARRAY: + if (unlikely(idx >= p->u.array.count)) return false; + *pval = js_float64(fromfp16(p->u.array.u.fp16_ptr[idx])); + return true; + case JS_CLASS_FLOAT32_ARRAY: + if (unlikely(idx >= p->u.array.count)) return false; + *pval = js_float64(p->u.array.u.float_ptr[idx]); + return true; + case JS_CLASS_FLOAT64_ARRAY: + if (unlikely(idx >= p->u.array.count)) return false; + *pval = js_float64(p->u.array.u.double_ptr[idx]); + return true; + default: + return false; + } +} + +static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj, + JSValue prop) +{ + JSAtom atom; + JSValue ret; + uint32_t tag; + + tag = JS_VALUE_GET_TAG(this_obj); + if (likely(tag == JS_TAG_OBJECT)) { + if (JS_VALUE_GET_TAG(prop) == JS_TAG_INT) { + JSObject *p = JS_VALUE_GET_OBJ(this_obj); + uint32_t idx = JS_VALUE_GET_INT(prop); + JSValue val; + /* fast path for array and typed array access */ + if (js_get_fast_array_element(ctx, p, idx, &val)) + return val; + } + } else if (unlikely(tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED)) { + // per spec: not allowed to call ToPropertyKey before ToObject + // so we must ensure to not invoke JS anything that's observable + // from JS code + atom = JS_ValueToAtomInternal(ctx, prop, JS_TO_STRING_NO_SIDE_EFFECTS); + JS_FreeValue(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) + return JS_EXCEPTION; + if (tag == JS_TAG_NULL) { + JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of null", atom); + } else { + JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of undefined", atom); + } + JS_FreeAtom(ctx, atom); + return JS_EXCEPTION; + } + atom = JS_ValueToAtom(ctx, prop); + JS_FreeValue(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) + return JS_EXCEPTION; + ret = JS_GetProperty(ctx, this_obj, atom); + JS_FreeAtom(ctx, atom); + return ret; +} + +JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj, + uint32_t idx) +{ + return JS_GetPropertyInt64(ctx, this_obj, idx); +} + +/* Check if an object has a generalized numeric property. Return value: + -1 for exception, *pval set to JS_EXCEPTION + true if property exists, stored into *pval, + false if property does not exist. *pval set to JS_UNDEFINED. + */ +static int JS_TryGetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, JSValue *pval) +{ + JSValue val; + JSAtom prop; + int present; + + if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT && + (uint64_t)idx <= INT32_MAX)) { + /* fast path for array and typed array access */ + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (js_get_fast_array_element(ctx, p, idx, pval)) + return true; + } + val = JS_EXCEPTION; + present = -1; + prop = JS_NewAtomInt64(ctx, idx); + if (likely(prop != JS_ATOM_NULL)) { + present = JS_HasProperty(ctx, obj, prop); + if (present > 0) { + val = JS_GetProperty(ctx, obj, prop); + if (unlikely(JS_IsException(val))) + present = -1; + } else if (present == false) { + val = JS_UNDEFINED; + } + JS_FreeAtom(ctx, prop); + } + *pval = val; + return present; +} + +JSValue JS_GetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx) +{ + JSAtom prop; + JSValue val; + + if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT && + (uint64_t)idx <= INT32_MAX)) { + /* fast path for array and typed array access */ + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (js_get_fast_array_element(ctx, p, idx, &val)) + return val; + } + prop = JS_NewAtomInt64(ctx, idx); + if (prop == JS_ATOM_NULL) + return JS_EXCEPTION; + + val = JS_GetProperty(ctx, obj, prop); + JS_FreeAtom(ctx, prop); + return val; +} + +/* `prop` may be pure ASCII or UTF-8 encoded */ +JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj, + const char *prop) +{ + JSAtom atom; + JSValue ret; + atom = JS_NewAtom(ctx, prop); + if (atom == JS_ATOM_NULL) + return JS_EXCEPTION; + ret = JS_GetProperty(ctx, this_obj, atom); + JS_FreeAtom(ctx, atom); + return ret; +} + +/* Note: the property value is not initialized. Return NULL if memory + error. */ +static JSProperty *add_property(JSContext *ctx, + JSObject *p, JSAtom prop, int prop_flags) +{ + JSShape *sh, *new_sh; + + if (unlikely(p->is_prototype)) { + /* track addition of small integer properties to + Array.prototype and Object.prototype */ + if (unlikely((p == JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_ARRAY]) || + p == JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_OBJECT])) && + __JS_AtomIsTaggedInt(prop))) { + ctx->std_array_prototype = false; + } + } + sh = p->shape; + if (sh->is_hashed) { + /* try to find an existing shape */ + new_sh = find_hashed_shape_prop(ctx->rt, sh, prop, prop_flags); + if (new_sh) { + /* matching shape found: use it */ + /* the property array may need to be resized */ + if (new_sh->prop_size != sh->prop_size) { + JSProperty *new_prop; + new_prop = js_realloc(ctx, p->prop, sizeof(p->prop[0]) * + new_sh->prop_size); + if (!new_prop) + return NULL; + p->prop = new_prop; + } + p->shape = js_dup_shape(new_sh); + js_free_shape(ctx->rt, sh); + return &p->prop[new_sh->prop_count - 1]; + } else if (sh->header.ref_count != 1) { + /* if the shape is shared, clone it */ + new_sh = js_clone_shape(ctx, sh); + if (!new_sh) + return NULL; + /* hash the cloned shape */ + new_sh->is_hashed = true; + js_shape_hash_link(ctx->rt, new_sh); + js_free_shape(ctx->rt, p->shape); + p->shape = new_sh; + } + } + assert(p->shape->header.ref_count == 1); + if (add_shape_property(ctx, &p->shape, p, prop, prop_flags)) + return NULL; + return &p->prop[p->shape->prop_count - 1]; +} + +/* can be called on JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS or + JS_CLASS_MAPPED_ARGUMENTS objects. return < 0 if memory alloc + error. */ +static no_inline __exception int convert_fast_array_to_array(JSContext *ctx, + JSObject *p) +{ + JSProperty *pr; + JSShape *sh; + uint32_t i, len, new_count; + + /* track modification of Array.prototype */ + if (unlikely(p == JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_ARRAY]))) { + ctx->std_array_prototype = false; + } + if (js_shape_prepare_update(ctx, p, NULL)) + return -1; + len = p->u.array.count; + /* resize the properties once to simplify the error handling */ + sh = p->shape; + new_count = sh->prop_count + len; + if (new_count > sh->prop_size) { + if (resize_properties(ctx, &p->shape, p, new_count)) + return -1; + } + + if (p->class_id == JS_CLASS_MAPPED_ARGUMENTS) { + JSVarRef **tab = p->u.array.u.var_refs; + for(i = 0; i < len; i++) { + /* add_property cannot fail here but + __JS_AtomFromUInt32(i) fails for i > INT32_MAX */ + pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF); + pr->u.var_ref = *tab++; + } + } else { + JSValue *tab = p->u.array.u.values; + for(i = 0; i < len; i++) { + /* add_property cannot fail here but + __JS_AtomFromUInt32(i) fails for i > INT32_MAX */ + pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E); + pr->u.value = *tab++; + } + } + js_free(ctx, p->u.array.u.values); + p->u.array.count = 0; + p->u.array.u.values = NULL; /* fail safe */ + p->u.array.u1.size = 0; + p->fast_array = 0; + return 0; +} + +static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom) +{ + JSShape *sh; + JSShapeProperty *pr, *lpr, *prop; + JSProperty *pr1; + uint32_t lpr_idx; + intptr_t h, h1; + + redo: + sh = p->shape; + h1 = atom & sh->prop_hash_mask; + h = prop_hash_end(sh)[-h1 - 1]; + prop = get_shape_prop(sh); + lpr = NULL; + lpr_idx = 0; /* prevent warning */ + while (h != 0) { + pr = &prop[h - 1]; + if (likely(pr->atom == atom)) { + /* found ! */ + if (!(pr->flags & JS_PROP_CONFIGURABLE)) + return false; + /* realloc the shape if needed */ + if (lpr) + lpr_idx = lpr - get_shape_prop(sh); + if (js_shape_prepare_update(ctx, p, &pr)) + return -1; + sh = p->shape; + /* remove property */ + if (lpr) { + lpr = get_shape_prop(sh) + lpr_idx; + lpr->hash_next = pr->hash_next; + } else { + prop_hash_end(sh)[-h1 - 1] = pr->hash_next; + } + sh->deleted_prop_count++; + /* free the entry */ + pr1 = &p->prop[h - 1]; + free_property(ctx->rt, pr1, pr->flags); + JS_FreeAtom(ctx, pr->atom); + /* put default values */ + pr->flags = 0; + pr->atom = JS_ATOM_NULL; + pr1->u.value = JS_UNDEFINED; + + /* compact the properties if too many deleted properties */ + if (sh->deleted_prop_count >= 8 && + sh->deleted_prop_count >= ((unsigned)sh->prop_count / 2)) { + compact_properties(ctx, p); + } + return true; + } + lpr = pr; + h = pr->hash_next; + } + + if (p->is_exotic) { + if (p->fast_array) { + uint32_t idx; + if (JS_AtomIsArrayIndex(ctx, &idx, atom) && + idx < p->u.array.count) { + if (p->class_id == JS_CLASS_ARRAY || + p->class_id == JS_CLASS_ARGUMENTS || + p->class_id == JS_CLASS_MAPPED_ARGUMENTS) { + /* Special case deleting the last element of a fast Array */ + if (idx == p->u.array.count - 1) { + if (p->class_id == JS_CLASS_MAPPED_ARGUMENTS) { + free_var_ref(ctx->rt, p->u.array.u.var_refs[idx]); + } else { + JS_FreeValue(ctx, p->u.array.u.values[idx]); + } + p->u.array.count = idx; + return true; + } + if (convert_fast_array_to_array(ctx, p)) + return -1; + goto redo; + } else { + return false; + } + } + } else { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em && em->delete_property) { + return em->delete_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), atom); + } + } + } + /* not found */ + return true; +} + +static int call_setter(JSContext *ctx, JSObject *setter, + JSValueConst this_obj, JSValue val, int flags) +{ + JSValue ret, func; + if (likely(setter)) { + func = JS_MKPTR(JS_TAG_OBJECT, setter); + /* Note: the field could be removed in the setter */ + func = js_dup(func); + ret = JS_CallFree(ctx, func, this_obj, 1, vc(&val)); + JS_FreeValue(ctx, val); + if (JS_IsException(ret)) + return -1; + JS_FreeValue(ctx, ret); + return true; + } else { + JS_FreeValue(ctx, val); + if ((flags & JS_PROP_THROW) || + ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { + JS_ThrowTypeError(ctx, "no setter for property"); + return -1; + } + return false; + } +} + +/* set the array length and remove the array elements if necessary. */ +static int set_array_length(JSContext *ctx, JSObject *p, JSValue val, + int flags) +{ + uint32_t len, idx, cur_len; + int i, ret; + + /* Note: this call can reallocate the properties of 'p' */ + ret = JS_ToArrayLengthFree(ctx, &len, val, false); + if (ret) + return -1; + /* JS_ToArrayLengthFree() must be done before the read-only test */ + if (unlikely(!(p->shape->prop[0].flags & JS_PROP_WRITABLE))) + return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); + + if (likely(p->fast_array)) { + uint32_t old_len = p->u.array.count; + if (len < old_len) { + for(i = len; i < old_len; i++) { + JS_FreeValue(ctx, p->u.array.u.values[i]); + } + p->u.array.count = len; + } + p->prop[0].u.value = js_uint32(len); + } else { + /* Note: length is always a uint32 because the object is an + array */ + JS_ToUint32(ctx, &cur_len, p->prop[0].u.value); + if (len < cur_len) { + uint32_t d; + JSShape *sh; + JSShapeProperty *pr; + + d = cur_len - len; + sh = p->shape; + if (d <= sh->prop_count) { + JSAtom atom; + + /* faster to iterate */ + while (cur_len > len) { + atom = JS_NewAtomUInt32(ctx, cur_len - 1); + ret = delete_property(ctx, p, atom); + JS_FreeAtom(ctx, atom); + if (unlikely(!ret)) { + /* unlikely case: property is not + configurable */ + break; + } + cur_len--; + } + } else { + /* faster to iterate thru all the properties. Need two + passes in case one of the property is not + configurable */ + cur_len = len; + for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; + i++, pr++) { + if (pr->atom != JS_ATOM_NULL && + JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) { + if (idx >= cur_len && + !(pr->flags & JS_PROP_CONFIGURABLE)) { + cur_len = idx + 1; + } + } + } + + for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; + i++, pr++) { + if (pr->atom != JS_ATOM_NULL && + JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) { + if (idx >= cur_len) { + /* remove the property */ + delete_property(ctx, p, pr->atom); + /* WARNING: the shape may have been modified */ + sh = p->shape; + pr = get_shape_prop(sh) + i; + } + } + } + } + } else { + cur_len = len; + } + set_value(ctx, &p->prop[0].u.value, js_uint32(cur_len)); + if (unlikely(cur_len > len)) { + return JS_ThrowTypeErrorOrFalse(ctx, flags, "not configurable"); + } + } + return true; +} + +/* return -1 if exception */ +static int expand_fast_array(JSContext *ctx, JSObject *p, uint32_t new_len) +{ + uint32_t new_size; + size_t slack; + JSValue *new_array_prop; + /* XXX: potential arithmetic overflow */ + new_size = max_int(new_len, p->u.array.u1.size * 3 / 2); + new_array_prop = js_realloc2(ctx, p->u.array.u.values, sizeof(JSValue) * new_size, &slack); + if (!new_array_prop) + return -1; + new_size += slack / sizeof(*new_array_prop); + p->u.array.u.values = new_array_prop; + p->u.array.u1.size = new_size; + return 0; +} + +/* Preconditions: 'p' must be of class JS_CLASS_ARRAY, p->fast_array = + true and p->extensible = true */ +static int add_fast_array_element(JSContext *ctx, JSObject *p, + JSValue val, int flags) +{ + uint32_t new_len, array_len; + /* extend the array by one */ + /* XXX: convert to slow array if new_len > 2^31-1 elements */ + new_len = p->u.array.count + 1; + /* update the length if necessary. We assume that if the length is + not an integer, then if it >= 2^31. */ + if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT)) { + array_len = JS_VALUE_GET_INT(p->prop[0].u.value); + if (new_len > array_len) { + if (unlikely(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) { + JS_FreeValue(ctx, val); + return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); + } + p->prop[0].u.value = js_int32(new_len); + } + } + if (unlikely(new_len > p->u.array.u1.size)) { + if (expand_fast_array(ctx, p, new_len)) { + JS_FreeValue(ctx, val); + return -1; + } + } + p->u.array.u.values[new_len - 1] = val; + p->u.array.count = new_len; + return true; +} + +static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc) +{ + JS_FreeValue(ctx, desc->getter); + JS_FreeValue(ctx, desc->setter); + JS_FreeValue(ctx, desc->value); +} + +/* return -1 in case of exception or true or false. Warning: 'val' is + freed by the function. 'flags' is a bitmask of JS_PROP_NO_ADD, + JS_PROP_THROW or JS_PROP_THROW_STRICT. If JS_PROP_NO_ADD is set, + the new property is not added and an error is raised. + 'obj' must be an object when obj != this_obj. + */ +static int JS_SetPropertyInternal2(JSContext *ctx, JSValueConst obj, JSAtom prop, + JSValue val, JSValueConst this_obj, int flags) +{ + JSObject *p, *p1; + JSShapeProperty *prs; + JSProperty *pr; + JSPropertyDescriptor desc; + int ret; + + switch(JS_VALUE_GET_TAG(this_obj)) { + case JS_TAG_NULL: + JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of null", prop); + goto fail; + case JS_TAG_UNDEFINED: + JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of undefined", prop); + goto fail; + case JS_TAG_OBJECT: + p = JS_VALUE_GET_OBJ(this_obj); + p1 = JS_VALUE_GET_OBJ(obj); + if (p == p1) + break; + goto retry2; + default: + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + obj = JS_GetPrototypePrimitive(ctx, obj); + p = NULL; + p1 = JS_VALUE_GET_OBJ(obj); + goto prototype_lookup; + } + +retry: + prs = find_own_property(&pr, p1, prop); + if (prs) { + if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE | + JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) { + /* fast case */ + set_value(ctx, &pr->u.value, val); + return true; + } else if (prs->flags & JS_PROP_LENGTH) { + assert(p->class_id == JS_CLASS_ARRAY); + assert(prop == JS_ATOM_length); + return set_array_length(ctx, p, val, flags); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + /* JS_PROP_WRITABLE is always true for variable + references, but they are write protected in module name + spaces. */ + if (p->class_id == JS_CLASS_MODULE_NS) + goto read_only_prop; + set_value(ctx, pr->u.var_ref->pvalue, val); + return true; + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry (potentially useless) */ + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) + goto fail; + goto retry; + } else { + goto read_only_prop; + } + } + + for(;;) { + if (p1->is_exotic) { + if (p1->fast_array) { + if (__JS_AtomIsTaggedInt(prop)) { + uint32_t idx = __JS_AtomToUInt32(prop); + if (idx < p1->u.array.count) { + if (unlikely(p == p1)) + return JS_SetPropertyValue(ctx, this_obj, js_int32(idx), val, flags); + else + break; + } else if (is_typed_array(p1->class_id)) { + goto typed_array_oob; + } + } else if (is_typed_array(p1->class_id)) { + ret = JS_AtomIsNumericIndex(ctx, prop); + if (ret != 0) { + if (ret < 0) + goto fail; + typed_array_oob: + // per spec: evaluate value for side effects + if (p1->class_id == JS_CLASS_BIG_INT64_ARRAY || + p1->class_id == JS_CLASS_BIG_UINT64_ARRAY) { + int64_t v; + if (JS_ToBigInt64Free(ctx, &v, val)) + return -1; + } else { + val = JS_ToNumberFree(ctx, val); + JS_FreeValue(ctx, val); + if (JS_IsException(val)) + return -1; + } + return true; + } + } + } else { + const JSClassExoticMethods *em = ctx->rt->class_array[p1->class_id].exotic; + if (em) { + JSValue obj1; + if (em->set_property) { + /* set_property can free the prototype */ + obj1 = js_dup(JS_MKPTR(JS_TAG_OBJECT, p1)); + ret = em->set_property(ctx, obj1, prop, + val, this_obj, flags); + JS_FreeValue(ctx, obj1); + JS_FreeValue(ctx, val); + return ret; + } + if (em->get_own_property) { + /* get_own_property can free the prototype */ + obj1 = js_dup(JS_MKPTR(JS_TAG_OBJECT, p1)); + ret = em->get_own_property(ctx, &desc, + obj1, prop); + JS_FreeValue(ctx, obj1); + if (ret < 0) + goto fail; + if (ret) { + if (desc.flags & JS_PROP_GETSET) { + JSObject *setter; + if (JS_IsUndefined(desc.setter)) + setter = NULL; + else + setter = JS_VALUE_GET_OBJ(desc.setter); + ret = call_setter(ctx, setter, this_obj, val, flags); + JS_FreeValue(ctx, desc.getter); + JS_FreeValue(ctx, desc.setter); + return ret; + } else { + JS_FreeValue(ctx, desc.value); + if (!(desc.flags & JS_PROP_WRITABLE)) + goto read_only_prop; + if (likely(p == p1)) { + ret = JS_DefineProperty(ctx, this_obj, prop, val, + JS_UNDEFINED, JS_UNDEFINED, + JS_PROP_HAS_VALUE); + JS_FreeValue(ctx, val); + return ret; + } else { + break; + } + } + } + } + } + } + } + p1 = p1->shape->proto; + prototype_lookup: + if (!p1) + break; + + retry2: + prs = find_own_property(&pr, p1, prop); + if (prs) { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry (potentially useless) */ + if (JS_AutoInitProperty(ctx, p1, prop, pr, prs)) + return -1; + goto retry2; + } else if (!(prs->flags & JS_PROP_WRITABLE)) { + goto read_only_prop; + } else { + break; + } + } + } + + if (unlikely(flags & JS_PROP_NO_ADD)) { + JS_ThrowReferenceErrorNotDefined(ctx, prop); + goto fail; + } + + if (unlikely(!p)) { + ret = JS_ThrowTypeErrorOrFalse(ctx, flags, "not an object"); + goto done; + } + + if (unlikely(!p->extensible)) { + ret = JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible"); + goto done; + } + + if (p == JS_VALUE_GET_OBJ(obj)) { + if (p->is_exotic) { + if (p->class_id == JS_CLASS_ARRAY && p->fast_array && + __JS_AtomIsTaggedInt(prop)) { + uint32_t idx = __JS_AtomToUInt32(prop); + if (idx == p->u.array.count) { + /* fast case */ + return add_fast_array_element(ctx, p, val, flags); + } + } + goto generic_create_prop; + } else { + pr = add_property(ctx, p, prop, JS_PROP_C_W_E); + if (!pr) + goto fail; + pr->u.value = val; + return true; + } + } + + // TODO(bnoordhuis) return JSProperty slot and update in place + // when plain property (not is_exotic/setter/etc.) to avoid + // calling find_own_property() thrice? + ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); + if (ret < 0) + goto fail; + + if (ret) { + JS_FreeValue(ctx, desc.value); + if (desc.flags & JS_PROP_GETSET) { + JS_FreeValue(ctx, desc.getter); + JS_FreeValue(ctx, desc.setter); + ret = JS_ThrowTypeErrorOrFalse(ctx, flags, "setter is forbidden"); + goto done; + } else if (!(desc.flags & JS_PROP_WRITABLE) || + p->class_id == JS_CLASS_MODULE_NS) { + read_only_prop: + ret = JS_ThrowTypeErrorReadOnly(ctx, flags, prop); + goto done; + } + ret = JS_DefineProperty(ctx, this_obj, prop, val, + JS_UNDEFINED, JS_UNDEFINED, + JS_PROP_HAS_VALUE); + } else { + generic_create_prop: + ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED, + flags | + JS_PROP_HAS_VALUE | + JS_PROP_HAS_ENUMERABLE | + JS_PROP_HAS_WRITABLE | + JS_PROP_HAS_CONFIGURABLE | + JS_PROP_C_W_E); + } + +done: + JS_FreeValue(ctx, val); + return ret; +fail: + JS_FreeValue(ctx, val); + return -1; +} + +static int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj, JSAtom prop, + JSValue val, int flags) +{ + return JS_SetPropertyInternal2(ctx, obj, prop, val, obj, flags); +} + +int JS_SetProperty(JSContext *ctx, JSValueConst this_obj, JSAtom prop, JSValue val) +{ + return JS_SetPropertyInternal(ctx, this_obj, prop, val, JS_PROP_THROW); +} + +/* flags can be JS_PROP_THROW or JS_PROP_THROW_STRICT */ +static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, + JSValue prop, JSValue val, int flags) +{ + if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT && + JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) { + JSObject *p; + uint32_t idx; + double d; + int32_t v; + + /* fast path for array access */ + p = JS_VALUE_GET_OBJ(this_obj); + idx = JS_VALUE_GET_INT(prop); + switch(p->class_id) { + case JS_CLASS_ARRAY: + if (unlikely(idx >= (uint32_t)p->u.array.count)) { + /* fast path to add an element to the array */ + if (unlikely(idx != (uint32_t)p->u.array.count || + !p->fast_array || + !p->extensible || + p->shape->proto != JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_ARRAY]) || + !ctx->std_array_prototype)) { + goto slow_path; + } + /* add element */ + return add_fast_array_element(ctx, p, val, flags); + } + set_value(ctx, &p->u.array.u.values[idx], val); + break; + case JS_CLASS_ARGUMENTS: + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto slow_path; + set_value(ctx, &p->u.array.u.values[idx], val); + break; + case JS_CLASS_MAPPED_ARGUMENTS: + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto slow_path; + set_value(ctx, p->u.array.u.var_refs[idx]->pvalue, val); + break; + case JS_CLASS_UINT8C_ARRAY: + if (JS_ToUint8ClampFree(ctx, &v, val)) + goto ta_cvt_fail; + if (typed_array_is_immutable(p)) + goto ta_immutable; + /* Note: the conversion can detach the typed array, so the + array bound check must be done after */ + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint8_ptr[idx] = v; + break; + case JS_CLASS_INT8_ARRAY: + case JS_CLASS_UINT8_ARRAY: + if (JS_ToInt32Free(ctx, &v, val)) + goto ta_cvt_fail; + if (typed_array_is_immutable(p)) + goto ta_immutable; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint8_ptr[idx] = v; + break; + case JS_CLASS_INT16_ARRAY: + case JS_CLASS_UINT16_ARRAY: + if (JS_ToInt32Free(ctx, &v, val)) + goto ta_cvt_fail; + if (typed_array_is_immutable(p)) + goto ta_immutable; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint16_ptr[idx] = v; + break; + case JS_CLASS_INT32_ARRAY: + case JS_CLASS_UINT32_ARRAY: + if (JS_ToInt32Free(ctx, &v, val)) + goto ta_cvt_fail; + if (typed_array_is_immutable(p)) + goto ta_immutable; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint32_ptr[idx] = v; + break; + case JS_CLASS_BIG_INT64_ARRAY: + case JS_CLASS_BIG_UINT64_ARRAY: + /* XXX: need specific conversion function */ + { + int64_t v; + if (JS_ToBigInt64Free(ctx, &v, val)) + goto ta_cvt_fail; + if (typed_array_is_immutable(p)) + goto ta_immutable; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint64_ptr[idx] = v; + } + break; + case JS_CLASS_FLOAT16_ARRAY: + if (JS_ToFloat64Free(ctx, &d, val)) + goto ta_cvt_fail; + if (typed_array_is_immutable(p)) + goto ta_immutable; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.fp16_ptr[idx] = tofp16(d); + break; + case JS_CLASS_FLOAT32_ARRAY: + if (JS_ToFloat64Free(ctx, &d, val)) + goto ta_cvt_fail; + if (typed_array_is_immutable(p)) + goto ta_immutable; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.float_ptr[idx] = d; + break; + case JS_CLASS_FLOAT64_ARRAY: + if (JS_ToFloat64Free(ctx, &d, val)) { + ta_cvt_fail: + if (flags & JS_PROP_REFLECT_DEFINE_PROPERTY) { + JS_FreeValue(ctx, JS_GetException(ctx)); + return false; + } + return -1; + } + if (typed_array_is_immutable(p)) { + ta_immutable: + return false; + } + if (unlikely(idx >= (uint32_t)p->u.array.count)) { + ta_out_of_bound: + if (typed_array_is_oob(p)) + if (flags & JS_PROP_DEFINE_PROPERTY) + return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index"); + return true; // per spec: no OOB exception + } + p->u.array.u.double_ptr[idx] = d; + break; + default: + goto slow_path; + } + return true; + } else { + JSAtom atom; + int ret; + slow_path: + atom = JS_ValueToAtom(ctx, prop); + JS_FreeValue(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) { + JS_FreeValue(ctx, val); + return -1; + } + ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, flags); + JS_FreeAtom(ctx, atom); + return ret; + } +} + +int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj, + uint32_t idx, JSValue val) +{ + return JS_SetPropertyValue(ctx, this_obj, js_uint32(idx), val, + JS_PROP_THROW); +} + +int JS_SetPropertyInt64(JSContext *ctx, JSValueConst this_obj, + int64_t idx, JSValue val) +{ + JSAtom prop; + int res; + + if ((uint64_t)idx <= INT32_MAX) { + /* fast path for fast arrays */ + return JS_SetPropertyValue(ctx, this_obj, js_int32(idx), val, + JS_PROP_THROW); + } + prop = JS_NewAtomInt64(ctx, idx); + if (prop == JS_ATOM_NULL) { + JS_FreeValue(ctx, val); + return -1; + } + res = JS_SetProperty(ctx, this_obj, prop, val); + JS_FreeAtom(ctx, prop); + return res; +} + +/* `prop` may be pure ASCII or UTF-8 encoded */ +int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj, + const char *prop, JSValue val) +{ + JSAtom atom; + int ret; + atom = JS_NewAtom(ctx, prop); + if (atom == JS_ATOM_NULL) { + JS_FreeValue(ctx, val); + return -1; + } + ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, JS_PROP_THROW); + JS_FreeAtom(ctx, atom); + return ret; +} + +/* compute the property flags. For each flag: (JS_PROP_HAS_x forces + it, otherwise def_flags is used) + Note: makes assumption about the bit pattern of the flags +*/ +static int get_prop_flags(int flags, int def_flags) +{ + int mask; + mask = (flags >> JS_PROP_HAS_SHIFT) & JS_PROP_C_W_E; + return (flags & mask) | (def_flags & ~mask); +} + +static int JS_CreateProperty(JSContext *ctx, JSObject *p, + JSAtom prop, JSValueConst val, + JSValueConst getter, JSValueConst setter, + int flags) +{ + JSProperty *pr; + int ret, prop_flags; + + /* add a new property or modify an existing exotic one */ + if (p->is_exotic) { + if (p->class_id == JS_CLASS_ARRAY) { + uint32_t idx, len; + + if (p->fast_array) { + if (__JS_AtomIsTaggedInt(prop)) { + idx = __JS_AtomToUInt32(prop); + if (idx == p->u.array.count) { + if (!p->extensible) + goto not_extensible; + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) + goto convert_to_array; + prop_flags = get_prop_flags(flags, 0); + if (prop_flags != JS_PROP_C_W_E) + goto convert_to_array; + return add_fast_array_element(ctx, p, + js_dup(val), flags); + } else { + goto convert_to_array; + } + } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) { + /* convert the fast array to normal array */ + convert_to_array: + if (convert_fast_array_to_array(ctx, p)) + return -1; + goto generic_array; + } + } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) { + JSProperty *plen; + JSShapeProperty *pslen; + generic_array: + /* update the length field */ + plen = &p->prop[0]; + JS_ToUint32(ctx, &len, plen->u.value); + if ((idx + 1) > len) { + pslen = get_shape_prop(p->shape); + if (unlikely(!(pslen->flags & JS_PROP_WRITABLE))) + return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); + /* XXX: should update the length after defining + the property */ + len = idx + 1; + set_value(ctx, &plen->u.value, js_uint32(len)); + } + } + } else if (is_typed_array(p->class_id)) { + ret = JS_AtomIsNumericIndex(ctx, prop); + if (ret != 0) { + if (ret < 0) + return -1; + return JS_ThrowTypeErrorOrFalse(ctx, flags, "cannot create numeric index in typed array"); + } + } else if (!(flags & JS_PROP_NO_EXOTIC)) { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em) { + if (em->define_own_property) { + return em->define_own_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), + prop, val, getter, setter, flags); + } + ret = JS_IsExtensible(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + if (ret < 0) + return -1; + if (!ret) + goto not_extensible; + } + } + } + + if (!p->extensible) { + not_extensible: + return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible"); + } + + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + prop_flags = (flags & (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) | + JS_PROP_GETSET; + } else { + prop_flags = flags & JS_PROP_C_W_E; + } + pr = add_property(ctx, p, prop, prop_flags); + if (unlikely(!pr)) + return -1; + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + pr->u.getset.getter = NULL; + if ((flags & JS_PROP_HAS_GET) && JS_IsFunction(ctx, getter)) { + pr->u.getset.getter = + JS_VALUE_GET_OBJ(js_dup(getter)); + } + pr->u.getset.setter = NULL; + if ((flags & JS_PROP_HAS_SET) && JS_IsFunction(ctx, setter)) { + pr->u.getset.setter = + JS_VALUE_GET_OBJ(js_dup(setter)); + } + } else { + if (flags & JS_PROP_HAS_VALUE) { + pr->u.value = js_dup(val); + } else { + pr->u.value = JS_UNDEFINED; + } + } + return true; +} + +/* return false if not OK */ +static bool check_define_prop_flags(int prop_flags, int flags) +{ + bool has_accessor, is_getset; + + if (!(prop_flags & JS_PROP_CONFIGURABLE)) { + if ((flags & (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) == + (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) { + return false; + } + if ((flags & JS_PROP_HAS_ENUMERABLE) && + (flags & JS_PROP_ENUMERABLE) != (prop_flags & JS_PROP_ENUMERABLE)) + return false; + } + if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | + JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + if (!(prop_flags & JS_PROP_CONFIGURABLE)) { + has_accessor = ((flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) != 0); + is_getset = ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET); + if (has_accessor != is_getset) + return false; + if (!has_accessor && !is_getset && !(prop_flags & JS_PROP_WRITABLE)) { + /* not writable: cannot set the writable bit */ + if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == + (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) + return false; + } + } + } + return true; +} + +/* ensure that the shape can be safely modified */ +static int js_shape_prepare_update(JSContext *ctx, JSObject *p, + JSShapeProperty **pprs) +{ + JSShape *sh; + uint32_t idx = 0; /* prevent warning */ + + sh = p->shape; + if (sh->is_hashed) { + if (sh->header.ref_count != 1) { + if (pprs) + idx = *pprs - get_shape_prop(sh); + /* clone the shape (the resulting one is no longer hashed) */ + sh = js_clone_shape(ctx, sh); + if (!sh) + return -1; + js_free_shape(ctx->rt, p->shape); + p->shape = sh; + if (pprs) + *pprs = get_shape_prop(sh) + idx; + } else { + js_shape_hash_unlink(ctx->rt, sh); + sh->is_hashed = false; + } + } + return 0; +} + +static int js_update_property_flags(JSContext *ctx, JSObject *p, + JSShapeProperty **pprs, int flags) +{ + if (flags != (*pprs)->flags) { + if (js_shape_prepare_update(ctx, p, pprs)) + return -1; + (*pprs)->flags = flags; + } + return 0; +} + +/* allowed flags: + JS_PROP_CONFIGURABLE, JS_PROP_WRITABLE, JS_PROP_ENUMERABLE + JS_PROP_HAS_GET, JS_PROP_HAS_SET, JS_PROP_HAS_VALUE, + JS_PROP_HAS_CONFIGURABLE, JS_PROP_HAS_WRITABLE, JS_PROP_HAS_ENUMERABLE, + JS_PROP_THROW, JS_PROP_NO_EXOTIC. + If JS_PROP_THROW is set, return an exception instead of false. + if JS_PROP_NO_EXOTIC is set, do not call the exotic + define_own_property callback. + return -1 (exception), false or true. +*/ +int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, + JSAtom prop, JSValueConst val, + JSValueConst getter, JSValueConst setter, int flags) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + int mask, res; + + if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + p = JS_VALUE_GET_OBJ(this_obj); + + redo_prop_update: + prs = find_own_property(&pr, p, prop); + if (prs) { + /* the range of the Array length property is always tested before */ + if ((prs->flags & JS_PROP_LENGTH) && (flags & JS_PROP_HAS_VALUE)) { + uint32_t array_length; + if (JS_ToArrayLengthFree(ctx, &array_length, + js_dup(val), false)) { + return -1; + } + /* this code relies on the fact that Uint32 are never allocated */ + val = js_uint32(array_length); + /* prs may have been modified */ + prs = find_own_property(&pr, p, prop); + assert(prs != NULL); + } + /* property already exists */ + if (!check_define_prop_flags(prs->flags, flags)) { + not_configurable: + return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable"); + } + + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry */ + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) + return -1; + goto redo_prop_update; + } + + if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | + JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + JSObject *new_getter, *new_setter; + + if (JS_IsFunction(ctx, getter)) { + new_getter = JS_VALUE_GET_OBJ(getter); + } else { + new_getter = NULL; + } + if (JS_IsFunction(ctx, setter)) { + new_setter = JS_VALUE_GET_OBJ(setter); + } else { + new_setter = NULL; + } + + if ((prs->flags & JS_PROP_TMASK) != JS_PROP_GETSET) { + if (js_shape_prepare_update(ctx, p, &prs)) + return -1; + /* convert to getset */ + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + free_var_ref(ctx->rt, pr->u.var_ref); + } else { + JS_FreeValue(ctx, pr->u.value); + } + prs->flags = (prs->flags & + (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) | + JS_PROP_GETSET; + pr->u.getset.getter = NULL; + pr->u.getset.setter = NULL; + } else { + if (!(prs->flags & JS_PROP_CONFIGURABLE)) { + if ((flags & JS_PROP_HAS_GET) && + new_getter != pr->u.getset.getter) { + goto not_configurable; + } + if ((flags & JS_PROP_HAS_SET) && + new_setter != pr->u.getset.setter) { + goto not_configurable; + } + } + } + if (flags & JS_PROP_HAS_GET) { + if (pr->u.getset.getter) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); + if (new_getter) + js_dup(getter); + pr->u.getset.getter = new_getter; + } + if (flags & JS_PROP_HAS_SET) { + if (pr->u.getset.setter) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); + if (new_setter) + js_dup(setter); + pr->u.getset.setter = new_setter; + } + } else { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + /* convert to data descriptor */ + if (js_shape_prepare_update(ctx, p, &prs)) + return -1; + if (pr->u.getset.getter) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); + if (pr->u.getset.setter) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); + prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE); + pr->u.value = JS_UNDEFINED; + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + /* Note: JS_PROP_VARREF is always writable */ + } else { + if ((prs->flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 && + (flags & JS_PROP_HAS_VALUE)) { + if (!js_same_value(ctx, val, pr->u.value)) { + goto not_configurable; + } else { + return true; + } + } + } + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + if (flags & JS_PROP_HAS_VALUE) { + if (p->class_id == JS_CLASS_MODULE_NS) { + /* JS_PROP_WRITABLE is always true for variable + references, but they are write protected in module name + spaces. */ + if (!js_same_value(ctx, val, *pr->u.var_ref->pvalue)) + goto not_configurable; + } + /* update the reference */ + set_value(ctx, pr->u.var_ref->pvalue, js_dup(val)); + } + /* if writable is set to false, no longer a + reference (for mapped arguments) */ + if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == JS_PROP_HAS_WRITABLE) { + JSValue val1; + if (js_shape_prepare_update(ctx, p, &prs)) + return -1; + val1 = js_dup(*pr->u.var_ref->pvalue); + free_var_ref(ctx->rt, pr->u.var_ref); + pr->u.value = val1; + prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE); + } + } else if (prs->flags & JS_PROP_LENGTH) { + if (flags & JS_PROP_HAS_VALUE) { + /* Note: no JS code is executable because + 'val' is guaranted to be a Uint32 */ + res = set_array_length(ctx, p, js_dup(val), flags); + } else { + res = true; + } + /* still need to reset the writable flag if + needed. The JS_PROP_LENGTH is kept because the + Uint32 test is still done if the length + property is read-only. */ + if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == + JS_PROP_HAS_WRITABLE) { + prs = get_shape_prop(p->shape); + if (js_update_property_flags(ctx, p, &prs, + prs->flags & ~JS_PROP_WRITABLE)) + return -1; + } + return res; + } else { + if (flags & JS_PROP_HAS_VALUE) { + JS_FreeValue(ctx, pr->u.value); + pr->u.value = js_dup(val); + } + if (flags & JS_PROP_HAS_WRITABLE) { + if (js_update_property_flags(ctx, p, &prs, + (prs->flags & ~JS_PROP_WRITABLE) | + (flags & JS_PROP_WRITABLE))) + return -1; + } + } + } + } + mask = 0; + if (flags & JS_PROP_HAS_CONFIGURABLE) + mask |= JS_PROP_CONFIGURABLE; + if (flags & JS_PROP_HAS_ENUMERABLE) + mask |= JS_PROP_ENUMERABLE; + if (js_update_property_flags(ctx, p, &prs, + (prs->flags & ~mask) | (flags & mask))) + return -1; + return true; + } + + /* handle modification of fast array elements */ + if (p->fast_array) { + uint32_t idx; + uint32_t prop_flags; + if (p->class_id == JS_CLASS_ARRAY) { + if (__JS_AtomIsTaggedInt(prop)) { + idx = __JS_AtomToUInt32(prop); + if (idx < p->u.array.count) { + prop_flags = get_prop_flags(flags, JS_PROP_C_W_E); + if (prop_flags != JS_PROP_C_W_E) + goto convert_to_slow_array; + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + convert_to_slow_array: + if (convert_fast_array_to_array(ctx, p)) + return -1; + else + goto redo_prop_update; + } + if (flags & JS_PROP_HAS_VALUE) { + set_value(ctx, &p->u.array.u.values[idx], js_dup(val)); + } + return true; + } + } + } else if (is_typed_array(p->class_id)) { + JSValue num; + int ret; + + if (!__JS_AtomIsTaggedInt(prop)) { + /* slow path with to handle all numeric indexes */ + num = JS_AtomIsNumericIndex1(ctx, prop); + if (JS_IsUndefined(num)) + goto typed_array_done; + if (JS_IsException(num)) + return -1; + ret = JS_NumberIsInteger(ctx, num); + if (ret < 0) { + JS_FreeValue(ctx, num); + return -1; + } + if (!ret) { + JS_FreeValue(ctx, num); + return JS_ThrowTypeErrorOrFalse(ctx, flags, "non integer index in typed array"); + } + ret = JS_NumberIsNegativeOrMinusZero(ctx, num); + JS_FreeValue(ctx, num); + if (ret) { + return JS_ThrowTypeErrorOrFalse(ctx, flags, "negative index in typed array"); + } + if (!__JS_AtomIsTaggedInt(prop)) + goto typed_array_oob; + } + idx = __JS_AtomToUInt32(prop); + /* if the typed array is detached, p->u.array.count = 0 */ + if (idx >= p->u.array.count) { + typed_array_oob: + return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound index in typed array"); + } + prop_flags = get_prop_flags(flags, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET) || + prop_flags != (JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE)) { + return JS_ThrowTypeErrorOrFalse(ctx, flags, "invalid descriptor flags"); + } + if (flags & JS_PROP_HAS_VALUE) { + return JS_SetPropertyValue(ctx, this_obj, js_int32(idx), js_dup(val), flags); + } + return true; + typed_array_done: ; + } + } + + return JS_CreateProperty(ctx, p, prop, val, getter, setter, flags); +} + +static int JS_DefineAutoInitProperty(JSContext *ctx, JSValueConst this_obj, + JSAtom prop, JSAutoInitIDEnum id, + void *opaque, int flags) +{ + JSObject *p; + JSProperty *pr; + + if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) + return false; + + p = JS_VALUE_GET_OBJ(this_obj); + + if (find_own_property(&pr, p, prop)) { + /* property already exists */ + abort(); + return false; + } + + /* Specialized CreateProperty */ + pr = add_property(ctx, p, prop, (flags & JS_PROP_C_W_E) | JS_PROP_AUTOINIT); + if (unlikely(!pr)) + return -1; + pr->u.init.realm_and_id = (uintptr_t)JS_DupContext(ctx); + assert((pr->u.init.realm_and_id & 3) == 0); + assert(id <= 3); + pr->u.init.realm_and_id |= id; + pr->u.init.opaque = opaque; + return true; +} + +/* shortcut to add or redefine a new property value */ +int JS_DefinePropertyValue(JSContext *ctx, JSValueConst this_obj, + JSAtom prop, JSValue val, int flags) +{ + int ret; + ret = JS_DefineProperty(ctx, this_obj, prop, val, JS_UNDEFINED, JS_UNDEFINED, + flags | JS_PROP_HAS_VALUE | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE); + JS_FreeValue(ctx, val); + return ret; +} + +int JS_DefinePropertyValueValue(JSContext *ctx, JSValueConst this_obj, + JSValue prop, JSValue val, int flags) +{ + JSAtom atom; + int ret; + atom = JS_ValueToAtom(ctx, prop); + JS_FreeValue(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) { + JS_FreeValue(ctx, val); + return -1; + } + ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags); + JS_FreeAtom(ctx, atom); + return ret; +} + +int JS_DefinePropertyValueUint32(JSContext *ctx, JSValueConst this_obj, + uint32_t idx, JSValue val, int flags) +{ + return JS_DefinePropertyValueValue(ctx, this_obj, js_uint32(idx), + val, flags); +} + +int JS_DefinePropertyValueInt64(JSContext *ctx, JSValueConst this_obj, + int64_t idx, JSValue val, int flags) +{ + return JS_DefinePropertyValueValue(ctx, this_obj, js_int64(idx), + val, flags); +} + +/* `prop` may be pure ASCII or UTF-8 encoded */ +int JS_DefinePropertyValueStr(JSContext *ctx, JSValueConst this_obj, + const char *prop, JSValue val, int flags) +{ + JSAtom atom; + int ret; + atom = JS_NewAtom(ctx, prop); + if (atom == JS_ATOM_NULL) { + JS_FreeValue(ctx, val); + return -1; + } + ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags); + JS_FreeAtom(ctx, atom); + return ret; +} + +/* shortcut to add getter & setter */ +int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj, + JSAtom prop, JSValue getter, JSValue setter, + int flags) +{ + int ret; + ret = JS_DefineProperty(ctx, this_obj, prop, JS_UNDEFINED, getter, setter, + flags | JS_PROP_HAS_GET | JS_PROP_HAS_SET | + JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE); + JS_FreeValue(ctx, getter); + JS_FreeValue(ctx, setter); + return ret; +} + +static int JS_CreateDataPropertyUint32(JSContext *ctx, JSValueConst this_obj, + int64_t idx, JSValue val, int flags) +{ + return JS_DefinePropertyValueValue(ctx, this_obj, js_int64(idx), + val, flags | JS_PROP_CONFIGURABLE | + JS_PROP_ENUMERABLE | JS_PROP_WRITABLE); +} + + +/* return true if 'obj' has a non empty 'name' string */ +static bool js_object_has_name(JSContext *ctx, JSValue obj) +{ + JSProperty *pr; + JSShapeProperty *prs; + JSValue val; + JSString *p; + + prs = find_own_property(&pr, JS_VALUE_GET_OBJ(obj), JS_ATOM_name); + if (!prs) + return false; + if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL) + return true; + val = pr->u.value; + if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING) + return true; + p = JS_VALUE_GET_STRING(val); + return (p->len != 0); +} + +static int JS_DefineObjectName(JSContext *ctx, JSValue obj, + JSAtom name, int flags) +{ + if (name != JS_ATOM_NULL + && JS_IsObject(obj) + && !js_object_has_name(ctx, obj) + && JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, JS_AtomToString(ctx, name), flags) < 0) { + return -1; + } + return 0; +} + +static int JS_DefineObjectNameComputed(JSContext *ctx, JSValue obj, + JSValue str, int flags) +{ + if (JS_IsObject(obj) && + !js_object_has_name(ctx, obj)) { + JSAtom prop; + JSValue name_str; + prop = JS_ValueToAtom(ctx, str); + if (prop == JS_ATOM_NULL) + return -1; + name_str = js_get_function_name(ctx, prop); + JS_FreeAtom(ctx, prop); + if (JS_IsException(name_str)) + return -1; + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, name_str, flags) < 0) + return -1; + } + return 0; +} + +#define DEFINE_GLOBAL_LEX_VAR (1 << 7) +#define DEFINE_GLOBAL_FUNC_VAR (1 << 6) + +static JSValue JS_ThrowSyntaxErrorVarRedeclaration(JSContext *ctx, JSAtom prop) +{ + return JS_ThrowSyntaxErrorAtom(ctx, "redeclaration of '%s'", prop); +} + +/* flags is 0, DEFINE_GLOBAL_LEX_VAR or DEFINE_GLOBAL_FUNC_VAR */ +/* XXX: could support exotic global object. */ +static int JS_CheckDefineGlobalVar(JSContext *ctx, JSAtom prop, int flags) +{ + JSObject *p; + JSShapeProperty *prs; + + p = JS_VALUE_GET_OBJ(ctx->global_obj); + prs = find_own_property1(p, prop); + /* XXX: should handle JS_PROP_AUTOINIT */ + if (flags & DEFINE_GLOBAL_LEX_VAR) { + if (prs && !(prs->flags & JS_PROP_CONFIGURABLE)) + goto fail_redeclaration; + } else { + if (!prs && !p->extensible) + goto define_error; + if (flags & DEFINE_GLOBAL_FUNC_VAR) { + if (prs) { + if (!(prs->flags & JS_PROP_CONFIGURABLE) && + ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET || + ((prs->flags & (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)) != + (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)))) { + define_error: + JS_ThrowTypeErrorAtom(ctx, "cannot define variable '%s'", + prop); + return -1; + } + } + } + } + /* check if there already is a lexical declaration */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property1(p, prop); + if (prs) { + fail_redeclaration: + JS_ThrowSyntaxErrorVarRedeclaration(ctx, prop); + return -1; + } + return 0; +} + +/* def_flags is (0, DEFINE_GLOBAL_LEX_VAR) | + JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE */ +/* XXX: could support exotic global object. */ +static int JS_DefineGlobalVar(JSContext *ctx, JSAtom prop, int def_flags) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + JSValue val; + int flags; + + if (def_flags & DEFINE_GLOBAL_LEX_VAR) { + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + flags = JS_PROP_ENUMERABLE | (def_flags & JS_PROP_WRITABLE) | + JS_PROP_CONFIGURABLE; + val = JS_UNINITIALIZED; + } else { + p = JS_VALUE_GET_OBJ(ctx->global_obj); + flags = JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | + (def_flags & JS_PROP_CONFIGURABLE); + val = JS_UNDEFINED; + } + prs = find_own_property1(p, prop); + if (prs) + return 0; + if (!p->extensible) + return 0; + pr = add_property(ctx, p, prop, flags); + if (unlikely(!pr)) + return -1; + pr->u.value = val; + return 0; +} + +/* 'def_flags' is 0 or JS_PROP_CONFIGURABLE. */ +/* XXX: could support exotic global object. */ +static int JS_DefineGlobalFunction(JSContext *ctx, JSAtom prop, + JSValue func, int def_flags) +{ + + JSObject *p; + JSShapeProperty *prs; + int flags; + + p = JS_VALUE_GET_OBJ(ctx->global_obj); + prs = find_own_property1(p, prop); + flags = JS_PROP_HAS_VALUE | JS_PROP_THROW; + if (!prs || (prs->flags & JS_PROP_CONFIGURABLE)) { + flags |= JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | def_flags | + JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE; + } + if (JS_DefineProperty(ctx, ctx->global_obj, prop, func, + JS_UNDEFINED, JS_UNDEFINED, flags) < 0) + return -1; + return 0; +} + +static JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop, + bool throw_ref_error) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + + /* no exotic behavior is possible in global_var_obj */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property(&pr, p, prop); + if (prs) { + /* XXX: should handle JS_PROP_TMASK properties */ + if (unlikely(JS_IsUninitialized(pr->u.value))) + return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return js_dup(pr->u.value); + } + + /* fast path */ + p = JS_VALUE_GET_OBJ(ctx->global_obj); + prs = find_own_property(&pr, p, prop); + if (prs) { + if (likely((prs->flags & JS_PROP_TMASK) == 0)) + return js_dup(pr->u.value); + } + return JS_GetPropertyInternal(ctx, ctx->global_obj, prop, + ctx->global_obj, throw_ref_error); +} + +/* construct a reference to a global variable */ +static int JS_GetGlobalVarRef(JSContext *ctx, JSAtom prop, JSValue *sp) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + + /* no exotic behavior is possible in global_var_obj */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property(&pr, p, prop); + if (prs) { + /* XXX: should handle JS_PROP_AUTOINIT properties? */ + /* XXX: conformance: do these tests in + OP_put_var_ref/OP_get_var_ref ? */ + if (unlikely(JS_IsUninitialized(pr->u.value))) { + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) { + return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop); + } + sp[0] = js_dup(ctx->global_var_obj); + } else { + int ret; + ret = JS_HasProperty(ctx, ctx->global_obj, prop); + if (ret < 0) + return -1; + if (ret) { + sp[0] = js_dup(ctx->global_obj); + } else { + sp[0] = JS_UNDEFINED; + } + } + sp[1] = JS_AtomToValue(ctx, prop); + return 0; +} + +/* flag = 0: normal variable write + flag = 1: initialize lexical variable +*/ +static inline int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val, + int flag) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + int ret; + + /* no exotic behavior is possible in global_var_obj */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property(&pr, p, prop); + if (prs) { + /* XXX: should handle JS_PROP_AUTOINIT properties? */ + if (flag != 1) { + if (unlikely(JS_IsUninitialized(pr->u.value))) { + JS_FreeValue(ctx, val); + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) { + JS_FreeValue(ctx, val); + return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop); + } + } + set_value(ctx, &pr->u.value, val); + return 0; + } + + p = JS_VALUE_GET_OBJ(ctx->global_obj); + prs = find_own_property(&pr, p, prop); + if (prs) { + if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE | + JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) { + /* fast path */ + set_value(ctx, &pr->u.value, val); + return 0; + } + } + /* slow path */ + ret = JS_HasProperty(ctx, ctx->global_obj, prop); + if (ret < 0) { + JS_FreeValue(ctx, val); + return -1; + } + if (ret == 0 && is_strict_mode(ctx)) { + JS_FreeValue(ctx, val); + JS_ThrowReferenceErrorNotDefined(ctx, prop); + return -1; + } + return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, + JS_PROP_THROW_STRICT); +} + +/* return -1, false or true */ +static int JS_DeleteGlobalVar(JSContext *ctx, JSAtom prop) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + int ret; + + /* 9.1.1.4.7 DeleteBinding ( N ) */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property(&pr, p, prop); + if (prs) + return false; /* lexical variables cannot be deleted */ + ret = JS_HasProperty(ctx, ctx->global_obj, prop); + if (ret < 0) + return -1; + if (ret) { + return JS_DeleteProperty(ctx, ctx->global_obj, prop, 0); + } else { + return true; + } +} + +/* return -1, false or true. return false if not configurable or + invalid object. return -1 in case of exception. + flags can be 0, JS_PROP_THROW or JS_PROP_THROW_STRICT */ +int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags) +{ + JSValue obj1; + JSObject *p; + int res; + + obj1 = JS_ToObject(ctx, obj); + if (JS_IsException(obj1)) + return -1; + p = JS_VALUE_GET_OBJ(obj1); + res = delete_property(ctx, p, prop); + JS_FreeValue(ctx, obj1); + if (res != false) + return res; + if ((flags & JS_PROP_THROW) || + ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { + JS_ThrowTypeError(ctx, "could not delete property"); + return -1; + } + return false; +} + +int JS_DeletePropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, int flags) +{ + JSAtom prop; + int res; + + if ((uint64_t)idx <= JS_ATOM_MAX_INT) { + /* fast path for fast arrays */ + return JS_DeleteProperty(ctx, obj, __JS_AtomFromUInt32(idx), flags); + } + prop = JS_NewAtomInt64(ctx, idx); + if (prop == JS_ATOM_NULL) + return -1; + res = JS_DeleteProperty(ctx, obj, prop, flags); + JS_FreeAtom(ctx, prop); + return res; +} + +bool JS_IsFunction(JSContext *ctx, JSValueConst val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return false; + p = JS_VALUE_GET_OBJ(val); + switch(p->class_id) { + case JS_CLASS_BYTECODE_FUNCTION: + return true; + case JS_CLASS_PROXY: + return p->u.proxy_data->is_func; + default: + return (ctx->rt->class_array[p->class_id].call != NULL); + } +} + +static bool JS_IsCFunction(JSContext *ctx, JSValueConst val, JSCFunction *func, + int magic) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return false; + p = JS_VALUE_GET_OBJ(val); + if (p->class_id == JS_CLASS_C_FUNCTION) + return (p->u.cfunc.c_function.generic == func && p->u.cfunc.magic == magic); + else + return false; +} + +bool JS_IsConstructor(JSContext *ctx, JSValueConst val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return false; + p = JS_VALUE_GET_OBJ(val); + return p->is_constructor; +} + +bool JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, bool val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) + return false; + p = JS_VALUE_GET_OBJ(func_obj); + p->is_constructor = val; + return true; +} + +bool JS_IsRegExp(JSValueConst val) +{ + return JS_CLASS_REGEXP == JS_GetClassID(val); +} + +bool JS_IsMap(JSValueConst val) +{ + return JS_CLASS_MAP == JS_GetClassID(val); +} + +bool JS_IsSet(JSValueConst val) +{ + return JS_CLASS_SET == JS_GetClassID(val); +} + +bool JS_IsWeakRef(JSValueConst val) +{ + return JS_CLASS_WEAK_REF == JS_GetClassID(val); +} + +bool JS_IsWeakSet(JSValueConst val) +{ + return JS_CLASS_WEAKSET == JS_GetClassID(val); +} + +bool JS_IsWeakMap(JSValueConst val) +{ + return JS_CLASS_WEAKMAP == JS_GetClassID(val); +} + +bool JS_IsDataView(JSValueConst val) +{ + return JS_CLASS_DATAVIEW == JS_GetClassID(val); +} + +bool JS_IsError(JSValueConst val) +{ + return JS_CLASS_ERROR == JS_GetClassID(val); +} + +/* used to avoid catching interrupt exceptions */ +bool JS_IsUncatchableError(JSValueConst val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return false; + p = JS_VALUE_GET_OBJ(val); + return p->class_id == JS_CLASS_ERROR && p->is_uncatchable_error; +} + +static void js_set_uncatchable_error(JSContext *ctx, JSValueConst val, bool flag) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return; + p = JS_VALUE_GET_OBJ(val); + if (p->class_id == JS_CLASS_ERROR) + p->is_uncatchable_error = flag; +} + +void JS_SetUncatchableError(JSContext *ctx, JSValueConst val) +{ + js_set_uncatchable_error(ctx, val, true); +} + +void JS_ClearUncatchableError(JSContext *ctx, JSValueConst val) +{ + js_set_uncatchable_error(ctx, val, false); +} + +void JS_ResetUncatchableError(JSContext *ctx) +{ + js_set_uncatchable_error(ctx, ctx->rt->current_exception, false); +} + +int JS_SetOpaque(JSValueConst obj, void *opaque) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + p = JS_VALUE_GET_OBJ(obj); + // User code can't set the opaque of internal objects. + if (p->class_id >= JS_CLASS_INIT_COUNT) { + p->u.opaque = opaque; + return 0; + } + } + + return -1; +} + +/* |obj| must be a JSObject of an internal class. */ +static void JS_SetOpaqueInternal(JSValueConst obj, void *opaque) +{ + JSObject *p; + assert(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT); + p = JS_VALUE_GET_OBJ(obj); + assert(p->class_id < JS_CLASS_INIT_COUNT); + p->u.opaque = opaque; +} + +/* return NULL if not an object of class class_id */ +void *JS_GetOpaque(JSValueConst obj, JSClassID class_id) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return NULL; + p = JS_VALUE_GET_OBJ(obj); + if (p->class_id != class_id) + return NULL; + return p->u.opaque; +} + +void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id) +{ + void *p = JS_GetOpaque(obj, class_id); + if (unlikely(!p)) { + JS_ThrowTypeErrorInvalidClass(ctx, class_id); + } + return p; +} + +void *JS_GetAnyOpaque(JSValueConst obj, JSClassID *class_id) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { + *class_id = 0; + return NULL; + } + p = JS_VALUE_GET_OBJ(obj); + *class_id = p->class_id; + return p->u.opaque; +} + +static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint) +{ + int i; + bool force_ordinary; + + JSAtom method_name; + JSValue method, ret; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return val; + force_ordinary = hint & HINT_FORCE_ORDINARY; + hint &= ~HINT_FORCE_ORDINARY; + if (!force_ordinary) { + method = JS_GetProperty(ctx, val, JS_ATOM_Symbol_toPrimitive); + if (JS_IsException(method)) + goto exception; + /* ECMA says *If exoticToPrim is not undefined* but tests in + test262 use null as a non callable converter */ + if (!JS_IsUndefined(method) && !JS_IsNull(method)) { + JSAtom atom; + JSValue arg; + switch(hint) { + case HINT_STRING: + atom = JS_ATOM_string; + break; + case HINT_NUMBER: + atom = JS_ATOM_number; + break; + default: + case HINT_NONE: + atom = JS_ATOM_default; + break; + } + arg = JS_AtomToString(ctx, atom); + ret = JS_CallFree(ctx, method, val, 1, vc(&arg)); + JS_FreeValue(ctx, arg); + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, val); + if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) + return ret; + JS_FreeValue(ctx, ret); + return JS_ThrowTypeError(ctx, "toPrimitive"); + } + } + if (hint != HINT_STRING) + hint = HINT_NUMBER; + for(i = 0; i < 2; i++) { + if ((i ^ hint) == 0) { + method_name = JS_ATOM_toString; + } else { + method_name = JS_ATOM_valueOf; + } + method = JS_GetProperty(ctx, val, method_name); + if (JS_IsException(method)) + goto exception; + if (JS_IsFunction(ctx, method)) { + ret = JS_CallFree(ctx, method, val, 0, NULL); + if (JS_IsException(ret)) + goto exception; + if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) { + JS_FreeValue(ctx, val); + return ret; + } + JS_FreeValue(ctx, ret); + } else { + JS_FreeValue(ctx, method); + } + } + JS_ThrowTypeError(ctx, "toPrimitive"); +exception: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static JSValue JS_ToPrimitive(JSContext *ctx, JSValueConst val, int hint) +{ + return JS_ToPrimitiveFree(ctx, js_dup(val), hint); +} + +void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return; + p = JS_VALUE_GET_OBJ(obj); + p->is_HTMLDDA = true; +} + +static inline bool JS_IsHTMLDDA(JSContext *ctx, JSValueConst obj) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return false; + p = JS_VALUE_GET_OBJ(obj); + return p->is_HTMLDDA; +} + +static int JS_ToBoolFree(JSContext *ctx, JSValue val) +{ + uint32_t tag = JS_VALUE_GET_TAG(val); + switch(tag) { + case JS_TAG_INT: + return JS_VALUE_GET_INT(val) != 0; + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + return JS_VALUE_GET_INT(val); + case JS_TAG_EXCEPTION: + return -1; + case JS_TAG_STRING: + { + bool ret = JS_VALUE_GET_STRING(val)->len != 0; + JS_FreeValue(ctx, val); + return ret; + } + case JS_TAG_STRING_ROPE: + { + bool ret = JS_VALUE_GET_STRING_ROPE(val)->len != 0; + JS_FreeValue(ctx, val); + return ret; + } + case JS_TAG_SHORT_BIG_INT: + return JS_VALUE_GET_SHORT_BIG_INT(val) != 0; + case JS_TAG_BIG_INT: + { + JSBigInt *p = JS_VALUE_GET_PTR(val); + bool ret; + int i; + + /* fail safe: we assume it is not necessarily + normalized. Beginning from the MSB ensures that the + test is fast. */ + ret = false; + for(i = p->len - 1; i >= 0; i--) { + if (p->tab[i] != 0) { + ret = true; + break; + } + } + JS_FreeValue(ctx, val); + return ret; + } + case JS_TAG_OBJECT: + { + JSObject *p = JS_VALUE_GET_OBJ(val); + bool ret = !p->is_HTMLDDA; + JS_FreeValue(ctx, val); + return ret; + } + break; + default: + if (JS_TAG_IS_FLOAT64(tag)) { + double d = JS_VALUE_GET_FLOAT64(val); + return !isnan(d) && d != 0; + } else { + JS_FreeValue(ctx, val); + return true; + } + } +} + +int JS_ToBool(JSContext *ctx, JSValueConst val) +{ + return JS_ToBoolFree(ctx, js_dup(val)); +} + +/* pc points to pure ASCII or UTF-8, null terminated contents */ +static int skip_spaces(const char *pc) +{ + const uint8_t *p, *p_next, *p_start; + uint32_t c; + + p = p_start = (const uint8_t *)pc; + for (;;) { + c = *p++; + if (c < 0x80) { + if (!((c >= 0x09 && c <= 0x0d) || (c == 0x20))) + break; + } else { + c = utf8_decode(p - 1, &p_next); + /* no need to test for invalid UTF-8, 0xFFFD is not a space */ + if (!lre_is_space(c)) + break; + p = p_next; + } + } + return p - 1 - p_start; +} + +static inline int js_to_digit(int c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'A' && c <= 'Z') + return c - 'A' + 10; + else if (c >= 'a' && c <= 'z') + return c - 'a' + 10; + else + return 36; +} + +/* bigint support */ + +#define ADDC(res, carry_out, op1, op2, carry_in) \ +do { \ + js_limb_t __v, __a, __k, __k1; \ + __v = (op1); \ + __a = __v + (op2); \ + __k1 = __a < __v; \ + __k = (carry_in); \ + __a = __a + __k; \ + carry_out = (__a < __k) | __k1; \ + res = __a; \ +} while (0) + +/* a != 0 */ +static inline js_limb_t js_limb_clz(js_limb_t a) +{ + if (!a) + return JS_LIMB_BITS; + return clz32(a); +} + +static js_limb_t js_mp_add(js_limb_t *res, const js_limb_t *op1, const js_limb_t *op2, + js_limb_t n, js_limb_t carry) +{ + int i; + for(i = 0;i < n; i++) { + ADDC(res[i], carry, op1[i], op2[i], carry); + } + return carry; +} + +static js_limb_t js_mp_sub(js_limb_t *res, const js_limb_t *op1, const js_limb_t *op2, + int n, js_limb_t carry) +{ + int i; + js_limb_t k, a, v, k1; + + k = carry; + for(i=0;i v; + v = a - k; + k = (v > a) | k1; + res[i] = v; + } + return k; +} + +/* compute 0 - op2. carry = 0 or 1. */ +static js_limb_t js_mp_neg(js_limb_t *res, const js_limb_t *op2, int n) +{ + int i; + js_limb_t v, carry; + + carry = 1; + for(i=0;i> JS_LIMB_BITS; + } + return l; +} + +static js_limb_t js_mp_div1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n, + js_limb_t b, js_limb_t r) +{ + js_slimb_t i; + js_dlimb_t a1; + for(i = n - 1; i >= 0; i--) { + a1 = ((js_dlimb_t)r << JS_LIMB_BITS) | taba[i]; + tabr[i] = a1 / b; + r = a1 % b; + } + return r; +} + +/* tabr[] += taba[] * b, return the high word. */ +static js_limb_t js_mp_add_mul1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n, + js_limb_t b) +{ + js_limb_t i, l; + js_dlimb_t t; + + l = 0; + for(i = 0; i < n; i++) { + t = (js_dlimb_t)taba[i] * (js_dlimb_t)b + l + tabr[i]; + tabr[i] = t; + l = t >> JS_LIMB_BITS; + } + return l; +} + +/* size of the result : op1_size + op2_size. */ +static void js_mp_mul_basecase(js_limb_t *result, + const js_limb_t *op1, js_limb_t op1_size, + const js_limb_t *op2, js_limb_t op2_size) +{ + int i; + js_limb_t r; + + result[op1_size] = js_mp_mul1(result, op1, op1_size, op2[0], 0); + for(i=1;i> JS_LIMB_BITS); + } + return l; +} + +/* WARNING: d must be >= 2^(JS_LIMB_BITS-1) */ +static inline js_limb_t js_udiv1norm_init(js_limb_t d) +{ + js_limb_t a0, a1; + a1 = -d - 1; + a0 = -1; + return (((js_dlimb_t)a1 << JS_LIMB_BITS) | a0) / d; +} + +/* return the quotient and the remainder in '*pr'of 'a1*2^JS_LIMB_BITS+a0 + / d' with 0 <= a1 < d. */ +static inline js_limb_t js_udiv1norm(js_limb_t *pr, js_limb_t a1, js_limb_t a0, + js_limb_t d, js_limb_t d_inv) +{ + js_limb_t n1m, n_adj, q, r, ah; + js_dlimb_t a; + n1m = ((js_slimb_t)a0 >> (JS_LIMB_BITS - 1)); + n_adj = a0 + (n1m & d); + a = (js_dlimb_t)d_inv * (a1 - n1m) + n_adj; + q = (a >> JS_LIMB_BITS) + a1; + /* compute a - q * r and update q so that the remainder is\ + between 0 and d - 1 */ + a = ((js_dlimb_t)a1 << JS_LIMB_BITS) | a0; + a = a - (js_dlimb_t)q * d - d; + ah = a >> JS_LIMB_BITS; + q += 1 + ah; + r = (js_limb_t)a + (ah & d); + *pr = r; + return q; +} + +#define UDIV1NORM_THRESHOLD 3 + +/* b must be >= 1 << (JS_LIMB_BITS - 1) */ +static js_limb_t js_mp_div1norm(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n, + js_limb_t b, js_limb_t r) +{ + js_slimb_t i; + + if (n >= UDIV1NORM_THRESHOLD) { + js_limb_t b_inv; + b_inv = js_udiv1norm_init(b); + for(i = n - 1; i >= 0; i--) { + tabr[i] = js_udiv1norm(&r, r, taba[i], b, b_inv); + } + } else { + js_dlimb_t a1; + for(i = n - 1; i >= 0; i--) { + a1 = ((js_dlimb_t)r << JS_LIMB_BITS) | taba[i]; + tabr[i] = a1 / b; + r = a1 % b; + } + } + return r; +} + +/* base case division: divides taba[0..na-1] by tabb[0..nb-1]. tabb[nb + - 1] must be >= 1 << (JS_LIMB_BITS - 1). na - nb must be >= 0. 'taba' + is modified and contains the remainder (nb limbs). tabq[0..na-nb] + contains the quotient with tabq[na - nb] <= 1. */ +static void js_mp_divnorm(js_limb_t *tabq, js_limb_t *taba, js_limb_t na, + const js_limb_t *tabb, js_limb_t nb) +{ + js_limb_t r, a, c, q, v, b1, b1_inv, n, dummy_r; + int i, j; + + b1 = tabb[nb - 1]; + if (nb == 1) { + taba[0] = js_mp_div1norm(tabq, taba, na, b1, 0); + return; + } + n = na - nb; + + if (n >= UDIV1NORM_THRESHOLD) + b1_inv = js_udiv1norm_init(b1); + else + b1_inv = 0; + + /* first iteration: the quotient is only 0 or 1 */ + q = 1; + for(j = nb - 1; j >= 0; j--) { + if (taba[n + j] != tabb[j]) { + if (taba[n + j] < tabb[j]) + q = 0; + break; + } + } + tabq[n] = q; + if (q) { + js_mp_sub(taba + n, taba + n, tabb, nb, 0); + } + + for(i = n - 1; i >= 0; i--) { + if (unlikely(taba[i + nb] >= b1)) { + q = -1; + } else if (b1_inv) { + q = js_udiv1norm(&dummy_r, taba[i + nb], taba[i + nb - 1], b1, b1_inv); + } else { + js_dlimb_t al; + al = ((js_dlimb_t)taba[i + nb] << JS_LIMB_BITS) | taba[i + nb - 1]; + q = al / b1; + r = al % b1; + } + r = js_mp_sub_mul1(taba + i, tabb, nb, q); + + v = taba[i + nb]; + a = v - r; + c = (a > v); + taba[i + nb] = a; + + if (c != 0) { + /* negative result */ + for(;;) { + q--; + c = js_mp_add(taba + i, taba + i, tabb, nb, 0); + /* propagate carry and test if positive result */ + if (c != 0) { + if (++taba[i + nb] == 0) { + break; + } + } + } + } + tabq[i] = q; + } +} + +/* 1 <= shift <= JS_LIMB_BITS - 1 */ +static js_limb_t js_mp_shl(js_limb_t *tabr, const js_limb_t *taba, int n, + int shift) +{ + int i; + js_limb_t l, v; + l = 0; + for(i = 0; i < n; i++) { + v = taba[i]; + tabr[i] = (v << shift) | l; + l = v >> (JS_LIMB_BITS - shift); + } + return l; +} + +/* r = (a + high*B^n) >> shift. Return the remainder r (0 <= r < 2^shift). + 1 <= shift <= LIMB_BITS - 1 */ +static js_limb_t js_mp_shr(js_limb_t *tab_r, const js_limb_t *tab, int n, + int shift, js_limb_t high) +{ + int i; + js_limb_t l, a; + + l = high; + for(i = n - 1; i >= 0; i--) { + a = tab[i]; + tab_r[i] = (a >> shift) | (l << (JS_LIMB_BITS - shift)); + l = a; + } + return l & (((js_limb_t)1 << shift) - 1); +} + +static JSBigInt *js_bigint_new(JSContext *ctx, int len) +{ + JSBigInt *r; + if (len > JS_BIGINT_MAX_SIZE) { + JS_ThrowRangeError(ctx, "BigInt is too large to allocate"); + return NULL; + } + r = js_malloc(ctx, sizeof(JSBigInt) + len * sizeof(js_limb_t)); + if (!r) + return NULL; + r->header.ref_count = 1; + r->len = len; + return r; +} + +static JSBigInt *js_bigint_set_si(JSBigIntBuf *buf, js_slimb_t a) +{ + JSBigInt *r = (JSBigInt *)buf->big_int_buf; + r->header.ref_count = 0; /* fail safe */ + r->len = 1; + r->tab[0] = a; + return r; +} + +static JSBigInt *js_bigint_set_si64(JSBigIntBuf *buf, int64_t a) +{ + JSBigInt *r = (JSBigInt *)buf->big_int_buf; + r->header.ref_count = 0; /* fail safe */ + if (a >= INT32_MIN && a <= INT32_MAX) { + r->len = 1; + r->tab[0] = a; + } else { + r->len = 2; + r->tab[0] = a; + r->tab[1] = a >> JS_LIMB_BITS; + } + return r; +} + +/* val must be a short big int */ +static JSBigInt *js_bigint_set_short(JSBigIntBuf *buf, JSValueConst val) +{ + return js_bigint_set_si(buf, JS_VALUE_GET_SHORT_BIG_INT(val)); +} + +static __maybe_unused void js_bigint_dump1(JSContext *ctx, const char *str, + const js_limb_t *tab, int len) +{ + int i; + printf("%s: ", str); + for(i = len - 1; i >= 0; i--) { + printf(" %08x", tab[i]); + } + printf("\n"); +} + +static __maybe_unused void js_bigint_dump(JSContext *ctx, const char *str, + const JSBigInt *p) +{ + js_bigint_dump1(ctx, str, p->tab, p->len); +} + +static JSBigInt *js_bigint_new_si(JSContext *ctx, js_slimb_t a) +{ + JSBigInt *r; + r = js_bigint_new(ctx, 1); + if (!r) + return NULL; + r->tab[0] = a; + return r; +} + +static JSBigInt *js_bigint_new_si64(JSContext *ctx, int64_t a) +{ + if (a >= INT32_MIN && a <= INT32_MAX) { + return js_bigint_new_si(ctx, a); + } else { + JSBigInt *r; + r = js_bigint_new(ctx, 2); + if (!r) + return NULL; + r->tab[0] = a; + r->tab[1] = a >> 32; + return r; + } +} + +static JSBigInt *js_bigint_new_ui64(JSContext *ctx, uint64_t a) +{ + if (a <= INT64_MAX) { + return js_bigint_new_si64(ctx, a); + } else { + JSBigInt *r; + r = js_bigint_new(ctx, (65 + JS_LIMB_BITS - 1) / JS_LIMB_BITS); + if (!r) + return NULL; + r->tab[0] = a; + r->tab[1] = a >> 32; + r->tab[2] = 0; + return r; + } +} + +static JSBigInt *js_bigint_new_di(JSContext *ctx, js_sdlimb_t a) +{ + JSBigInt *r; + if (a == (js_slimb_t)a) { + r = js_bigint_new(ctx, 1); + if (!r) + return NULL; + r->tab[0] = a; + } else { + r = js_bigint_new(ctx, 2); + if (!r) + return NULL; + r->tab[0] = a; + r->tab[1] = a >> JS_LIMB_BITS; + } + return r; +} + +/* Remove redundant high order limbs. Warning: 'a' may be + reallocated. Can never fail. +*/ +static JSBigInt *js_bigint_normalize1(JSContext *ctx, JSBigInt *a, int l) +{ + js_limb_t v; + + assert(a->header.ref_count == 1); + while (l > 1) { + v = a->tab[l - 1]; + if ((v != 0 && v != -1) || + (v & 1) != (a->tab[l - 2] >> (JS_LIMB_BITS - 1))) { + break; + } + l--; + } + if (l != a->len) { + JSBigInt *a1; + /* realloc to reduce the size */ + a->len = l; + a1 = js_realloc(ctx, a, sizeof(JSBigInt) + l * sizeof(js_limb_t)); + if (a1) + a = a1; + } + return a; +} + +static JSBigInt *js_bigint_normalize(JSContext *ctx, JSBigInt *a) +{ + return js_bigint_normalize1(ctx, a, a->len); +} + +/* return 0 or 1 depending on the sign */ +static inline int js_bigint_sign(const JSBigInt *a) +{ + return a->tab[a->len - 1] >> (JS_LIMB_BITS - 1); +} + +static js_slimb_t js_bigint_get_si_sat(const JSBigInt *a) +{ + if (a->len == 1) { + return a->tab[0]; + } else { + if (js_bigint_sign(a)) + return INT32_MIN; + else + return INT32_MAX; + } +} + +/* add the op1 limb */ +static JSBigInt *js_bigint_extend(JSContext *ctx, JSBigInt *r, + js_limb_t op1) +{ + int n2 = r->len; + if ((op1 != 0 && op1 != -1) || + (op1 & 1) != r->tab[n2 - 1] >> (JS_LIMB_BITS - 1)) { + JSBigInt *r1; + r1 = js_realloc(ctx, r, + sizeof(JSBigInt) + (n2 + 1) * sizeof(js_limb_t)); + if (!r1) { + js_free(ctx, r); + return NULL; + } + r = r1; + r->len = n2 + 1; + r->tab[n2] = op1; + } else { + /* otherwise still need to normalize the result */ + r = js_bigint_normalize(ctx, r); + } + return r; +} + +/* return NULL in case of error. Compute a + b (b_neg = 0) or a - b + (b_neg = 1) */ +/* XXX: optimize */ +static JSBigInt *js_bigint_add(JSContext *ctx, const JSBigInt *a, + const JSBigInt *b, int b_neg) +{ + JSBigInt *r; + int n1, n2, i; + js_limb_t carry, op1, op2, a_sign, b_sign; + + n2 = max_int(a->len, b->len); + n1 = min_int(a->len, b->len); + r = js_bigint_new(ctx, n2); + if (!r) + return NULL; + /* XXX: optimize */ + /* common part */ + carry = b_neg; + for(i = 0; i < n1; i++) { + op1 = a->tab[i]; + op2 = b->tab[i] ^ (-b_neg); + ADDC(r->tab[i], carry, op1, op2, carry); + } + a_sign = -js_bigint_sign(a); + b_sign = (-js_bigint_sign(b)) ^ (-b_neg); + /* part with sign extension of one operand */ + if (a->len > b->len) { + for(i = n1; i < n2; i++) { + op1 = a->tab[i]; + ADDC(r->tab[i], carry, op1, b_sign, carry); + } + } else if (a->len < b->len) { + for(i = n1; i < n2; i++) { + op2 = b->tab[i] ^ (-b_neg); + ADDC(r->tab[i], carry, a_sign, op2, carry); + } + } + + /* part with sign extension for both operands. Extend the result + if necessary */ + return js_bigint_extend(ctx, r, a_sign + b_sign + carry); +} + +/* XXX: optimize */ +static JSBigInt *js_bigint_neg(JSContext *ctx, const JSBigInt *a) +{ + JSBigIntBuf buf; + JSBigInt *b; + b = js_bigint_set_si(&buf, 0); + return js_bigint_add(ctx, b, a, 1); +} + +static JSBigInt *js_bigint_mul(JSContext *ctx, const JSBigInt *a, + const JSBigInt *b) +{ + JSBigInt *r; + + r = js_bigint_new(ctx, a->len + b->len); + if (!r) + return NULL; + js_mp_mul_basecase(r->tab, a->tab, a->len, b->tab, b->len); + /* correct the result if negative operands (no overflow is + possible) */ + if (js_bigint_sign(a)) + js_mp_sub(r->tab + a->len, r->tab + a->len, b->tab, b->len, 0); + if (js_bigint_sign(b)) + js_mp_sub(r->tab + b->len, r->tab + b->len, a->tab, a->len, 0); + return js_bigint_normalize(ctx, r); +} + +/* return the division or the remainder. 'b' must be != 0. return NULL + in case of exception (division by zero or memory error) */ +static JSBigInt *js_bigint_divrem(JSContext *ctx, const JSBigInt *a, + const JSBigInt *b, bool is_rem) +{ + JSBigInt *r, *q; + js_limb_t *tabb, h; + int na, nb, a_sign, b_sign, shift; + + if (b->len == 1 && b->tab[0] == 0) { + JS_ThrowRangeError(ctx, "BigInt division by zero"); + return NULL; + } + + a_sign = js_bigint_sign(a); + b_sign = js_bigint_sign(b); + na = a->len; + nb = b->len; + + r = js_bigint_new(ctx, na + 2); + if (!r) + return NULL; + if (a_sign) { + js_mp_neg(r->tab, a->tab, na); + } else { + memcpy(r->tab, a->tab, na * sizeof(a->tab[0])); + } + /* normalize */ + while (na > 1 && r->tab[na - 1] == 0) + na--; + + tabb = js_malloc(ctx, nb * sizeof(tabb[0])); + if (!tabb) { + js_free(ctx, r); + return NULL; + } + if (b_sign) { + js_mp_neg(tabb, b->tab, nb); + } else { + memcpy(tabb, b->tab, nb * sizeof(tabb[0])); + } + /* normalize */ + while (nb > 1 && tabb[nb - 1] == 0) + nb--; + + /* trivial case if 'a' is small */ + if (na < nb) { + js_free(ctx, r); + js_free(ctx, tabb); + if (is_rem) { + /* r = a */ + r = js_bigint_new(ctx, a->len); + if (!r) + return NULL; + memcpy(r->tab, a->tab, a->len * sizeof(a->tab[0])); + return r; + } else { + /* q = 0 */ + return js_bigint_new_si(ctx, 0); + } + } + + /* normalize 'b' */ + shift = js_limb_clz(tabb[nb - 1]); + if (shift != 0) { + js_mp_shl(tabb, tabb, nb, shift); + h = js_mp_shl(r->tab, r->tab, na, shift); + if (h != 0) + r->tab[na++] = h; + } + + q = js_bigint_new(ctx, na - nb + 2); /* one more limb for the sign */ + if (!q) { + js_free(ctx, r); + js_free(ctx, tabb); + return NULL; + } + + // js_bigint_dump1(ctx, "a", r->tab, na); + // js_bigint_dump1(ctx, "b", tabb, nb); + js_mp_divnorm(q->tab, r->tab, na, tabb, nb); + js_free(ctx, tabb); + + if (is_rem) { + js_free(ctx, q); + if (shift != 0) + js_mp_shr(r->tab, r->tab, nb, shift, 0); + r->tab[nb++] = 0; + if (a_sign) + js_mp_neg(r->tab, r->tab, nb); + r = js_bigint_normalize1(ctx, r, nb); + return r; + } else { + js_free(ctx, r); + q->tab[na - nb + 1] = 0; + if (a_sign ^ b_sign) { + js_mp_neg(q->tab, q->tab, q->len); + } + q = js_bigint_normalize(ctx, q); + return q; + } +} + +/* and, or, xor */ +static JSBigInt *js_bigint_logic(JSContext *ctx, const JSBigInt *a, + const JSBigInt *b, OPCodeEnum op) +{ + JSBigInt *r; + js_limb_t b_sign; + int a_len, b_len, i; + + if (a->len < b->len) { + const JSBigInt *tmp; + tmp = a; + a = b; + b = tmp; + } + /* a_len >= b_len */ + a_len = a->len; + b_len = b->len; + b_sign = -js_bigint_sign(b); + + r = js_bigint_new(ctx, a_len); + if (!r) + return NULL; + switch(op) { + case OP_or: + for(i = 0; i < b_len; i++) { + r->tab[i] = a->tab[i] | b->tab[i]; + } + for(i = b_len; i < a_len; i++) { + r->tab[i] = a->tab[i] | b_sign; + } + break; + case OP_and: + for(i = 0; i < b_len; i++) { + r->tab[i] = a->tab[i] & b->tab[i]; + } + for(i = b_len; i < a_len; i++) { + r->tab[i] = a->tab[i] & b_sign; + } + break; + case OP_xor: + for(i = 0; i < b_len; i++) { + r->tab[i] = a->tab[i] ^ b->tab[i]; + } + for(i = b_len; i < a_len; i++) { + r->tab[i] = a->tab[i] ^ b_sign; + } + break; + default: + abort(); + } + return js_bigint_normalize(ctx, r); +} + +static JSBigInt *js_bigint_not(JSContext *ctx, const JSBigInt *a) +{ + JSBigInt *r; + int i; + + r = js_bigint_new(ctx, a->len); + if (!r) + return NULL; + for(i = 0; i < a->len; i++) { + r->tab[i] = ~a->tab[i]; + } + /* no normalization is needed */ + return r; +} + +static JSBigInt *js_bigint_shl(JSContext *ctx, const JSBigInt *a, + unsigned int shift1) +{ + int d, i, shift; + JSBigInt *r; + js_limb_t l; + + if (a->len == 1 && a->tab[0] == 0) + return js_bigint_new_si(ctx, 0); /* zero case */ + d = shift1 / JS_LIMB_BITS; + shift = shift1 % JS_LIMB_BITS; + r = js_bigint_new(ctx, a->len + d); + if (!r) + return NULL; + for(i = 0; i < d; i++) + r->tab[i] = 0; + if (shift == 0) { + for(i = 0; i < a->len; i++) { + r->tab[i + d] = a->tab[i]; + } + } else { + l = js_mp_shl(r->tab + d, a->tab, a->len, shift); + if (js_bigint_sign(a)) + l |= (js_limb_t)(-1) << shift; + r = js_bigint_extend(ctx, r, l); + } + return r; +} + +static JSBigInt *js_bigint_shr(JSContext *ctx, const JSBigInt *a, + unsigned int shift1) +{ + int d, i, shift, a_sign, n1; + JSBigInt *r; + + d = shift1 / JS_LIMB_BITS; + shift = shift1 % JS_LIMB_BITS; + a_sign = js_bigint_sign(a); + if (d >= a->len) + return js_bigint_new_si(ctx, -a_sign); + n1 = a->len - d; + r = js_bigint_new(ctx, n1); + if (!r) + return NULL; + if (shift == 0) { + for(i = 0; i < n1; i++) { + r->tab[i] = a->tab[i + d]; + } + /* no normalization is needed */ + } else { + js_mp_shr(r->tab, a->tab + d, n1, shift, -a_sign); + r = js_bigint_normalize(ctx, r); + } + return r; +} + +static JSBigInt *js_bigint_pow(JSContext *ctx, const JSBigInt *a, JSBigInt *b) +{ + uint32_t e; + int n_bits, i; + JSBigInt *r, *r1; + + /* b must be >= 0 */ + if (js_bigint_sign(b)) { + JS_ThrowRangeError(ctx, "BigInt negative exponent"); + return NULL; + } + if (b->len == 1 && b->tab[0] == 0) { + /* a^0 = 1 */ + return js_bigint_new_si(ctx, 1); + } else if (a->len == 1) { + js_limb_t v; + bool is_neg; + + v = a->tab[0]; + if (v <= 1) + return js_bigint_new_si(ctx, v); + else if (v == -1) + return js_bigint_new_si(ctx, 1 - 2 * (b->tab[0] & 1)); + is_neg = (js_slimb_t)v < 0; + if (is_neg) + v = -v; + if ((v & (v - 1)) == 0) { + uint64_t e1; + int n; + /* v = 2^n */ + n = JS_LIMB_BITS - 1 - js_limb_clz(v); + if (b->len > 1) + goto overflow; + if (b->tab[0] > INT32_MAX) + goto overflow; + e = b->tab[0]; + e1 = (uint64_t)e * n; + if (e1 > JS_BIGINT_MAX_SIZE * JS_LIMB_BITS) + goto overflow; + e = e1; + if (is_neg) + is_neg = b->tab[0] & 1; + r = js_bigint_new(ctx, + (e + JS_LIMB_BITS + 1 - is_neg) / JS_LIMB_BITS); + if (!r) + return NULL; + memset(r->tab, 0, sizeof(r->tab[0]) * r->len); + r->tab[e / JS_LIMB_BITS] = + (js_limb_t)(1 - 2 * is_neg) << (e % JS_LIMB_BITS); + return r; + } + } + if (b->len > 1) + goto overflow; + if (b->tab[0] > INT32_MAX) + goto overflow; + e = b->tab[0]; + n_bits = 32 - clz32(e); + + r = js_bigint_new(ctx, a->len); + if (!r) + return NULL; + memcpy(r->tab, a->tab, a->len * sizeof(a->tab[0])); + for(i = n_bits - 2; i >= 0; i--) { + r1 = js_bigint_mul(ctx, r, r); + if (!r1) + return NULL; + js_free(ctx, r); + r = r1; + if ((e >> i) & 1) { + r1 = js_bigint_mul(ctx, r, a); + if (!r1) + return NULL; + js_free(ctx, r); + r = r1; + } + } + return r; + overflow: + JS_ThrowRangeError(ctx, "BigInt is too large"); + return NULL; +} + +/* return (mant, exp) so that abs(a) ~ mant*2^(exp - (limb_bits - + 1). a must be != 0. */ +static uint64_t js_bigint_get_mant_exp(JSContext *ctx, + int *pexp, const JSBigInt *a) +{ + js_limb_t t[4 - JS_LIMB_BITS / 32], carry, v, low_bits; + int n1, n2, sgn, shift, i, j, e; + uint64_t a1, a0; + + n2 = 4 - JS_LIMB_BITS / 32; + n1 = a->len - n2; + sgn = js_bigint_sign(a); + + /* low_bits != 0 if there are a non zero low bit in abs(a) */ + low_bits = 0; + carry = sgn; + for(i = 0; i < n1; i++) { + v = (a->tab[i] ^ (-sgn)) + carry; + carry = v < carry; + low_bits |= v; + } + /* get the n2 high limbs of abs(a) */ + for(j = 0; j < n2; j++) { + i = j + n1; + if (i < 0) { + v = 0; + } else { + v = (a->tab[i] ^ (-sgn)) + carry; + carry = v < carry; + } + t[j] = v; + } + + a1 = ((uint64_t)t[2] << 32) | t[1]; + a0 = (uint64_t)t[0] << 32; + a0 |= (low_bits != 0); + /* normalize */ + { + shift = clz64(a1); + if (shift != 0) { + a1 = (a1 << shift) | (a0 >> (64 - shift)); + a0 <<= shift; + } + } + a1 |= (a0 != 0); /* keep the bits for the final rounding */ + /* compute the exponent */ + e = a->len * JS_LIMB_BITS - shift - 1; + *pexp = e; + return a1; +} + +/* shift left with round to nearest, ties to even. n >= 1 */ +static uint64_t shr_rndn(uint64_t a, int n) +{ + uint64_t addend = ((a >> n) & 1) + ((1 << (n - 1)) - 1); + return (a + addend) >> n; +} + +/* convert to float64 with round to nearest, ties to even. Return + +/-infinity if too large. */ +static double js_bigint_to_float64(JSContext *ctx, const JSBigInt *a) +{ + int sgn, e; + uint64_t mant; + + if (a->len == 1) { + /* fast case, including zero */ + return (double)(js_slimb_t)a->tab[0]; + } + + sgn = js_bigint_sign(a); + mant = js_bigint_get_mant_exp(ctx, &e, a); + if (e > 1023) { + /* overflow: return infinity */ + mant = 0; + e = 1024; + } else { + mant = (mant >> 1) | (mant & 1); /* avoid overflow in rounding */ + mant = shr_rndn(mant, 10); + /* rounding can cause an overflow */ + if (mant >= ((uint64_t)1 << 53)) { + mant >>= 1; + e++; + } + mant &= (((uint64_t)1 << 52) - 1); + } + return uint64_as_float64(((uint64_t)sgn << 63) | + ((uint64_t)(e + 1023) << 52) | + mant); +} + +/* return (1, NULL) if not an integer, (2, NULL) if NaN or Infinity, + (0, n) if an integer, (0, NULL) in case of memory error */ +static JSBigInt *js_bigint_from_float64(JSContext *ctx, int *pres, double a1) +{ + uint64_t a = float64_as_uint64(a1); + int sgn, e, shift; + uint64_t mant; + JSBigIntBuf buf; + JSBigInt *r; + + sgn = a >> 63; + e = (a >> 52) & ((1 << 11) - 1); + mant = a & (((uint64_t)1 << 52) - 1); + if (e == 2047) { + /* NaN, Infinity */ + *pres = 2; + return NULL; + } + if (e == 0 && mant == 0) { + /* zero */ + *pres = 0; + return js_bigint_new_si(ctx, 0); + } + e -= 1023; + /* 0 < a < 1 : not an integer */ + if (e < 0) + goto not_an_integer; + mant |= (uint64_t)1 << 52; + if (e < 52) { + shift = 52 - e; + /* check that there is no fractional part */ + if (mant & (((uint64_t)1 << shift) - 1)) { + not_an_integer: + *pres = 1; + return NULL; + } + mant >>= shift; + e = 0; + } else { + e -= 52; + } + if (sgn) + mant = -mant; + /* the integer is mant*2^e */ + r = js_bigint_set_si64(&buf, (int64_t)mant); + *pres = 0; + return js_bigint_shl(ctx, r, e); +} + +/* return -1, 0, 1 or (2) (unordered) */ +static int js_bigint_float64_cmp(JSContext *ctx, const JSBigInt *a, + double b) +{ + int b_sign, a_sign, e, f; + uint64_t mant, b1, a_mant; + + b1 = float64_as_uint64(b); + b_sign = b1 >> 63; + e = (b1 >> 52) & ((1 << 11) - 1); + mant = b1 & (((uint64_t)1 << 52) - 1); + a_sign = js_bigint_sign(a); + if (e == 2047) { + if (mant != 0) { + /* NaN */ + return 2; + } else { + /* +/- infinity */ + return 2 * b_sign - 1; + } + } else if (e == 0 && mant == 0) { + /* b = +/-0 */ + if (a->len == 1 && a->tab[0] == 0) + return 0; + else + return 1 - 2 * a_sign; + } else if (a->len == 1 && a->tab[0] == 0) { + /* a = 0, b != 0 */ + return 2 * b_sign - 1; + } else if (a_sign != b_sign) { + return 1 - 2 * a_sign; + } else { + e -= 1023; + /* Note: handling denormals is not necessary because we + compare to integers hence f >= 0 */ + /* compute f so that 2^f <= abs(a) < 2^(f+1) */ + a_mant = js_bigint_get_mant_exp(ctx, &f, a); + if (f != e) { + if (f < e) + return -1; + else + return 1; + } else { + mant = (mant | ((uint64_t)1 << 52)) << 11; /* align to a_mant */ + if (a_mant < mant) + return 2 * a_sign - 1; + else if (a_mant > mant) + return 1 - 2 * a_sign; + else + return 0; + } + } +} + +/* return -1, 0 or 1 */ +static int js_bigint_cmp(JSContext *ctx, const JSBigInt *a, + const JSBigInt *b) +{ + int a_sign, b_sign, res, i; + a_sign = js_bigint_sign(a); + b_sign = js_bigint_sign(b); + if (a_sign != b_sign) { + res = 1 - 2 * a_sign; + } else { + /* we assume the numbers are normalized */ + if (a->len != b->len) { + if (a->len < b->len) + res = 2 * a_sign - 1; + else + res = 1 - 2 * a_sign; + } else { + res = 0; + for(i = a->len -1; i >= 0; i--) { + if (a->tab[i] != b->tab[i]) { + if (a->tab[i] < b->tab[i]) + res = -1; + else + res = 1; + break; + } + } + } + } + return res; +} + +/* contains 10^i */ +static const js_limb_t js_pow_dec[JS_LIMB_DIGITS + 1] = { + 1U, + 10U, + 100U, + 1000U, + 10000U, + 100000U, + 1000000U, + 10000000U, + 100000000U, + 1000000000U, +}; + +/* syntax: [-]digits in base radix. Return NULL if memory error. radix + = 10, 2, 8 or 16. */ +static JSBigInt *js_bigint_from_string(JSContext *ctx, + const char *str, int radix) +{ + const char *p = str; + size_t n_digits1; + int is_neg, n_digits, n_limbs, len, log2_radix, n_bits, i; + JSBigInt *r; + js_limb_t v, c, h; + + is_neg = 0; + if (*p == '-') { + is_neg = 1; + p++; + } + while (*p == '0') + p++; + n_digits1 = strlen(p); + /* the real check for overflox is done js_bigint_new(). Here + we just avoid integer overflow */ + if (n_digits1 > JS_BIGINT_MAX_SIZE * JS_LIMB_BITS) { + JS_ThrowRangeError(ctx, "BigInt is too large to allocate"); + return NULL; + } + n_digits = n_digits1; + log2_radix = 32 - clz32(radix - 1); /* ceil(log2(radix)) */ + /* compute the maximum number of limbs */ + if (radix == 10) { + n_bits = (n_digits * 27 + 7) / 8; /* >= ceil(n_digits * log2(10)) */ + } else { + n_bits = n_digits * log2_radix; + } + /* we add one extra bit for the sign */ + n_limbs = max_int(1, n_bits / JS_LIMB_BITS + 1); + r = js_bigint_new(ctx, n_limbs); + if (!r) + return NULL; + if (radix == 10) { + int digits_per_limb = JS_LIMB_DIGITS; + len = 1; + r->tab[0] = 0; + for(;;) { + /* XXX: slow */ + v = 0; + for(i = 0; i < digits_per_limb; i++) { + c = js_to_digit(*p); + if (c >= radix) + break; + p++; + v = v * 10 + c; + } + if (i == 0) + break; + if (len == 1 && r->tab[0] == 0) { + r->tab[0] = v; + } else { + h = js_mp_mul1(r->tab, r->tab, len, js_pow_dec[i], v); + if (h != 0) { + r->tab[len++] = h; + } + } + } + /* add one extra limb to have the correct sign*/ + if ((r->tab[len - 1] >> (JS_LIMB_BITS - 1)) != 0) + r->tab[len++] = 0; + r->len = len; + } else { + unsigned int bit_pos, shift, pos; + + /* power of two base: no multiplication is needed */ + r->len = n_limbs; + memset(r->tab, 0, sizeof(r->tab[0]) * n_limbs); + for(i = 0; i < n_digits; i++) { + c = js_to_digit(p[n_digits - 1 - i]); + assert(c < radix); + bit_pos = i * log2_radix; + shift = bit_pos & (JS_LIMB_BITS - 1); + pos = bit_pos / JS_LIMB_BITS; + r->tab[pos] |= c << shift; + /* if log2_radix does not divide JS_LIMB_BITS, needed an + additional op */ + if (shift + log2_radix > JS_LIMB_BITS) { + r->tab[pos + 1] |= c >> (JS_LIMB_BITS - shift); + } + } + } + r = js_bigint_normalize(ctx, r); + /* XXX: could do it in place */ + if (is_neg) { + JSBigInt *r1; + r1 = js_bigint_neg(ctx, r); + js_free(ctx, r); + r = r1; + } + return r; +} + +/* 2 <= base <= 36 */ +static char const digits[36] = { + '0','1','2','3','4','5','6','7','8','9', + 'a','b','c','d','e','f','g','h','i','j', + 'k','l','m','n','o','p','q','r','s','t', + 'u','v','w','x','y','z' +}; + +/* special version going backwards */ +/* XXX: use dtoa.c */ +static char *js_u64toa(char *q, int64_t n, unsigned int base) +{ + int digit; + if (base == 10) { + /* division by known base uses multiplication */ + do { + digit = (uint64_t)n % 10; + n = (uint64_t)n / 10; + *--q = '0' + digit; + } while (n != 0); + } else { + do { + digit = (uint64_t)n % base; + n = (uint64_t)n / base; + *--q = digits[digit]; + } while (n != 0); + } + return q; +} + +/* len >= 1. 2 <= radix <= 36 */ +static char *js_limb_to_a(char *q, js_limb_t n, unsigned int radix, int len) +{ + int digit, i; + + if (radix == 10) { + /* specific case with constant divisor */ + /* XXX: optimize */ + for(i = 0; i < len; i++) { + digit = (js_limb_t)n % 10; + n = (js_limb_t)n / 10; + *--q = digit + '0'; + } + } else { + for(i = 0; i < len; i++) { + digit = (js_limb_t)n % radix; + n = (js_limb_t)n / radix; + *--q = digits[digit]; + } + } + return q; +} + +#define JS_RADIX_MAX 36 + +static const uint8_t js_digits_per_limb_table[JS_RADIX_MAX - 1] = { +32,20,16,13,12,11,10,10, 9, 9, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, +}; + +static const js_limb_t js_radix_base_table[JS_RADIX_MAX - 1] = { + 0x00000000, 0xcfd41b91, 0x00000000, 0x48c27395, + 0x81bf1000, 0x75db9c97, 0x40000000, 0xcfd41b91, + 0x3b9aca00, 0x8c8b6d2b, 0x19a10000, 0x309f1021, + 0x57f6c100, 0x98c29b81, 0x00000000, 0x18754571, + 0x247dbc80, 0x3547667b, 0x4c4b4000, 0x6b5a6e1d, + 0x94ace180, 0xcaf18367, 0x0b640000, 0x0e8d4a51, + 0x1269ae40, 0x17179149, 0x1cb91000, 0x23744899, + 0x2b73a840, 0x34e63b41, 0x40000000, 0x4cfa3cc1, + 0x5c13d840, 0x6d91b519, 0x81bf1000, +}; + +static JSValue js_bigint_to_string1(JSContext *ctx, JSValueConst val, int radix) +{ + if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) { + char buf[66]; + int len; + len = i64toa_radix(buf, JS_VALUE_GET_SHORT_BIG_INT(val), radix); + return js_new_string8_len(ctx, buf, len); + } else { + JSBigInt *r, *tmp = NULL; + char *buf, *q, *buf_end; + int is_neg, n_bits, log2_radix, n_digits; + bool is_binary_radix; + JSValue res; + + assert(JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT); + r = JS_VALUE_GET_PTR(val); + if (r->len == 1 && r->tab[0] == 0) { + /* '0' case */ + return js_new_string8_len(ctx, "0", 1); + } + is_binary_radix = ((radix & (radix - 1)) == 0); + is_neg = js_bigint_sign(r); + if (is_neg) { + tmp = js_bigint_neg(ctx, r); + if (!tmp) + return JS_EXCEPTION; + r = tmp; + } else if (!is_binary_radix) { + /* need to modify 'r' */ + tmp = js_bigint_new(ctx, r->len); + if (!tmp) + return JS_EXCEPTION; + memcpy(tmp->tab, r->tab, r->len * sizeof(r->tab[0])); + r = tmp; + } + log2_radix = 31 - clz32(radix); /* floor(log2(radix)) */ + n_bits = r->len * JS_LIMB_BITS - js_limb_clz(r->tab[r->len - 1]); + /* n_digits is exact only if radix is a power of + two. Otherwise it is >= the exact number of digits */ + n_digits = (n_bits + log2_radix - 1) / log2_radix; + /* XXX: could directly build the JSString */ + buf = js_malloc(ctx, n_digits + is_neg + 1); + if (!buf) { + js_free(ctx, tmp); + return JS_EXCEPTION; + } + q = buf + n_digits + is_neg + 1; + *--q = '\0'; + buf_end = q; + if (!is_binary_radix) { + int len; + js_limb_t radix_base, v; + radix_base = js_radix_base_table[radix - 2]; + len = r->len; + for(;;) { + /* remove leading zero limbs */ + while (len > 1 && r->tab[len - 1] == 0) + len--; + if (len == 1 && r->tab[0] < radix_base) { + v = r->tab[0]; + if (v != 0) { + q = js_u64toa(q, v, radix); + } + break; + } else { + v = js_mp_div1(r->tab, r->tab, len, radix_base, 0); + q = js_limb_to_a(q, v, radix, js_digits_per_limb_table[radix - 2]); + } + } + } else { + int i, shift; + unsigned int bit_pos, pos, c; + + /* radix is a power of two */ + for(i = 0; i < n_digits; i++) { + bit_pos = i * log2_radix; + pos = bit_pos / JS_LIMB_BITS; + shift = bit_pos % JS_LIMB_BITS; + c = r->tab[pos] >> shift; + if ((shift + log2_radix) > JS_LIMB_BITS && + (pos + 1) < r->len) { + c |= r->tab[pos + 1] << (JS_LIMB_BITS - shift); + } + c &= (radix - 1); + *--q = digits[c]; + } + } + if (is_neg) + *--q = '-'; + js_free(ctx, tmp); + res = js_new_string8_len(ctx, q, buf_end - q); + js_free(ctx, buf); + return res; + } +} + +/* if possible transform a BigInt to short big and free it, otherwise + return a normal bigint */ +static JSValue JS_CompactBigInt(JSContext *ctx, JSBigInt *p) +{ + JSValue res; + if (p->len == 1) { + res = __JS_NewShortBigInt(ctx, (js_slimb_t)p->tab[0]); + js_free(ctx, p); + return res; + } else { + return JS_MKPTR(JS_TAG_BIG_INT, p); + } +} + +#define ATOD_INT_ONLY (1 << 0) +/* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */ +#define ATOD_ACCEPT_BIN_OCT (1 << 2) +/* accept O prefix as octal if radix == 0 and properly formed (Annex B) */ +#define ATOD_ACCEPT_LEGACY_OCTAL (1 << 4) +/* accept _ between digits as a digit separator */ +#define ATOD_ACCEPT_UNDERSCORES (1 << 5) +/* allow a suffix to override the type */ +#define ATOD_ACCEPT_SUFFIX (1 << 6) +/* default type */ +#define ATOD_TYPE_MASK (3 << 7) +#define ATOD_TYPE_FLOAT64 (0 << 7) +#define ATOD_TYPE_BIG_INT (1 << 7) +/* accept -0x1 */ +#define ATOD_ACCEPT_PREFIX_AFTER_SIGN (1 << 10) + +/* return an exception in case of memory error. Return JS_NAN if + invalid syntax */ +/* XXX: directly use js_atod() */ +static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, + int radix, int flags) +{ + const char *p, *p_start; + int sep, is_neg; + bool is_float, has_legacy_octal; + int atod_type = flags & ATOD_TYPE_MASK; + char buf1[64], *buf; + int i, j, len; + bool buf_allocated = false; + JSValue val; + JSATODTempMem atod_mem; + + /* optional separator between digits */ + sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256; + has_legacy_octal = false; + + p = str; + p_start = p; + is_neg = 0; + if (p[0] == '+') { + p++; + p_start++; + if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN)) + goto no_radix_prefix; + } else if (p[0] == '-') { + p++; + p_start++; + is_neg = 1; + if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN)) + goto no_radix_prefix; + } + if (p[0] == '0') { + if ((p[1] == 'x' || p[1] == 'X') && + (radix == 0 || radix == 16)) { + p += 2; + radix = 16; + } else if ((p[1] == 'o' || p[1] == 'O') && + radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) { + p += 2; + radix = 8; + } else if ((p[1] == 'b' || p[1] == 'B') && + radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) { + p += 2; + radix = 2; + } else if ((p[1] >= '0' && p[1] <= '9') && + radix == 0 && (flags & ATOD_ACCEPT_LEGACY_OCTAL)) { + int i; + has_legacy_octal = true; + sep = 256; + for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++) + continue; + if (p[i] == '8' || p[i] == '9') + goto no_prefix; + p += 1; + radix = 8; + } else { + goto no_prefix; + } + /* there must be a digit after the prefix */ + if (js_to_digit((uint8_t)*p) >= radix) + goto fail; + no_prefix: ; + } else { + no_radix_prefix: + if (!(flags & ATOD_INT_ONLY) && + (atod_type == ATOD_TYPE_FLOAT64) && + js__strstart(p, "Infinity", &p)) { + double d = INFINITY; + if (is_neg) + d = -d; + val = js_float64(d); + goto done; + } + } + if (radix == 0) + radix = 10; + is_float = false; + p_start = p; + while (js_to_digit((uint8_t)*p) < radix + || (*p == sep && (radix != 10 || + p != p_start + 1 || p[-1] != '0') && + js_to_digit((uint8_t)p[1]) < radix)) { + p++; + } + if (!(flags & ATOD_INT_ONLY) && radix == 10) { + if (*p == '.' && (p > p_start || js_to_digit((uint8_t)p[1]) < radix)) { + is_float = true; + p++; + if (*p == sep) + goto fail; + while (js_to_digit((uint8_t)*p) < radix || + (*p == sep && js_to_digit((uint8_t)p[1]) < radix)) + p++; + } + if (p > p_start && (*p == 'e' || *p == 'E')) { + const char *p1 = p + 1; + is_float = true; + if (*p1 == '+') { + p1++; + } else if (*p1 == '-') { + p1++; + } + if (is_digit((uint8_t)*p1)) { + p = p1 + 1; + while (is_digit((uint8_t)*p) || (*p == sep && is_digit((uint8_t)p[1]))) + p++; + } + } + } + if (p == p_start) + goto fail; + + buf = buf1; + buf_allocated = false; + len = p - p_start; + if (unlikely((len + 2) > sizeof(buf1))) { + buf = js_malloc_rt(ctx->rt, len + 2); /* no exception raised */ + if (!buf) + goto mem_error; + buf_allocated = true; + } + /* remove the separators and the radix prefixes */ + j = 0; + if (is_neg) + buf[j++] = '-'; + for (i = 0; i < len; i++) { + if (p_start[i] != '_') + buf[j++] = p_start[i]; + } + buf[j] = '\0'; + + if (flags & ATOD_ACCEPT_SUFFIX) { + if (*p == 'n') { + p++; + atod_type = ATOD_TYPE_BIG_INT; + } + } + + switch(atod_type) { + case ATOD_TYPE_FLOAT64: + { + double d; + d = js_atod(buf, NULL, radix, is_float ? 0 : JS_ATOD_INT_ONLY, + &atod_mem); + /* return int or float64 */ + val = js_number(d); + } + break; + case ATOD_TYPE_BIG_INT: + { + JSBigInt *r; + if (has_legacy_octal || is_float) + goto fail; + r = js_bigint_from_string(ctx, buf, radix); + if (!r) { + val = JS_EXCEPTION; + goto done; + } + val = JS_CompactBigInt(ctx, r); + } + break; + default: + abort(); + } + +done: + if (buf_allocated) + js_free_rt(ctx->rt, buf); + if (pp) + *pp = p; + return val; + fail: + val = JS_NAN; + goto done; + mem_error: + val = JS_ThrowOutOfMemory(ctx); + goto done; +} + +typedef enum JSToNumberHintEnum { + TON_FLAG_NUMBER, + TON_FLAG_NUMERIC, +} JSToNumberHintEnum; + +static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, + JSToNumberHintEnum flag) +{ + uint32_t tag; + JSValue ret; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_BIG_INT: + case JS_TAG_SHORT_BIG_INT: + if (flag != TON_FLAG_NUMERIC) { + JS_FreeValue(ctx, val); + return JS_ThrowTypeError(ctx, "cannot convert BigInt to number"); + } + ret = val; + break; + case JS_TAG_FLOAT64: + case JS_TAG_INT: + case JS_TAG_EXCEPTION: + ret = val; + break; + case JS_TAG_BOOL: + case JS_TAG_NULL: + ret = js_int32(JS_VALUE_GET_INT(val)); + break; + case JS_TAG_UNDEFINED: + ret = JS_NAN; + break; + case JS_TAG_OBJECT: + val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); + if (JS_IsException(val)) + return JS_EXCEPTION; + goto redo; + case JS_TAG_STRING: + case JS_TAG_STRING_ROPE: + { + const char *str; + const char *p; + size_t len; + + str = JS_ToCStringLen(ctx, &len, val); + JS_FreeValue(ctx, val); + if (!str) + return JS_EXCEPTION; + p = str; + p += skip_spaces(p); + if ((p - str) == len) { + ret = JS_NewInt32(ctx, 0); + } else { + int flags = ATOD_ACCEPT_BIN_OCT; + ret = js_atof(ctx, p, &p, 0, flags); + if (!JS_IsException(ret)) { + p += skip_spaces(p); + if ((p - str) != len) { + JS_FreeValue(ctx, ret); + ret = JS_NAN; + } + } + } + JS_FreeCString(ctx, str); + } + break; + case JS_TAG_SYMBOL: + JS_FreeValue(ctx, val); + return JS_ThrowTypeError(ctx, "cannot convert symbol to number"); + default: + JS_FreeValue(ctx, val); + ret = JS_NAN; + break; + } + return ret; +} + +static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val) +{ + return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER); +} + +static JSValue JS_ToNumericFree(JSContext *ctx, JSValue val) +{ + return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC); +} + +static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val) +{ + return JS_ToNumericFree(ctx, js_dup(val)); +} + +static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres, + JSValue val) +{ + double d; + uint32_t tag; + + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + goto fail; + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + d = JS_VALUE_GET_INT(val); + break; + case JS_TAG_FLOAT64: + d = JS_VALUE_GET_FLOAT64(val); + break; + default: + abort(); + } + *pres = d; + return 0; +fail: + *pres = NAN; + return -1; +} + +static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val) +{ + uint32_t tag; + + tag = JS_VALUE_GET_TAG(val); + if (tag <= JS_TAG_NULL) { + *pres = JS_VALUE_GET_INT(val); + return 0; + } else if (JS_TAG_IS_FLOAT64(tag)) { + *pres = JS_VALUE_GET_FLOAT64(val); + return 0; + } else { + return __JS_ToFloat64Free(ctx, pres, val); + } +} + +int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val) +{ + return JS_ToFloat64Free(ctx, pres, js_dup(val)); +} + +JSValue JS_ToNumber(JSContext *ctx, JSValueConst val) +{ + return JS_ToNumberFree(ctx, js_dup(val)); +} + +/* same as JS_ToNumber() but return 0 in case of NaN/Undefined */ +static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) +{ + uint32_t tag; + JSValue ret; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + ret = js_int32(JS_VALUE_GET_INT(val)); + break; + case JS_TAG_FLOAT64: + { + double d = JS_VALUE_GET_FLOAT64(val); + if (isnan(d)) { + ret = js_int32(0); + } else { + /* convert -0 to +0 */ + d = trunc(d) + 0.0; + ret = js_number(d); + } + } + break; + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + return val; + goto redo; + } + return ret; +} + +/* Note: the integer value is satured to 32 bits */ +static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val) +{ + uint32_t tag; + int ret; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + ret = JS_VALUE_GET_INT(val); + break; + case JS_TAG_EXCEPTION: + *pres = 0; + return -1; + case JS_TAG_FLOAT64: + { + double d = JS_VALUE_GET_FLOAT64(val); + if (isnan(d)) { + ret = 0; + } else { + if (d < INT32_MIN) + ret = INT32_MIN; + else if (d > INT32_MAX) + ret = INT32_MAX; + else + ret = (int)d; + } + } + break; + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } + *pres = ret; + return 0; +} + +static int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValueConst val) +{ + return JS_ToInt32SatFree(ctx, pres, js_dup(val)); +} + +static int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValueConst val, + int min, int max, int min_offset) +{ + int res = JS_ToInt32SatFree(ctx, pres, js_dup(val)); + if (res == 0) { + if (*pres < min) { + *pres += min_offset; + if (*pres < min) + *pres = min; + } else { + if (*pres > max) + *pres = max; + } + } + return res; +} + +static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val) +{ + uint32_t tag; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + *pres = JS_VALUE_GET_INT(val); + return 0; + case JS_TAG_EXCEPTION: + *pres = 0; + return -1; + case JS_TAG_FLOAT64: + { + double d = JS_VALUE_GET_FLOAT64(val); + if (isnan(d)) { + *pres = 0; + } else { + if (d < INT64_MIN) + *pres = INT64_MIN; + else if (d >= 0x1p63) + *pres = INT64_MAX; + else + *pres = (int64_t)d; + } + } + return 0; + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } +} + +int JS_ToInt64Sat(JSContext *ctx, int64_t *pres, JSValueConst val) +{ + return JS_ToInt64SatFree(ctx, pres, js_dup(val)); +} + +int JS_ToInt64Clamp(JSContext *ctx, int64_t *pres, JSValueConst val, + int64_t min, int64_t max, int64_t neg_offset) +{ + int res = JS_ToInt64SatFree(ctx, pres, js_dup(val)); + if (res == 0) { + if (*pres < 0) + *pres += neg_offset; + if (*pres < min) + *pres = min; + else if (*pres > max) + *pres = max; + } + return res; +} + +/* Same as JS_ToInt32Free() but with a 64 bit result. Return (<0, 0) + in case of exception */ +static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val) +{ + uint32_t tag; + int64_t ret; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + ret = JS_VALUE_GET_INT(val); + break; + case JS_TAG_FLOAT64: + { + JSFloat64Union u; + double d; + int e; + d = JS_VALUE_GET_FLOAT64(val); + u.d = d; + /* we avoid doing fmod(x, 2^64) */ + e = (u.u64 >> 52) & 0x7ff; + if (likely(e <= (1023 + 62))) { + /* fast case */ + ret = (int64_t)d; + } else if (e <= (1023 + 62 + 53)) { + uint64_t v; + /* remainder modulo 2^64 */ + v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52); + ret = v << ((e - 1023) - 52); + /* take the sign into account */ + if (u.u64 >> 63) + if (ret != INT64_MIN) + ret = -ret; + } else { + ret = 0; /* also handles NaN and +inf */ + } + } + break; + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } + *pres = ret; + return 0; +} + +int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val) +{ + return JS_ToInt64Free(ctx, pres, js_dup(val)); +} + +int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val) +{ + if (JS_IsBigInt(val)) + return JS_ToBigInt64(ctx, pres, val); + else + return JS_ToInt64(ctx, pres, val); +} + +/* return (<0, 0) in case of exception */ +static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val) +{ + uint32_t tag; + int32_t ret; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + ret = JS_VALUE_GET_INT(val); + break; + case JS_TAG_FLOAT64: + { + JSFloat64Union u; + double d; + int e; + d = JS_VALUE_GET_FLOAT64(val); + u.d = d; + /* we avoid doing fmod(x, 2^32) */ + e = (u.u64 >> 52) & 0x7ff; + if (likely(e <= (1023 + 30))) { + /* fast case */ + ret = (int32_t)d; + } else if (e <= (1023 + 30 + 53)) { + uint64_t v; + /* remainder modulo 2^32 */ + v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52); + v = v << ((e - 1023) - 52 + 32); + ret = v >> 32; + /* take the sign into account */ + if (u.u64 >> 63) + if (ret != INT32_MIN) + ret = -ret; + } else { + ret = 0; /* also handles NaN and +inf */ + } + } + break; + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } + *pres = ret; + return 0; +} + +int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val) +{ + return JS_ToInt32Free(ctx, pres, js_dup(val)); +} + +static inline int JS_ToUint32Free(JSContext *ctx, uint32_t *pres, JSValue val) +{ + return JS_ToInt32Free(ctx, (int32_t *)pres, val); +} + +static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val) +{ + uint32_t tag; + int res; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + res = JS_VALUE_GET_INT(val); + res = max_int(0, min_int(255, res)); + break; + case JS_TAG_FLOAT64: + { + double d = JS_VALUE_GET_FLOAT64(val); + if (isnan(d)) { + res = 0; + } else { + if (d < 0) + res = 0; + else if (d > 255) + res = 255; + else + res = lrint(d); + } + } + break; + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } + *pres = res; + return 0; +} + +static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, + JSValue val, bool is_array_ctor) +{ + uint32_t tag, len; + + tag = JS_VALUE_GET_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + { + int v; + v = JS_VALUE_GET_INT(val); + if (v < 0) + goto fail; + len = v; + } + break; + default: + if (JS_TAG_IS_FLOAT64(tag)) { + double d; + d = JS_VALUE_GET_FLOAT64(val); + if (!(d >= 0 && d <= UINT32_MAX)) + goto fail; + len = (uint32_t)d; + if (len != d) + goto fail; + } else { + uint32_t len1; + + if (is_array_ctor) { + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + return -1; + /* cannot recurse because val is a number */ + if (JS_ToArrayLengthFree(ctx, &len, val, true)) + return -1; + } else { + /* legacy behavior: must do the conversion twice and compare */ + if (JS_ToUint32(ctx, &len, val)) { + JS_FreeValue(ctx, val); + return -1; + } + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + return -1; + /* cannot recurse because val is a number */ + if (JS_ToArrayLengthFree(ctx, &len1, val, false)) + return -1; + if (len1 != len) { + fail: + JS_ThrowRangeError(ctx, "invalid array length"); + return -1; + } + } + } + break; + } + *plen = len; + return 0; +} + +#define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1) + +static bool is_safe_integer(double d) +{ + return isfinite(d) && floor(d) == d && + fabs(d) <= (double)MAX_SAFE_INTEGER; +} + +int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val) +{ + int64_t v; + if (JS_ToInt64Sat(ctx, &v, val)) + return -1; + if (v < 0 || v > MAX_SAFE_INTEGER) { + JS_ThrowRangeError(ctx, "invalid array index"); + *plen = 0; + return -1; + } + *plen = v; + return 0; +} + +/* convert a value to a length between 0 and MAX_SAFE_INTEGER. + return -1 for exception */ +static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen, + JSValue val) +{ + int res = JS_ToInt64Clamp(ctx, plen, val, 0, MAX_SAFE_INTEGER, 0); + JS_FreeValue(ctx, val); + return res; +} + +/* Note: can return an exception */ +static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val) +{ + double d; + if (!JS_IsNumber(val)) + return false; + if (unlikely(JS_ToFloat64(ctx, &d, val))) + return -1; + return isfinite(d) && floor(d) == d; +} + +static bool JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val) +{ + uint32_t tag; + + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + { + int v; + v = JS_VALUE_GET_INT(val); + return (v < 0); + } + case JS_TAG_FLOAT64: + { + JSFloat64Union u; + u.d = JS_VALUE_GET_FLOAT64(val); + return (u.u64 >> 63); + } + case JS_TAG_SHORT_BIG_INT: + return (JS_VALUE_GET_SHORT_BIG_INT(val) < 0); + case JS_TAG_BIG_INT: + { + JSBigInt *p = JS_VALUE_GET_PTR(val); + return js_bigint_sign(p); + } + default: + return false; + } +} + +static JSValue js_bigint_to_string(JSContext *ctx, JSValueConst val) +{ + return js_bigint_to_string1(ctx, val, 10); +} + +/*---- floating point number to string conversions ----*/ + +static JSValue js_dtoa2(JSContext *ctx, + double d, int radix, int n_digits, int flags) +{ + char static_buf[128], *buf, *tmp_buf; + int len, len_max; + JSValue res; + JSDTOATempMem dtoa_mem; + len_max = js_dtoa_max_len(d, radix, n_digits, flags); + + /* longer buffer may be used if radix != 10 */ + if (len_max > sizeof(static_buf) - 1) { + tmp_buf = js_malloc(ctx, len_max + 1); + if (!tmp_buf) + return JS_EXCEPTION; + buf = tmp_buf; + } else { + tmp_buf = NULL; + buf = static_buf; + } + len = js_dtoa(buf, d, radix, n_digits, flags, &dtoa_mem); + res = js_new_string8_len(ctx, buf, len); + js_free(ctx, tmp_buf); + return res; +} + +static JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, + int flags) +{ + uint32_t tag; + char buf[32]; + size_t len; + + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_STRING: + return js_dup(val); + case JS_TAG_STRING_ROPE: + return js_linearize_string_rope(ctx, val); + case JS_TAG_INT: + len = i32toa(buf, JS_VALUE_GET_INT(val)); + return js_new_string8_len(ctx, buf, len); + case JS_TAG_BOOL: + return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ? + JS_ATOM_true : JS_ATOM_false); + case JS_TAG_NULL: + return JS_AtomToString(ctx, JS_ATOM_null); + case JS_TAG_UNDEFINED: + return JS_AtomToString(ctx, JS_ATOM_undefined); + case JS_TAG_EXCEPTION: + return JS_EXCEPTION; + case JS_TAG_OBJECT: + if (flags & JS_TO_STRING_NO_SIDE_EFFECTS) { + return js_new_string8(ctx, "{}"); + } else { + JSValue val1, ret; + val1 = JS_ToPrimitive(ctx, val, HINT_STRING); + if (JS_IsException(val1)) + return val1; + ret = JS_ToStringInternal(ctx, val1, flags); + JS_FreeValue(ctx, val1); + return ret; + } + break; + case JS_TAG_FUNCTION_BYTECODE: + return js_new_string8(ctx, "[function bytecode]"); + case JS_TAG_SYMBOL: + if (flags & JS_TO_STRING_IS_PROPERTY_KEY) { + return js_dup(val); + } else { + return JS_ThrowTypeError(ctx, "cannot convert symbol to string"); + } + case JS_TAG_FLOAT64: + return js_dtoa2(ctx, JS_VALUE_GET_FLOAT64(val), 10, 0, + JS_DTOA_FORMAT_FREE); + case JS_TAG_SHORT_BIG_INT: + case JS_TAG_BIG_INT: + return js_bigint_to_string(ctx, val); + case JS_TAG_UNINITIALIZED: + return js_new_string8(ctx, "[uninitialized]"); + default: + return js_new_string8(ctx, "[unsupported type]"); + } +} + +JSValue JS_ToString(JSContext *ctx, JSValueConst val) +{ + return JS_ToStringInternal(ctx, val, /*flags*/0); +} + +static JSValue JS_ToStringFree(JSContext *ctx, JSValue val) +{ + JSValue ret; + ret = JS_ToString(ctx, val); + JS_FreeValue(ctx, val); + return ret; +} + +static JSValue JS_ToLocaleStringFree(JSContext *ctx, JSValue val) +{ + if (JS_IsUndefined(val) || JS_IsNull(val)) + return JS_ToStringFree(ctx, val); + return JS_InvokeFree(ctx, val, JS_ATOM_toLocaleString, 0, NULL); +} + +static JSValue JS_ToPropertyKeyInternal(JSContext *ctx, JSValueConst val, + int flags) +{ + return JS_ToStringInternal(ctx, val, flags | JS_TO_STRING_IS_PROPERTY_KEY); +} + +JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val) +{ + return JS_ToPropertyKeyInternal(ctx, val, /*flags*/0); +} + +static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValueConst val) +{ + uint32_t tag = JS_VALUE_GET_TAG(val); + if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) + return JS_ThrowTypeError(ctx, "null or undefined are forbidden"); + return JS_ToString(ctx, val); +} + +static JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1) +{ + JSValue val; + JSString *p; + int i; + uint32_t c; + StringBuffer b_s, *b = &b_s; + char buf[16]; + + val = JS_ToStringCheckObject(ctx, val1); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_STRING(val); + + if (string_buffer_init(ctx, b, p->len + 2)) + goto fail; + + if (string_buffer_putc8(b, '\"')) + goto fail; + for(i = 0; i < p->len; ) { + c = string_getc(p, &i); + switch(c) { + case '\t': + c = 't'; + goto quote; + case '\r': + c = 'r'; + goto quote; + case '\n': + c = 'n'; + goto quote; + case '\b': + c = 'b'; + goto quote; + case '\f': + c = 'f'; + goto quote; + case '\"': + case '\\': + quote: + if (string_buffer_putc8(b, '\\')) + goto fail; + if (string_buffer_putc8(b, c)) + goto fail; + break; + default: + if (c < 32 || is_surrogate(c)) { + snprintf(buf, sizeof(buf), "\\u%04x", c); + if (string_buffer_write8(b, (uint8_t*)buf, 6)) + goto fail; + } else { + if (string_buffer_putc(b, c)) + goto fail; + } + break; + } + } + if (string_buffer_putc8(b, '\"')) + goto fail; + JS_FreeValue(ctx, val); + return string_buffer_end(b); + fail: + JS_FreeValue(ctx, val); + string_buffer_free(b); + return JS_EXCEPTION; +} + +static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt) +{ + printf("%14s %4s %4s %14s %10s %s\n", + "ADDRESS", "REFS", "SHRF", "PROTO", "CLASS", "PROPS"); +} + +/* for debug only: dump an object without side effect */ +static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) +{ + uint32_t i; + char atom_buf[ATOM_GET_STR_BUF_SIZE]; + JSShape *sh; + JSShapeProperty *prs; + JSProperty *pr; + bool is_first = true; + + /* XXX: should encode atoms with special characters */ + sh = p->shape; /* the shape can be NULL while freeing an object */ + printf("%14p %4d ", + (void *)p, + p->header.ref_count); + if (sh) { + printf("%3d%c %14p ", + sh->header.ref_count, + " *"[sh->is_hashed], + (void *)sh->proto); + } else { + printf("%3s %14s ", "-", "-"); + } + printf("%10s ", + JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), rt->class_array[p->class_id].class_name)); + if (p->is_exotic && p->fast_array) { + printf("[ "); + for(i = 0; i < p->u.array.count; i++) { + if (i != 0) + printf(", "); + switch (p->class_id) { + case JS_CLASS_ARRAY: + case JS_CLASS_ARGUMENTS: + JS_DumpValue(rt, p->u.array.u.values[i]); + break; + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_INT8_ARRAY: + case JS_CLASS_UINT8_ARRAY: + case JS_CLASS_INT16_ARRAY: + case JS_CLASS_UINT16_ARRAY: + case JS_CLASS_INT32_ARRAY: + case JS_CLASS_UINT32_ARRAY: + case JS_CLASS_BIG_INT64_ARRAY: + case JS_CLASS_BIG_UINT64_ARRAY: + case JS_CLASS_FLOAT16_ARRAY: + case JS_CLASS_FLOAT32_ARRAY: + case JS_CLASS_FLOAT64_ARRAY: + { + int size = 1 << typed_array_size_log2(p->class_id); + const uint8_t *b = p->u.array.u.uint8_ptr + i * size; + while (size-- > 0) + printf("%02X", *b++); + } + break; + } + } + printf(" ] "); + } + + if (sh) { + printf("{ "); + for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { + if (prs->atom != JS_ATOM_NULL) { + pr = &p->prop[i]; + if (!is_first) + printf(", "); + printf("%s: ", + JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), prs->atom)); + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + printf("[getset %p %p]", (void *)pr->u.getset.getter, + (void *)pr->u.getset.setter); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + printf("[varref %p]", (void *)pr->u.var_ref); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + printf("[autoinit %p %d %p]", + (void *)js_autoinit_get_realm(pr), + js_autoinit_get_id(pr), + (void *)pr->u.init.opaque); + } else { + JS_DumpValue(rt, pr->u.value); + } + is_first = false; + } + } + printf(" }"); + } + + if (js_class_has_bytecode(p->class_id)) { + JSFunctionBytecode *b = p->u.func.function_bytecode; + JSVarRef **var_refs; + if (b->closure_var_count) { + var_refs = p->u.func.var_refs; + printf(" Closure:"); + for(i = 0; i < b->closure_var_count; i++) { + printf(" "); + JS_DumpValue(rt, var_refs[i]->value); + } + if (p->u.func.home_object) { + printf(" HomeObject: "); + JS_DumpValue(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object)); + } + } + } + printf("\n"); +} + +static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p) +{ + if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { + JS_DumpObject(rt, (JSObject *)p); + } else { + printf("%14p %4d ", + (void *)p, + p->ref_count); + switch(p->gc_obj_type) { + case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: + printf("[function bytecode]"); + break; + case JS_GC_OBJ_TYPE_SHAPE: + printf("[shape]"); + break; + case JS_GC_OBJ_TYPE_VAR_REF: + printf("[var_ref]"); + break; + case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: + printf("[async_function]"); + break; + case JS_GC_OBJ_TYPE_JS_CONTEXT: + printf("[js_context]"); + break; + default: + printf("[unknown %d]", p->gc_obj_type); + break; + } + printf("\n"); + } +} + +static __maybe_unused void JS_DumpValue(JSRuntime *rt, JSValueConst val) +{ + uint32_t tag = JS_VALUE_GET_NORM_TAG(val); + const char *str; + + switch(tag) { + case JS_TAG_INT: + printf("%d", JS_VALUE_GET_INT(val)); + break; + case JS_TAG_BOOL: + if (JS_VALUE_GET_BOOL(val)) + str = "true"; + else + str = "false"; + goto print_str; + case JS_TAG_NULL: + str = "null"; + goto print_str; + case JS_TAG_EXCEPTION: + str = "exception"; + goto print_str; + case JS_TAG_UNINITIALIZED: + str = "uninitialized"; + goto print_str; + case JS_TAG_UNDEFINED: + str = "undefined"; + print_str: + printf("%s", str); + break; + case JS_TAG_FLOAT64: + printf("%.14g", JS_VALUE_GET_FLOAT64(val)); + break; + case JS_TAG_SHORT_BIG_INT: + printf("%" PRId64 "n", (int64_t)JS_VALUE_GET_SHORT_BIG_INT(val)); + break; + case JS_TAG_BIG_INT: + { + JSBigInt *p = JS_VALUE_GET_PTR(val); + int sgn, i; + /* In order to avoid allocations we just dump the limbs */ + sgn = js_bigint_sign(p); + if (sgn) + printf("BigInt.asIntN(%d,", p->len * JS_LIMB_BITS); + printf("0x"); + for(i = p->len - 1; i >= 0; i--) { + if (i != p->len - 1) + printf("_"); + printf("%08x", p->tab[i]); + } + printf("n"); + if (sgn) + printf(")"); + } + break; + case JS_TAG_STRING: + { + JSString *p; + p = JS_VALUE_GET_STRING(val); + JS_DumpString(rt, p); + } + break; + case JS_TAG_STRING_ROPE: + { + JSStringRope *r = JS_VALUE_GET_STRING_ROPE(val); + printf("[rope len=%d depth=%d]", r->len, r->depth); + } + break; + case JS_TAG_FUNCTION_BYTECODE: + { + JSFunctionBytecode *b = JS_VALUE_GET_PTR(val); + char buf[ATOM_GET_STR_BUF_SIZE]; + if (b->func_name) { + printf("[bytecode %s]", JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name)); + } else { + printf("[bytecode (anonymous)]"); + } + } + break; + case JS_TAG_OBJECT: + { + JSObject *p = JS_VALUE_GET_OBJ(val); + JSAtom atom = rt->class_array[p->class_id].class_name; + char atom_buf[ATOM_GET_STR_BUF_SIZE]; + printf("[%s %p]", + JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), atom), (void *)p); + } + break; + case JS_TAG_SYMBOL: + { + JSAtomStruct *p = JS_VALUE_GET_PTR(val); + char atom_buf[ATOM_GET_STR_BUF_SIZE]; + printf("Symbol(%s)", + JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), js_get_atom_index(rt, p))); + } + break; + case JS_TAG_MODULE: + printf("[module]"); + break; + default: + printf("[unknown tag %d]", tag); + break; + } +} + +bool JS_IsArray(JSValueConst val) +{ + if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(val); + return p->class_id == JS_CLASS_ARRAY; + } + return false; +} + +/* return -1 if exception (proxy case) or true/false */ +static int js_is_array(JSContext *ctx, JSValueConst val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) { + p = JS_VALUE_GET_OBJ(val); + if (unlikely(p->class_id == JS_CLASS_PROXY)) + return js_proxy_isArray(ctx, val); + else + return p->class_id == JS_CLASS_ARRAY; + } else { + return false; + } +} + +static double js_math_pow(double a, double b) +{ + double d; + + if (unlikely(!isfinite(b)) && fabs(a) == 1) { + /* not compatible with IEEE 754 */ + d = NAN; + } else { + JS_X87_FPCW_SAVE_AND_ADJUST(fpcw); + d = pow(a, b); + JS_X87_FPCW_RESTORE(fpcw); + } + return d; +} + +JSValue JS_NewBigInt64(JSContext *ctx, int64_t v) +{ + if (v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX) { + return __JS_NewShortBigInt(ctx, v); + } else { + JSBigInt *p; + p = js_bigint_new_si64(ctx, v); + if (!p) + return JS_EXCEPTION; + return JS_MKPTR(JS_TAG_BIG_INT, p); + } +} + +JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v) +{ + if (v <= JS_SHORT_BIG_INT_MAX) { + return __JS_NewShortBigInt(ctx, v); + } else { + JSBigInt *p; + p = js_bigint_new_ui64(ctx, v); + if (!p) + return JS_EXCEPTION; + return JS_MKPTR(JS_TAG_BIG_INT, p); + } +} + +/* return NaN if bad bigint literal */ +static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val) +{ + const char *str, *p; + size_t len; + int flags; + + str = JS_ToCStringLen(ctx, &len, val); + JS_FreeValue(ctx, val); + if (!str) + return JS_EXCEPTION; + p = str; + p += skip_spaces(p); + if ((p - str) == len) { + val = JS_NewBigInt64(ctx, 0); + } else { + flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT; + val = js_atof(ctx, p, &p, 0, flags); + p += skip_spaces(p); + if (!JS_IsException(val)) { + if ((p - str) != len) { + JS_FreeValue(ctx, val); + val = JS_NAN; + } + } + } + JS_FreeCString(ctx, str); + return val; +} + +static JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val) +{ + val = JS_StringToBigInt(ctx, val); + if (JS_VALUE_IS_NAN(val)) + return JS_ThrowSyntaxError(ctx, "invalid BigInt literal"); + return val; +} + +/* JS Numbers are not allowed */ +static JSValue JS_ToBigIntFree(JSContext *ctx, JSValue val) +{ + uint32_t tag; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_SHORT_BIG_INT: + case JS_TAG_BIG_INT: + break; + case JS_TAG_INT: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + case JS_TAG_FLOAT64: + goto fail; + case JS_TAG_BOOL: + val = __JS_NewShortBigInt(ctx, JS_VALUE_GET_INT(val)); + break; + case JS_TAG_STRING: + case JS_TAG_STRING_ROPE: + val = JS_StringToBigIntErr(ctx, val); + if (JS_IsException(val)) + return val; + goto redo; + case JS_TAG_OBJECT: + val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); + if (JS_IsException(val)) + return val; + goto redo; + default: + fail: + JS_FreeValue(ctx, val); + return JS_ThrowTypeError(ctx, "cannot convert to bigint"); + } + return val; +} + +static JSValue JS_ToBigInt(JSContext *ctx, JSValueConst val) +{ + return JS_ToBigIntFree(ctx, js_dup(val)); +} + +/* XXX: merge with JS_ToInt64Free with a specific flag */ +static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val) +{ + uint64_t res; + + val = JS_ToBigIntFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) { + res = JS_VALUE_GET_SHORT_BIG_INT(val); + } else { + JSBigInt *p = JS_VALUE_GET_PTR(val); + /* return the value mod 2^64 */ + res = p->tab[0]; + if (p->len >= 2) + res |= (uint64_t)p->tab[1] << 32; + JS_FreeValue(ctx, val); + } + *pres = res; + return 0; +} + +int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val) +{ + return JS_ToBigInt64Free(ctx, pres, js_dup(val)); +} + +int JS_ToBigUint64(JSContext *ctx, uint64_t *pres, JSValueConst val) +{ + return JS_ToBigInt64Free(ctx, (int64_t *)pres, js_dup(val)); +} + +static no_inline __exception int js_unary_arith_slow(JSContext *ctx, + JSValue *sp, + OPCodeEnum op) +{ + JSValue op1; + int v; + uint32_t tag; + JSBigIntBuf buf1; + JSBigInt *p1; + + op1 = sp[-1]; + /* fast path for float64 */ + if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1))) + goto handle_float64; + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) + goto exception; + tag = JS_VALUE_GET_TAG(op1); + switch(tag) { + case JS_TAG_INT: + { + int64_t v64; + v64 = JS_VALUE_GET_INT(op1); + switch(op) { + case OP_inc: + case OP_dec: + v = 2 * (op - OP_dec) - 1; + v64 += v; + break; + case OP_plus: + break; + case OP_neg: + if (v64 == 0) { + sp[-1] = js_float64(-0.0); + return 0; + } else { + v64 = -v64; + } + break; + default: + abort(); + } + sp[-1] = js_int64(v64); + } + break; + case JS_TAG_SHORT_BIG_INT: + { + int64_t v; + v = JS_VALUE_GET_SHORT_BIG_INT(op1); + switch(op) { + case OP_plus: + JS_ThrowTypeError(ctx, "bigint argument with unary +"); + goto exception; + case OP_inc: + if (v == JS_SHORT_BIG_INT_MAX) + goto bigint_slow_case; + sp[-1] = __JS_NewShortBigInt(ctx, v + 1); + break; + case OP_dec: + if (v == JS_SHORT_BIG_INT_MIN) + goto bigint_slow_case; + sp[-1] = __JS_NewShortBigInt(ctx, v - 1); + break; + case OP_neg: + v = JS_VALUE_GET_SHORT_BIG_INT(op1); + if (v == JS_SHORT_BIG_INT_MIN) { + bigint_slow_case: + p1 = js_bigint_set_short(&buf1, op1); + goto bigint_slow_case1; + } + sp[-1] = __JS_NewShortBigInt(ctx, -v); + break; + default: + abort(); + } + } + break; + case JS_TAG_BIG_INT: + { + JSBigInt *r; + p1 = JS_VALUE_GET_PTR(op1); + bigint_slow_case1: + switch(op) { + case OP_plus: + JS_ThrowTypeError(ctx, "bigint argument with unary +"); + JS_FreeValue(ctx, op1); + goto exception; + case OP_inc: + case OP_dec: + { + JSBigIntBuf buf2; + JSBigInt *p2; + p2 = js_bigint_set_si(&buf2, 2 * (op - OP_dec) - 1); + r = js_bigint_add(ctx, p1, p2, 0); + } + break; + case OP_neg: + r = js_bigint_neg(ctx, p1); + break; + case OP_not: + r = js_bigint_not(ctx, p1); + break; + default: + abort(); + } + JS_FreeValue(ctx, op1); + if (!r) + goto exception; + sp[-1] = JS_CompactBigInt(ctx, r); + } + break; + default: + handle_float64: + { + double d; + d = JS_VALUE_GET_FLOAT64(op1); + switch(op) { + case OP_inc: + case OP_dec: + v = 2 * (op - OP_dec) - 1; + d += v; + break; + case OP_plus: + break; + case OP_neg: + d = -d; + break; + default: + abort(); + } + sp[-1] = js_float64(d); + } + break; + } + return 0; + exception: + sp[-1] = JS_UNDEFINED; + return -1; +} + +static __exception int js_post_inc_slow(JSContext *ctx, + JSValue *sp, OPCodeEnum op) +{ + JSValue op1; + + /* XXX: allow custom operators */ + op1 = sp[-1]; + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + sp[-1] = JS_UNDEFINED; + return -1; + } + sp[-1] = op1; + sp[0] = js_dup(op1); + return js_unary_arith_slow(ctx, sp + 1, op - OP_post_dec + OP_dec); +} + +static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) +{ + JSValue op1; + + op1 = sp[-1]; + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) + goto exception; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) { + sp[-1] = __JS_NewShortBigInt(ctx, ~JS_VALUE_GET_SHORT_BIG_INT(op1)); + } else if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) { + JSBigInt *r; + r = js_bigint_not(ctx, JS_VALUE_GET_PTR(op1)); + JS_FreeValue(ctx, op1); + if (!r) + goto exception; + sp[-1] = JS_CompactBigInt(ctx, r); + } else { + int32_t v1; + if (unlikely(JS_ToInt32Free(ctx, &v1, op1))) + goto exception; + sp[-1] = js_int32(~v1); + } + return 0; + exception: + sp[-1] = JS_UNDEFINED; + return -1; +} + +static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp, + OPCodeEnum op) +{ + JSValue op1, op2; + uint32_t tag1, tag2; + double d1, d2; + + op1 = sp[-2]; + op2 = sp[-1]; + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + /* fast path for float operations */ + if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) { + d1 = JS_VALUE_GET_FLOAT64(op1); + d2 = JS_VALUE_GET_FLOAT64(op2); + goto handle_float64; + } + /* fast path for short big int operations */ + if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) { + js_slimb_t v1, v2; + js_sdlimb_t v; + v1 = JS_VALUE_GET_SHORT_BIG_INT(op1); + v2 = JS_VALUE_GET_SHORT_BIG_INT(op2); + switch(op) { + case OP_sub: + v = (js_sdlimb_t)v1 - (js_sdlimb_t)v2; + break; + case OP_mul: + v = (js_sdlimb_t)v1 * (js_sdlimb_t)v2; + break; + case OP_div: + if (v2 == 0 || + ((js_limb_t)v1 == (js_limb_t)1 << (JS_LIMB_BITS - 1) && + v2 == -1)) { + goto slow_big_int; + } + sp[-2] = __JS_NewShortBigInt(ctx, v1 / v2); + return 0; + case OP_mod: + if (v2 == 0 || + ((js_limb_t)v1 == (js_limb_t)1 << (JS_LIMB_BITS - 1) && + v2 == -1)) { + goto slow_big_int; + } + sp[-2] = __JS_NewShortBigInt(ctx, v1 % v2); + return 0; + case OP_pow: + goto slow_big_int; + default: + abort(); + } + if (likely(v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX)) { + sp[-2] = __JS_NewShortBigInt(ctx, v); + } else { + JSBigInt *r = js_bigint_new_di(ctx, v); + if (!r) + goto exception; + sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r); + } + return 0; + } + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { + int32_t v1, v2; + int64_t v; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + switch(op) { + case OP_sub: + v = (int64_t)v1 - (int64_t)v2; + break; + case OP_mul: + v = (int64_t)v1 * (int64_t)v2; + if (v == 0 && (v1 | v2) < 0) { + sp[-2] = js_float64(-0.0); + return 0; + } + break; + case OP_div: + { + JS_X87_FPCW_SAVE_AND_ADJUST(fpcw); + sp[-2] = js_number((double)v1 / (double)v2); + JS_X87_FPCW_RESTORE(fpcw); + } + return 0; + case OP_mod: + if (v1 < 0 || v2 <= 0) { + JS_X87_FPCW_SAVE_AND_ADJUST(fpcw); + sp[-2] = js_number(fmod(v1, v2)); + JS_X87_FPCW_RESTORE(fpcw); + return 0; + } else { + v = (int64_t)v1 % (int64_t)v2; + } + break; + case OP_pow: + sp[-2] = js_number(js_math_pow(v1, v2)); + return 0; + default: + abort(); + } + sp[-2] = js_int64(v); + } else if ((tag1 == JS_TAG_SHORT_BIG_INT || tag1 == JS_TAG_BIG_INT) && + (tag2 == JS_TAG_SHORT_BIG_INT || tag2 == JS_TAG_BIG_INT)) { + JSBigInt *p1, *p2, *r; + JSBigIntBuf buf1, buf2; + slow_big_int: + /* bigint result */ + if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) + p1 = js_bigint_set_short(&buf1, op1); + else + p1 = JS_VALUE_GET_PTR(op1); + if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) + p2 = js_bigint_set_short(&buf2, op2); + else + p2 = JS_VALUE_GET_PTR(op2); + switch(op) { + case OP_add: + r = js_bigint_add(ctx, p1, p2, 0); + break; + case OP_sub: + r = js_bigint_add(ctx, p1, p2, 1); + break; + case OP_mul: + r = js_bigint_mul(ctx, p1, p2); + break; + case OP_div: + r = js_bigint_divrem(ctx, p1, p2, false); + break; + case OP_mod: + r = js_bigint_divrem(ctx, p1, p2, true); + break; + case OP_pow: + r = js_bigint_pow(ctx, p1, p2); + break; + default: + abort(); + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (!r) + goto exception; + sp[-2] = JS_CompactBigInt(ctx, r); + } else { + double dr; + /* float64 result */ + if (JS_ToFloat64Free(ctx, &d1, op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (JS_ToFloat64Free(ctx, &d2, op2)) + goto exception; + handle_float64: + JS_X87_FPCW_SAVE_AND_ADJUST(fpcw); + switch(op) { + case OP_sub: + dr = d1 - d2; + break; + case OP_mul: + dr = d1 * d2; + break; + case OP_div: + dr = d1 / d2; + break; + case OP_mod: + dr = fmod(d1, d2); + break; + case OP_pow: + dr = js_math_pow(d1, d2); + break; + default: + abort(); + } + JS_X87_FPCW_RESTORE(fpcw); + sp[-2] = js_float64(dr); + } + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + uint32_t tag1, tag2; + + op1 = sp[-2]; + op2 = sp[-1]; + + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + /* fast path for float64 */ + if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) { + double d1, d2; + d1 = JS_VALUE_GET_FLOAT64(op1); + d2 = JS_VALUE_GET_FLOAT64(op2); + sp[-2] = js_float64(d1 + d2); + return 0; + } + /* fast path for short bigint */ + if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) { + js_slimb_t v1, v2; + js_sdlimb_t v; + v1 = JS_VALUE_GET_SHORT_BIG_INT(op1); + v2 = JS_VALUE_GET_SHORT_BIG_INT(op2); + v = (js_sdlimb_t)v1 + (js_sdlimb_t)v2; + if (likely(v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX)) { + sp[-2] = __JS_NewShortBigInt(ctx, v); + } else { + JSBigInt *r = js_bigint_new_di(ctx, v); + if (!r) + goto exception; + sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r); + } + return 0; + } + + if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) { + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + + op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + } + + if (tag_is_string(tag1) || tag_is_string(tag2)) { + sp[-2] = JS_ConcatString(ctx, op1, op2); + if (JS_IsException(sp[-2])) + goto exception; + return 0; + } + + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { + int32_t v1, v2; + int64_t v; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + v = (int64_t)v1 + (int64_t)v2; + sp[-2] = js_int64(v); + } else if ((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) && + (tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT)) { + JSBigInt *p1, *p2, *r; + JSBigIntBuf buf1, buf2; + /* bigint result */ + if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) + p1 = js_bigint_set_short(&buf1, op1); + else + p1 = JS_VALUE_GET_PTR(op1); + if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) + p2 = js_bigint_set_short(&buf2, op2); + else + p2 = JS_VALUE_GET_PTR(op2); + r = js_bigint_add(ctx, p1, p2, 0); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (!r) + goto exception; + sp[-2] = JS_CompactBigInt(ctx, r); + } else { + double d1, d2; + /* float64 result */ + if (JS_ToFloat64Free(ctx, &d1, op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (JS_ToFloat64Free(ctx, &d2, op2)) + goto exception; + JS_X87_FPCW_SAVE_AND_ADJUST(fpcw); + sp[-2] = js_float64(d1 + d2); + JS_X87_FPCW_RESTORE(fpcw); + } + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static no_inline __exception int js_binary_logic_slow(JSContext *ctx, + JSValue *sp, + OPCodeEnum op) +{ + JSValue op1, op2; + uint32_t tag1, tag2; + uint32_t v1, v2, r; + + op1 = sp[-2]; + op2 = sp[-1]; + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) { + js_slimb_t v1, v2, v; + js_sdlimb_t vd; + v1 = JS_VALUE_GET_SHORT_BIG_INT(op1); + v2 = JS_VALUE_GET_SHORT_BIG_INT(op2); + /* bigint fast path */ + switch(op) { + case OP_and: + v = v1 & v2; + break; + case OP_or: + v = v1 | v2; + break; + case OP_xor: + v = v1 ^ v2; + break; + case OP_sar: + if (v2 > (JS_LIMB_BITS - 1)) { + goto slow_big_int; + } else if (v2 < 0) { + if (v2 < -(JS_LIMB_BITS - 1)) + goto slow_big_int; + v2 = -v2; + goto bigint_shl; + } + bigint_sar: + v = v1 >> v2; + break; + case OP_shl: + if (v2 > (JS_LIMB_BITS - 1)) { + goto slow_big_int; + } else if (v2 < 0) { + if (v2 < -(JS_LIMB_BITS - 1)) + goto slow_big_int; + v2 = -v2; + goto bigint_sar; + } + bigint_shl: + vd = (js_dlimb_t)v1 << v2; + if (likely(vd >= JS_SHORT_BIG_INT_MIN && + vd <= JS_SHORT_BIG_INT_MAX)) { + v = vd; + } else { + JSBigInt *r = js_bigint_new_di(ctx, vd); + if (!r) + goto exception; + sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r); + return 0; + } + break; + default: + abort(); + } + sp[-2] = __JS_NewShortBigInt(ctx, v); + return 0; + } + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + + tag1 = JS_VALUE_GET_TAG(op1); + tag2 = JS_VALUE_GET_TAG(op2); + if ((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) && + (tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT)) { + JSBigInt *p1, *p2, *r; + JSBigIntBuf buf1, buf2; + slow_big_int: + if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) + p1 = js_bigint_set_short(&buf1, op1); + else + p1 = JS_VALUE_GET_PTR(op1); + if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) + p2 = js_bigint_set_short(&buf2, op2); + else + p2 = JS_VALUE_GET_PTR(op2); + switch(op) { + case OP_and: + case OP_or: + case OP_xor: + r = js_bigint_logic(ctx, p1, p2, op); + break; + case OP_shl: + case OP_sar: + { + js_slimb_t shift; + shift = js_bigint_get_si_sat(p2); + if (shift > INT32_MAX) + shift = INT32_MAX; + else if (shift < -INT32_MAX) + shift = -INT32_MAX; + if (op == OP_sar) + shift = -shift; + if (shift >= 0) + r = js_bigint_shl(ctx, p1, shift); + else + r = js_bigint_shr(ctx, p1, -shift); + } + break; + default: + abort(); + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (!r) + goto exception; + sp[-2] = JS_CompactBigInt(ctx, r); + } else { + if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2))) + goto exception; + switch(op) { + case OP_shl: + r = v1 << (v2 & 0x1f); + break; + case OP_sar: + r = (int)v1 >> (v2 & 0x1f); + break; + case OP_and: + r = v1 & v2; + break; + case OP_or: + r = v1 | v2; + break; + case OP_xor: + r = v1 ^ v2; + break; + default: + abort(); + } + sp[-2] = js_int32(r); + } + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +/* op1 must be a bigint or int. */ +static JSBigInt *JS_ToBigIntBuf(JSContext *ctx, JSBigIntBuf *buf1, + JSValue op1) +{ + JSBigInt *p1; + + switch(JS_VALUE_GET_TAG(op1)) { + case JS_TAG_INT: + p1 = js_bigint_set_si(buf1, JS_VALUE_GET_INT(op1)); + break; + case JS_TAG_SHORT_BIG_INT: + p1 = js_bigint_set_short(buf1, op1); + break; + case JS_TAG_BIG_INT: + p1 = JS_VALUE_GET_PTR(op1); + break; + default: + abort(); + } + return p1; +} + +/* op1 and op2 must be numeric types and at least one must be a + bigint. No exception is generated. */ +static int js_compare_bigint(JSContext *ctx, OPCodeEnum op, + JSValue op1, JSValue op2) +{ + int res, val, tag1, tag2; + JSBigIntBuf buf1, buf2; + JSBigInt *p1, *p2; + + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + if ((tag1 == JS_TAG_SHORT_BIG_INT || tag1 == JS_TAG_INT) && + (tag2 == JS_TAG_SHORT_BIG_INT || tag2 == JS_TAG_INT)) { + /* fast path */ + js_slimb_t v1, v2; + if (tag1 == JS_TAG_INT) + v1 = JS_VALUE_GET_INT(op1); + else + v1 = JS_VALUE_GET_SHORT_BIG_INT(op1); + if (tag2 == JS_TAG_INT) + v2 = JS_VALUE_GET_INT(op2); + else + v2 = JS_VALUE_GET_SHORT_BIG_INT(op2); + val = (v1 > v2) - (v1 < v2); + } else { + if (tag1 == JS_TAG_FLOAT64) { + p2 = JS_ToBigIntBuf(ctx, &buf2, op2); + val = js_bigint_float64_cmp(ctx, p2, JS_VALUE_GET_FLOAT64(op1)); + if (val == 2) + goto unordered; + val = -val; + } else if (tag2 == JS_TAG_FLOAT64) { + p1 = JS_ToBigIntBuf(ctx, &buf1, op1); + val = js_bigint_float64_cmp(ctx, p1, JS_VALUE_GET_FLOAT64(op2)); + if (val == 2) { + unordered: + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return false; + } + } else { + p1 = JS_ToBigIntBuf(ctx, &buf1, op1); + p2 = JS_ToBigIntBuf(ctx, &buf2, op2); + val = js_bigint_cmp(ctx, p1, p2); + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + } + + switch(op) { + case OP_lt: + res = val < 0; + break; + case OP_lte: + res = val <= 0; + break; + case OP_gt: + res = val > 0; + break; + case OP_gte: + res = val >= 0; + break; + case OP_eq: + res = val == 0; + break; + default: + abort(); + } + return res; +} + +static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, + OPCodeEnum op) +{ + JSValue op1, op2; + int res; + uint32_t tag1, tag2; + + op1 = sp[-2]; + op2 = sp[-1]; + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + if (tag_is_string(tag1) && tag_is_string(tag2)) { + if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) { + res = js_string_compare(JS_VALUE_GET_STRING(op1), + JS_VALUE_GET_STRING(op2)); + } else { + res = js_string_rope_compare(op1, op2, false); + } + switch(op) { + case OP_lt: + res = (res < 0); + break; + case OP_lte: + res = (res <= 0); + break; + case OP_gt: + res = (res > 0); + break; + default: + case OP_gte: + res = (res >= 0); + break; + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + } else if ((tag1 <= JS_TAG_NULL || tag1 == JS_TAG_FLOAT64) && + (tag2 <= JS_TAG_NULL || tag2 == JS_TAG_FLOAT64)) { + /* fast path for float64/int */ + goto float64_compare; + } else { + if ((((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) && + tag2 == JS_TAG_STRING) || + ((tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) && + tag1 == JS_TAG_STRING))) { + if (tag1 == JS_TAG_STRING) { + op1 = JS_StringToBigInt(ctx, op1); + if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT && + JS_VALUE_GET_TAG(op1) != JS_TAG_SHORT_BIG_INT) + goto invalid_bigint_string; + } + if (tag2 == JS_TAG_STRING) { + op2 = JS_StringToBigInt(ctx, op2); + if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT && + JS_VALUE_GET_TAG(op2) != JS_TAG_SHORT_BIG_INT) { + invalid_bigint_string: + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + res = false; + goto done; + } + } + } else { + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + } + + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + if (tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT || + tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) { + res = js_compare_bigint(ctx, op, op1, op2); + } else { + double d1, d2; + + float64_compare: + /* can use floating point comparison */ + if (tag1 == JS_TAG_FLOAT64) { + d1 = JS_VALUE_GET_FLOAT64(op1); + } else { + d1 = JS_VALUE_GET_INT(op1); + } + if (tag2 == JS_TAG_FLOAT64) { + d2 = JS_VALUE_GET_FLOAT64(op2); + } else { + d2 = JS_VALUE_GET_INT(op2); + } + switch(op) { + case OP_lt: + res = (d1 < d2); /* if NaN return false */ + break; + case OP_lte: + res = (d1 <= d2); /* if NaN return false */ + break; + case OP_gt: + res = (d1 > d2); /* if NaN return false */ + break; + default: + case OP_gte: + res = (d1 >= d2); /* if NaN return false */ + break; + } + } + } + done: + sp[-2] = js_bool(res); + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static bool tag_is_number(uint32_t tag) +{ + return (tag == JS_TAG_INT || + tag == JS_TAG_FLOAT64 || + tag == JS_TAG_BIG_INT || tag == JS_TAG_SHORT_BIG_INT); +} + +static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, + bool is_neq) +{ + JSValue op1, op2; + int res; + uint32_t tag1, tag2; + + op1 = sp[-2]; + op2 = sp[-1]; + redo: + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + if (tag_is_number(tag1) && tag_is_number(tag2)) { + if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { + res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2); + } else if ((tag1 == JS_TAG_FLOAT64 && + (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64)) || + (tag2 == JS_TAG_FLOAT64 && + (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64))) { + double d1, d2; + if (tag1 == JS_TAG_FLOAT64) { + d1 = JS_VALUE_GET_FLOAT64(op1); + } else { + d1 = JS_VALUE_GET_INT(op1); + } + if (tag2 == JS_TAG_FLOAT64) { + d2 = JS_VALUE_GET_FLOAT64(op2); + } else { + d2 = JS_VALUE_GET_INT(op2); + } + res = (d1 == d2); + } else { + res = js_compare_bigint(ctx, OP_eq, op1, op2); + if (res < 0) + goto exception; + } + } else if (tag1 == tag2) { + res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); + } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) || + (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) { + res = true; + } else if ((tag_is_string(tag1) && tag_is_number(tag2)) || + (tag_is_string(tag2) && tag_is_number(tag1))) { + + if (tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT || + tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) { + if (tag_is_string(tag1)) { + op1 = JS_StringToBigInt(ctx, op1); + if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT && + JS_VALUE_GET_TAG(op1) != JS_TAG_SHORT_BIG_INT) + goto invalid_bigint_string; + } + if (tag_is_string(tag2)) { + op2 = JS_StringToBigInt(ctx, op2); + if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT && + JS_VALUE_GET_TAG(op2) != JS_TAG_SHORT_BIG_INT ) { + invalid_bigint_string: + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + res = false; + goto done; + } + } + } else { + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + } + res = js_strict_eq(ctx, op1, op2); + } else if (tag1 == JS_TAG_BOOL) { + op1 = js_int32(JS_VALUE_GET_INT(op1)); + goto redo; + } else if (tag2 == JS_TAG_BOOL) { + op2 = js_int32(JS_VALUE_GET_INT(op2)); + goto redo; + } else if ((tag1 == JS_TAG_OBJECT && + (tag_is_number(tag2) || tag_is_string(tag2) || tag2 == JS_TAG_SYMBOL)) || + (tag2 == JS_TAG_OBJECT && + (tag_is_number(tag1) || tag_is_string(tag1) || tag1 == JS_TAG_SYMBOL))) { + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + goto redo; + } else { + /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */ + if ((JS_IsHTMLDDA(ctx, op1) && + (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) || + (JS_IsHTMLDDA(ctx, op2) && + (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) { + res = true; + } else { + res = false; + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + } + done: + sp[-2] = js_bool(res ^ is_neq); + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + uint32_t v1, v2, r; + + op1 = sp[-2]; + op2 = sp[-1]; + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + + if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT || + JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT || + JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT || + JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) { + JS_ThrowTypeError(ctx, "BigInt operands are forbidden for >>>"); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + goto exception; + } + /* cannot give an exception */ + JS_ToUint32Free(ctx, &v1, op1); + JS_ToUint32Free(ctx, &v2, op2); + r = v1 >> (v2 & 0x1f); + sp[-2] = js_uint32(r); + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static bool js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, + JSStrictEqModeEnum eq_mode) +{ + bool res; + int tag1, tag2; + double d1, d2; + + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + switch(tag1) { + case JS_TAG_BOOL: + if (tag1 != tag2) { + res = false; + } else { + res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2); + goto done_no_free; + } + break; + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + res = (tag1 == tag2); + break; + case JS_TAG_STRING: + case JS_TAG_STRING_ROPE: + { + if (!tag_is_string(tag2)) { + res = false; + } else if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) { + res = js_string_eq(JS_VALUE_GET_STRING(op1), + JS_VALUE_GET_STRING(op2)); + } else { + res = (js_string_rope_compare(op1, op2, true) == 0); + } + } + break; + case JS_TAG_SYMBOL: + { + JSAtomStruct *p1, *p2; + if (tag1 != tag2) { + res = false; + } else { + p1 = JS_VALUE_GET_PTR(op1); + p2 = JS_VALUE_GET_PTR(op2); + res = (p1 == p2); + } + } + break; + case JS_TAG_OBJECT: + if (tag1 != tag2) + res = false; + else + res = JS_VALUE_GET_OBJ(op1) == JS_VALUE_GET_OBJ(op2); + break; + case JS_TAG_INT: + d1 = JS_VALUE_GET_INT(op1); + if (tag2 == JS_TAG_INT) { + d2 = JS_VALUE_GET_INT(op2); + goto number_test; + } else if (tag2 == JS_TAG_FLOAT64) { + d2 = JS_VALUE_GET_FLOAT64(op2); + goto number_test; + } else { + res = false; + } + break; + case JS_TAG_FLOAT64: + d1 = JS_VALUE_GET_FLOAT64(op1); + if (tag2 == JS_TAG_FLOAT64) { + d2 = JS_VALUE_GET_FLOAT64(op2); + } else if (tag2 == JS_TAG_INT) { + d2 = JS_VALUE_GET_INT(op2); + } else { + res = false; + break; + } + number_test: + if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) { + JSFloat64Union u1, u2; + /* NaN is not always normalized, so this test is necessary */ + if (isnan(d1) || isnan(d2)) { + res = isnan(d1) == isnan(d2); + } else if (eq_mode == JS_EQ_SAME_VALUE_ZERO) { + res = (d1 == d2); /* +0 == -0 */ + } else { + u1.d = d1; + u2.d = d2; + res = (u1.u64 == u2.u64); /* +0 != -0 */ + } + } else { + res = (d1 == d2); /* if NaN return false and +0 == -0 */ + } + goto done_no_free; + case JS_TAG_SHORT_BIG_INT: + case JS_TAG_BIG_INT: + { + JSBigIntBuf buf1, buf2; + JSBigInt *p1, *p2; + + if (tag2 != JS_TAG_SHORT_BIG_INT && + tag2 != JS_TAG_BIG_INT) { + res = false; + break; + } + + if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) + p1 = js_bigint_set_short(&buf1, op1); + else + p1 = JS_VALUE_GET_PTR(op1); + if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) + p2 = js_bigint_set_short(&buf2, op2); + else + p2 = JS_VALUE_GET_PTR(op2); + res = (js_bigint_cmp(ctx, p1, p2) == 0); + } + break; + default: + res = false; + break; + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + done_no_free: + return res; +} + +static bool js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2) +{ + return js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); +} + +static bool js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2) +{ + return js_strict_eq2(ctx, js_dup(op1), js_dup(op2), JS_EQ_SAME_VALUE); +} + +static bool js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2) +{ + return js_strict_eq2(ctx, js_dup(op1), js_dup(op2), JS_EQ_SAME_VALUE_ZERO); +} + +static no_inline int js_strict_eq_slow(JSContext *ctx, JSValue *sp, + bool is_neq) +{ + bool res; + res = js_strict_eq(ctx, sp[-2], sp[-1]); + sp[-2] = js_bool(res ^ is_neq); + return 0; +} + +static __exception int js_operator_in(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + JSAtom atom; + int ret; + + op1 = sp[-2]; + op2 = sp[-1]; + + if (JS_VALUE_GET_TAG(op2) != JS_TAG_OBJECT) { + JS_ThrowTypeError(ctx, "invalid 'in' operand"); + return -1; + } + atom = JS_ValueToAtom(ctx, op1); + if (unlikely(atom == JS_ATOM_NULL)) + return -1; + ret = JS_HasProperty(ctx, op2, atom); + JS_FreeAtom(ctx, atom); + if (ret < 0) + return -1; + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + sp[-2] = js_bool(ret); + return 0; +} + +static __exception int js_operator_private_in(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + int ret; + op1 = sp[-2]; /* object */ + op2 = sp[-1]; /* field name or method function */ + if (JS_VALUE_GET_TAG(op1) != JS_TAG_OBJECT) { + JS_ThrowTypeError(ctx, "invalid 'in' operand"); + return -1; + } + if (JS_IsObject(op2)) { + /* method: use the brand */ + ret = JS_CheckBrand(ctx, op1, op2); + if (ret < 0) + return -1; + } else { + JSAtom atom; + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + /* field */ + atom = JS_ValueToAtom(ctx, op2); + if (unlikely(atom == JS_ATOM_NULL)) + return -1; + p = JS_VALUE_GET_OBJ(op1); + prs = find_own_property(&pr, p, atom); + JS_FreeAtom(ctx, atom); + ret = (prs != NULL); + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + sp[-2] = js_bool(ret); + return 0; +} + +static __exception int js_has_unscopable(JSContext *ctx, JSValue obj, + JSAtom atom) +{ + JSValue arr, val; + int ret; + + arr = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_unscopables); + if (JS_IsException(arr)) + return -1; + ret = 0; + if (JS_IsObject(arr)) { + val = JS_GetProperty(ctx, arr, atom); + ret = JS_ToBoolFree(ctx, val); + } + JS_FreeValue(ctx, arr); + return ret; +} + +static __exception int js_operator_instanceof(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + int ret; + + op1 = sp[-2]; + op2 = sp[-1]; + ret = JS_IsInstanceOf(ctx, op1, op2); + if (ret < 0) + return ret; + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + sp[-2] = js_bool(ret); + return 0; +} + +static __exception int js_operator_typeof(JSContext *ctx, JSValue op1) +{ + JSAtom atom; + uint32_t tag; + + tag = JS_VALUE_GET_NORM_TAG(op1); + switch(tag) { + case JS_TAG_SHORT_BIG_INT: + case JS_TAG_BIG_INT: + atom = JS_ATOM_bigint; + break; + case JS_TAG_INT: + case JS_TAG_FLOAT64: + atom = JS_ATOM_number; + break; + case JS_TAG_UNDEFINED: + atom = JS_ATOM_undefined; + break; + case JS_TAG_BOOL: + atom = JS_ATOM_boolean; + break; + case JS_TAG_STRING: + case JS_TAG_STRING_ROPE: + atom = JS_ATOM_string; + break; + case JS_TAG_OBJECT: + { + JSObject *p; + p = JS_VALUE_GET_OBJ(op1); + if (unlikely(p->is_HTMLDDA)) + atom = JS_ATOM_undefined; + else if (JS_IsFunction(ctx, op1)) + atom = JS_ATOM_function; + else + goto obj_type; + } + break; + case JS_TAG_NULL: + obj_type: + atom = JS_ATOM_object; + break; + case JS_TAG_SYMBOL: + atom = JS_ATOM_symbol; + break; + default: + atom = JS_ATOM_unknown; + break; + } + return atom; +} + +static __exception int js_operator_delete(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + JSAtom atom; + int ret; + + op1 = sp[-2]; + op2 = sp[-1]; + atom = JS_ValueToAtom(ctx, op2); + if (unlikely(atom == JS_ATOM_NULL)) + return -1; + ret = JS_DeleteProperty(ctx, op1, atom, JS_PROP_THROW_STRICT); + JS_FreeAtom(ctx, atom); + if (unlikely(ret < 0)) + return -1; + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + sp[-2] = js_bool(ret); + return 0; +} + +static JSValue js_throw_type_error(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); + if (!b || b->is_strict_mode || !b->has_prototype) { + return JS_ThrowTypeError(ctx, "invalid property access"); + } + return JS_UNDEFINED; +} + +static JSValue js_function_proto_fileName(JSContext *ctx, + JSValueConst this_val) +{ + JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); + if (b) { + return JS_AtomToString(ctx, b->filename); + } + return JS_UNDEFINED; +} + +static JSValue js_function_proto_int32(JSContext *ctx, + JSValueConst this_val, + int magic) +{ + JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); + if (b) { + int *field = (int *) ((char *)b + magic); + return js_int32(*field); + } + return JS_UNDEFINED; +} + +static int js_arguments_define_own_property(JSContext *ctx, + JSValueConst this_obj, + JSAtom prop, JSValueConst val, + JSValueConst getter, + JSValueConst setter, int flags) +{ + JSObject *p; + uint32_t idx; + p = JS_VALUE_GET_OBJ(this_obj); + /* convert to normal array when redefining an existing numeric field */ + if (p->fast_array && JS_AtomIsArrayIndex(ctx, &idx, prop) && + idx < p->u.array.count) { + if (convert_fast_array_to_array(ctx, p)) + return -1; + } + /* run the default define own property */ + return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter, + flags | JS_PROP_NO_EXOTIC); +} + +static const JSClassExoticMethods js_arguments_exotic_methods = { + .define_own_property = js_arguments_define_own_property, +}; + +static JSValue js_build_arguments(JSContext *ctx, int argc, JSValueConst *argv) +{ + JSValue val, *tab; + JSProperty props[3]; + JSObject *p; + int i; + + props[0].u.value = js_int32(argc); /* length */ + props[1].u.value = js_dup(ctx->array_proto_values); /* Symbol.iterator */ + props[2].u.getset.getter = JS_VALUE_GET_OBJ(js_dup(ctx->throw_type_error)); /* callee */ + props[2].u.getset.setter = JS_VALUE_GET_OBJ(js_dup(ctx->throw_type_error)); /* callee */ + + val = JS_NewObjectFromShape(ctx, js_dup_shape(ctx->arguments_shape), + JS_CLASS_ARGUMENTS, props); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_OBJ(val); + + /* initialize the fast array part */ + tab = NULL; + if (argc > 0) { + tab = js_malloc(ctx, sizeof(tab[0]) * argc); + if (!tab) + goto fail; + for(i = 0; i < argc; i++) { + tab[i] = js_dup(argv[i]); + } + } + p->u.array.u.values = tab; + p->u.array.count = argc; + + return val; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +#define GLOBAL_VAR_OFFSET 0x40000000 +#define ARGUMENT_VAR_OFFSET 0x20000000 + +static void js_mapped_arguments_finalizer(JSRuntime *rt, JSValueConst val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + if (p->fast_array) { + JSVarRef **var_refs = p->u.array.u.var_refs; + int i; + if (var_refs) { + for(i = 0; i < p->u.array.count; i++) { + if (var_refs[i]) + free_var_ref(rt, var_refs[i]); + } + js_free_rt(rt, var_refs); + } + } +} + +static void js_mapped_arguments_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + if (p->fast_array) { + JSVarRef **var_refs = p->u.array.u.var_refs; + int i; + if (var_refs) { + for(i = 0; i < p->u.array.count; i++) { + if (var_refs[i]) + mark_func(rt, &var_refs[i]->header); + } + } + } +} + +/* legacy arguments object: add references to the function arguments */ +static JSValue js_build_mapped_arguments(JSContext *ctx, int argc, + JSValueConst *argv, + JSStackFrame *sf, int arg_count) +{ + JSValue val; + JSProperty props[3]; + JSVarRef **tab, *var_ref; + JSObject *p; + int i, j; + + props[0].u.value = js_int32(argc); /* length */ + props[1].u.value = js_dup(ctx->array_proto_values); /* Symbol.iterator */ + props[2].u.value = js_dup(ctx->rt->current_stack_frame->cur_func); /* callee */ + + val = JS_NewObjectFromShape(ctx, js_dup_shape(ctx->mapped_arguments_shape), + JS_CLASS_MAPPED_ARGUMENTS, props); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_OBJ(val); + + /* initialize the fast array part */ + tab = NULL; + if (argc > 0) { + tab = js_malloc(ctx, sizeof(tab[0]) * argc); + if (!tab) + goto fail; + for(i = 0; i < arg_count; i++) { + var_ref = get_var_ref(ctx, sf, i, true); + if (!var_ref) + goto fail1; + tab[i] = var_ref; + } + for(i = arg_count; i < argc; i++) { + var_ref = js_create_var_ref(ctx, true); + if (!var_ref) { + fail1: + for(j = 0; j < i; j++) + free_var_ref(ctx->rt, tab[j]); + js_free(ctx, tab); + goto fail; + } + var_ref->value = js_dup(argv[i]); + tab[i] = var_ref; + } + } + p->u.array.u.var_refs = tab; + p->u.array.count = argc; + return val; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj) +{ + JSObject *p; + JSPropertyEnum *tab_atom; + int i; + JSValue enum_obj, obj1; + JSForInIterator *it; + uint32_t tag, tab_atom_count; + + tag = JS_VALUE_GET_TAG(obj); + if (tag != JS_TAG_OBJECT && tag != JS_TAG_NULL && tag != JS_TAG_UNDEFINED) { + obj = JS_ToObjectFree(ctx, obj); + } + + it = js_malloc(ctx, sizeof(*it)); + if (!it) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + enum_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_FOR_IN_ITERATOR); + if (JS_IsException(enum_obj)) { + js_free(ctx, it); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + it->is_array = false; + it->obj = obj; + it->idx = 0; + p = JS_VALUE_GET_OBJ(enum_obj); + p->u.for_in_iterator = it; + + if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) + return enum_obj; + + /* fast path: assume no enumerable properties in the prototype chain */ + obj1 = js_dup(obj); + for(;;) { + obj1 = JS_GetPrototypeFree(ctx, obj1); + if (JS_IsNull(obj1)) + break; + if (JS_IsException(obj1)) + goto fail; + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, + JS_VALUE_GET_OBJ(obj1), + JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) { + JS_FreeValue(ctx, obj1); + goto fail; + } + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + if (tab_atom_count != 0) { + JS_FreeValue(ctx, obj1); + goto slow_path; + } + /* must check for timeout to avoid infinite loop */ + if (js_poll_interrupts(ctx)) { + JS_FreeValue(ctx, obj1); + goto fail; + } + } + + p = JS_VALUE_GET_OBJ(obj); + + if (p->fast_array) { + JSShape *sh; + JSShapeProperty *prs; + /* check that there are no enumerable normal fields */ + sh = p->shape; + for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { + if (prs->flags & JS_PROP_ENUMERABLE) + goto normal_case; + } + /* for fast arrays, we only store the number of elements */ + it->is_array = true; + it->array_length = p->u.array.count; + } else { + normal_case: + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p, + JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) + goto fail; + for(i = 0; i < tab_atom_count; i++) { + JS_SetPropertyInternal(ctx, enum_obj, tab_atom[i].atom, JS_NULL, 0); + } + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + } + return enum_obj; + + slow_path: + /* non enumerable properties hide the enumerables ones in the + prototype chain */ + obj1 = js_dup(obj); + for(;;) { + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, + JS_VALUE_GET_OBJ(obj1), + JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) { + JS_FreeValue(ctx, obj1); + goto fail; + } + for(i = 0; i < tab_atom_count; i++) { + JS_DefinePropertyValue(ctx, enum_obj, tab_atom[i].atom, JS_NULL, + (tab_atom[i].is_enumerable ? + JS_PROP_ENUMERABLE : 0)); + } + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + obj1 = JS_GetPrototypeFree(ctx, obj1); + if (JS_IsNull(obj1)) + break; + if (JS_IsException(obj1)) + goto fail; + /* must check for timeout to avoid infinite loop */ + if (js_poll_interrupts(ctx)) { + JS_FreeValue(ctx, obj1); + goto fail; + } + } + return enum_obj; + + fail: + JS_FreeValue(ctx, enum_obj); + return JS_EXCEPTION; +} + +/* obj -> enum_obj */ +static __exception int js_for_in_start(JSContext *ctx, JSValue *sp) +{ + sp[-1] = build_for_in_iterator(ctx, sp[-1]); + if (JS_IsException(sp[-1])) + return -1; + return 0; +} + +/* enum_obj -> enum_obj value done */ +static __exception int js_for_in_next(JSContext *ctx, JSValue *sp) +{ + JSValue enum_obj; + JSObject *p; + JSAtom prop; + JSForInIterator *it; + int ret; + + enum_obj = sp[-1]; + /* fail safe */ + if (JS_VALUE_GET_TAG(enum_obj) != JS_TAG_OBJECT) + goto done; + p = JS_VALUE_GET_OBJ(enum_obj); + if (p->class_id != JS_CLASS_FOR_IN_ITERATOR) + goto done; + it = p->u.for_in_iterator; + + for(;;) { + if (it->is_array) { + if (it->idx >= it->array_length) + goto done; + prop = __JS_AtomFromUInt32(it->idx); + it->idx++; + } else { + JSShape *sh = p->shape; + JSShapeProperty *prs; + if (it->idx >= sh->prop_count) + goto done; + prs = get_shape_prop(sh) + it->idx; + prop = prs->atom; + it->idx++; + if (prop == JS_ATOM_NULL || !(prs->flags & JS_PROP_ENUMERABLE)) + continue; + } + // check if the property was deleted unless we're dealing with a proxy + JSValue obj = it->obj; + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (p->class_id == JS_CLASS_PROXY) + break; + } + ret = JS_HasProperty(ctx, obj, prop); + if (ret < 0) + return ret; + if (ret) + break; + } + /* return the property */ + sp[0] = JS_AtomToValue(ctx, prop); + sp[1] = JS_FALSE; + return 0; + done: + /* return the end */ + sp[0] = JS_UNDEFINED; + sp[1] = JS_TRUE; + return 0; +} + +static JSValue JS_GetIterator2(JSContext *ctx, JSValueConst obj, + JSValueConst method) +{ + JSValue enum_obj; + + enum_obj = JS_Call(ctx, method, obj, 0, NULL); + if (JS_IsException(enum_obj)) + return enum_obj; + if (!JS_IsObject(enum_obj)) { + JS_FreeValue(ctx, enum_obj); + return JS_ThrowTypeErrorNotAnObject(ctx); + } + return enum_obj; +} + +static JSValue JS_GetIterator(JSContext *ctx, JSValueConst obj, bool is_async) +{ + JSValue method, ret, sync_iter; + + if (is_async) { + method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_asyncIterator); + if (JS_IsException(method)) + return method; + if (JS_IsUndefined(method) || JS_IsNull(method)) { + method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); + if (JS_IsException(method)) + return method; + sync_iter = JS_GetIterator2(ctx, obj, method); + JS_FreeValue(ctx, method); + if (JS_IsException(sync_iter)) + return sync_iter; + ret = JS_CreateAsyncFromSyncIterator(ctx, sync_iter); + JS_FreeValue(ctx, sync_iter); + return ret; + } + } else { + method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); + if (JS_IsException(method)) + return method; + } + if (!JS_IsFunction(ctx, method)) { + JS_FreeValue(ctx, method); + return JS_ThrowTypeError(ctx, "value is not iterable"); + } + ret = JS_GetIterator2(ctx, obj, method); + JS_FreeValue(ctx, method); + return ret; +} + +/* return *pdone = 2 if the iterator object is not parsed */ +static JSValue JS_IteratorNext2(JSContext *ctx, JSValueConst enum_obj, + JSValueConst method, + int argc, JSValueConst *argv, int *pdone) +{ + JSValue obj; + + /* fast path for the built-in iterators (avoid creating the + intermediate result object) */ + if (JS_IsObject(method)) { + JSObject *p = JS_VALUE_GET_OBJ(method); + if (p->class_id == JS_CLASS_C_FUNCTION && + p->u.cfunc.cproto == JS_CFUNC_iterator_next) { + JSCFunctionType func; + JSValueConst args[1]; + + /* in case the function expects one argument */ + if (argc == 0) { + args[0] = JS_UNDEFINED; + argv = args; + } + func = p->u.cfunc.c_function; + return func.iterator_next(ctx, enum_obj, argc, argv, + pdone, p->u.cfunc.magic); + } + } + obj = JS_Call(ctx, method, enum_obj, argc, argv); + if (JS_IsException(obj)) + goto fail; + if (!JS_IsObject(obj)) { + JS_FreeValue(ctx, obj); + JS_ThrowTypeError(ctx, "iterator must return an object"); + goto fail; + } + *pdone = 2; + return obj; + fail: + *pdone = false; + return JS_EXCEPTION; +} + +static JSValue JS_IteratorNext(JSContext *ctx, JSValueConst enum_obj, + JSValueConst method, + int argc, JSValueConst *argv, int *pdone) +{ + JSValue obj, value, done_val; + int done; + + obj = JS_IteratorNext2(ctx, enum_obj, method, argc, argv, &done); + if (JS_IsException(obj)) + goto fail; + if (likely(done == 0)) { + *pdone = false; + return obj; + } else if (done != 2) { + JS_FreeValue(ctx, obj); + *pdone = true; + return JS_UNDEFINED; + } else { + done_val = JS_GetProperty(ctx, obj, JS_ATOM_done); + if (JS_IsException(done_val)) + goto fail; + *pdone = JS_ToBoolFree(ctx, done_val); + value = JS_UNDEFINED; + if (!*pdone) { + value = JS_GetProperty(ctx, obj, JS_ATOM_value); + } + JS_FreeValue(ctx, obj); + return value; + } + fail: + JS_FreeValue(ctx, obj); + *pdone = false; + return JS_EXCEPTION; +} + +/* return < 0 in case of exception */ +static int JS_IteratorClose(JSContext *ctx, JSValueConst enum_obj, + bool is_exception_pending) +{ + JSValue method, ret, ex_obj; + int res; + + if (is_exception_pending) { + ex_obj = ctx->rt->current_exception; + ctx->rt->current_exception = JS_UNINITIALIZED; + res = -1; + } else { + ex_obj = JS_UNDEFINED; + res = 0; + } + method = JS_GetProperty(ctx, enum_obj, JS_ATOM_return); + if (JS_IsException(method)) { + res = -1; + goto done; + } + if (JS_IsUndefined(method) || JS_IsNull(method)) { + goto done; + } + ret = JS_CallFree(ctx, method, enum_obj, 0, NULL); + if (!is_exception_pending) { + if (JS_IsException(ret)) { + res = -1; + } else if (!JS_IsObject(ret)) { + JS_ThrowTypeErrorNotAnObject(ctx); + res = -1; + } + } + JS_FreeValue(ctx, ret); + done: + if (is_exception_pending) { + JS_Throw(ctx, ex_obj); + } + return res; +} + +/* obj -> enum_rec (3 slots) */ +static __exception int js_for_of_start(JSContext *ctx, JSValue *sp, + bool is_async) +{ + JSValue op1, obj, method; + op1 = sp[-1]; + obj = JS_GetIterator(ctx, op1, is_async); + if (JS_IsException(obj)) + return -1; + JS_FreeValue(ctx, op1); + sp[-1] = obj; + method = JS_GetProperty(ctx, obj, JS_ATOM_next); + if (JS_IsException(method)) + return -1; + sp[0] = method; + return 0; +} + +/* enum_rec [objs] -> enum_rec [objs] value done. There are 'offset' + objs. If 'done' is true or in case of exception, 'enum_rec' is set + to undefined. If 'done' is true, 'value' is always set to + undefined. */ +static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset) +{ + JSValue value = JS_UNDEFINED; + int done = 1; + + if (likely(!JS_IsUndefined(sp[offset]))) { + value = JS_IteratorNext(ctx, sp[offset], sp[offset + 1], 0, NULL, &done); + if (JS_IsException(value)) + done = -1; + if (done) { + /* value is JS_UNDEFINED or JS_EXCEPTION */ + /* replace the iteration object with undefined */ + JS_FreeValue(ctx, sp[offset]); + sp[offset] = JS_UNDEFINED; + if (done < 0) { + return -1; + } else { + JS_FreeValue(ctx, value); + value = JS_UNDEFINED; + } + } + } + sp[0] = value; + sp[1] = js_bool(done); + return 0; +} + +static JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValue obj, + int *pdone) +{ + JSValue done_val, value; + int done; + done_val = JS_GetProperty(ctx, obj, JS_ATOM_done); + if (JS_IsException(done_val)) + goto fail; + done = JS_ToBoolFree(ctx, done_val); + value = JS_GetProperty(ctx, obj, JS_ATOM_value); + if (JS_IsException(value)) + goto fail; + *pdone = done; + return value; + fail: + *pdone = false; + return JS_EXCEPTION; +} + +static __exception int js_iterator_get_value_done(JSContext *ctx, JSValue *sp) +{ + JSValue obj, value; + int done; + obj = sp[-1]; + if (!JS_IsObject(obj)) { + JS_ThrowTypeError(ctx, "iterator must return an object"); + return -1; + } + value = JS_IteratorGetCompleteValue(ctx, obj, &done); + if (JS_IsException(value)) + return -1; + JS_FreeValue(ctx, obj); + sp[-1] = value; + sp[0] = js_bool(done); + return 0; +} + +static JSValue js_create_iterator_result(JSContext *ctx, + JSValue val, + bool done) +{ + JSValue obj; + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) { + JS_FreeValue(ctx, val); + return obj; + } + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_value, + val, JS_PROP_C_W_E) < 0) { + goto fail; + } + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_done, + js_bool(done), JS_PROP_C_W_E) < 0) { + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + return obj; +} + +static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + int *pdone, int magic); + +static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic); + +static bool js_is_fast_array(JSContext *ctx, JSValue obj) +{ + /* Try and handle fast arrays explicitly */ + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (p->class_id == JS_CLASS_ARRAY && p->fast_array) { + return true; + } + } + return false; +} + +/* Access an Array's internal JSValue array if available */ +static bool js_get_fast_array(JSContext *ctx, JSValue obj, + JSValue **arrpp, uint32_t *countp) +{ + /* Try and handle fast arrays explicitly */ + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (p->class_id == JS_CLASS_ARRAY && p->fast_array) { + *countp = p->u.array.count; + *arrpp = p->u.array.u.values; + return true; + } + } + return false; +} + +static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp) +{ + JSValue iterator, enumobj, method, value; + int is_array_iterator; + JSValue *arrp; + uint32_t i, count32, pos; + + if (JS_VALUE_GET_TAG(sp[-2]) != JS_TAG_INT) { + JS_ThrowInternalError(ctx, "invalid index for append"); + return -1; + } + + pos = JS_VALUE_GET_INT(sp[-2]); + + /* XXX: further optimisations: + - use ctx->array_proto_values? + - check if array_iterator_prototype next method is built-in and + avoid constructing actual iterator object? + - build this into js_for_of_start and use in all `for (x of o)` loops + */ + iterator = JS_GetProperty(ctx, sp[-1], JS_ATOM_Symbol_iterator); + if (JS_IsException(iterator)) + return -1; + /* Used to squelch a -Wcast-function-type warning. */ + JSCFunctionType ft = { .generic_magic = js_create_array_iterator }; + is_array_iterator = JS_IsCFunction(ctx, iterator, + ft.generic, + JS_ITERATOR_KIND_VALUE); + JS_FreeValue(ctx, iterator); + + enumobj = JS_GetIterator(ctx, sp[-1], false); + if (JS_IsException(enumobj)) + return -1; + method = JS_GetProperty(ctx, enumobj, JS_ATOM_next); + if (JS_IsException(method)) { + JS_FreeValue(ctx, enumobj); + return -1; + } + /* Used to squelch a -Wcast-function-type warning. */ + JSCFunctionType ft2 = { .iterator_next = js_array_iterator_next }; + if (is_array_iterator + && JS_IsCFunction(ctx, method, ft2.generic, 0) + && js_get_fast_array(ctx, sp[-1], &arrp, &count32)) { + uint32_t len; + if (js_get_length32(ctx, &len, sp[-1])) + goto exception; + /* if len > count32, the elements >= count32 might be read in + the prototypes and might have side effects */ + if (len != count32) + goto general_case; + /* Handle fast arrays explicitly */ + for (i = 0; i < count32; i++) { + if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, + js_dup(arrp[i]), JS_PROP_C_W_E) < 0) + goto exception; + } + } else { + general_case: + for (;;) { + int done; + value = JS_IteratorNext(ctx, enumobj, method, 0, NULL, &done); + if (JS_IsException(value)) + goto exception; + if (done) { + /* value is JS_UNDEFINED */ + break; + } + if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, value, JS_PROP_C_W_E) < 0) + goto exception; + } + } + /* Note: could raise an error if too many elements */ + sp[-2] = js_int32(pos); + JS_FreeValue(ctx, enumobj); + JS_FreeValue(ctx, method); + return 0; + +exception: + JS_IteratorClose(ctx, enumobj, true); + JS_FreeValue(ctx, enumobj); + JS_FreeValue(ctx, method); + return -1; +} + +static __exception int JS_CopyDataProperties(JSContext *ctx, + JSValue target, + JSValue source, + JSValue excluded, + bool setprop) +{ + JSPropertyEnum *tab_atom; + JSValue val; + uint32_t i, tab_atom_count; + JSObject *p; + JSObject *pexcl = NULL; + int ret, gpn_flags; + JSPropertyDescriptor desc; + bool is_enumerable; + + if (JS_VALUE_GET_TAG(source) != JS_TAG_OBJECT) + return 0; + + if (JS_VALUE_GET_TAG(excluded) == JS_TAG_OBJECT) + pexcl = JS_VALUE_GET_OBJ(excluded); + + p = JS_VALUE_GET_OBJ(source); + + gpn_flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK | JS_GPN_ENUM_ONLY; + if (p->is_exotic) { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + /* cannot use JS_GPN_ENUM_ONLY with e.g. proxies because it + introduces a visible change */ + if (em && em->get_own_property_names) { + gpn_flags &= ~JS_GPN_ENUM_ONLY; + } + } + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p, + gpn_flags)) + return -1; + + for (i = 0; i < tab_atom_count; i++) { + if (pexcl) { + ret = JS_GetOwnPropertyInternal(ctx, NULL, pexcl, tab_atom[i].atom); + if (ret) { + if (ret < 0) + goto exception; + continue; + } + } + if (!(gpn_flags & JS_GPN_ENUM_ONLY)) { + /* test if the property is enumerable */ + ret = JS_GetOwnPropertyInternal(ctx, &desc, p, tab_atom[i].atom); + if (ret < 0) + goto exception; + if (!ret) + continue; + is_enumerable = (desc.flags & JS_PROP_ENUMERABLE) != 0; + js_free_desc(ctx, &desc); + if (!is_enumerable) + continue; + } + val = JS_GetProperty(ctx, source, tab_atom[i].atom); + if (JS_IsException(val)) + goto exception; + if (setprop) + ret = JS_SetProperty(ctx, target, tab_atom[i].atom, val); + else + ret = JS_DefinePropertyValue(ctx, target, tab_atom[i].atom, val, + JS_PROP_C_W_E); + if (ret < 0) + goto exception; + } + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + return 0; + exception: + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + return -1; +} + +/* only valid inside C functions */ +static JSValueConst JS_GetActiveFunction(JSContext *ctx) +{ + return ctx->rt->current_stack_frame->cur_func; +} + +/* create a detached var ref */ +static JSVarRef *js_create_var_ref(JSContext *ctx, bool is_gc_object) +{ + JSVarRef *var_ref; + var_ref = js_malloc(ctx, sizeof(JSVarRef)); + if (!var_ref) + return NULL; + var_ref->header.ref_count = 1; + var_ref->is_detached = true; + var_ref->value = JS_UNDEFINED; + var_ref->pvalue = &var_ref->value; + if (is_gc_object) + add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); + return var_ref; +} + +static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx, + bool is_arg) +{ + JSObject *p; + JSFunctionBytecode *b; + JSVarRef *var_ref; + JSValue *pvalue; + int var_ref_idx; + JSVarDef *vd; + + p = JS_VALUE_GET_OBJ(sf->cur_func); + b = p->u.func.function_bytecode; + + if (is_arg) { + vd = &b->vardefs[var_idx]; + pvalue = &sf->arg_buf[var_idx]; + } else { + vd = &b->vardefs[b->arg_count + var_idx]; + pvalue = &sf->var_buf[var_idx]; + } + + /* If the variable is captured, use the pre-computed index for O(1) lookup */ + if (vd->is_captured) { + var_ref_idx = vd->var_ref_idx; + var_ref = sf->var_refs[var_ref_idx]; + if (var_ref) { + /* reference to the already created local variable */ + var_ref->header.ref_count++; + return var_ref; + } + + /* create a new one */ + var_ref = js_malloc(ctx, sizeof(JSVarRef)); + if (!var_ref) + return NULL; + var_ref->header.ref_count = 1; + var_ref->is_detached = false; + var_ref->is_lexical = false; + var_ref->is_const = false; + var_ref->var_ref_idx = var_ref_idx; + var_ref->stack_frame = sf; + sf->var_refs[var_ref_idx] = var_ref; + var_ref->pvalue = pvalue; + return var_ref; + } else { + /* Variable is not captured (e.g., from eval closures on uncaptured vars). + Create a detached var_ref that holds a copy of the value. */ + var_ref = js_malloc(ctx, sizeof(JSVarRef)); + if (!var_ref) + return NULL; + var_ref->header.ref_count = 1; + var_ref->is_detached = true; + var_ref->value = js_dup(*pvalue); + var_ref->pvalue = &var_ref->value; + add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); + return var_ref; + } +} + +static JSValue js_closure2(JSContext *ctx, JSValue func_obj, + JSFunctionBytecode *b, + JSVarRef **cur_var_refs, + JSStackFrame *sf) +{ + JSObject *p; + JSVarRef **var_refs; + int i; + + p = JS_VALUE_GET_OBJ(func_obj); + p->u.func.function_bytecode = b; + p->u.func.home_object = NULL; + p->u.func.var_refs = NULL; + if (b->closure_var_count) { + var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count); + if (!var_refs) + goto fail; + p->u.func.var_refs = var_refs; + for(i = 0; i < b->closure_var_count; i++) { + JSClosureVar *cv = &b->closure_var[i]; + JSVarRef *var_ref; + switch(cv->closure_type) { + case JS_CLOSURE_LOCAL: + /* reuse the existing variable reference if it already exists */ + var_ref = get_var_ref(ctx, sf, cv->var_idx, false); + break; + case JS_CLOSURE_ARG: + /* reuse the existing variable reference if it already exists */ + var_ref = get_var_ref(ctx, sf, cv->var_idx, true); + break; + case JS_CLOSURE_REF: + case JS_CLOSURE_GLOBAL_REF: + var_ref = cur_var_refs[cv->var_idx]; + var_ref->header.ref_count++; + break; + default: + abort(); + } + if (!var_ref) + goto fail; + var_refs[i] = var_ref; + } + } + return func_obj; + fail: + /* bfunc is freed when func_obj is freed */ + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; +} + +static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque) +{ + JSValue obj, this_val; + int ret; + + this_val = JS_MKPTR(JS_TAG_OBJECT, p); + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + return JS_EXCEPTION; + ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_constructor, + js_dup(this_val), + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + if (ret < 0) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + return obj; +} + +static const uint16_t func_kind_to_class_id[] = { + [JS_FUNC_NORMAL] = JS_CLASS_BYTECODE_FUNCTION, + [JS_FUNC_GENERATOR] = JS_CLASS_GENERATOR_FUNCTION, + [JS_FUNC_ASYNC] = JS_CLASS_ASYNC_FUNCTION, + [JS_FUNC_ASYNC_GENERATOR] = JS_CLASS_ASYNC_GENERATOR_FUNCTION, +}; + +static JSValue js_closure(JSContext *ctx, JSValue bfunc, + JSVarRef **cur_var_refs, + JSStackFrame *sf) +{ + JSFunctionBytecode *b; + JSValue func_obj; + JSAtom name_atom; + + b = JS_VALUE_GET_PTR(bfunc); + func_obj = JS_NewObjectClass(ctx, func_kind_to_class_id[b->func_kind]); + if (JS_IsException(func_obj)) { + JS_FreeValue(ctx, bfunc); + return JS_EXCEPTION; + } + func_obj = js_closure2(ctx, func_obj, b, cur_var_refs, sf); + if (JS_IsException(func_obj)) { + /* bfunc has been freed */ + goto fail; + } + name_atom = b->func_name; + if (name_atom == JS_ATOM_NULL) + name_atom = JS_ATOM_empty_string; + js_function_set_properties(ctx, func_obj, name_atom, + b->defined_arg_count); + + if (b->func_kind & JS_FUNC_GENERATOR) { + JSValue proto; + int proto_class_id; + /* generators have a prototype field which is used as + prototype for the generator object */ + if (b->func_kind == JS_FUNC_ASYNC_GENERATOR) + proto_class_id = JS_CLASS_ASYNC_GENERATOR; + else + proto_class_id = JS_CLASS_GENERATOR; + proto = JS_NewObjectProto(ctx, ctx->class_proto[proto_class_id]); + if (JS_IsException(proto)) + goto fail; + JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype, proto, + JS_PROP_WRITABLE); + } else if (b->has_prototype) { + /* add the 'prototype' property: delay instantiation to avoid + creating cycles for every javascript function. The prototype + object is created on the fly when first accessed */ + JS_SetConstructorBit(ctx, func_obj, true); + JS_DefineAutoInitProperty(ctx, func_obj, JS_ATOM_prototype, + JS_AUTOINIT_ID_PROTOTYPE, NULL, + JS_PROP_WRITABLE); + } + return func_obj; + fail: + /* bfunc is freed when func_obj is freed */ + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; +} + +#define JS_DEFINE_CLASS_HAS_HERITAGE (1 << 0) + +static int js_op_define_class(JSContext *ctx, JSValue *sp, + JSAtom class_name, int class_flags, + JSVarRef **cur_var_refs, + JSStackFrame *sf, bool is_computed_name) +{ + JSValue bfunc, parent_class, proto = JS_UNDEFINED; + JSValue ctor = JS_UNDEFINED, parent_proto = JS_UNDEFINED; + JSFunctionBytecode *b; + + parent_class = sp[-2]; + bfunc = sp[-1]; + + if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE) { + if (JS_IsNull(parent_class)) { + parent_proto = JS_NULL; + parent_class = js_dup(ctx->function_proto); + } else { + if (!JS_IsConstructor(ctx, parent_class)) { + JS_ThrowTypeError(ctx, "parent class must be constructor"); + goto fail; + } + parent_proto = JS_GetProperty(ctx, parent_class, JS_ATOM_prototype); + if (JS_IsException(parent_proto)) + goto fail; + if (!JS_IsNull(parent_proto) && !JS_IsObject(parent_proto)) { + JS_ThrowTypeError(ctx, "parent prototype must be an object or null"); + goto fail; + } + } + } else { + /* parent_class is JS_UNDEFINED in this case */ + parent_proto = js_dup(ctx->class_proto[JS_CLASS_OBJECT]); + parent_class = js_dup(ctx->function_proto); + } + proto = JS_NewObjectProto(ctx, parent_proto); + if (JS_IsException(proto)) + goto fail; + + b = JS_VALUE_GET_PTR(bfunc); + assert(b->func_kind == JS_FUNC_NORMAL); + ctor = JS_NewObjectProtoClass(ctx, parent_class, + JS_CLASS_BYTECODE_FUNCTION); + if (JS_IsException(ctor)) + goto fail; + ctor = js_closure2(ctx, ctor, b, cur_var_refs, sf); + bfunc = JS_UNDEFINED; + if (JS_IsException(ctor)) + goto fail; + js_method_set_home_object(ctx, ctor, proto); + JS_SetConstructorBit(ctx, ctor, true); + + JS_DefinePropertyValue(ctx, ctor, JS_ATOM_length, + js_int32(b->defined_arg_count), + JS_PROP_CONFIGURABLE); + + if (is_computed_name) { + if (JS_DefineObjectNameComputed(ctx, ctor, sp[-3], + JS_PROP_CONFIGURABLE) < 0) + goto fail; + } else { + if (JS_DefineObjectName(ctx, ctor, class_name, JS_PROP_CONFIGURABLE) < 0) + goto fail; + } + + /* the constructor property must be first. It can be overriden by + computed property names */ + if (JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor, + js_dup(ctor), + JS_PROP_CONFIGURABLE | + JS_PROP_WRITABLE | JS_PROP_THROW) < 0) + goto fail; + /* set the prototype property */ + if (JS_DefinePropertyValue(ctx, ctor, JS_ATOM_prototype, + js_dup(proto), JS_PROP_THROW) < 0) + goto fail; + + JS_FreeValue(ctx, parent_proto); + JS_FreeValue(ctx, parent_class); + + sp[-2] = ctor; + sp[-1] = proto; + return 0; + fail: + JS_FreeValue(ctx, parent_class); + JS_FreeValue(ctx, parent_proto); + JS_FreeValue(ctx, bfunc); + JS_FreeValue(ctx, proto); + JS_FreeValue(ctx, ctor); + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static void close_var_ref(JSRuntime *rt, JSVarRef *var_ref) +{ + var_ref->value = js_dup(*var_ref->pvalue); + var_ref->pvalue = &var_ref->value; + /* the reference is no longer to a local variable */ + var_ref->is_detached = true; + add_gc_object(rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); +} + +static void close_var_refs(JSRuntime *rt, JSStackFrame *sf) +{ + JSVarRef *var_ref; + int i; + + for (i = 0; i < sf->var_ref_count; i++) { + var_ref = sf->var_refs[i]; + if (var_ref) + close_var_ref(rt, var_ref); + } +} + +static void close_lexical_var(JSContext *ctx, JSFunctionBytecode *b, + JSStackFrame *sf, int var_idx) +{ + JSVarRef *var_ref; + int var_ref_idx; + + var_ref_idx = b->vardefs[b->arg_count + var_idx].var_ref_idx; + var_ref = sf->var_refs[var_ref_idx]; + if (var_ref) { + close_var_ref(ctx->rt, var_ref); + sf->var_refs[var_ref_idx] = NULL; + } +} + +#define JS_CALL_FLAG_COPY_ARGV (1 << 1) +#define JS_CALL_FLAG_GENERATOR (1 << 2) + +static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags) +{ + JSRuntime *rt = ctx->rt; + JSCFunctionType func; + JSObject *p; + JSStackFrame sf_s, *sf = &sf_s, *prev_sf; + JSValue ret_val; + JSValueConst *arg_buf; + int arg_count, i; + JSCFunctionEnum cproto; + + p = JS_VALUE_GET_OBJ(func_obj); + cproto = p->u.cfunc.cproto; + arg_count = p->u.cfunc.length; + + /* better to always check stack overflow */ + if (js_check_stack_overflow(rt, sizeof(arg_buf[0]) * arg_count)) + return JS_ThrowStackOverflow(ctx); + + prev_sf = rt->current_stack_frame; + sf->prev_frame = prev_sf; + rt->current_stack_frame = sf; + ctx = p->u.cfunc.realm; /* change the current realm */ + + sf->is_strict_mode = false; + sf->cur_func = unsafe_unconst(func_obj); + sf->arg_count = argc; + arg_buf = argv; + + if (unlikely(argc < arg_count)) { + /* ensure that at least argc_count arguments are readable */ + arg_buf = alloca(sizeof(arg_buf[0]) * arg_count); + for(i = 0; i < argc; i++) + arg_buf[i] = argv[i]; + for(i = argc; i < arg_count; i++) + arg_buf[i] = JS_UNDEFINED; + sf->arg_count = arg_count; + } + sf->arg_buf = (JSValue *)arg_buf; + + func = p->u.cfunc.c_function; + switch(cproto) { + case JS_CFUNC_constructor: + case JS_CFUNC_constructor_or_func: + if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) { + if (cproto == JS_CFUNC_constructor) { + not_a_constructor: + ret_val = JS_ThrowTypeError(ctx, "must be called with new"); + break; + } else { + this_obj = JS_UNDEFINED; + } + } + /* here this_obj is new_target */ + /* fall thru */ + case JS_CFUNC_generic: + ret_val = func.generic(ctx, this_obj, argc, arg_buf); + break; + case JS_CFUNC_constructor_magic: + case JS_CFUNC_constructor_or_func_magic: + if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) { + if (cproto == JS_CFUNC_constructor_magic) { + goto not_a_constructor; + } else { + this_obj = JS_UNDEFINED; + } + } + /* fall thru */ + case JS_CFUNC_generic_magic: + ret_val = func.generic_magic(ctx, this_obj, argc, arg_buf, + p->u.cfunc.magic); + break; + case JS_CFUNC_getter: + ret_val = func.getter(ctx, this_obj); + break; + case JS_CFUNC_setter: + ret_val = func.setter(ctx, this_obj, arg_buf[0]); + break; + case JS_CFUNC_getter_magic: + ret_val = func.getter_magic(ctx, this_obj, p->u.cfunc.magic); + break; + case JS_CFUNC_setter_magic: + ret_val = func.setter_magic(ctx, this_obj, arg_buf[0], p->u.cfunc.magic); + break; + case JS_CFUNC_f_f: + { + double d1; + + if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) { + ret_val = JS_EXCEPTION; + break; + } + ret_val = js_number(func.f_f(d1)); + } + break; + case JS_CFUNC_f_f_f: + { + double d1, d2; + + if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) { + ret_val = JS_EXCEPTION; + break; + } + if (unlikely(JS_ToFloat64(ctx, &d2, arg_buf[1]))) { + ret_val = JS_EXCEPTION; + break; + } + ret_val = js_number(func.f_f_f(d1, d2)); + } + break; + case JS_CFUNC_iterator_next: + { + int done; + ret_val = func.iterator_next(ctx, this_obj, argc, arg_buf, + &done, p->u.cfunc.magic); + if (!JS_IsException(ret_val) && done != 2) { + ret_val = js_create_iterator_result(ctx, ret_val, done); + } + } + break; + default: + abort(); + } + + rt->current_stack_frame = sf->prev_frame; + return ret_val; +} + +static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags) +{ + JSObject *p; + JSBoundFunction *bf; + JSValueConst *arg_buf, new_target; + int arg_count, i; + + p = JS_VALUE_GET_OBJ(func_obj); + bf = p->u.bound_function; + arg_count = bf->argc + argc; + if (js_check_stack_overflow(ctx->rt, sizeof(JSValue) * arg_count)) + return JS_ThrowStackOverflow(ctx); + arg_buf = alloca(sizeof(JSValue) * arg_count); + for(i = 0; i < bf->argc; i++) { + arg_buf[i] = bf->argv[i]; + } + for(i = 0; i < argc; i++) { + arg_buf[bf->argc + i] = argv[i]; + } + if (flags & JS_CALL_FLAG_CONSTRUCTOR) { + new_target = this_obj; + if (js_same_value(ctx, func_obj, new_target)) + new_target = bf->func_obj; + return JS_CallConstructor2(ctx, bf->func_obj, new_target, + arg_count, arg_buf); + } else { + return JS_Call(ctx, bf->func_obj, bf->this_val, + arg_count, arg_buf); + } +} + +/* argument of OP_special_object */ +typedef enum { + OP_SPECIAL_OBJECT_ARGUMENTS, + OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS, + OP_SPECIAL_OBJECT_THIS_FUNC, + OP_SPECIAL_OBJECT_NEW_TARGET, + OP_SPECIAL_OBJECT_HOME_OBJECT, + OP_SPECIAL_OBJECT_VAR_OBJECT, + OP_SPECIAL_OBJECT_IMPORT_META, + OP_SPECIAL_OBJECT_NULL_PROTO, +} OPSpecialObjectEnum; + +#define FUNC_RET_AWAIT 0 +#define FUNC_RET_YIELD 1 +#define FUNC_RET_YIELD_STAR 2 + +#ifdef ENABLE_DUMPS // JS_DUMP_BYTECODE_* +static void dump_single_byte_code(JSContext *ctx, const uint8_t *pc, + JSFunctionBytecode *b, int start_pos); +static void print_func_name(JSFunctionBytecode *b); +#endif + +static bool needs_backtrace(JSValue exc) +{ + JSObject *p; + + if (JS_VALUE_GET_TAG(exc) != JS_TAG_OBJECT) + return false; + p = JS_VALUE_GET_OBJ(exc); + if (p->class_id != JS_CLASS_ERROR) + return false; + return !find_own_property1(p, JS_ATOM_stack); +} + +/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ +static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, + JSValueConst this_obj, JSValueConst new_target, + int argc, JSValueConst *argv, int flags) +{ + JSRuntime *rt = caller_ctx->rt; + JSContext *ctx; + JSObject *p; + JSFunctionBytecode *b; + JSStackFrame sf_s, *sf = &sf_s; + uint8_t *pc; + int opcode, arg_allocated_size, i; + JSValue *local_buf, *stack_buf, *var_buf, *arg_buf, *sp, ret_val, *pval; + JSVarRef **var_refs; + size_t alloca_size; + +#ifdef ENABLE_DUMPS // JS_DUMP_BYTECODE_STEP +#define DUMP_BYTECODE_OR_DONT(pc) \ + if (check_dump_flag(ctx->rt, JS_DUMP_BYTECODE_STEP)) dump_single_byte_code(ctx, pc, b, 0); +#else +#define DUMP_BYTECODE_OR_DONT(pc) +#endif + +#if !DIRECT_DISPATCH +#define SWITCH(pc) DUMP_BYTECODE_OR_DONT(pc) switch (opcode = *pc++) +#define CASE(op) case op +#define DEFAULT default +#define BREAK break +#else + __extension__ static const void * const dispatch_table[256] = { +#define DEF(id, size, n_pop, n_push, f) && case_OP_ ## id, +#define def(id, size, n_pop, n_push, f) +/* + * QuickJS opcode definitions + * + * Copyright (c) 2017-2018 Fabrice Bellard + * Copyright (c) 2017-2018 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef FMT +FMT(none) +FMT(none_int) +FMT(none_loc) +FMT(none_arg) +FMT(none_var_ref) +FMT(u8) +FMT(i8) +FMT(loc8) +FMT(const8) +FMT(label8) +FMT(u16) +FMT(i16) +FMT(label16) +FMT(npop) +FMT(npopx) +FMT(npop_u16) +FMT(loc) +FMT(arg) +FMT(var_ref) +FMT(u32) +FMT(u32x2) +FMT(i32) +FMT(const) +FMT(label) +FMT(atom) +FMT(atom_u8) +FMT(atom_u16) +FMT(atom_label_u8) +FMT(atom_label_u16) +FMT(label_u16) +#undef FMT +#endif /* FMT */ + +#ifdef DEF + +#ifndef def +#define def(id, size, n_pop, n_push, f) DEF(id, size, n_pop, n_push, f) +#endif + +DEF(invalid, 1, 0, 0, none) /* never emitted */ + +/* push values */ +DEF( push_i32, 5, 0, 1, i32) +DEF( push_const, 5, 0, 1, const) +DEF( fclosure, 5, 0, 1, const) /* must follow push_const */ +DEF(push_atom_value, 5, 0, 1, atom) +DEF( private_symbol, 5, 0, 1, atom) +DEF( undefined, 1, 0, 1, none) +DEF( null, 1, 0, 1, none) +DEF( push_this, 1, 0, 1, none) /* only used at the start of a function */ +DEF( push_false, 1, 0, 1, none) +DEF( push_true, 1, 0, 1, none) +DEF( object, 1, 0, 1, none) +DEF( special_object, 2, 0, 1, u8) /* only used at the start of a function */ +DEF( rest, 3, 0, 1, u16) /* only used at the start of a function */ + +DEF( drop, 1, 1, 0, none) /* a -> */ +DEF( nip, 1, 2, 1, none) /* a b -> b */ +DEF( nip1, 1, 3, 2, none) /* a b c -> b c */ +DEF( dup, 1, 1, 2, none) /* a -> a a */ +DEF( dup1, 1, 2, 3, none) /* a b -> a a b */ +DEF( dup2, 1, 2, 4, none) /* a b -> a b a b */ +DEF( dup3, 1, 3, 6, none) /* a b c -> a b c a b c */ +DEF( insert2, 1, 2, 3, none) /* obj a -> a obj a (dup_x1) */ +DEF( insert3, 1, 3, 4, none) /* obj prop a -> a obj prop a (dup_x2) */ +DEF( insert4, 1, 4, 5, none) /* this obj prop a -> a this obj prop a */ +DEF( perm3, 1, 3, 3, none) /* obj a b -> a obj b */ +DEF( perm4, 1, 4, 4, none) /* obj prop a b -> a obj prop b */ +DEF( perm5, 1, 5, 5, none) /* this obj prop a b -> a this obj prop b */ +DEF( swap, 1, 2, 2, none) /* a b -> b a */ +DEF( swap2, 1, 4, 4, none) /* a b c d -> c d a b */ +DEF( rot3l, 1, 3, 3, none) /* x a b -> a b x */ +DEF( rot3r, 1, 3, 3, none) /* a b x -> x a b */ +DEF( rot4l, 1, 4, 4, none) /* x a b c -> a b c x */ +DEF( rot5l, 1, 5, 5, none) /* x a b c d -> a b c d x */ + +DEF(call_constructor, 3, 2, 1, npop) /* func new.target args -> ret. arguments are not counted in n_pop */ +DEF( call, 3, 1, 1, npop) /* arguments are not counted in n_pop */ +DEF( tail_call, 3, 1, 0, npop) /* arguments are not counted in n_pop */ +DEF( call_method, 3, 2, 1, npop) /* arguments are not counted in n_pop */ +DEF(tail_call_method, 3, 2, 0, npop) /* arguments are not counted in n_pop */ +DEF( array_from, 3, 0, 1, npop) /* arguments are not counted in n_pop */ +DEF( apply, 3, 3, 1, u16) +DEF( return, 1, 1, 0, none) +DEF( return_undef, 1, 0, 0, none) +DEF(check_ctor_return, 1, 1, 2, none) +DEF( check_ctor, 1, 0, 0, none) +DEF( init_ctor, 1, 0, 1, none) +DEF( check_brand, 1, 2, 2, none) /* this_obj func -> this_obj func */ +DEF( add_brand, 1, 2, 0, none) /* this_obj home_obj -> */ +DEF( return_async, 1, 1, 0, none) +DEF( throw, 1, 1, 0, none) +DEF( throw_error, 6, 0, 0, atom_u8) +DEF( eval, 5, 1, 1, npop_u16) /* func args... -> ret_val */ +DEF( apply_eval, 3, 2, 1, u16) /* func array -> ret_eval */ +DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a + bytecode string */ +DEF( get_super, 1, 1, 1, none) +DEF( import, 1, 2, 1, none) /* dynamic module import */ + +DEF( get_var_undef, 5, 0, 1, atom) /* push undefined if the variable does not exist */ +DEF( get_var, 5, 0, 1, atom) /* throw an exception if the variable does not exist */ +DEF( put_var, 5, 1, 0, atom) /* must come after get_var */ +DEF( put_var_init, 5, 1, 0, atom) /* must come after put_var. Used to initialize a global lexical variable */ + +DEF( get_ref_value, 1, 2, 3, none) +DEF( put_ref_value, 1, 3, 0, none) + +DEF( define_var, 6, 0, 0, atom_u8) +DEF(check_define_var, 6, 0, 0, atom_u8) +DEF( define_func, 6, 1, 0, atom_u8) + +// order matters, see IC counterparts +DEF( get_field, 5, 1, 1, atom) +DEF( get_field2, 5, 1, 2, atom) +DEF( put_field, 5, 2, 0, atom) + +DEF( get_private_field, 1, 2, 1, none) /* obj prop -> value */ +DEF( put_private_field, 1, 3, 0, none) /* obj value prop -> */ +DEF(define_private_field, 1, 3, 1, none) /* obj prop value -> obj */ +DEF( get_array_el, 1, 2, 1, none) +DEF( get_array_el2, 1, 2, 2, none) /* obj prop -> obj value */ +DEF( put_array_el, 1, 3, 0, none) +DEF(get_super_value, 1, 3, 1, none) /* this obj prop -> value */ +DEF(put_super_value, 1, 4, 0, none) /* this obj prop value -> */ +DEF( define_field, 5, 2, 1, atom) +DEF( set_name, 5, 1, 1, atom) +DEF(set_name_computed, 1, 2, 2, none) +DEF( set_proto, 1, 2, 1, none) +DEF(set_home_object, 1, 2, 2, none) +DEF(define_array_el, 1, 3, 2, none) +DEF( append, 1, 3, 2, none) /* append enumerated object, update length */ +DEF(copy_data_properties, 2, 3, 3, u8) +DEF( define_method, 6, 2, 1, atom_u8) +DEF(define_method_computed, 2, 3, 1, u8) /* must come after define_method */ +DEF( define_class, 6, 2, 2, atom_u8) /* parent ctor -> ctor proto */ +DEF( define_class_computed, 6, 3, 3, atom_u8) /* field_name parent ctor -> field_name ctor proto (class with computed name) */ + +DEF( get_loc, 3, 0, 1, loc) +DEF( put_loc, 3, 1, 0, loc) /* must come after get_loc */ +DEF( set_loc, 3, 1, 1, loc) /* must come after put_loc */ +DEF( get_arg, 3, 0, 1, arg) +DEF( put_arg, 3, 1, 0, arg) /* must come after get_arg */ +DEF( set_arg, 3, 1, 1, arg) /* must come after put_arg */ +DEF( get_var_ref, 3, 0, 1, var_ref) +DEF( put_var_ref, 3, 1, 0, var_ref) /* must come after get_var_ref */ +DEF( set_var_ref, 3, 1, 1, var_ref) /* must come after put_var_ref */ +DEF(set_loc_uninitialized, 3, 0, 0, loc) +DEF( get_loc_check, 3, 0, 1, loc) +DEF( put_loc_check, 3, 1, 0, loc) /* must come after get_loc_check */ +DEF( put_loc_check_init, 3, 1, 0, loc) +DEF(get_var_ref_check, 3, 0, 1, var_ref) +DEF(put_var_ref_check, 3, 1, 0, var_ref) /* must come after get_var_ref_check */ +DEF(put_var_ref_check_init, 3, 1, 0, var_ref) +DEF( close_loc, 3, 0, 0, loc) +DEF( if_false, 5, 1, 0, label) +DEF( if_true, 5, 1, 0, label) /* must come after if_false */ +DEF( goto, 5, 0, 0, label) /* must come after if_true */ +DEF( catch, 5, 0, 1, label) +DEF( gosub, 5, 0, 0, label) /* used to execute the finally block */ +DEF( ret, 1, 1, 0, none) /* used to return from the finally block */ +DEF( nip_catch, 1, 2, 1, none) /* catch ... a -> a */ + +DEF( to_object, 1, 1, 1, none) +//DEF( to_string, 1, 1, 1, none) +DEF( to_propkey, 1, 1, 1, none) +DEF( to_propkey2, 1, 2, 2, none) + +DEF( with_get_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF( with_put_var, 10, 2, 1, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF(with_delete_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF( with_make_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF( with_get_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF(with_get_ref_undef, 10, 1, 0, atom_label_u8) + +DEF( make_loc_ref, 7, 0, 2, atom_u16) +DEF( make_arg_ref, 7, 0, 2, atom_u16) +DEF(make_var_ref_ref, 7, 0, 2, atom_u16) +DEF( make_var_ref, 5, 0, 2, atom) + +DEF( for_in_start, 1, 1, 1, none) +DEF( for_of_start, 1, 1, 3, none) +DEF(for_await_of_start, 1, 1, 3, none) +DEF( for_in_next, 1, 1, 3, none) +DEF( for_of_next, 2, 3, 5, u8) +DEF(iterator_check_object, 1, 1, 1, none) +DEF(iterator_get_value_done, 1, 1, 2, none) +DEF( iterator_close, 1, 3, 0, none) +DEF( iterator_next, 1, 4, 4, none) +DEF( iterator_call, 2, 4, 5, u8) +DEF( initial_yield, 1, 0, 0, none) +DEF( yield, 1, 1, 2, none) +DEF( yield_star, 1, 1, 2, none) +DEF(async_yield_star, 1, 1, 2, none) +DEF( await, 1, 1, 1, none) + +/* arithmetic/logic operations */ +DEF( neg, 1, 1, 1, none) +DEF( plus, 1, 1, 1, none) +DEF( dec, 1, 1, 1, none) +DEF( inc, 1, 1, 1, none) +DEF( post_dec, 1, 1, 2, none) +DEF( post_inc, 1, 1, 2, none) +DEF( dec_loc, 2, 0, 0, loc8) +DEF( inc_loc, 2, 0, 0, loc8) +DEF( add_loc, 2, 1, 0, loc8) +DEF( not, 1, 1, 1, none) +DEF( lnot, 1, 1, 1, none) +DEF( typeof, 1, 1, 1, none) +DEF( delete, 1, 2, 1, none) +DEF( delete_var, 5, 0, 1, atom) + +/* warning: order matters (see js_parse_assign_expr) */ +DEF( mul, 1, 2, 1, none) +DEF( div, 1, 2, 1, none) +DEF( mod, 1, 2, 1, none) +DEF( add, 1, 2, 1, none) +DEF( sub, 1, 2, 1, none) +DEF( shl, 1, 2, 1, none) +DEF( sar, 1, 2, 1, none) +DEF( shr, 1, 2, 1, none) +DEF( and, 1, 2, 1, none) +DEF( xor, 1, 2, 1, none) +DEF( or, 1, 2, 1, none) +DEF( pow, 1, 2, 1, none) + +DEF( lt, 1, 2, 1, none) +DEF( lte, 1, 2, 1, none) +DEF( gt, 1, 2, 1, none) +DEF( gte, 1, 2, 1, none) +DEF( instanceof, 1, 2, 1, none) +DEF( in, 1, 2, 1, none) +DEF( eq, 1, 2, 1, none) +DEF( neq, 1, 2, 1, none) +DEF( strict_eq, 1, 2, 1, none) +DEF( strict_neq, 1, 2, 1, none) +DEF(is_undefined_or_null, 1, 1, 1, none) +DEF( private_in, 1, 2, 1, none) +DEF(push_bigint_i32, 5, 0, 1, i32) +/* must be the last non short and non temporary opcode */ +DEF( nop, 1, 0, 0, none) + +/* temporary opcodes: never emitted in the final bytecode */ + +def( enter_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ +def( leave_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ + +def( label, 5, 0, 0, label) /* emitted in phase 1, removed in phase 3 */ + +def(scope_get_var_undef, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_get_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_put_var, 7, 1, 0, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_delete_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_make_ref, 11, 0, 2, atom_label_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_get_ref, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_put_var_init, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_get_private_field, 7, 1, 1, atom_u16) /* obj -> value, emitted in phase 1, removed in phase 2 */ +def(scope_get_private_field2, 7, 1, 2, atom_u16) /* obj -> obj value, emitted in phase 1, removed in phase 2 */ +def(scope_put_private_field, 7, 2, 0, atom_u16) /* obj value ->, emitted in phase 1, removed in phase 2 */ +def(scope_in_private_field, 7, 1, 1, atom_u16) /* obj -> res emitted in phase 1, removed in phase 2 */ +def(get_field_opt_chain, 5, 1, 1, atom) /* emitted in phase 1, removed in phase 2 */ +def(get_array_el_opt_chain, 1, 2, 1, none) /* emitted in phase 1, removed in phase 2 */ +def( set_class_name, 5, 1, 1, u32) /* emitted in phase 1, removed in phase 2 */ + +def( source_loc, 9, 0, 0, u32x2) /* emitted in phase 1, removed in phase 3 */ + +DEF( push_minus1, 1, 0, 1, none_int) +DEF( push_0, 1, 0, 1, none_int) +DEF( push_1, 1, 0, 1, none_int) +DEF( push_2, 1, 0, 1, none_int) +DEF( push_3, 1, 0, 1, none_int) +DEF( push_4, 1, 0, 1, none_int) +DEF( push_5, 1, 0, 1, none_int) +DEF( push_6, 1, 0, 1, none_int) +DEF( push_7, 1, 0, 1, none_int) +DEF( push_i8, 2, 0, 1, i8) +DEF( push_i16, 3, 0, 1, i16) +DEF( push_const8, 2, 0, 1, const8) +DEF( fclosure8, 2, 0, 1, const8) /* must follow push_const8 */ +DEF(push_empty_string, 1, 0, 1, none) + +DEF( get_loc8, 2, 0, 1, loc8) +DEF( put_loc8, 2, 1, 0, loc8) +DEF( set_loc8, 2, 1, 1, loc8) + +DEF( get_loc0_loc1, 1, 0, 2, none_loc) +DEF( get_loc0, 1, 0, 1, none_loc) +DEF( get_loc1, 1, 0, 1, none_loc) +DEF( get_loc2, 1, 0, 1, none_loc) +DEF( get_loc3, 1, 0, 1, none_loc) +DEF( put_loc0, 1, 1, 0, none_loc) +DEF( put_loc1, 1, 1, 0, none_loc) +DEF( put_loc2, 1, 1, 0, none_loc) +DEF( put_loc3, 1, 1, 0, none_loc) +DEF( set_loc0, 1, 1, 1, none_loc) +DEF( set_loc1, 1, 1, 1, none_loc) +DEF( set_loc2, 1, 1, 1, none_loc) +DEF( set_loc3, 1, 1, 1, none_loc) +DEF( get_arg0, 1, 0, 1, none_arg) +DEF( get_arg1, 1, 0, 1, none_arg) +DEF( get_arg2, 1, 0, 1, none_arg) +DEF( get_arg3, 1, 0, 1, none_arg) +DEF( put_arg0, 1, 1, 0, none_arg) +DEF( put_arg1, 1, 1, 0, none_arg) +DEF( put_arg2, 1, 1, 0, none_arg) +DEF( put_arg3, 1, 1, 0, none_arg) +DEF( set_arg0, 1, 1, 1, none_arg) +DEF( set_arg1, 1, 1, 1, none_arg) +DEF( set_arg2, 1, 1, 1, none_arg) +DEF( set_arg3, 1, 1, 1, none_arg) +DEF( get_var_ref0, 1, 0, 1, none_var_ref) +DEF( get_var_ref1, 1, 0, 1, none_var_ref) +DEF( get_var_ref2, 1, 0, 1, none_var_ref) +DEF( get_var_ref3, 1, 0, 1, none_var_ref) +DEF( put_var_ref0, 1, 1, 0, none_var_ref) +DEF( put_var_ref1, 1, 1, 0, none_var_ref) +DEF( put_var_ref2, 1, 1, 0, none_var_ref) +DEF( put_var_ref3, 1, 1, 0, none_var_ref) +DEF( set_var_ref0, 1, 1, 1, none_var_ref) +DEF( set_var_ref1, 1, 1, 1, none_var_ref) +DEF( set_var_ref2, 1, 1, 1, none_var_ref) +DEF( set_var_ref3, 1, 1, 1, none_var_ref) + +DEF( get_length, 1, 1, 1, none) + +DEF( if_false8, 2, 1, 0, label8) +DEF( if_true8, 2, 1, 0, label8) /* must come after if_false8 */ +DEF( goto8, 2, 0, 0, label8) /* must come after if_true8 */ +DEF( goto16, 3, 0, 0, label16) + +DEF( call0, 1, 1, 1, npopx) +DEF( call1, 1, 1, 1, npopx) +DEF( call2, 1, 1, 1, npopx) +DEF( call3, 1, 1, 1, npopx) + +DEF( is_undefined, 1, 1, 1, none) +DEF( is_null, 1, 1, 1, none) +DEF(typeof_is_undefined, 1, 1, 1, none) +DEF( typeof_is_function, 1, 1, 1, none) + +#undef DEF +#undef def +#endif /* DEF */ + + [ OP_COUNT ... 255 ] = &&case_default + }; +#define SWITCH(pc) DUMP_BYTECODE_OR_DONT(pc) __extension__ ({ goto *dispatch_table[opcode = *pc++]; }); +#define CASE(op) case_ ## op +#define DEFAULT case_default +#define BREAK SWITCH(pc) +#endif + + if (js_poll_interrupts(caller_ctx)) + return JS_EXCEPTION; + if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) { + if (flags & JS_CALL_FLAG_GENERATOR) { + JSAsyncFunctionState *s = JS_VALUE_GET_PTR(func_obj); + /* func_obj get contains a pointer to JSFuncAsyncState */ + /* the stack frame is already allocated */ + sf = &s->frame; + p = JS_VALUE_GET_OBJ(sf->cur_func); + b = p->u.func.function_bytecode; + ctx = b->realm; + var_refs = p->u.func.var_refs; + local_buf = arg_buf = sf->arg_buf; + var_buf = sf->var_buf; + stack_buf = sf->var_buf + b->var_count; + sp = sf->cur_sp; + sf->cur_sp = NULL; /* cur_sp is NULL if the function is running */ + pc = sf->cur_pc; + sf->prev_frame = rt->current_stack_frame; + rt->current_stack_frame = sf; + if (s->throw_flag) + goto exception; + else + goto restart; + } else { + goto not_a_function; + } + } + p = JS_VALUE_GET_OBJ(func_obj); + if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) { + JSClassCall *call_func; + call_func = rt->class_array[p->class_id].call; + if (!call_func) { + not_a_function: + return JS_ThrowTypeErrorNotAFunction(caller_ctx); + } + return call_func(caller_ctx, func_obj, this_obj, argc, + argv, flags); + } + b = p->u.func.function_bytecode; + + if (unlikely(argc < b->arg_count || (flags & JS_CALL_FLAG_COPY_ARGV))) { + arg_allocated_size = b->arg_count; + } else { + arg_allocated_size = 0; + } + + alloca_size = sizeof(JSValue) * (arg_allocated_size + b->var_count + + b->stack_size) + + sizeof(JSVarRef *) * b->var_ref_count; + if (js_check_stack_overflow(rt, alloca_size)) + return JS_ThrowStackOverflow(caller_ctx); + + sf->is_strict_mode = b->is_strict_mode; + arg_buf = (JSValue *)argv; + sf->arg_count = argc; + sf->cur_func = unsafe_unconst(func_obj); + var_refs = p->u.func.var_refs; + + local_buf = alloca(alloca_size); + if (unlikely(arg_allocated_size)) { + int n = min_int(argc, b->arg_count); + arg_buf = local_buf; + for(i = 0; i < n; i++) + arg_buf[i] = js_dup(argv[i]); + for(; i < b->arg_count; i++) + arg_buf[i] = JS_UNDEFINED; + sf->arg_count = b->arg_count; + } + var_buf = local_buf + arg_allocated_size; + sf->var_buf = var_buf; + sf->arg_buf = arg_buf; + + for(i = 0; i < b->var_count; i++) + var_buf[i] = JS_UNDEFINED; + + stack_buf = var_buf + b->var_count; + sf->var_refs = (JSVarRef **)(stack_buf + b->stack_size); + sf->var_ref_count = b->var_ref_count; + for(i = 0; i < b->var_ref_count; i++) + sf->var_refs[i] = NULL; + sp = stack_buf; + pc = b->byte_code_buf; + /* sf->cur_pc must we set to pc before any recursive calls to JS_CallInternal. */ + sf->cur_pc = NULL; + sf->prev_frame = rt->current_stack_frame; + rt->current_stack_frame = sf; + ctx = b->realm; /* set the current realm */ + +#ifdef ENABLE_DUMPS // JS_DUMP_BYTECODE_STEP + if (check_dump_flag(ctx->rt, JS_DUMP_BYTECODE_STEP)) + print_func_name(b); +#endif + + restart: + for(;;) { + int call_argc; + JSValue *call_argv; + + SWITCH(pc) { + CASE(OP_push_i32): + *sp++ = js_int32(get_u32(pc)); + pc += 4; + BREAK; + CASE(OP_push_bigint_i32): + *sp++ = __JS_NewShortBigInt(ctx, (int)get_u32(pc)); + pc += 4; + BREAK; + CASE(OP_push_const): + *sp++ = js_dup(b->cpool[get_u32(pc)]); + pc += 4; + BREAK; + CASE(OP_push_minus1): + CASE(OP_push_0): + CASE(OP_push_1): + CASE(OP_push_2): + CASE(OP_push_3): + CASE(OP_push_4): + CASE(OP_push_5): + CASE(OP_push_6): + CASE(OP_push_7): + *sp++ = js_int32(opcode - OP_push_0); + BREAK; + CASE(OP_push_i8): + *sp++ = js_int32(get_i8(pc)); + pc += 1; + BREAK; + CASE(OP_push_i16): + *sp++ = js_int32(get_i16(pc)); + pc += 2; + BREAK; + CASE(OP_push_const8): + *sp++ = js_dup(b->cpool[*pc++]); + BREAK; + CASE(OP_fclosure8): + *sp++ = js_closure(ctx, js_dup(b->cpool[*pc++]), var_refs, sf); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + BREAK; + CASE(OP_push_empty_string): + *sp++ = js_empty_string(rt); + BREAK; + CASE(OP_get_length): + { + JSValue val, obj; + JSAtom atom; + JSObject *p; + JSProperty *pr; + JSShapeProperty *prs; + + atom = JS_ATOM_length; + + obj = sp[-1]; + if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT)) { + p = JS_VALUE_GET_OBJ(obj); + for(;;) { + prs = find_own_property(&pr, p, atom); + if (prs) { + /* found */ + if (unlikely(prs->flags & JS_PROP_TMASK)) + goto get_length_slow_path; + val = js_dup(pr->u.value); + break; + } + if (unlikely(p->is_exotic)) { + obj = JS_MKPTR(JS_TAG_OBJECT, p); + goto get_length_slow_path; + } + p = p->shape->proto; + if (!p) { + val = JS_UNDEFINED; + break; + } + } + } else { + get_length_slow_path: + sf->cur_pc = pc; + val = JS_GetPropertyInternal(ctx, obj, atom, sp[-1], false); + if (unlikely(JS_IsException(val))) + goto exception; + } + JS_FreeValue(ctx, sp[-1]); + sp[-1] = val; + } + BREAK; + CASE(OP_push_atom_value): + *sp++ = JS_AtomToValue(ctx, get_u32(pc)); + pc += 4; + BREAK; + CASE(OP_undefined): + *sp++ = JS_UNDEFINED; + BREAK; + CASE(OP_null): + *sp++ = JS_NULL; + BREAK; + CASE(OP_push_this): + /* OP_push_this is only called at the start of a function */ + { + JSValue val; + if (!b->is_strict_mode) { + uint32_t tag = JS_VALUE_GET_TAG(this_obj); + if (likely(tag == JS_TAG_OBJECT)) + goto normal_this; + if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) { + val = js_dup(ctx->global_obj); + } else { + val = JS_ToObject(ctx, this_obj); + if (JS_IsException(val)) + goto exception; + } + } else { + normal_this: + val = js_dup(this_obj); + } + *sp++ = val; + } + BREAK; + CASE(OP_push_false): + *sp++ = JS_FALSE; + BREAK; + CASE(OP_push_true): + *sp++ = JS_TRUE; + BREAK; + CASE(OP_object): + *sp++ = JS_NewObject(ctx); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + BREAK; + CASE(OP_special_object): + { + int arg = *pc++; + switch(arg) { + case OP_SPECIAL_OBJECT_ARGUMENTS: + *sp++ = js_build_arguments(ctx, argc, argv); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + break; + case OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS: + *sp++ = js_build_mapped_arguments(ctx, argc, argv, + sf, min_int(argc, b->arg_count)); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + break; + case OP_SPECIAL_OBJECT_THIS_FUNC: + *sp++ = js_dup(sf->cur_func); + break; + case OP_SPECIAL_OBJECT_NEW_TARGET: + *sp++ = js_dup(new_target); + break; + case OP_SPECIAL_OBJECT_HOME_OBJECT: + { + JSObject *p1; + p1 = p->u.func.home_object; + if (unlikely(!p1)) + *sp++ = JS_UNDEFINED; + else + *sp++ = js_dup(JS_MKPTR(JS_TAG_OBJECT, p1)); + } + break; + case OP_SPECIAL_OBJECT_VAR_OBJECT: + *sp++ = JS_NewObjectProto(ctx, JS_NULL); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + break; + case OP_SPECIAL_OBJECT_IMPORT_META: + *sp++ = js_import_meta(ctx); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + break; + case OP_SPECIAL_OBJECT_NULL_PROTO: + *sp++ = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_OBJECT); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + break; + default: + abort(); + } + } + BREAK; + CASE(OP_rest): + { + int i, n, first = get_u16(pc); + pc += 2; + i = min_int(first, argc); + n = argc - i; + *sp++ = js_create_array(ctx, n, &argv[i]); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + } + BREAK; + + CASE(OP_drop): + JS_FreeValue(ctx, sp[-1]); + sp--; + BREAK; + CASE(OP_nip): + JS_FreeValue(ctx, sp[-2]); + sp[-2] = sp[-1]; + sp--; + BREAK; + CASE(OP_nip1): /* a b c -> b c */ + JS_FreeValue(ctx, sp[-3]); + sp[-3] = sp[-2]; + sp[-2] = sp[-1]; + sp--; + BREAK; + CASE(OP_dup): + sp[0] = js_dup(sp[-1]); + sp++; + BREAK; + CASE(OP_dup2): /* a b -> a b a b */ + sp[0] = js_dup(sp[-2]); + sp[1] = js_dup(sp[-1]); + sp += 2; + BREAK; + CASE(OP_dup3): /* a b c -> a b c a b c */ + sp[0] = js_dup(sp[-3]); + sp[1] = js_dup(sp[-2]); + sp[2] = js_dup(sp[-1]); + sp += 3; + BREAK; + CASE(OP_dup1): /* a b -> a a b */ + sp[0] = sp[-1]; + sp[-1] = js_dup(sp[-2]); + sp++; + BREAK; + CASE(OP_insert2): /* obj a -> a obj a (dup_x1) */ + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = js_dup(sp[0]); + sp++; + BREAK; + CASE(OP_insert3): /* obj prop a -> a obj prop a (dup_x2) */ + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = js_dup(sp[0]); + sp++; + BREAK; + CASE(OP_insert4): /* this obj prop a -> a this obj prop a */ + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = sp[-4]; + sp[-4] = js_dup(sp[0]); + sp++; + BREAK; + CASE(OP_perm3): /* obj a b -> a obj b (213) */ + { + JSValue tmp; + tmp = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = tmp; + } + BREAK; + CASE(OP_rot3l): /* x a b -> a b x (231) */ + { + JSValue tmp; + tmp = sp[-3]; + sp[-3] = sp[-2]; + sp[-2] = sp[-1]; + sp[-1] = tmp; + } + BREAK; + CASE(OP_rot4l): /* x a b c -> a b c x */ + { + JSValue tmp; + tmp = sp[-4]; + sp[-4] = sp[-3]; + sp[-3] = sp[-2]; + sp[-2] = sp[-1]; + sp[-1] = tmp; + } + BREAK; + CASE(OP_rot5l): /* x a b c d -> a b c d x */ + { + JSValue tmp; + tmp = sp[-5]; + sp[-5] = sp[-4]; + sp[-4] = sp[-3]; + sp[-3] = sp[-2]; + sp[-2] = sp[-1]; + sp[-1] = tmp; + } + BREAK; + CASE(OP_rot3r): /* a b x -> x a b (312) */ + { + JSValue tmp; + tmp = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = tmp; + } + BREAK; + CASE(OP_perm4): /* obj prop a b -> a obj prop b */ + { + JSValue tmp; + tmp = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = sp[-4]; + sp[-4] = tmp; + } + BREAK; + CASE(OP_perm5): /* this obj prop a b -> a this obj prop b */ + { + JSValue tmp; + tmp = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = sp[-4]; + sp[-4] = sp[-5]; + sp[-5] = tmp; + } + BREAK; + CASE(OP_swap): /* a b -> b a */ + { + JSValue tmp; + tmp = sp[-2]; + sp[-2] = sp[-1]; + sp[-1] = tmp; + } + BREAK; + CASE(OP_swap2): /* a b c d -> c d a b */ + { + JSValue tmp1, tmp2; + tmp1 = sp[-4]; + tmp2 = sp[-3]; + sp[-4] = sp[-2]; + sp[-3] = sp[-1]; + sp[-2] = tmp1; + sp[-1] = tmp2; + } + BREAK; + + CASE(OP_fclosure): + { + JSValue bfunc = js_dup(b->cpool[get_u32(pc)]); + pc += 4; + *sp++ = js_closure(ctx, bfunc, var_refs, sf); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + } + BREAK; + CASE(OP_call0): + CASE(OP_call1): + CASE(OP_call2): + CASE(OP_call3): + call_argc = opcode - OP_call0; + goto has_call_argc; + CASE(OP_call): + CASE(OP_tail_call): + { + call_argc = get_u16(pc); + pc += 2; + goto has_call_argc; + has_call_argc: + call_argv = sp - call_argc; + sf->cur_pc = pc; + ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED, + JS_UNDEFINED, call_argc, + vc(call_argv), 0); + if (unlikely(JS_IsException(ret_val))) + goto exception; + if (opcode == OP_tail_call) + goto done; + for(i = -1; i < call_argc; i++) + JS_FreeValue(ctx, call_argv[i]); + sp -= call_argc + 1; + *sp++ = ret_val; + } + BREAK; + CASE(OP_call_constructor): + { + call_argc = get_u16(pc); + pc += 2; + call_argv = sp - call_argc; + sf->cur_pc = pc; + ret_val = JS_CallConstructorInternal(ctx, call_argv[-2], + call_argv[-1], call_argc, + vc(call_argv), 0); + if (unlikely(JS_IsException(ret_val))) + goto exception; + for(i = -2; i < call_argc; i++) + JS_FreeValue(ctx, call_argv[i]); + sp -= call_argc + 2; + *sp++ = ret_val; + } + BREAK; + CASE(OP_call_method): + CASE(OP_tail_call_method): + { + call_argc = get_u16(pc); + pc += 2; + call_argv = sp - call_argc; + sf->cur_pc = pc; + ret_val = JS_CallInternal(ctx, call_argv[-1], call_argv[-2], + JS_UNDEFINED, call_argc, + vc(call_argv), 0); + if (unlikely(JS_IsException(ret_val))) + goto exception; + if (opcode == OP_tail_call_method) + goto done; + for(i = -2; i < call_argc; i++) + JS_FreeValue(ctx, call_argv[i]); + sp -= call_argc + 2; + *sp++ = ret_val; + } + BREAK; + CASE(OP_array_from): + { + call_argc = get_u16(pc); + pc += 2; + call_argv = sp - call_argc; + ret_val = JS_NewArrayFrom(ctx, call_argc, call_argv); + sp -= call_argc; + if (unlikely(JS_IsException(ret_val))) + goto exception; + *sp++ = ret_val; + } + BREAK; + + CASE(OP_apply): + { + int magic; + magic = get_u16(pc); + pc += 2; + sf->cur_pc = pc; + + ret_val = js_function_apply(ctx, sp[-3], 2, vc(&sp[-2]), magic); + if (unlikely(JS_IsException(ret_val))) + goto exception; + JS_FreeValue(ctx, sp[-3]); + JS_FreeValue(ctx, sp[-2]); + JS_FreeValue(ctx, sp[-1]); + sp -= 3; + *sp++ = ret_val; + } + BREAK; + CASE(OP_return): + ret_val = *--sp; + goto done; + CASE(OP_return_undef): + ret_val = JS_UNDEFINED; + goto done; + + CASE(OP_check_ctor_return): + /* return true if 'this' should be returned */ + if (!JS_IsObject(sp[-1])) { + if (!JS_IsUndefined(sp[-1])) { + JS_ThrowTypeError(caller_ctx, "derived class constructor must return an object or undefined"); + goto exception; + } + sp[0] = JS_TRUE; + } else { + sp[0] = JS_FALSE; + } + sp++; + BREAK; + CASE(OP_check_ctor): + if (JS_IsUndefined(new_target)) { + non_ctor_call: + JS_ThrowTypeError(ctx, "class constructors must be invoked with 'new'"); + goto exception; + } + BREAK; + CASE(OP_init_ctor): + { + JSValue super, ret; + sf->cur_pc = pc; + if (JS_IsUndefined(new_target)) + goto non_ctor_call; + super = JS_GetPrototype(ctx, func_obj); + if (JS_IsException(super)) + goto exception; + ret = JS_CallConstructor2(ctx, super, new_target, argc, argv); + JS_FreeValue(ctx, super); + if (JS_IsException(ret)) + goto exception; + *sp++ = ret; + } + BREAK; + CASE(OP_check_brand): + { + int ret = JS_CheckBrand(ctx, sp[-2], sp[-1]); + if (ret < 0) + goto exception; + if (!ret) { + JS_ThrowTypeError(ctx, "invalid brand on object"); + goto exception; + } + } + BREAK; + CASE(OP_add_brand): + if (JS_AddBrand(ctx, sp[-2], sp[-1]) < 0) + goto exception; + JS_FreeValue(ctx, sp[-2]); + JS_FreeValue(ctx, sp[-1]); + sp -= 2; + BREAK; + + CASE(OP_throw): + JS_Throw(ctx, *--sp); + goto exception; + + CASE(OP_throw_error): +#define JS_THROW_VAR_RO 0 +#define JS_THROW_VAR_REDECL 1 +#define JS_THROW_VAR_UNINITIALIZED 2 +#define JS_THROW_ERROR_DELETE_SUPER 3 +#define JS_THROW_ERROR_ITERATOR_THROW 4 + { + JSAtom atom; + int type; + atom = get_u32(pc); + type = pc[4]; + pc += 5; + if (type == JS_THROW_VAR_RO) + JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, atom); + else + if (type == JS_THROW_VAR_REDECL) + JS_ThrowSyntaxErrorVarRedeclaration(ctx, atom); + else + if (type == JS_THROW_VAR_UNINITIALIZED) + JS_ThrowReferenceErrorUninitialized(ctx, atom); + else + if (type == JS_THROW_ERROR_DELETE_SUPER) + JS_ThrowReferenceError(ctx, "unsupported reference to 'super'"); + else + if (type == JS_THROW_ERROR_ITERATOR_THROW) + JS_ThrowTypeError(ctx, "iterator does not have a throw method"); + else + JS_ThrowInternalError(ctx, "invalid throw var type %d", type); + } + goto exception; + + CASE(OP_eval): + { + JSValue obj; + int scope_idx; + call_argc = get_u16(pc); + scope_idx = get_u16(pc + 2) - 1; + pc += 4; + call_argv = sp - call_argc; + sf->cur_pc = pc; + if (js_same_value(ctx, call_argv[-1], ctx->eval_obj)) { + if (call_argc >= 1) + obj = call_argv[0]; + else + obj = JS_UNDEFINED; + ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj, + JS_EVAL_TYPE_DIRECT, scope_idx); + } else { + ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED, + JS_UNDEFINED, call_argc, + vc(call_argv), 0); + } + if (unlikely(JS_IsException(ret_val))) + goto exception; + for(i = -1; i < call_argc; i++) + JS_FreeValue(ctx, call_argv[i]); + sp -= call_argc + 1; + *sp++ = ret_val; + } + BREAK; + /* could merge with OP_apply */ + CASE(OP_apply_eval): + { + int scope_idx; + uint32_t len; + JSValue *tab; + JSValue obj; + + scope_idx = get_u16(pc) - 1; + pc += 2; + sf->cur_pc = pc; + tab = build_arg_list(ctx, &len, sp[-1]); + if (!tab) + goto exception; + if (js_same_value(ctx, sp[-2], ctx->eval_obj)) { + if (len >= 1) + obj = tab[0]; + else + obj = JS_UNDEFINED; + ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj, + JS_EVAL_TYPE_DIRECT, scope_idx); + } else { + ret_val = JS_Call(ctx, sp[-2], JS_UNDEFINED, len, vc(tab)); + } + free_arg_list(ctx, tab, len); + if (unlikely(JS_IsException(ret_val))) + goto exception; + JS_FreeValue(ctx, sp[-2]); + JS_FreeValue(ctx, sp[-1]); + sp -= 2; + *sp++ = ret_val; + } + BREAK; + + CASE(OP_regexp): + { + sp[-2] = js_regexp_constructor_internal(ctx, JS_UNDEFINED, + sp[-2], sp[-1]); + sp--; + } + BREAK; + + CASE(OP_get_super): + { + JSValue proto; + proto = JS_GetPrototype(ctx, sp[-1]); + if (JS_IsException(proto)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = proto; + } + BREAK; + + CASE(OP_import): + { + JSValue val; + sf->cur_pc = pc; + val = js_dynamic_import(ctx, sp[-2], sp[-1]); + if (JS_IsException(val)) + goto exception; + JS_FreeValue(ctx, sp[-2]); + JS_FreeValue(ctx, sp[-1]); + sp--; + sp[-1] = val; + } + BREAK; + + CASE(OP_get_var_undef): + CASE(OP_get_var): + { + JSValue val; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + sf->cur_pc = pc; + + val = JS_GetGlobalVar(ctx, atom, opcode - OP_get_var_undef); + if (unlikely(JS_IsException(val))) + goto exception; + *sp++ = val; + } + BREAK; + + CASE(OP_put_var): + CASE(OP_put_var_init): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + sf->cur_pc = pc; + + ret = JS_SetGlobalVar(ctx, atom, sp[-1], opcode - OP_put_var); + sp--; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_check_define_var): + { + JSAtom atom; + int flags; + atom = get_u32(pc); + flags = pc[4]; + pc += 5; + if (JS_CheckDefineGlobalVar(ctx, atom, flags)) + goto exception; + } + BREAK; + CASE(OP_define_var): + { + JSAtom atom; + int flags; + atom = get_u32(pc); + flags = pc[4]; + pc += 5; + if (JS_DefineGlobalVar(ctx, atom, flags)) + goto exception; + } + BREAK; + CASE(OP_define_func): + { + JSAtom atom; + int flags; + atom = get_u32(pc); + flags = pc[4]; + pc += 5; + if (JS_DefineGlobalFunction(ctx, atom, sp[-1], flags)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp--; + } + BREAK; + + CASE(OP_get_loc): + { + int idx; + idx = get_u16(pc); + pc += 2; + sp[0] = js_dup(var_buf[idx]); + sp++; + } + BREAK; + CASE(OP_put_loc): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &var_buf[idx], sp[-1]); + sp--; + } + BREAK; + CASE(OP_set_loc): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &var_buf[idx], js_dup(sp[-1])); + } + BREAK; + CASE(OP_get_arg): + { + int idx; + idx = get_u16(pc); + pc += 2; + sp[0] = js_dup(arg_buf[idx]); + sp++; + } + BREAK; + CASE(OP_put_arg): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &arg_buf[idx], sp[-1]); + sp--; + } + BREAK; + CASE(OP_set_arg): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &arg_buf[idx], js_dup(sp[-1])); + } + BREAK; + + CASE(OP_get_loc8): *sp++ = js_dup(var_buf[*pc++]); BREAK; + CASE(OP_put_loc8): set_value(ctx, &var_buf[*pc++], *--sp); BREAK; + CASE(OP_set_loc8): set_value(ctx, &var_buf[*pc++], js_dup(sp[-1])); BREAK; + + // Observation: get_loc0 and get_loc1 are individually very + // frequent opcodes _and_ they are very often paired together, + // making them ideal candidates for opcode fusion. + CASE(OP_get_loc0_loc1): + *sp++ = js_dup(var_buf[0]); + *sp++ = js_dup(var_buf[1]); + BREAK; + + CASE(OP_get_loc0): *sp++ = js_dup(var_buf[0]); BREAK; + CASE(OP_get_loc1): *sp++ = js_dup(var_buf[1]); BREAK; + CASE(OP_get_loc2): *sp++ = js_dup(var_buf[2]); BREAK; + CASE(OP_get_loc3): *sp++ = js_dup(var_buf[3]); BREAK; + CASE(OP_put_loc0): set_value(ctx, &var_buf[0], *--sp); BREAK; + CASE(OP_put_loc1): set_value(ctx, &var_buf[1], *--sp); BREAK; + CASE(OP_put_loc2): set_value(ctx, &var_buf[2], *--sp); BREAK; + CASE(OP_put_loc3): set_value(ctx, &var_buf[3], *--sp); BREAK; + CASE(OP_set_loc0): set_value(ctx, &var_buf[0], js_dup(sp[-1])); BREAK; + CASE(OP_set_loc1): set_value(ctx, &var_buf[1], js_dup(sp[-1])); BREAK; + CASE(OP_set_loc2): set_value(ctx, &var_buf[2], js_dup(sp[-1])); BREAK; + CASE(OP_set_loc3): set_value(ctx, &var_buf[3], js_dup(sp[-1])); BREAK; + CASE(OP_get_arg0): *sp++ = js_dup(arg_buf[0]); BREAK; + CASE(OP_get_arg1): *sp++ = js_dup(arg_buf[1]); BREAK; + CASE(OP_get_arg2): *sp++ = js_dup(arg_buf[2]); BREAK; + CASE(OP_get_arg3): *sp++ = js_dup(arg_buf[3]); BREAK; + CASE(OP_put_arg0): set_value(ctx, &arg_buf[0], *--sp); BREAK; + CASE(OP_put_arg1): set_value(ctx, &arg_buf[1], *--sp); BREAK; + CASE(OP_put_arg2): set_value(ctx, &arg_buf[2], *--sp); BREAK; + CASE(OP_put_arg3): set_value(ctx, &arg_buf[3], *--sp); BREAK; + CASE(OP_set_arg0): set_value(ctx, &arg_buf[0], js_dup(sp[-1])); BREAK; + CASE(OP_set_arg1): set_value(ctx, &arg_buf[1], js_dup(sp[-1])); BREAK; + CASE(OP_set_arg2): set_value(ctx, &arg_buf[2], js_dup(sp[-1])); BREAK; + CASE(OP_set_arg3): set_value(ctx, &arg_buf[3], js_dup(sp[-1])); BREAK; + CASE(OP_get_var_ref0): *sp++ = js_dup(*var_refs[0]->pvalue); BREAK; + CASE(OP_get_var_ref1): *sp++ = js_dup(*var_refs[1]->pvalue); BREAK; + CASE(OP_get_var_ref2): *sp++ = js_dup(*var_refs[2]->pvalue); BREAK; + CASE(OP_get_var_ref3): *sp++ = js_dup(*var_refs[3]->pvalue); BREAK; + CASE(OP_put_var_ref0): set_value(ctx, var_refs[0]->pvalue, *--sp); BREAK; + CASE(OP_put_var_ref1): set_value(ctx, var_refs[1]->pvalue, *--sp); BREAK; + CASE(OP_put_var_ref2): set_value(ctx, var_refs[2]->pvalue, *--sp); BREAK; + CASE(OP_put_var_ref3): set_value(ctx, var_refs[3]->pvalue, *--sp); BREAK; + CASE(OP_set_var_ref0): set_value(ctx, var_refs[0]->pvalue, js_dup(sp[-1])); BREAK; + CASE(OP_set_var_ref1): set_value(ctx, var_refs[1]->pvalue, js_dup(sp[-1])); BREAK; + CASE(OP_set_var_ref2): set_value(ctx, var_refs[2]->pvalue, js_dup(sp[-1])); BREAK; + CASE(OP_set_var_ref3): set_value(ctx, var_refs[3]->pvalue, js_dup(sp[-1])); BREAK; + + CASE(OP_get_var_ref): + { + int idx; + JSValue val; + idx = get_u16(pc); + pc += 2; + val = *var_refs[idx]->pvalue; + sp[0] = js_dup(val); + sp++; + } + BREAK; + CASE(OP_put_var_ref): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, var_refs[idx]->pvalue, sp[-1]); + sp--; + } + BREAK; + CASE(OP_set_var_ref): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, var_refs[idx]->pvalue, js_dup(sp[-1])); + } + BREAK; + CASE(OP_get_var_ref_check): + { + int idx; + JSValue val; + idx = get_u16(pc); + pc += 2; + val = *var_refs[idx]->pvalue; + if (unlikely(JS_IsUninitialized(val))) { + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, true); + goto exception; + } + sp[0] = js_dup(val); + sp++; + } + BREAK; + CASE(OP_put_var_ref_check): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(JS_IsUninitialized(*var_refs[idx]->pvalue))) { + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, true); + goto exception; + } + set_value(ctx, var_refs[idx]->pvalue, sp[-1]); + sp--; + } + BREAK; + CASE(OP_put_var_ref_check_init): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(!JS_IsUninitialized(*var_refs[idx]->pvalue))) { + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, true); + goto exception; + } + set_value(ctx, var_refs[idx]->pvalue, sp[-1]); + sp--; + } + BREAK; + CASE(OP_set_loc_uninitialized): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &var_buf[idx], JS_UNINITIALIZED); + } + BREAK; + CASE(OP_get_loc_check): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(JS_IsUninitialized(var_buf[idx]))) { + JS_ThrowReferenceErrorUninitialized2(caller_ctx, b, idx, + false); + goto exception; + } + sp[0] = js_dup(var_buf[idx]); + sp++; + } + BREAK; + CASE(OP_put_loc_check): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(JS_IsUninitialized(var_buf[idx]))) { + JS_ThrowReferenceErrorUninitialized2(caller_ctx, b, idx, + false); + goto exception; + } + set_value(ctx, &var_buf[idx], sp[-1]); + sp--; + } + BREAK; + CASE(OP_put_loc_check_init): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(!JS_IsUninitialized(var_buf[idx]))) { + JS_ThrowReferenceError(caller_ctx, + "'this' can be initialized only once"); + goto exception; + } + set_value(ctx, &var_buf[idx], sp[-1]); + sp--; + } + BREAK; + CASE(OP_close_loc): + { + int idx; + idx = get_u16(pc); + pc += 2; + close_lexical_var(ctx, b, sf, idx); + } + BREAK; + + CASE(OP_make_loc_ref): + CASE(OP_make_arg_ref): + CASE(OP_make_var_ref_ref): + { + JSVarRef *var_ref; + JSProperty *pr; + JSAtom atom; + int idx; + atom = get_u32(pc); + idx = get_u16(pc + 4); + pc += 6; + *sp++ = JS_NewObjectProto(ctx, JS_NULL); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + if (opcode == OP_make_var_ref_ref) { + var_ref = var_refs[idx]; + var_ref->header.ref_count++; + } else { + var_ref = get_var_ref(ctx, sf, idx, opcode == OP_make_arg_ref); + if (!var_ref) + goto exception; + } + pr = add_property(ctx, JS_VALUE_GET_OBJ(sp[-1]), atom, + JS_PROP_WRITABLE | JS_PROP_VARREF); + if (!pr) { + free_var_ref(rt, var_ref); + goto exception; + } + pr->u.var_ref = var_ref; + *sp++ = JS_AtomToValue(ctx, atom); + } + BREAK; + CASE(OP_make_var_ref): + { + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + if (JS_GetGlobalVarRef(ctx, atom, sp)) + goto exception; + sp += 2; + } + BREAK; + + CASE(OP_goto): + pc += (int32_t)get_u32(pc); + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + BREAK; + CASE(OP_goto16): + pc += (int16_t)get_u16(pc); + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + BREAK; + CASE(OP_goto8): + pc += (int8_t)pc[0]; + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + BREAK; + CASE(OP_if_true): + { + int res; + JSValue op1; + + op1 = sp[-1]; + pc += 4; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1); + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp--; + if (res) { + pc += (int32_t)get_u32(pc - 4) - 4; + } + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + } + BREAK; + CASE(OP_if_false): + { + int res; + JSValue op1; + + op1 = sp[-1]; + pc += 4; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1); + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp--; + if (!res) { + pc += (int32_t)get_u32(pc - 4) - 4; + } + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + } + BREAK; + CASE(OP_if_true8): + { + int res; + JSValue op1; + + op1 = sp[-1]; + pc += 1; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1); + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp--; + if (res) { + pc += (int8_t)pc[-1] - 1; + } + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + } + BREAK; + CASE(OP_if_false8): + { + int res; + JSValue op1; + + op1 = sp[-1]; + pc += 1; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1); + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp--; + if (!res) { + pc += (int8_t)pc[-1] - 1; + } + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + } + BREAK; + CASE(OP_catch): + { + int32_t diff; + diff = get_u32(pc); + sp[0] = JS_NewCatchOffset(ctx, pc + diff - b->byte_code_buf); + sp++; + pc += 4; + } + BREAK; + CASE(OP_gosub): + { + int32_t diff; + diff = get_u32(pc); + /* XXX: should have a different tag to avoid security flaw */ + sp[0] = js_int32(pc + 4 - b->byte_code_buf); + sp++; + pc += diff; + } + BREAK; + CASE(OP_ret): + { + JSValue op1; + uint32_t pos; + op1 = sp[-1]; + if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_INT)) + goto ret_fail; + pos = JS_VALUE_GET_INT(op1); + if (unlikely(pos >= b->byte_code_len)) { + ret_fail: + JS_ThrowInternalError(ctx, "invalid ret value"); + goto exception; + } + sp--; + pc = b->byte_code_buf + pos; + } + BREAK; + + CASE(OP_for_in_start): + sf->cur_pc = pc; + if (js_for_in_start(ctx, sp)) + goto exception; + BREAK; + CASE(OP_for_in_next): + sf->cur_pc = pc; + if (js_for_in_next(ctx, sp)) + goto exception; + sp += 2; + BREAK; + CASE(OP_for_of_start): + sf->cur_pc = pc; + if (js_for_of_start(ctx, sp, false)) + goto exception; + sp += 1; + *sp++ = JS_NewCatchOffset(ctx, 0); + BREAK; + CASE(OP_for_of_next): + { + int offset = -3 - pc[0]; + pc += 1; + sf->cur_pc = pc; + if (js_for_of_next(ctx, sp, offset)) + goto exception; + sp += 2; + } + BREAK; + CASE(OP_for_await_of_start): + sf->cur_pc = pc; + if (js_for_of_start(ctx, sp, true)) + goto exception; + sp += 1; + *sp++ = JS_NewCatchOffset(ctx, 0); + BREAK; + CASE(OP_iterator_get_value_done): + sf->cur_pc = pc; + if (js_iterator_get_value_done(ctx, sp)) + goto exception; + sp += 1; + BREAK; + CASE(OP_iterator_check_object): + if (unlikely(!JS_IsObject(sp[-1]))) { + JS_ThrowTypeError(ctx, "iterator must return an object"); + goto exception; + } + BREAK; + + CASE(OP_iterator_close): + /* iter_obj next catch_offset -> */ + sp--; /* drop the catch offset to avoid getting caught by exception */ + JS_FreeValue(ctx, sp[-1]); /* drop the next method */ + sp--; + if (!JS_IsUndefined(sp[-1])) { + sf->cur_pc = pc; + if (JS_IteratorClose(ctx, sp[-1], false)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + } + sp--; + BREAK; + CASE(OP_nip_catch): + { + JSValue ret_val; + /* catch_offset ... ret_val -> ret_eval */ + ret_val = *--sp; + while (sp > stack_buf && + JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_CATCH_OFFSET) { + JS_FreeValue(ctx, *--sp); + } + if (unlikely(sp == stack_buf)) { + JS_ThrowInternalError(ctx, "nip_catch"); + JS_FreeValue(ctx, ret_val); + goto exception; + } + sp[-1] = ret_val; + } + BREAK; + + CASE(OP_iterator_next): + /* stack: iter_obj next catch_offset val */ + { + JSValue ret; + sf->cur_pc = pc; + ret = JS_Call(ctx, sp[-3], sp[-4], 1, vc(sp - 1)); + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret; + } + BREAK; + + CASE(OP_iterator_call): + /* stack: iter_obj next catch_offset val */ + { + JSValue method, ret; + bool ret_flag; + int flags; + flags = *pc++; + sf->cur_pc = pc; + method = JS_GetProperty(ctx, sp[-4], (flags & 1) ? + JS_ATOM_throw : JS_ATOM_return); + if (JS_IsException(method)) + goto exception; + if (JS_IsUndefined(method) || JS_IsNull(method)) { + ret_flag = true; + } else { + if (flags & 2) { + /* no argument */ + ret = JS_CallFree(ctx, method, sp[-4], + 0, NULL); + } else { + ret = JS_CallFree(ctx, method, sp[-4], + 1, vc(sp - 1)); + } + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret; + ret_flag = false; + } + sp[0] = js_bool(ret_flag); + sp += 1; + } + BREAK; + + CASE(OP_lnot): + { + int res; + JSValue op1; + + op1 = sp[-1]; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1) != 0; + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp[-1] = js_bool(!res); + } + BREAK; + + CASE(OP_get_field): + { + JSValue val, obj; + JSAtom atom; + JSObject *p; + JSProperty *pr; + JSShapeProperty *prs; + + atom = get_u32(pc); + pc += 4; + + obj = sp[-1]; + if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT)) { + p = JS_VALUE_GET_OBJ(obj); + for(;;) { + prs = find_own_property(&pr, p, atom); + if (prs) { + /* found */ + if (unlikely(prs->flags & JS_PROP_TMASK)) + goto get_field_slow_path; + val = js_dup(pr->u.value); + break; + } + if (unlikely(p->is_exotic)) { + /* XXX: should avoid the slow path for arrays + and typed arrays by ensuring that 'prop' is + not numeric */ + obj = JS_MKPTR(JS_TAG_OBJECT, p); + goto get_field_slow_path; + } + p = p->shape->proto; + if (!p) { + val = JS_UNDEFINED; + break; + } + } + } else { + get_field_slow_path: + sf->cur_pc = pc; + val = JS_GetPropertyInternal(ctx, obj, atom, sp[-1], false); + if (unlikely(JS_IsException(val))) + goto exception; + } + JS_FreeValue(ctx, sp[-1]); + sp[-1] = val; + } + BREAK; + + CASE(OP_get_field2): + { + JSValue val, obj; + JSAtom atom; + JSObject *p; + JSProperty *pr; + JSShapeProperty *prs; + + atom = get_u32(pc); + pc += 4; + + obj = sp[-1]; + if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT)) { + p = JS_VALUE_GET_OBJ(obj); + for(;;) { + prs = find_own_property(&pr, p, atom); + if (prs) { + /* found */ + if (unlikely(prs->flags & JS_PROP_TMASK)) + goto get_field2_slow_path; + val = js_dup(pr->u.value); + break; + } + if (unlikely(p->is_exotic)) { + /* XXX: should avoid the slow path for arrays + and typed arrays by ensuring that 'prop' is + not numeric */ + obj = JS_MKPTR(JS_TAG_OBJECT, p); + goto get_field2_slow_path; + } + p = p->shape->proto; + if (!p) { + val = JS_UNDEFINED; + break; + } + } + } else { + get_field2_slow_path: + sf->cur_pc = pc; + val = JS_GetPropertyInternal(ctx, obj, atom, sp[-1], false); + if (unlikely(JS_IsException(val))) + goto exception; + } + *sp++ = val; + } + BREAK; + + CASE(OP_put_field): + { + int ret; + JSValue obj; + JSAtom atom; + JSObject *p; + JSProperty *pr; + JSShapeProperty *prs; + + atom = get_u32(pc); + pc += 4; + + obj = sp[-2]; + if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT)) { + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, atom); + if (!prs) + goto put_field_slow_path; + if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE | + JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) { + /* fast path */ + set_value(ctx, &pr->u.value, sp[-1]); + } else { + goto put_field_slow_path; + } + JS_FreeValue(ctx, obj); + sp -= 2; + } else { + put_field_slow_path: + sf->cur_pc = pc; + ret = JS_SetPropertyInternal2(ctx, obj, atom, sp[-1], obj, + JS_PROP_THROW_STRICT); + JS_FreeValue(ctx, obj); + sp -= 2; + if (unlikely(ret < 0)) + goto exception; + } + } + BREAK; + + CASE(OP_private_symbol): + { + JSAtom atom; + JSValue val; + + atom = get_u32(pc); + pc += 4; + val = JS_NewSymbolFromAtom(ctx, atom, JS_ATOM_TYPE_PRIVATE); + if (JS_IsException(val)) + goto exception; + *sp++ = val; + } + BREAK; + + CASE(OP_get_private_field): + { + JSValue val; + sf->cur_pc = pc; + val = JS_GetPrivateField(ctx, sp[-2], sp[-1]); + JS_FreeValue(ctx, sp[-1]); + JS_FreeValue(ctx, sp[-2]); + sp[-2] = val; + sp--; + if (unlikely(JS_IsException(val))) + goto exception; + } + BREAK; + + CASE(OP_put_private_field): + { + int ret; + sf->cur_pc = pc; + ret = JS_SetPrivateField(ctx, sp[-3], sp[-1], sp[-2]); + JS_FreeValue(ctx, sp[-3]); + JS_FreeValue(ctx, sp[-1]); + sp -= 3; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_define_private_field): + { + int ret; + ret = JS_DefinePrivateField(ctx, sp[-3], sp[-2], sp[-1]); + JS_FreeValue(ctx, sp[-2]); + sp -= 2; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_define_field): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + ret = JS_DefinePropertyValue(ctx, sp[-2], atom, sp[-1], + JS_PROP_C_W_E | JS_PROP_THROW); + sp--; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_set_name): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + ret = JS_DefineObjectName(ctx, sp[-1], atom, JS_PROP_CONFIGURABLE); + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + CASE(OP_set_name_computed): + { + int ret; + ret = JS_DefineObjectNameComputed(ctx, sp[-1], sp[-2], JS_PROP_CONFIGURABLE); + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + CASE(OP_set_proto): + { + JSValue proto; + proto = sp[-1]; + if (JS_IsObject(proto) || JS_IsNull(proto)) { + if (JS_SetPrototypeInternal(ctx, sp[-2], proto, true) < 0) + goto exception; + } + JS_FreeValue(ctx, proto); + sp--; + } + BREAK; + CASE(OP_set_home_object): + js_method_set_home_object(ctx, sp[-1], sp[-2]); + BREAK; + CASE(OP_define_method): + CASE(OP_define_method_computed): + { + JSValue getter, setter, value; + JSValue obj; + JSAtom atom; + int flags, ret, op_flags; + bool is_computed; +#define OP_DEFINE_METHOD_METHOD 0 +#define OP_DEFINE_METHOD_GETTER 1 +#define OP_DEFINE_METHOD_SETTER 2 +#define OP_DEFINE_METHOD_ENUMERABLE 4 + + is_computed = (opcode == OP_define_method_computed); + if (is_computed) { + atom = JS_ValueToAtom(ctx, sp[-2]); + if (unlikely(atom == JS_ATOM_NULL)) + goto exception; + opcode += OP_define_method - OP_define_method_computed; + } else { + atom = get_u32(pc); + pc += 4; + } + op_flags = *pc++; + + obj = sp[-2 - is_computed]; + flags = JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE | + JS_PROP_HAS_ENUMERABLE | JS_PROP_THROW; + if (op_flags & OP_DEFINE_METHOD_ENUMERABLE) + flags |= JS_PROP_ENUMERABLE; + op_flags &= 3; + value = JS_UNDEFINED; + getter = JS_UNDEFINED; + setter = JS_UNDEFINED; + if (op_flags == OP_DEFINE_METHOD_METHOD) { + value = sp[-1]; + flags |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE; + } else if (op_flags == OP_DEFINE_METHOD_GETTER) { + getter = sp[-1]; + flags |= JS_PROP_HAS_GET; + } else { + setter = sp[-1]; + flags |= JS_PROP_HAS_SET; + } + ret = js_method_set_properties(ctx, sp[-1], atom, flags, obj); + if (ret >= 0) { + ret = JS_DefineProperty(ctx, obj, atom, value, + getter, setter, flags); + } + JS_FreeValue(ctx, sp[-1]); + if (is_computed) { + JS_FreeAtom(ctx, atom); + JS_FreeValue(ctx, sp[-2]); + } + sp -= 1 + is_computed; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_define_class): + CASE(OP_define_class_computed): + { + int class_flags; + JSAtom atom; + + atom = get_u32(pc); + class_flags = pc[4]; + pc += 5; + if (js_op_define_class(ctx, sp, atom, class_flags, + var_refs, sf, + (opcode == OP_define_class_computed)) < 0) + goto exception; + } + BREAK; + + CASE(OP_get_array_el): + { + JSValue val; + + sf->cur_pc = pc; + val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]); + JS_FreeValue(ctx, sp[-2]); + sp[-2] = val; + sp--; + if (unlikely(JS_IsException(val))) + goto exception; + } + BREAK; + + CASE(OP_get_array_el2): + { + JSValue val; + + sf->cur_pc = pc; + val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]); + sp[-1] = val; + if (unlikely(JS_IsException(val))) + goto exception; + } + BREAK; + + CASE(OP_get_ref_value): + { + JSValue val; + sf->cur_pc = pc; + if (unlikely(JS_IsUndefined(sp[-2]))) { + JSAtom atom = JS_ValueToAtom(ctx, sp[-1]); + if (atom != JS_ATOM_NULL) { + JS_ThrowReferenceErrorNotDefined(ctx, atom); + JS_FreeAtom(ctx, atom); + } + goto exception; + } + val = JS_GetPropertyValue(ctx, sp[-2], + js_dup(sp[-1])); + if (unlikely(JS_IsException(val))) + goto exception; + sp[0] = val; + sp++; + } + BREAK; + + CASE(OP_get_super_value): + { + JSValue val; + JSAtom atom; + sf->cur_pc = pc; + atom = JS_ValueToAtom(ctx, sp[-1]); + if (unlikely(atom == JS_ATOM_NULL)) + goto exception; + val = JS_GetPropertyInternal(ctx, sp[-2], atom, sp[-3], false); + JS_FreeAtom(ctx, atom); + if (unlikely(JS_IsException(val))) + goto exception; + JS_FreeValue(ctx, sp[-1]); + JS_FreeValue(ctx, sp[-2]); + JS_FreeValue(ctx, sp[-3]); + sp[-3] = val; + sp -= 2; + } + BREAK; + + CASE(OP_put_array_el): + { + int ret; + JSValue val; + uint32_t idx; + JSObject *p; + + val = sp[-1]; + if (likely(JS_VALUE_GET_TAG(sp[-2]) == JS_TAG_INT)) { + idx = JS_VALUE_GET_INT(sp[-2]); + if (likely(JS_VALUE_GET_TAG(sp[-3]) == JS_TAG_OBJECT)) { + p = JS_VALUE_GET_OBJ(sp[-3]); + if (likely(p->class_id == JS_CLASS_ARRAY && + idx < (uint32_t)p->u.array.count)) { + set_value(ctx, &p->u.array.u.values[idx], val); + JS_FreeValue(ctx, sp[-3]); + sp -= 3; + BREAK; + } + if (likely(p->class_id == JS_CLASS_ARRAY && + idx == (uint32_t)p->u.array.count && + p->fast_array && + p->extensible && + p->shape->proto == JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_ARRAY]) && + ctx->std_array_prototype)) { + /* fast path to add an element */ + uint32_t array_len; + if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT)) { + uint32_t new_len = idx + 1; + array_len = JS_VALUE_GET_INT(p->prop[0].u.value); + if (likely(new_len <= p->u.array.u1.size)) { + p->u.array.u.values[idx] = val; + p->u.array.count = new_len; + if (new_len > array_len) + p->prop[0].u.value = js_int32(new_len); + JS_FreeValue(ctx, sp[-3]); + sp -= 3; + BREAK; + } + } + } + } + } + sf->cur_pc = pc; + ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT); + JS_FreeValue(ctx, sp[-3]); + sp -= 3; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_put_ref_value): + { + int ret, flags; + sf->cur_pc = pc; + flags = JS_PROP_THROW_STRICT; + if (unlikely(JS_IsUndefined(sp[-3]))) { + if (is_strict_mode(ctx)) { + JSAtom atom = JS_ValueToAtom(ctx, sp[-2]); + if (atom != JS_ATOM_NULL) { + JS_ThrowReferenceErrorNotDefined(ctx, atom); + JS_FreeAtom(ctx, atom); + } + goto exception; + } else { + sp[-3] = js_dup(ctx->global_obj); + } + } else { + if (is_strict_mode(ctx)) + flags |= JS_PROP_NO_ADD; + } + ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], flags); + JS_FreeValue(ctx, sp[-3]); + sp -= 3; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_put_super_value): + { + int ret; + JSAtom atom; + sf->cur_pc = pc; + if (JS_VALUE_GET_TAG(sp[-3]) != JS_TAG_OBJECT) { + JS_ThrowTypeErrorNotAnObject(ctx); + goto exception; + } + atom = JS_ValueToAtom(ctx, sp[-2]); + if (unlikely(atom == JS_ATOM_NULL)) + goto exception; + ret = JS_SetPropertyInternal2(ctx, + sp[-3], atom, + sp[-1], sp[-4], + JS_PROP_THROW_STRICT); + JS_FreeAtom(ctx, atom); + JS_FreeValue(ctx, sp[-4]); + JS_FreeValue(ctx, sp[-3]); + JS_FreeValue(ctx, sp[-2]); + sp -= 4; + if (ret < 0) + goto exception; + } + BREAK; + + CASE(OP_define_array_el): + { + int ret; + ret = JS_DefinePropertyValueValue(ctx, sp[-3], js_dup(sp[-2]), sp[-1], + JS_PROP_C_W_E | JS_PROP_THROW); + sp -= 1; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_append): /* array pos enumobj -- array pos */ + { + sf->cur_pc = pc; + if (js_append_enumerate(ctx, sp)) + goto exception; + JS_FreeValue(ctx, *--sp); + } + BREAK; + + CASE(OP_copy_data_properties): /* target source excludeList */ + { + /* stack offsets (-1 based): + 2 bits for target, + 3 bits for source, + 2 bits for exclusionList */ + int mask; + + mask = *pc++; + sf->cur_pc = pc; + if (JS_CopyDataProperties(ctx, sp[-1 - (mask & 3)], + sp[-1 - ((mask >> 2) & 7)], + sp[-1 - ((mask >> 5) & 7)], 0)) + goto exception; + } + BREAK; + + CASE(OP_add): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int64_t r; + r = (int64_t)JS_VALUE_GET_INT(op1) + JS_VALUE_GET_INT(op2); + if (unlikely(r < INT32_MIN || r > INT32_MAX)) + sp[-2] = js_float64(r); + else + sp[-2] = js_int32(r); + sp--; + } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { + JS_X87_FPCW_SAVE_AND_ADJUST(fpcw); + sp[-2] = js_float64(JS_VALUE_GET_FLOAT64(op1) + + JS_VALUE_GET_FLOAT64(op2)); + JS_X87_FPCW_RESTORE(fpcw); + sp--; + } else { + sf->cur_pc = pc; + if (js_add_slow(ctx, sp)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_add_loc): + { + JSValue *pv; + int idx; + idx = *pc; + pc += 1; + + pv = &var_buf[idx]; + if (likely(JS_VALUE_IS_BOTH_INT(*pv, sp[-1]))) { + int64_t r; + r = (int64_t)JS_VALUE_GET_INT(*pv) + + JS_VALUE_GET_INT(sp[-1]); + if (unlikely((int)r != r)) + *pv = __JS_NewFloat64((double)r); + else + *pv = js_int32(r); + sp--; + } else if (JS_VALUE_GET_TAG(*pv) == JS_TAG_STRING) { + JSValue op1; + op1 = sp[-1]; + sp--; + sf->cur_pc = pc; + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); + if (JS_IsException(op1)) + goto exception; + op1 = JS_ConcatString(ctx, js_dup(*pv), op1); + if (JS_IsException(op1)) + goto exception; + set_value(ctx, pv, op1); + } else { + JSValue ops[2]; + /* In case of exception, js_add_slow frees ops[0] + and ops[1], so we must duplicate *pv */ + sf->cur_pc = pc; + ops[0] = js_dup(*pv); + ops[1] = sp[-1]; + sp--; + if (js_add_slow(ctx, ops + 2)) + goto exception; + set_value(ctx, pv, ops[0]); + } + } + BREAK; + CASE(OP_sub): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int64_t r; + r = (int64_t)JS_VALUE_GET_INT(op1) - JS_VALUE_GET_INT(op2); + if (unlikely((int)r != r)) + sp[-2] = __JS_NewFloat64((double)r); + else + sp[-2] = js_int32(r); + sp--; + } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { + JS_X87_FPCW_SAVE_AND_ADJUST(fpcw); + sp[-2] = js_float64(JS_VALUE_GET_FLOAT64(op1) - + JS_VALUE_GET_FLOAT64(op2)); + JS_X87_FPCW_RESTORE(fpcw); + sp--; + } else { + goto binary_arith_slow; + } + } + BREAK; + CASE(OP_mul): + { + JSValue op1, op2; + double d; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int32_t v1, v2; + int64_t r; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + r = (int64_t)v1 * v2; + if (unlikely((int)r != r)) { + d = (double)r; + goto mul_fp_res; + } + /* need to test zero case for -0 result */ + if (unlikely(r == 0 && (v1 | v2) < 0)) { + d = -0.0; + goto mul_fp_res; + } + sp[-2] = js_int32(r); + sp--; + } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { + JS_X87_FPCW_SAVE_AND_ADJUST(fpcw); + d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2); + JS_X87_FPCW_RESTORE(fpcw); + mul_fp_res: + sp[-2] = js_float64(d); + sp--; + } else { + goto binary_arith_slow; + } + } + BREAK; + CASE(OP_div): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int v1, v2; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + sp[-2] = js_number((double)v1 / (double)v2); + sp--; + } else { + goto binary_arith_slow; + } + } + BREAK; + CASE(OP_mod): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int v1, v2, r; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + /* We must avoid v2 = 0, v1 = INT32_MIN and v2 = + -1 and the cases where the result is -0. */ + if (unlikely(v1 < 0 || v2 <= 0)) + goto binary_arith_slow; + r = v1 % v2; + sp[-2] = js_int32(r); + sp--; + } else { + goto binary_arith_slow; + } + } + BREAK; + CASE(OP_pow): + binary_arith_slow: + sf->cur_pc = pc; + if (js_binary_arith_slow(ctx, sp, opcode)) + goto exception; + sp--; + BREAK; + + CASE(OP_plus): + { + JSValue op1; + uint32_t tag; + op1 = sp[-1]; + tag = JS_VALUE_GET_TAG(op1); + if (tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag)) { + } else { + sf->cur_pc = pc; + if (js_unary_arith_slow(ctx, sp, opcode)) + goto exception; + } + } + BREAK; + CASE(OP_neg): + { + JSValue op1; + uint32_t tag; + int val; + double d; + op1 = sp[-1]; + tag = JS_VALUE_GET_TAG(op1); + if (tag == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + /* Note: -0 cannot be expressed as integer */ + if (unlikely(val == 0)) { + d = -0.0; + goto neg_fp_res; + } + if (unlikely(val == INT32_MIN)) { + d = -(double)val; + goto neg_fp_res; + } + sp[-1] = js_int32(-val); + } else if (JS_TAG_IS_FLOAT64(tag)) { + d = -JS_VALUE_GET_FLOAT64(op1); + neg_fp_res: + sp[-1] = js_float64(d); + } else { + sf->cur_pc = pc; + if (js_unary_arith_slow(ctx, sp, opcode)) + goto exception; + } + } + BREAK; + CASE(OP_inc): + { + JSValue op1; + int val; + op1 = sp[-1]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (unlikely(val == INT32_MAX)) + goto inc_slow; + sp[-1] = js_int32(val + 1); + } else { + inc_slow: + sf->cur_pc = pc; + if (js_unary_arith_slow(ctx, sp, opcode)) + goto exception; + } + } + BREAK; + CASE(OP_dec): + { + JSValue op1; + int val; + op1 = sp[-1]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (unlikely(val == INT32_MIN)) + goto dec_slow; + sp[-1] = js_int32(val - 1); + } else { + dec_slow: + sf->cur_pc = pc; + if (js_unary_arith_slow(ctx, sp, opcode)) + goto exception; + } + } + BREAK; + CASE(OP_post_inc): + { + JSValue op1; + int val; + op1 = sp[-1]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (unlikely(val == INT32_MAX)) + goto post_inc_slow; + sp[0] = js_int32(val + 1); + } else { + post_inc_slow: + sf->cur_pc = pc; + if (js_post_inc_slow(ctx, sp, opcode)) + goto exception; + } + sp++; + } + BREAK; + CASE(OP_post_dec): + { + JSValue op1; + int val; + op1 = sp[-1]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (unlikely(val == INT32_MIN)) + goto post_dec_slow; + sp[0] = js_int32(val - 1); + } else { + post_dec_slow: + sf->cur_pc = pc; + if (js_post_inc_slow(ctx, sp, opcode)) + goto exception; + } + sp++; + } + BREAK; + CASE(OP_inc_loc): + { + JSValue op1; + int val; + int idx; + idx = *pc; + pc += 1; + + op1 = var_buf[idx]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (unlikely(val == INT32_MAX)) + goto inc_loc_slow; + var_buf[idx] = js_int32(val + 1); + } else { + inc_loc_slow: + sf->cur_pc = pc; + /* must duplicate otherwise the variable value may + be destroyed before JS code accesses it */ + op1 = js_dup(op1); + if (js_unary_arith_slow(ctx, &op1 + 1, OP_inc)) + goto exception; + set_value(ctx, &var_buf[idx], op1); + } + } + BREAK; + CASE(OP_dec_loc): + { + JSValue op1; + int val; + int idx; + idx = *pc; + pc += 1; + + op1 = var_buf[idx]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (unlikely(val == INT32_MIN)) + goto dec_loc_slow; + var_buf[idx] = js_int32(val - 1); + } else { + dec_loc_slow: + sf->cur_pc = pc; + /* must duplicate otherwise the variable value may + be destroyed before JS code accesses it */ + op1 = js_dup(op1); + if (js_unary_arith_slow(ctx, &op1 + 1, OP_dec)) + goto exception; + set_value(ctx, &var_buf[idx], op1); + } + } + BREAK; + CASE(OP_not): + { + JSValue op1; + op1 = sp[-1]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + sp[-1] = js_int32(~JS_VALUE_GET_INT(op1)); + } else { + sf->cur_pc = pc; + if (js_not_slow(ctx, sp)) + goto exception; + } + } + BREAK; + + CASE(OP_shl): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + uint32_t v1, v2; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2) & 0x1f; + sp[-2] = js_int32(v1 << v2); + sp--; + } else { + sf->cur_pc = pc; + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_shr): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + uint32_t v2; + v2 = JS_VALUE_GET_INT(op2); + v2 &= 0x1f; + sp[-2] = js_uint32((uint32_t)JS_VALUE_GET_INT(op1) >> v2); + sp--; + } else { + sf->cur_pc = pc; + if (js_shr_slow(ctx, sp)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_sar): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + uint32_t v2; + v2 = JS_VALUE_GET_INT(op2); + if (unlikely(v2 > 0x1f)) { + v2 &= 0x1f; + } + sp[-2] = js_int32((int)JS_VALUE_GET_INT(op1) >> v2); + sp--; + } else { + sf->cur_pc = pc; + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_and): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + sp[-2] = js_int32(JS_VALUE_GET_INT(op1) & JS_VALUE_GET_INT(op2)); + sp--; + } else { + sf->cur_pc = pc; + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_or): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + sp[-2] = js_int32(JS_VALUE_GET_INT(op1) | JS_VALUE_GET_INT(op2)); + sp--; + } else { + sf->cur_pc = pc; + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_xor): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + sp[-2] = js_int32(JS_VALUE_GET_INT(op1) ^ JS_VALUE_GET_INT(op2)); + sp--; + } else { + sf->cur_pc = pc; + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; + + +#define OP_CMP(opcode, binary_op, slow_call) \ + CASE(opcode): \ + { \ + JSValue op1, op2; \ + op1 = sp[-2]; \ + op2 = sp[-1]; \ + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { \ + sp[-2] = js_bool(JS_VALUE_GET_INT(op1) binary_op JS_VALUE_GET_INT(op2)); \ + sp--; \ + } else { \ + sf->cur_pc = pc; \ + if (slow_call) \ + goto exception; \ + sp--; \ + } \ + } \ + BREAK + + OP_CMP(OP_lt, <, js_relational_slow(ctx, sp, opcode)); + OP_CMP(OP_lte, <=, js_relational_slow(ctx, sp, opcode)); + OP_CMP(OP_gt, >, js_relational_slow(ctx, sp, opcode)); + OP_CMP(OP_gte, >=, js_relational_slow(ctx, sp, opcode)); + OP_CMP(OP_eq, ==, js_eq_slow(ctx, sp, 0)); + OP_CMP(OP_neq, !=, js_eq_slow(ctx, sp, 1)); + OP_CMP(OP_strict_eq, ==, js_strict_eq_slow(ctx, sp, 0)); + OP_CMP(OP_strict_neq, !=, js_strict_eq_slow(ctx, sp, 1)); + + CASE(OP_in): + sf->cur_pc = pc; + if (js_operator_in(ctx, sp)) + goto exception; + sp--; + BREAK; + CASE(OP_private_in): + if (js_operator_private_in(ctx, sp)) + goto exception; + sp--; + BREAK; + CASE(OP_instanceof): + sf->cur_pc = pc; + if (js_operator_instanceof(ctx, sp)) + goto exception; + sp--; + BREAK; + CASE(OP_typeof): + { + JSValue op1; + JSAtom atom; + + op1 = sp[-1]; + atom = js_operator_typeof(ctx, op1); + JS_FreeValue(ctx, op1); + sp[-1] = JS_AtomToString(ctx, atom); + } + BREAK; + CASE(OP_delete): + sf->cur_pc = pc; + if (js_operator_delete(ctx, sp)) + goto exception; + sp--; + BREAK; + CASE(OP_delete_var): + { + JSAtom atom; + int ret; + + atom = get_u32(pc); + pc += 4; + + sf->cur_pc = pc; + ret = JS_DeleteGlobalVar(ctx, atom); + if (unlikely(ret < 0)) + goto exception; + *sp++ = js_bool(ret); + } + BREAK; + + CASE(OP_to_object): + if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_OBJECT) { + sf->cur_pc = pc; + ret_val = JS_ToObject(ctx, sp[-1]); + if (JS_IsException(ret_val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret_val; + } + BREAK; + + CASE(OP_to_propkey): + switch (JS_VALUE_GET_TAG(sp[-1])) { + case JS_TAG_INT: + case JS_TAG_STRING: + case JS_TAG_SYMBOL: + break; + default: + sf->cur_pc = pc; + ret_val = JS_ToPropertyKey(ctx, sp[-1]); + if (JS_IsException(ret_val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret_val; + break; + } + BREAK; + + CASE(OP_to_propkey2): + /* must be tested first */ + if (unlikely(JS_IsUndefined(sp[-2]) || JS_IsNull(sp[-2]))) { + JS_ThrowTypeError(ctx, "value has no property"); + goto exception; + } + switch (JS_VALUE_GET_TAG(sp[-1])) { + case JS_TAG_INT: + case JS_TAG_STRING: + case JS_TAG_SYMBOL: + break; + default: + sf->cur_pc = pc; + ret_val = JS_ToPropertyKey(ctx, sp[-1]); + if (JS_IsException(ret_val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret_val; + break; + } + BREAK; + CASE(OP_with_get_var): + CASE(OP_with_put_var): + CASE(OP_with_delete_var): + CASE(OP_with_make_ref): + CASE(OP_with_get_ref): + CASE(OP_with_get_ref_undef): + { + JSAtom atom; + int32_t diff; + JSValue obj, val; + int ret, is_with; + atom = get_u32(pc); + diff = get_u32(pc + 4); + is_with = pc[8]; + pc += 9; + sf->cur_pc = pc; + + obj = sp[-1]; + ret = JS_HasProperty(ctx, obj, atom); + if (unlikely(ret < 0)) + goto exception; + if (ret) { + if (is_with) { + ret = js_has_unscopable(ctx, obj, atom); + if (unlikely(ret < 0)) + goto exception; + if (ret) + goto no_with; + } + switch (opcode) { + case OP_with_get_var: + val = JS_GetProperty(ctx, obj, atom); + if (unlikely(JS_IsException(val))) + goto exception; + set_value(ctx, &sp[-1], val); + break; + case OP_with_put_var: + /* XXX: check if strict mode */ + ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2], + JS_PROP_THROW_STRICT); + JS_FreeValue(ctx, sp[-1]); + sp -= 2; + if (unlikely(ret < 0)) + goto exception; + break; + case OP_with_delete_var: + ret = JS_DeleteProperty(ctx, obj, atom, 0); + if (unlikely(ret < 0)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = js_bool(ret); + break; + case OP_with_make_ref: + /* produce a pair object/propname on the stack */ + *sp++ = JS_AtomToValue(ctx, atom); + break; + case OP_with_get_ref: + /* produce a pair object/method on the stack */ + val = JS_GetProperty(ctx, obj, atom); + if (unlikely(JS_IsException(val))) + goto exception; + *sp++ = val; + break; + case OP_with_get_ref_undef: + /* produce a pair undefined/function on the stack */ + val = JS_GetProperty(ctx, obj, atom); + if (unlikely(JS_IsException(val))) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = JS_UNDEFINED; + *sp++ = val; + break; + } + pc += diff - 5; + } else { + no_with: + /* if not jumping, drop the object argument */ + JS_FreeValue(ctx, sp[-1]); + sp--; + } + } + BREAK; + + CASE(OP_await): + ret_val = js_int32(FUNC_RET_AWAIT); + goto done_generator; + CASE(OP_yield): + ret_val = js_int32(FUNC_RET_YIELD); + goto done_generator; + CASE(OP_yield_star): + CASE(OP_async_yield_star): + ret_val = js_int32(FUNC_RET_YIELD_STAR); + goto done_generator; + CASE(OP_return_async): + CASE(OP_initial_yield): + ret_val = JS_UNDEFINED; + goto done_generator; + + CASE(OP_nop): + BREAK; + CASE(OP_is_undefined_or_null): + if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED || + JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) { + goto set_true; + } else { + goto free_and_set_false; + } + CASE(OP_is_undefined): + if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED) { + goto set_true; + } else { + goto free_and_set_false; + } + CASE(OP_is_null): + if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) { + goto set_true; + } else { + goto free_and_set_false; + } + /* XXX: could merge to a single opcode */ + CASE(OP_typeof_is_undefined): + /* different from OP_is_undefined because of isHTMLDDA */ + if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_undefined) { + goto free_and_set_true; + } else { + goto free_and_set_false; + } + CASE(OP_typeof_is_function): + if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_function) { + goto free_and_set_true; + } else { + goto free_and_set_false; + } + free_and_set_true: + JS_FreeValue(ctx, sp[-1]); + set_true: + sp[-1] = JS_TRUE; + BREAK; + free_and_set_false: + JS_FreeValue(ctx, sp[-1]); + sp[-1] = JS_FALSE; + BREAK; + CASE(OP_invalid): + DEFAULT: + JS_ThrowInternalError(ctx, "invalid opcode: pc=%u opcode=0x%02x", + (int)(pc - b->byte_code_buf - 1), opcode); + goto exception; + } + } + exception: + if (needs_backtrace(rt->current_exception) + || JS_IsUndefined(ctx->error_back_trace)) { + sf->cur_pc = pc; + build_backtrace(ctx, rt->current_exception, JS_UNDEFINED, + NULL, 0, 0, 0); + } + if (!JS_IsUncatchableError(rt->current_exception)) { + while (sp > stack_buf) { + JSValue val = *--sp; + JS_FreeValue(ctx, val); + if (JS_VALUE_GET_TAG(val) == JS_TAG_CATCH_OFFSET) { + int pos = JS_VALUE_GET_INT(val); + if (pos == 0) { + /* enumerator: close it with a throw */ + JS_FreeValue(ctx, sp[-1]); /* drop the next method */ + sp--; + JS_IteratorClose(ctx, sp[-1], true); + } else { + *sp++ = rt->current_exception; + rt->current_exception = JS_UNINITIALIZED; + JS_FreeValueRT(rt, ctx->error_back_trace); + ctx->error_back_trace = JS_UNDEFINED; + pc = b->byte_code_buf + pos; + goto restart; + } + } + } + } + ret_val = JS_EXCEPTION; + /* the local variables are freed by the caller in the generator + case. Hence the label 'done' should never be reached in a + generator function. */ + if (b->func_kind != JS_FUNC_NORMAL) { + done_generator: + sf->cur_pc = pc; + sf->cur_sp = sp; + } else { + done: + if (unlikely(sf->var_ref_count != 0)) { + /* variable references reference the stack: must close them */ + close_var_refs(rt, sf); + } + /* free the local variables and stack */ + for(pval = local_buf; pval < sp; pval++) { + JS_FreeValue(ctx, *pval); + } + } + rt->current_stack_frame = sf->prev_frame; + return ret_val; +} + +JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj, + int argc, JSValueConst *argv) +{ + return JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED, + argc, argv, JS_CALL_FLAG_COPY_ARGV); +} + +static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj, + int argc, JSValueConst *argv) +{ + JSValue res = JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED, + argc, argv, JS_CALL_FLAG_COPY_ARGV); + JS_FreeValue(ctx, func_obj); + return res; +} + +/* warning: the refcount of the context is not incremented. Return + NULL in case of exception (case of revoked proxy only) */ +static JSContext *JS_GetFunctionRealm(JSContext *ctx, JSValueConst func_obj) +{ + JSObject *p; + JSContext *realm; + + if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) + return ctx; + p = JS_VALUE_GET_OBJ(func_obj); + switch(p->class_id) { + case JS_CLASS_C_FUNCTION: + realm = p->u.cfunc.realm; + break; + case JS_CLASS_BYTECODE_FUNCTION: + case JS_CLASS_GENERATOR_FUNCTION: + case JS_CLASS_ASYNC_FUNCTION: + case JS_CLASS_ASYNC_GENERATOR_FUNCTION: + { + JSFunctionBytecode *b; + b = p->u.func.function_bytecode; + realm = b->realm; + } + break; + case JS_CLASS_PROXY: + { + JSProxyData *s = p->u.opaque; + if (!s) + return ctx; + if (s->is_revoked) { + JS_ThrowTypeErrorRevokedProxy(ctx); + return NULL; + } else { + realm = JS_GetFunctionRealm(ctx, s->target); + } + } + break; + case JS_CLASS_BOUND_FUNCTION: + { + JSBoundFunction *bf = p->u.bound_function; + realm = JS_GetFunctionRealm(ctx, bf->func_obj); + } + break; + default: + realm = ctx; + break; + } + return realm; +} + +static JSValue js_create_from_ctor(JSContext *ctx, JSValueConst ctor, + int class_id) +{ + JSValue proto, obj; + JSContext *realm; + + if (JS_IsUndefined(ctor)) { + proto = js_dup(ctx->class_proto[class_id]); + } else { + proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype); + if (JS_IsException(proto)) + return proto; + if (!JS_IsObject(proto)) { + JS_FreeValue(ctx, proto); + realm = JS_GetFunctionRealm(ctx, ctor); + if (!realm) + return JS_EXCEPTION; + proto = js_dup(realm->class_proto[class_id]); + } + } + obj = JS_NewObjectProtoClass(ctx, proto, class_id); + JS_FreeValue(ctx, proto); + return obj; +} + +/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ +static JSValue JS_CallConstructorInternal(JSContext *ctx, + JSValueConst func_obj, + JSValueConst new_target, + int argc, JSValueConst *argv, + int flags) +{ + JSObject *p; + JSFunctionBytecode *b; + + if (js_poll_interrupts(ctx)) + return JS_EXCEPTION; + flags |= JS_CALL_FLAG_CONSTRUCTOR; + if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) + goto not_a_function; + p = JS_VALUE_GET_OBJ(func_obj); + if (unlikely(!p->is_constructor)) + return JS_ThrowTypeErrorNotAConstructor(ctx, func_obj); + if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) { + JSClassCall *call_func; + call_func = ctx->rt->class_array[p->class_id].call; + if (!call_func) { + not_a_function: + return JS_ThrowTypeErrorNotAFunction(ctx); + } + return call_func(ctx, func_obj, new_target, argc, + argv, flags); + } + + b = p->u.func.function_bytecode; + if (b->is_derived_class_constructor) { + return JS_CallInternal(ctx, func_obj, JS_UNDEFINED, new_target, argc, argv, flags); + } else { + JSValue obj, ret; + /* legacy constructor behavior */ + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT); + if (JS_IsException(obj)) + return JS_EXCEPTION; + ret = JS_CallInternal(ctx, func_obj, obj, new_target, argc, argv, flags); + if (JS_VALUE_GET_TAG(ret) == JS_TAG_OBJECT || + JS_IsException(ret)) { + JS_FreeValue(ctx, obj); + return ret; + } else { + JS_FreeValue(ctx, ret); + return obj; + } + } +} + +JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj, + JSValueConst new_target, + int argc, JSValueConst *argv) +{ + return JS_CallConstructorInternal(ctx, func_obj, new_target, + argc, argv, + JS_CALL_FLAG_COPY_ARGV); +} + +JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj, + int argc, JSValueConst *argv) +{ + return JS_CallConstructorInternal(ctx, func_obj, func_obj, + argc, argv, + JS_CALL_FLAG_COPY_ARGV); +} + +JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom, + int argc, JSValueConst *argv) +{ + JSValue func_obj; + func_obj = JS_GetProperty(ctx, this_val, atom); + if (JS_IsException(func_obj)) + return func_obj; + return JS_CallFree(ctx, func_obj, this_val, argc, argv); +} + +static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom, + int argc, JSValueConst *argv) +{ + JSValue res = JS_Invoke(ctx, this_val, atom, argc, argv); + JS_FreeValue(ctx, this_val); + return res; +} + +/* JSAsyncFunctionState (used by generator and async functions) */ +static __exception int async_func_init(JSContext *ctx, JSAsyncFunctionState *s, + JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv) +{ + JSObject *p; + JSFunctionBytecode *b; + JSStackFrame *sf; + int local_count, i, arg_buf_len, n; + size_t alloc_size; + + sf = &s->frame; + p = JS_VALUE_GET_OBJ(func_obj); + b = p->u.func.function_bytecode; + sf->is_strict_mode = b->is_strict_mode; + sf->cur_pc = b->byte_code_buf; + arg_buf_len = max_int(b->arg_count, argc); + local_count = arg_buf_len + b->var_count + b->stack_size; + alloc_size = sizeof(JSValue) * max_int(local_count, 1) + + sizeof(JSVarRef *) * b->var_ref_count; + sf->arg_buf = js_malloc(ctx, alloc_size); + if (!sf->arg_buf) + return -1; + sf->cur_func = js_dup(func_obj); + s->this_val = js_dup(this_obj); + s->argc = argc; + sf->arg_count = arg_buf_len; + sf->var_buf = sf->arg_buf + arg_buf_len; + sf->cur_sp = sf->var_buf + b->var_count; + sf->var_refs = (JSVarRef **)(sf->cur_sp + b->stack_size); + sf->var_ref_count = b->var_ref_count; + for(i = 0; i < b->var_ref_count; i++) + sf->var_refs[i] = NULL; + for(i = 0; i < argc; i++) + sf->arg_buf[i] = js_dup(argv[i]); + n = arg_buf_len + b->var_count; + for(i = argc; i < n; i++) + sf->arg_buf[i] = JS_UNDEFINED; + return 0; +} + +static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s, + JS_MarkFunc *mark_func) +{ + JSStackFrame *sf; + JSValue *sp; + + sf = &s->frame; + JS_MarkValue(rt, sf->cur_func, mark_func); + JS_MarkValue(rt, s->this_val, mark_func); + if (sf->cur_sp) { + /* if the function is running, cur_sp is not known so we + cannot mark the stack. Marking the variables is not needed + because a running function cannot be part of a removable + cycle */ + for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) + JS_MarkValue(rt, *sp, mark_func); + } +} + +static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s) +{ + JSStackFrame *sf; + JSValue *sp; + + sf = &s->frame; + + if (sf->arg_buf) { + /* close the closure variables. */ + if (sf->var_ref_count != 0) + close_var_refs(rt, sf); + + /* cannot free the function if it is running */ + assert(sf->cur_sp != NULL); + for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) { + JS_FreeValueRT(rt, *sp); + } + js_free_rt(rt, sf->arg_buf); + } + JS_FreeValueRT(rt, sf->cur_func); + JS_FreeValueRT(rt, s->this_val); +} + +static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s) +{ + JSValue func_obj; + + if (js_check_stack_overflow(ctx->rt, 0)) + return JS_ThrowStackOverflow(ctx); + + /* the tag does not matter provided it is not an object */ + func_obj = JS_MKPTR(JS_TAG_INT, s); + return JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED, + s->argc, vc(s->frame.arg_buf), + JS_CALL_FLAG_GENERATOR); +} + + +/* Generators */ + +typedef enum JSGeneratorStateEnum { + JS_GENERATOR_STATE_SUSPENDED_START, + JS_GENERATOR_STATE_SUSPENDED_YIELD, + JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR, + JS_GENERATOR_STATE_EXECUTING, + JS_GENERATOR_STATE_COMPLETED, +} JSGeneratorStateEnum; + +typedef struct JSGeneratorData { + JSGeneratorStateEnum state; + JSAsyncFunctionState func_state; +} JSGeneratorData; + +static void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s) +{ + if (s->state == JS_GENERATOR_STATE_COMPLETED) + return; + async_func_free(rt, &s->func_state); + s->state = JS_GENERATOR_STATE_COMPLETED; +} + +static void js_generator_finalizer(JSRuntime *rt, JSValueConst obj) +{ + JSGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_GENERATOR); + + if (s) { + free_generator_stack_rt(rt, s); + js_free_rt(rt, s); + } +} + +static void free_generator_stack(JSContext *ctx, JSGeneratorData *s) +{ + free_generator_stack_rt(ctx->rt, s); +} + +static void js_generator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSGeneratorData *s = p->u.generator_data; + + if (!s || s->state == JS_GENERATOR_STATE_COMPLETED) + return; + async_func_mark(rt, &s->func_state, mark_func); +} + +/* XXX: use enum */ +#define GEN_MAGIC_NEXT 0 +#define GEN_MAGIC_RETURN 1 +#define GEN_MAGIC_THROW 2 + +static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + int *pdone, int magic) +{ + JSGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_GENERATOR); + JSStackFrame *sf; + JSValue ret, func_ret; + + *pdone = true; + if (!s) + return JS_ThrowTypeError(ctx, "not a generator"); + sf = &s->func_state.frame; + switch(s->state) { + default: + case JS_GENERATOR_STATE_SUSPENDED_START: + if (magic == GEN_MAGIC_NEXT) { + goto exec_no_arg; + } else { + free_generator_stack(ctx, s); + goto done; + } + break; + case JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR: + case JS_GENERATOR_STATE_SUSPENDED_YIELD: + /* cur_sp[-1] was set to JS_UNDEFINED in the previous call */ + ret = js_dup(argv[0]); + if (magic == GEN_MAGIC_THROW && + s->state == JS_GENERATOR_STATE_SUSPENDED_YIELD) { + JS_Throw(ctx, ret); + s->func_state.throw_flag = true; + } else { + sf->cur_sp[-1] = ret; + sf->cur_sp[0] = js_int32(magic); + sf->cur_sp++; + exec_no_arg: + s->func_state.throw_flag = false; + } + s->state = JS_GENERATOR_STATE_EXECUTING; + func_ret = async_func_resume(ctx, &s->func_state); + s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD; + if (JS_IsException(func_ret)) { + /* finalize the execution in case of exception */ + free_generator_stack(ctx, s); + return func_ret; + } + if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) { + /* get the returned yield value at the top of the stack */ + ret = sf->cur_sp[-1]; + sf->cur_sp[-1] = JS_UNDEFINED; + if (JS_VALUE_GET_INT(func_ret) == FUNC_RET_YIELD_STAR) { + s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR; + /* return (value, done) object */ + *pdone = 2; + } else { + *pdone = false; + } + } else { + /* end of iterator */ + ret = sf->cur_sp[-1]; + sf->cur_sp[-1] = JS_UNDEFINED; + JS_FreeValue(ctx, func_ret); + free_generator_stack(ctx, s); + } + break; + case JS_GENERATOR_STATE_COMPLETED: + done: + /* execution is finished */ + switch(magic) { + default: + case GEN_MAGIC_NEXT: + ret = JS_UNDEFINED; + break; + case GEN_MAGIC_RETURN: + ret = js_dup(argv[0]); + break; + case GEN_MAGIC_THROW: + ret = JS_Throw(ctx, js_dup(argv[0])); + break; + } + break; + case JS_GENERATOR_STATE_EXECUTING: + ret = JS_ThrowTypeError(ctx, "cannot invoke a running generator"); + break; + } + return ret; +} + +static JSValue js_call_generator_function(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, + int flags) +{ + JSValue obj, func_ret; + JSGeneratorData *s; + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + s->state = JS_GENERATOR_STATE_SUSPENDED_START; + if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) { + s->state = JS_GENERATOR_STATE_COMPLETED; + goto fail; + } + + /* execute the function up to 'OP_initial_yield' */ + func_ret = async_func_resume(ctx, &s->func_state); + if (JS_IsException(func_ret)) + goto fail; + JS_FreeValue(ctx, func_ret); + + obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_GENERATOR); + if (JS_IsException(obj)) + goto fail; + JS_SetOpaqueInternal(obj, s); + return obj; + fail: + free_generator_stack_rt(ctx->rt, s); + js_free(ctx, s); + return JS_EXCEPTION; +} + +/* AsyncFunction */ + +static void js_async_function_terminate(JSRuntime *rt, JSAsyncFunctionData *s) +{ + if (s->is_active) { + async_func_free(rt, &s->func_state); + s->is_active = false; + } +} + +static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s) +{ + js_async_function_terminate(rt, s); + JS_FreeValueRT(rt, s->resolving_funcs[0]); + JS_FreeValueRT(rt, s->resolving_funcs[1]); + remove_gc_object(&s->header); + js_free_rt(rt, s); +} + +static void js_async_function_free(JSRuntime *rt, JSAsyncFunctionData *s) +{ + if (--s->header.ref_count == 0) { + js_async_function_free0(rt, s); + } +} + +static void js_async_function_resolve_finalizer(JSRuntime *rt, + JSValueConst val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSAsyncFunctionData *s = p->u.async_function_data; + if (s) { + js_async_function_free(rt, s); + } +} + +static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSAsyncFunctionData *s = p->u.async_function_data; + if (s) { + mark_func(rt, &s->header); + } +} + +static int js_async_function_resolve_create(JSContext *ctx, + JSAsyncFunctionData *s, + JSValue *resolving_funcs) +{ + int i; + JSObject *p; + + for(i = 0; i < 2; i++) { + resolving_funcs[i] = + JS_NewObjectProtoClass(ctx, ctx->function_proto, + JS_CLASS_ASYNC_FUNCTION_RESOLVE + i); + if (JS_IsException(resolving_funcs[i])) { + if (i == 1) + JS_FreeValue(ctx, resolving_funcs[0]); + return -1; + } + p = JS_VALUE_GET_OBJ(resolving_funcs[i]); + s->header.ref_count++; + p->u.async_function_data = s; + } + return 0; +} + +static bool js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s) +{ + bool is_success = true; + JSValue func_ret, ret2; + + func_ret = async_func_resume(ctx, &s->func_state); + if (JS_IsException(func_ret)) { + fail: + if (unlikely(JS_IsUncatchableError(ctx->rt->current_exception))) { + is_success = false; + } else { + JSValue error = JS_GetException(ctx); + ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED, + 1, vc(&error)); + JS_FreeValue(ctx, error); + resolved: + if (unlikely(JS_IsException(ret2))) { + if (JS_IsUncatchableError(ctx->rt->current_exception)) { + is_success = false; + } else { + abort(); /* BUG */ + } + } + JS_FreeValue(ctx, ret2); + } + js_async_function_terminate(ctx->rt, s); + } else { + JSValue value; + value = s->func_state.frame.cur_sp[-1]; + s->func_state.frame.cur_sp[-1] = JS_UNDEFINED; + if (JS_IsUndefined(func_ret)) { + /* function returned */ + ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED, + 1, vc(&value)); + JS_FreeValue(ctx, value); + goto resolved; + } else { + JSValue promise, resolving_funcs[2], resolving_funcs1[2]; + int i, res; + + /* await */ + JS_FreeValue(ctx, func_ret); /* not used */ + promise = js_promise_resolve(ctx, ctx->promise_ctor, + 1, vc(&value), 0); + JS_FreeValue(ctx, value); + if (JS_IsException(promise)) + goto fail; + if (js_async_function_resolve_create(ctx, s, resolving_funcs)) { + JS_FreeValue(ctx, promise); + goto fail; + } + + /* Note: no need to create 'thrownawayCapability' as in + the spec */ + for(i = 0; i < 2; i++) + resolving_funcs1[i] = JS_UNDEFINED; + res = perform_promise_then(ctx, promise, + vc(resolving_funcs), + vc(resolving_funcs1)); + JS_FreeValue(ctx, promise); + for(i = 0; i < 2; i++) + JS_FreeValue(ctx, resolving_funcs[i]); + if (res) + goto fail; + } + } + return is_success; +} + +static JSValue js_async_function_resolve_call(JSContext *ctx, + JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, + int flags) +{ + JSObject *p = JS_VALUE_GET_OBJ(func_obj); + JSAsyncFunctionData *s = p->u.async_function_data; + bool is_reject = p->class_id - JS_CLASS_ASYNC_FUNCTION_RESOLVE; + JSValueConst arg; + + if (argc > 0) + arg = argv[0]; + else + arg = JS_UNDEFINED; + s->func_state.throw_flag = is_reject; + if (is_reject) { + JS_Throw(ctx, js_dup(arg)); + } else { + /* return value of await */ + s->func_state.frame.cur_sp[-1] = js_dup(arg); + } + if (!js_async_function_resume(ctx, s)) + return JS_EXCEPTION; + return JS_UNDEFINED; +} + +static JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags) +{ + JSValue promise; + JSAsyncFunctionData *s; + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + s->header.ref_count = 1; + add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION); + s->is_active = false; + s->resolving_funcs[0] = JS_UNDEFINED; + s->resolving_funcs[1] = JS_UNDEFINED; + + promise = JS_NewPromiseCapability(ctx, s->resolving_funcs); + if (JS_IsException(promise)) + goto fail; + + if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) { + fail: + JS_FreeValue(ctx, promise); + js_async_function_free(ctx->rt, s); + return JS_EXCEPTION; + } + s->is_active = true; + + if (!js_async_function_resume(ctx, s)) + goto fail; + + js_async_function_free(ctx->rt, s); + + return promise; +} + +/* AsyncGenerator */ + +typedef enum JSAsyncGeneratorStateEnum { + JS_ASYNC_GENERATOR_STATE_SUSPENDED_START, + JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD, + JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR, + JS_ASYNC_GENERATOR_STATE_EXECUTING, + JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN, + JS_ASYNC_GENERATOR_STATE_COMPLETED, +} JSAsyncGeneratorStateEnum; + +typedef struct JSAsyncGeneratorRequest { + struct list_head link; + /* completion */ + int completion_type; /* GEN_MAGIC_x */ + JSValue result; + /* promise capability */ + JSValue promise; + JSValue resolving_funcs[2]; +} JSAsyncGeneratorRequest; + +typedef struct JSAsyncGeneratorData { + JSObject *generator; /* back pointer to the object (const) */ + JSAsyncGeneratorStateEnum state; + JSAsyncFunctionState func_state; + struct list_head queue; /* list of JSAsyncGeneratorRequest.link */ +} JSAsyncGeneratorData; + +static void js_async_generator_free(JSRuntime *rt, + JSAsyncGeneratorData *s) +{ + struct list_head *el, *el1; + JSAsyncGeneratorRequest *req; + + list_for_each_safe(el, el1, &s->queue) { + req = list_entry(el, JSAsyncGeneratorRequest, link); + JS_FreeValueRT(rt, req->result); + JS_FreeValueRT(rt, req->promise); + JS_FreeValueRT(rt, req->resolving_funcs[0]); + JS_FreeValueRT(rt, req->resolving_funcs[1]); + js_free_rt(rt, req); + } + if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED && + s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) { + async_func_free(rt, &s->func_state); + } + js_free_rt(rt, s); +} + +static void js_async_generator_finalizer(JSRuntime *rt, JSValueConst obj) +{ + JSAsyncGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_ASYNC_GENERATOR); + + if (s) { + js_async_generator_free(rt, s); + } +} + +static void js_async_generator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSAsyncGeneratorData *s = JS_GetOpaque(val, JS_CLASS_ASYNC_GENERATOR); + struct list_head *el; + JSAsyncGeneratorRequest *req; + if (s) { + list_for_each(el, &s->queue) { + req = list_entry(el, JSAsyncGeneratorRequest, link); + JS_MarkValue(rt, req->result, mark_func); + JS_MarkValue(rt, req->promise, mark_func); + JS_MarkValue(rt, req->resolving_funcs[0], mark_func); + JS_MarkValue(rt, req->resolving_funcs[1], mark_func); + } + if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED && + s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) { + async_func_mark(rt, &s->func_state, mark_func); + } + } +} + +static JSValue js_async_generator_resolve_function(JSContext *ctx, + JSValueConst this_obj, + int argc, JSValueConst *argv, + int magic, JSValueConst *func_data); + +static int js_async_generator_resolve_function_create(JSContext *ctx, + JSValue generator, + JSValue *resolving_funcs, + bool is_resume_next) +{ + int i; + JSValue func; + + for(i = 0; i < 2; i++) { + func = JS_NewCFunctionData(ctx, js_async_generator_resolve_function, 1, + i + is_resume_next * 2, 1, vc(&generator)); + if (JS_IsException(func)) { + if (i == 1) + JS_FreeValue(ctx, resolving_funcs[0]); + return -1; + } + resolving_funcs[i] = func; + } + return 0; +} + +static int js_async_generator_await(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValue value) +{ + JSValue promise, resolving_funcs[2], resolving_funcs1[2]; + int i, res; + + promise = js_promise_resolve(ctx, ctx->promise_ctor, + 1, vc(&value), 0); + if (JS_IsException(promise)) + goto fail; + + if (js_async_generator_resolve_function_create(ctx, JS_MKPTR(JS_TAG_OBJECT, s->generator), + resolving_funcs, false)) { + JS_FreeValue(ctx, promise); + goto fail; + } + + /* Note: no need to create 'thrownawayCapability' as in + the spec */ + for(i = 0; i < 2; i++) + resolving_funcs1[i] = JS_UNDEFINED; + res = perform_promise_then(ctx, promise, + vc(resolving_funcs), + vc(resolving_funcs1)); + JS_FreeValue(ctx, promise); + for(i = 0; i < 2; i++) + JS_FreeValue(ctx, resolving_funcs[i]); + if (res) + goto fail; + return 0; + fail: + return -1; +} + +static void js_async_generator_resolve_or_reject(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValueConst result, + int is_reject) +{ + JSAsyncGeneratorRequest *next; + JSValue ret; + + next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link); + list_del(&next->link); + ret = JS_Call(ctx, next->resolving_funcs[is_reject], JS_UNDEFINED, 1, + &result); + JS_FreeValue(ctx, ret); + JS_FreeValue(ctx, next->result); + JS_FreeValue(ctx, next->promise); + JS_FreeValue(ctx, next->resolving_funcs[0]); + JS_FreeValue(ctx, next->resolving_funcs[1]); + js_free(ctx, next); +} + +static void js_async_generator_resolve(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValueConst value, + bool done) +{ + JSValue result; + result = js_create_iterator_result(ctx, js_dup(value), done); + /* XXX: better exception handling ? */ + js_async_generator_resolve_or_reject(ctx, s, result, 0); + JS_FreeValue(ctx, result); + } + +static void js_async_generator_reject(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValueConst exception) +{ + js_async_generator_resolve_or_reject(ctx, s, exception, 1); +} + +static void js_async_generator_complete(JSContext *ctx, + JSAsyncGeneratorData *s) +{ + if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED) { + s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; + async_func_free(ctx->rt, &s->func_state); + } +} + +static int js_async_generator_completed_return(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValue value) +{ + JSValue promise, resolving_funcs[2], resolving_funcs1[2]; + int res; + + // Can fail looking up JS_ATOM_constructor when is_reject==0. + promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, vc(&value), + /*is_reject*/0); + // A poisoned .constructor property is observable and the resulting + // exception should be delivered to the catch handler. + if (JS_IsException(promise)) { + JSValue err = JS_GetException(ctx); + promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, vc(&err), + /*is_reject*/1); + JS_FreeValue(ctx, err); + if (JS_IsException(promise)) + return -1; + } + if (js_async_generator_resolve_function_create(ctx, + JS_MKPTR(JS_TAG_OBJECT, s->generator), + resolving_funcs1, + true)) { + JS_FreeValue(ctx, promise); + return -1; + } + resolving_funcs[0] = JS_UNDEFINED; + resolving_funcs[1] = JS_UNDEFINED; + res = perform_promise_then(ctx, promise, + vc(resolving_funcs1), + vc(resolving_funcs)); + JS_FreeValue(ctx, resolving_funcs1[0]); + JS_FreeValue(ctx, resolving_funcs1[1]); + JS_FreeValue(ctx, promise); + return res; +} + +static void js_async_generator_resume_next(JSContext *ctx, + JSAsyncGeneratorData *s) +{ + JSAsyncGeneratorRequest *next; + JSValue func_ret, value; + + for(;;) { + if (list_empty(&s->queue)) + break; + next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link); + switch(s->state) { + case JS_ASYNC_GENERATOR_STATE_EXECUTING: + /* only happens when restarting execution after await() */ + goto resume_exec; + case JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN: + goto done; + case JS_ASYNC_GENERATOR_STATE_SUSPENDED_START: + if (next->completion_type == GEN_MAGIC_NEXT) { + goto exec_no_arg; + } else { + js_async_generator_complete(ctx, s); + } + break; + case JS_ASYNC_GENERATOR_STATE_COMPLETED: + if (next->completion_type == GEN_MAGIC_NEXT) { + js_async_generator_resolve(ctx, s, JS_UNDEFINED, true); + } else if (next->completion_type == GEN_MAGIC_RETURN) { + s->state = JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN; + js_async_generator_completed_return(ctx, s, next->result); + } else { + js_async_generator_reject(ctx, s, next->result); + } + goto done; + case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD: + case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR: + value = js_dup(next->result); + if (next->completion_type == GEN_MAGIC_THROW && + s->state == JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD) { + JS_Throw(ctx, value); + s->func_state.throw_flag = true; + } else { + /* 'yield' returns a value. 'yield *' also returns a value + in case the 'throw' method is called */ + s->func_state.frame.cur_sp[-1] = value; + s->func_state.frame.cur_sp[0] = + js_int32(next->completion_type); + s->func_state.frame.cur_sp++; + exec_no_arg: + s->func_state.throw_flag = false; + } + s->state = JS_ASYNC_GENERATOR_STATE_EXECUTING; + resume_exec: + func_ret = async_func_resume(ctx, &s->func_state); + if (JS_IsException(func_ret)) { + value = JS_GetException(ctx); + js_async_generator_complete(ctx, s); + js_async_generator_reject(ctx, s, value); + JS_FreeValue(ctx, value); + } else if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) { + int func_ret_code, ret; + value = s->func_state.frame.cur_sp[-1]; + s->func_state.frame.cur_sp[-1] = JS_UNDEFINED; + func_ret_code = JS_VALUE_GET_INT(func_ret); + switch(func_ret_code) { + case FUNC_RET_YIELD: + case FUNC_RET_YIELD_STAR: + if (func_ret_code == FUNC_RET_YIELD_STAR) + s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR; + else + s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD; + js_async_generator_resolve(ctx, s, value, false); + JS_FreeValue(ctx, value); + break; + case FUNC_RET_AWAIT: + ret = js_async_generator_await(ctx, s, value); + JS_FreeValue(ctx, value); + if (ret < 0) { + /* exception: throw it */ + s->func_state.throw_flag = true; + goto resume_exec; + } + goto done; + default: + abort(); + } + } else { + assert(JS_IsUndefined(func_ret)); + /* end of function */ + value = s->func_state.frame.cur_sp[-1]; + s->func_state.frame.cur_sp[-1] = JS_UNDEFINED; + js_async_generator_complete(ctx, s); + js_async_generator_resolve(ctx, s, value, true); + JS_FreeValue(ctx, value); + } + break; + default: + abort(); + } + } + done: ; +} + +static JSValue js_async_generator_resolve_function(JSContext *ctx, + JSValueConst this_obj, + int argc, JSValueConst *argv, + int magic, JSValueConst *func_data) +{ + bool is_reject = magic & 1; + JSAsyncGeneratorData *s = JS_GetOpaque(func_data[0], JS_CLASS_ASYNC_GENERATOR); + JSValueConst arg = argv[0]; + + /* XXX: what if s == NULL */ + + if (magic >= 2) { + /* resume next case in AWAITING_RETURN state */ + assert(s->state == JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN || + s->state == JS_ASYNC_GENERATOR_STATE_COMPLETED); + s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; + if (is_reject) { + js_async_generator_reject(ctx, s, arg); + } else { + js_async_generator_resolve(ctx, s, arg, true); + } + } else { + /* restart function execution after await() */ + assert(s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING); + s->func_state.throw_flag = is_reject; + if (is_reject) { + JS_Throw(ctx, js_dup(arg)); + } else { + /* return value of await */ + s->func_state.frame.cur_sp[-1] = js_dup(arg); + } + js_async_generator_resume_next(ctx, s); + } + return JS_UNDEFINED; +} + +/* magic = GEN_MAGIC_x */ +static JSValue js_async_generator_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + int magic) +{ + JSAsyncGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_GENERATOR); + JSValue promise, resolving_funcs[2]; + JSAsyncGeneratorRequest *req; + + promise = JS_NewPromiseCapability(ctx, resolving_funcs); + if (JS_IsException(promise)) + return JS_EXCEPTION; + if (!s) { + JSValue err, res2; + JS_ThrowTypeError(ctx, "not an AsyncGenerator object"); + err = JS_GetException(ctx); + res2 = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, + 1, vc(&err)); + JS_FreeValue(ctx, err); + JS_FreeValue(ctx, res2); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return promise; + } + req = js_mallocz(ctx, sizeof(*req)); + if (!req) + goto fail; + req->completion_type = magic; + req->result = js_dup(argv[0]); + req->promise = js_dup(promise); + req->resolving_funcs[0] = resolving_funcs[0]; + req->resolving_funcs[1] = resolving_funcs[1]; + list_add_tail(&req->link, &s->queue); + if (s->state != JS_ASYNC_GENERATOR_STATE_EXECUTING) { + js_async_generator_resume_next(ctx, s); + } + return promise; + fail: + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + JS_FreeValue(ctx, promise); + return JS_EXCEPTION; +} + +static JSValue js_async_generator_function_call(JSContext *ctx, + JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, + int flags) +{ + JSValue obj, func_ret; + JSAsyncGeneratorData *s; + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_START; + init_list_head(&s->queue); + if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) { + s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; + goto fail; + } + + /* execute the function up to 'OP_initial_yield' (no yield nor + await are possible) */ + func_ret = async_func_resume(ctx, &s->func_state); + if (JS_IsException(func_ret)) + goto fail; + JS_FreeValue(ctx, func_ret); + + obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_ASYNC_GENERATOR); + if (JS_IsException(obj)) + goto fail; + s->generator = JS_VALUE_GET_OBJ(obj); + JS_SetOpaqueInternal(obj, s); + return obj; + fail: + js_async_generator_free(ctx->rt, s); + return JS_EXCEPTION; +} + +/* JS parser */ + +enum { + TOK_NUMBER = -128, + TOK_STRING, + TOK_TEMPLATE, + TOK_IDENT, + TOK_REGEXP, + /* warning: order matters (see js_parse_assign_expr) */ + TOK_MUL_ASSIGN, + TOK_DIV_ASSIGN, + TOK_MOD_ASSIGN, + TOK_PLUS_ASSIGN, + TOK_MINUS_ASSIGN, + TOK_SHL_ASSIGN, + TOK_SAR_ASSIGN, + TOK_SHR_ASSIGN, + TOK_AND_ASSIGN, + TOK_XOR_ASSIGN, + TOK_OR_ASSIGN, + TOK_POW_ASSIGN, + TOK_LAND_ASSIGN, + TOK_LOR_ASSIGN, + TOK_DOUBLE_QUESTION_MARK_ASSIGN, + TOK_DEC, + TOK_INC, + TOK_SHL, + TOK_SAR, + TOK_SHR, + TOK_LT, + TOK_LTE, + TOK_GT, + TOK_GTE, + TOK_EQ, + TOK_STRICT_EQ, + TOK_NEQ, + TOK_STRICT_NEQ, + TOK_LAND, + TOK_LOR, + TOK_POW, + TOK_ARROW, + TOK_ELLIPSIS, + TOK_DOUBLE_QUESTION_MARK, + TOK_QUESTION_MARK_DOT, + TOK_ERROR, + TOK_PRIVATE_NAME, + TOK_EOF, + /* keywords: WARNING: same order as atoms */ + TOK_NULL, /* must be first */ + TOK_FALSE, + TOK_TRUE, + TOK_IF, + TOK_ELSE, + TOK_RETURN, + TOK_VAR, + TOK_THIS, + TOK_DELETE, + TOK_VOID, + TOK_TYPEOF, + TOK_NEW, + TOK_IN, + TOK_INSTANCEOF, + TOK_DO, + TOK_WHILE, + TOK_FOR, + TOK_BREAK, + TOK_CONTINUE, + TOK_SWITCH, + TOK_CASE, + TOK_DEFAULT, + TOK_THROW, + TOK_TRY, + TOK_CATCH, + TOK_FINALLY, + TOK_FUNCTION, + TOK_DEBUGGER, + TOK_WITH, + /* FutureReservedWord */ + TOK_CLASS, + TOK_CONST, + TOK_ENUM, + TOK_EXPORT, + TOK_EXTENDS, + TOK_IMPORT, + TOK_SUPER, + /* FutureReservedWords when parsing strict mode code */ + TOK_IMPLEMENTS, + TOK_INTERFACE, + TOK_LET, + TOK_PACKAGE, + TOK_PRIVATE, + TOK_PROTECTED, + TOK_PUBLIC, + TOK_STATIC, + TOK_YIELD, + TOK_AWAIT, /* must be last */ + TOK_OF, /* only used for js_parse_skip_parens_token() */ +}; + +#define TOK_FIRST_KEYWORD TOK_NULL +#define TOK_LAST_KEYWORD TOK_AWAIT + +/* unicode code points */ +#define CP_NBSP 0x00a0 +#define CP_BOM 0xfeff + +#define CP_LS 0x2028 +#define CP_PS 0x2029 + +typedef struct BlockEnv { + struct BlockEnv *prev; + JSAtom label_name; /* JS_ATOM_NULL if none */ + int label_break; /* -1 if none */ + int label_cont; /* -1 if none */ + int drop_count; /* number of stack elements to drop */ + int label_finally; /* -1 if none */ + int scope_level; + uint8_t has_iterator : 1; + uint8_t is_regular_stmt : 1; // i.e. not a loop statement +} BlockEnv; + +typedef struct JSGlobalVar { + int cpool_idx; /* if >= 0, index in the constant pool for hoisted + function defintion*/ + uint8_t force_init : 1; /* force initialization to undefined */ + uint8_t is_lexical : 1; /* global let/const definition */ + uint8_t is_const : 1; /* const definition */ + int scope_level; /* scope of definition */ + JSAtom var_name; /* variable name */ +} JSGlobalVar; + +typedef struct RelocEntry { + struct RelocEntry *next; + uint32_t addr; /* address to patch */ + int size; /* address size: 1, 2 or 4 bytes */ +} RelocEntry; + +typedef struct JumpSlot { + int op; + int size; + int pos; + int label; +} JumpSlot; + +typedef struct LabelSlot { + int ref_count; + int pos; /* phase 1 address, -1 means not resolved yet */ + int pos2; /* phase 2 address, -1 means not resolved yet */ + int addr; /* phase 3 address, -1 means not resolved yet */ + RelocEntry *first_reloc; +} LabelSlot; + +typedef struct SourceLocSlot { + uint32_t pc; + int line_num; + int col_num; +} SourceLocSlot; + +typedef enum JSParseFunctionEnum { + JS_PARSE_FUNC_STATEMENT, + JS_PARSE_FUNC_VAR, + JS_PARSE_FUNC_EXPR, + JS_PARSE_FUNC_ARROW, + JS_PARSE_FUNC_GETTER, + JS_PARSE_FUNC_SETTER, + JS_PARSE_FUNC_METHOD, + JS_PARSE_FUNC_CLASS_STATIC_INIT, + JS_PARSE_FUNC_CLASS_CONSTRUCTOR, + JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR, +} JSParseFunctionEnum; + +typedef enum JSParseExportEnum { + JS_PARSE_EXPORT_NONE, + JS_PARSE_EXPORT_NAMED, + JS_PARSE_EXPORT_DEFAULT, +} JSParseExportEnum; + +typedef struct JSFunctionDef { + JSContext *ctx; + struct JSFunctionDef *parent; + int parent_cpool_idx; /* index in the constant pool of the parent + or -1 if none */ + int parent_scope_level; /* scope level in parent at point of definition */ + struct list_head child_list; /* list of JSFunctionDef.link */ + struct list_head link; + + int eval_type; /* only valid if is_eval = true */ + + /* Pack all boolean flags together as 1-bit fields to reduce struct size + while avoiding padding and compiler deoptimization. */ + bool is_eval : 1; /* true if eval code */ + bool is_global_var : 1; /* true if variables are not defined locally: + eval global, eval module or non strict eval */ + bool is_func_expr : 1; /* true if function expression */ + bool has_home_object : 1; /* true if the home object is available */ + bool has_prototype : 1; /* true if a prototype field is necessary */ + bool has_simple_parameter_list : 1; + bool has_parameter_expressions : 1; /* if true, an argument scope is created */ + bool has_use_strict : 1; /* to reject directive in special cases */ + bool has_eval_call : 1; /* true if the function contains a call to eval() */ + bool has_arguments_binding : 1; /* true if the 'arguments' binding is + available in the function */ + bool has_this_binding : 1; /* true if the 'this' and new.target binding are + available in the function */ + bool new_target_allowed : 1; /* true if the 'new.target' does not + throw a syntax error */ + bool super_call_allowed : 1; /* true if super() is allowed */ + bool super_allowed : 1; /* true if super. or super[] is allowed */ + bool arguments_allowed : 1; /* true if the 'arguments' identifier is allowed */ + bool is_derived_class_constructor : 1; + bool in_function_body : 1; + bool backtrace_barrier : 1; + bool need_home_object : 1; + bool use_short_opcodes : 1; /* true if short opcodes are used in byte_code */ + bool has_await : 1; /* true if await is used (used in module eval) */ + + JSFunctionKindEnum func_kind : 8; + JSParseFunctionEnum func_type : 7; + uint8_t is_strict_mode : 1; + JSAtom func_name; /* JS_ATOM_NULL if no name */ + + JSVarDef *vars; + uint32_t *vars_htab; // indexes into vars[] + int var_size; /* allocated size for vars[] */ + int var_count; + JSVarDef *args; + int arg_size; /* allocated size for args[] */ + int arg_count; /* number of arguments */ + int defined_arg_count; + int var_ref_count; /* number of local/arg variable references */ + int var_object_idx; /* -1 if none */ + int arg_var_object_idx; /* -1 if none (var object for the argument scope) */ + int arguments_var_idx; /* -1 if none */ + int arguments_arg_idx; /* argument variable definition in argument scope, + -1 if none */ + int func_var_idx; /* variable containing the current function (-1 + if none, only used if is_func_expr is true) */ + int eval_ret_idx; /* variable containing the return value of the eval, -1 if none */ + int this_var_idx; /* variable containg the 'this' value, -1 if none */ + int new_target_var_idx; /* variable containg the 'new.target' value, -1 if none */ + int this_active_func_var_idx; /* variable containg the 'this.active_func' value, -1 if none */ + int home_object_var_idx; + + int scope_level; /* index into fd->scopes if the current lexical scope */ + int scope_first; /* index into vd->vars of first lexically scoped variable */ + int scope_size; /* allocated size of fd->scopes array */ + int scope_count; /* number of entries used in the fd->scopes array */ + JSVarScope *scopes; + JSVarScope def_scope_array[4]; + int body_scope; /* scope of the body of the function or eval */ + + int global_var_count; + int global_var_size; + JSGlobalVar *global_vars; + + DynBuf byte_code; + int last_opcode_pos; /* -1 if no last opcode */ + + LabelSlot *label_slots; + int label_size; /* allocated size for label_slots[] */ + int label_count; + BlockEnv *top_break; /* break/continue label stack */ + + /* constant pool (strings, functions, numbers) */ + JSValue *cpool; + int cpool_count; + int cpool_size; + + /* list of variables in the closure */ + int closure_var_count; + int closure_var_size; + JSClosureVar *closure_var; + + JumpSlot *jump_slots; + int jump_size; + int jump_count; + + SourceLocSlot *source_loc_slots; + int source_loc_size; + int source_loc_count; + int line_number_last; + int line_number_last_pc; + int col_number_last; + + /* pc2line table */ + JSAtom filename; + int line_num; + int col_num; + DynBuf pc2line; + + char *source; /* raw source, utf-8 encoded */ + int source_len; + + JSModuleDef *module; /* != NULL when parsing a module */ +} JSFunctionDef; + +typedef struct JSToken { + int val; + int line_num; /* line number of token start */ + int col_num; /* column number of token start */ + const uint8_t *ptr; + union { + struct { + JSValue str; + int sep; + } str; + struct { + JSValue val; + } num; + struct { + JSAtom atom; + bool has_escape; + bool is_reserved; + } ident; + struct { + JSValue body; + JSValue flags; + } regexp; + } u; +} JSToken; + +typedef struct JSParseState { + JSContext *ctx; + int last_line_num; /* line number of last token */ + int last_col_num; /* column number of last token */ + int line_num; /* line number of current offset */ + int col_num; /* column number of current offset */ + const char *filename; + JSToken token; + bool got_lf; /* true if got line feed before the current token */ + const uint8_t *last_ptr; + const uint8_t *buf_start; + const uint8_t *buf_ptr; + const uint8_t *buf_end; + const uint8_t *eol; // most recently seen end-of-line character + const uint8_t *mark; // first token character, invariant: eol < mark + + /* current function code */ + JSFunctionDef *cur_func; + bool is_module; /* parsing a module */ + bool allow_html_comments; +} JSParseState; + +typedef struct JSOpCode { +#ifdef ENABLE_DUMPS // JS_DUMP_BYTECODE_* + const char *name; +#endif + uint8_t size; /* in bytes */ + /* the opcodes remove n_pop items from the top of the stack, then + pushes n_push items */ + uint8_t n_pop; + uint8_t n_push; + uint8_t fmt; +} JSOpCode; + +static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = { +#define FMT(f) +#ifdef ENABLE_DUMPS // JS_DUMP_BYTECODE_* +#define DEF(id, size, n_pop, n_push, f) { #id, size, n_pop, n_push, OP_FMT_ ## f }, +#else +#define DEF(id, size, n_pop, n_push, f) { size, n_pop, n_push, OP_FMT_ ## f }, +#endif +/* + * QuickJS opcode definitions + * + * Copyright (c) 2017-2018 Fabrice Bellard + * Copyright (c) 2017-2018 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef FMT +FMT(none) +FMT(none_int) +FMT(none_loc) +FMT(none_arg) +FMT(none_var_ref) +FMT(u8) +FMT(i8) +FMT(loc8) +FMT(const8) +FMT(label8) +FMT(u16) +FMT(i16) +FMT(label16) +FMT(npop) +FMT(npopx) +FMT(npop_u16) +FMT(loc) +FMT(arg) +FMT(var_ref) +FMT(u32) +FMT(u32x2) +FMT(i32) +FMT(const) +FMT(label) +FMT(atom) +FMT(atom_u8) +FMT(atom_u16) +FMT(atom_label_u8) +FMT(atom_label_u16) +FMT(label_u16) +#undef FMT +#endif /* FMT */ + +#ifdef DEF + +#ifndef def +#define def(id, size, n_pop, n_push, f) DEF(id, size, n_pop, n_push, f) +#endif + +DEF(invalid, 1, 0, 0, none) /* never emitted */ + +/* push values */ +DEF( push_i32, 5, 0, 1, i32) +DEF( push_const, 5, 0, 1, const) +DEF( fclosure, 5, 0, 1, const) /* must follow push_const */ +DEF(push_atom_value, 5, 0, 1, atom) +DEF( private_symbol, 5, 0, 1, atom) +DEF( undefined, 1, 0, 1, none) +DEF( null, 1, 0, 1, none) +DEF( push_this, 1, 0, 1, none) /* only used at the start of a function */ +DEF( push_false, 1, 0, 1, none) +DEF( push_true, 1, 0, 1, none) +DEF( object, 1, 0, 1, none) +DEF( special_object, 2, 0, 1, u8) /* only used at the start of a function */ +DEF( rest, 3, 0, 1, u16) /* only used at the start of a function */ + +DEF( drop, 1, 1, 0, none) /* a -> */ +DEF( nip, 1, 2, 1, none) /* a b -> b */ +DEF( nip1, 1, 3, 2, none) /* a b c -> b c */ +DEF( dup, 1, 1, 2, none) /* a -> a a */ +DEF( dup1, 1, 2, 3, none) /* a b -> a a b */ +DEF( dup2, 1, 2, 4, none) /* a b -> a b a b */ +DEF( dup3, 1, 3, 6, none) /* a b c -> a b c a b c */ +DEF( insert2, 1, 2, 3, none) /* obj a -> a obj a (dup_x1) */ +DEF( insert3, 1, 3, 4, none) /* obj prop a -> a obj prop a (dup_x2) */ +DEF( insert4, 1, 4, 5, none) /* this obj prop a -> a this obj prop a */ +DEF( perm3, 1, 3, 3, none) /* obj a b -> a obj b */ +DEF( perm4, 1, 4, 4, none) /* obj prop a b -> a obj prop b */ +DEF( perm5, 1, 5, 5, none) /* this obj prop a b -> a this obj prop b */ +DEF( swap, 1, 2, 2, none) /* a b -> b a */ +DEF( swap2, 1, 4, 4, none) /* a b c d -> c d a b */ +DEF( rot3l, 1, 3, 3, none) /* x a b -> a b x */ +DEF( rot3r, 1, 3, 3, none) /* a b x -> x a b */ +DEF( rot4l, 1, 4, 4, none) /* x a b c -> a b c x */ +DEF( rot5l, 1, 5, 5, none) /* x a b c d -> a b c d x */ + +DEF(call_constructor, 3, 2, 1, npop) /* func new.target args -> ret. arguments are not counted in n_pop */ +DEF( call, 3, 1, 1, npop) /* arguments are not counted in n_pop */ +DEF( tail_call, 3, 1, 0, npop) /* arguments are not counted in n_pop */ +DEF( call_method, 3, 2, 1, npop) /* arguments are not counted in n_pop */ +DEF(tail_call_method, 3, 2, 0, npop) /* arguments are not counted in n_pop */ +DEF( array_from, 3, 0, 1, npop) /* arguments are not counted in n_pop */ +DEF( apply, 3, 3, 1, u16) +DEF( return, 1, 1, 0, none) +DEF( return_undef, 1, 0, 0, none) +DEF(check_ctor_return, 1, 1, 2, none) +DEF( check_ctor, 1, 0, 0, none) +DEF( init_ctor, 1, 0, 1, none) +DEF( check_brand, 1, 2, 2, none) /* this_obj func -> this_obj func */ +DEF( add_brand, 1, 2, 0, none) /* this_obj home_obj -> */ +DEF( return_async, 1, 1, 0, none) +DEF( throw, 1, 1, 0, none) +DEF( throw_error, 6, 0, 0, atom_u8) +DEF( eval, 5, 1, 1, npop_u16) /* func args... -> ret_val */ +DEF( apply_eval, 3, 2, 1, u16) /* func array -> ret_eval */ +DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a + bytecode string */ +DEF( get_super, 1, 1, 1, none) +DEF( import, 1, 2, 1, none) /* dynamic module import */ + +DEF( get_var_undef, 5, 0, 1, atom) /* push undefined if the variable does not exist */ +DEF( get_var, 5, 0, 1, atom) /* throw an exception if the variable does not exist */ +DEF( put_var, 5, 1, 0, atom) /* must come after get_var */ +DEF( put_var_init, 5, 1, 0, atom) /* must come after put_var. Used to initialize a global lexical variable */ + +DEF( get_ref_value, 1, 2, 3, none) +DEF( put_ref_value, 1, 3, 0, none) + +DEF( define_var, 6, 0, 0, atom_u8) +DEF(check_define_var, 6, 0, 0, atom_u8) +DEF( define_func, 6, 1, 0, atom_u8) + +// order matters, see IC counterparts +DEF( get_field, 5, 1, 1, atom) +DEF( get_field2, 5, 1, 2, atom) +DEF( put_field, 5, 2, 0, atom) + +DEF( get_private_field, 1, 2, 1, none) /* obj prop -> value */ +DEF( put_private_field, 1, 3, 0, none) /* obj value prop -> */ +DEF(define_private_field, 1, 3, 1, none) /* obj prop value -> obj */ +DEF( get_array_el, 1, 2, 1, none) +DEF( get_array_el2, 1, 2, 2, none) /* obj prop -> obj value */ +DEF( put_array_el, 1, 3, 0, none) +DEF(get_super_value, 1, 3, 1, none) /* this obj prop -> value */ +DEF(put_super_value, 1, 4, 0, none) /* this obj prop value -> */ +DEF( define_field, 5, 2, 1, atom) +DEF( set_name, 5, 1, 1, atom) +DEF(set_name_computed, 1, 2, 2, none) +DEF( set_proto, 1, 2, 1, none) +DEF(set_home_object, 1, 2, 2, none) +DEF(define_array_el, 1, 3, 2, none) +DEF( append, 1, 3, 2, none) /* append enumerated object, update length */ +DEF(copy_data_properties, 2, 3, 3, u8) +DEF( define_method, 6, 2, 1, atom_u8) +DEF(define_method_computed, 2, 3, 1, u8) /* must come after define_method */ +DEF( define_class, 6, 2, 2, atom_u8) /* parent ctor -> ctor proto */ +DEF( define_class_computed, 6, 3, 3, atom_u8) /* field_name parent ctor -> field_name ctor proto (class with computed name) */ + +DEF( get_loc, 3, 0, 1, loc) +DEF( put_loc, 3, 1, 0, loc) /* must come after get_loc */ +DEF( set_loc, 3, 1, 1, loc) /* must come after put_loc */ +DEF( get_arg, 3, 0, 1, arg) +DEF( put_arg, 3, 1, 0, arg) /* must come after get_arg */ +DEF( set_arg, 3, 1, 1, arg) /* must come after put_arg */ +DEF( get_var_ref, 3, 0, 1, var_ref) +DEF( put_var_ref, 3, 1, 0, var_ref) /* must come after get_var_ref */ +DEF( set_var_ref, 3, 1, 1, var_ref) /* must come after put_var_ref */ +DEF(set_loc_uninitialized, 3, 0, 0, loc) +DEF( get_loc_check, 3, 0, 1, loc) +DEF( put_loc_check, 3, 1, 0, loc) /* must come after get_loc_check */ +DEF( put_loc_check_init, 3, 1, 0, loc) +DEF(get_var_ref_check, 3, 0, 1, var_ref) +DEF(put_var_ref_check, 3, 1, 0, var_ref) /* must come after get_var_ref_check */ +DEF(put_var_ref_check_init, 3, 1, 0, var_ref) +DEF( close_loc, 3, 0, 0, loc) +DEF( if_false, 5, 1, 0, label) +DEF( if_true, 5, 1, 0, label) /* must come after if_false */ +DEF( goto, 5, 0, 0, label) /* must come after if_true */ +DEF( catch, 5, 0, 1, label) +DEF( gosub, 5, 0, 0, label) /* used to execute the finally block */ +DEF( ret, 1, 1, 0, none) /* used to return from the finally block */ +DEF( nip_catch, 1, 2, 1, none) /* catch ... a -> a */ + +DEF( to_object, 1, 1, 1, none) +//DEF( to_string, 1, 1, 1, none) +DEF( to_propkey, 1, 1, 1, none) +DEF( to_propkey2, 1, 2, 2, none) + +DEF( with_get_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF( with_put_var, 10, 2, 1, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF(with_delete_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF( with_make_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF( with_get_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF(with_get_ref_undef, 10, 1, 0, atom_label_u8) + +DEF( make_loc_ref, 7, 0, 2, atom_u16) +DEF( make_arg_ref, 7, 0, 2, atom_u16) +DEF(make_var_ref_ref, 7, 0, 2, atom_u16) +DEF( make_var_ref, 5, 0, 2, atom) + +DEF( for_in_start, 1, 1, 1, none) +DEF( for_of_start, 1, 1, 3, none) +DEF(for_await_of_start, 1, 1, 3, none) +DEF( for_in_next, 1, 1, 3, none) +DEF( for_of_next, 2, 3, 5, u8) +DEF(iterator_check_object, 1, 1, 1, none) +DEF(iterator_get_value_done, 1, 1, 2, none) +DEF( iterator_close, 1, 3, 0, none) +DEF( iterator_next, 1, 4, 4, none) +DEF( iterator_call, 2, 4, 5, u8) +DEF( initial_yield, 1, 0, 0, none) +DEF( yield, 1, 1, 2, none) +DEF( yield_star, 1, 1, 2, none) +DEF(async_yield_star, 1, 1, 2, none) +DEF( await, 1, 1, 1, none) + +/* arithmetic/logic operations */ +DEF( neg, 1, 1, 1, none) +DEF( plus, 1, 1, 1, none) +DEF( dec, 1, 1, 1, none) +DEF( inc, 1, 1, 1, none) +DEF( post_dec, 1, 1, 2, none) +DEF( post_inc, 1, 1, 2, none) +DEF( dec_loc, 2, 0, 0, loc8) +DEF( inc_loc, 2, 0, 0, loc8) +DEF( add_loc, 2, 1, 0, loc8) +DEF( not, 1, 1, 1, none) +DEF( lnot, 1, 1, 1, none) +DEF( typeof, 1, 1, 1, none) +DEF( delete, 1, 2, 1, none) +DEF( delete_var, 5, 0, 1, atom) + +/* warning: order matters (see js_parse_assign_expr) */ +DEF( mul, 1, 2, 1, none) +DEF( div, 1, 2, 1, none) +DEF( mod, 1, 2, 1, none) +DEF( add, 1, 2, 1, none) +DEF( sub, 1, 2, 1, none) +DEF( shl, 1, 2, 1, none) +DEF( sar, 1, 2, 1, none) +DEF( shr, 1, 2, 1, none) +DEF( and, 1, 2, 1, none) +DEF( xor, 1, 2, 1, none) +DEF( or, 1, 2, 1, none) +DEF( pow, 1, 2, 1, none) + +DEF( lt, 1, 2, 1, none) +DEF( lte, 1, 2, 1, none) +DEF( gt, 1, 2, 1, none) +DEF( gte, 1, 2, 1, none) +DEF( instanceof, 1, 2, 1, none) +DEF( in, 1, 2, 1, none) +DEF( eq, 1, 2, 1, none) +DEF( neq, 1, 2, 1, none) +DEF( strict_eq, 1, 2, 1, none) +DEF( strict_neq, 1, 2, 1, none) +DEF(is_undefined_or_null, 1, 1, 1, none) +DEF( private_in, 1, 2, 1, none) +DEF(push_bigint_i32, 5, 0, 1, i32) +/* must be the last non short and non temporary opcode */ +DEF( nop, 1, 0, 0, none) + +/* temporary opcodes: never emitted in the final bytecode */ + +def( enter_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ +def( leave_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ + +def( label, 5, 0, 0, label) /* emitted in phase 1, removed in phase 3 */ + +def(scope_get_var_undef, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_get_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_put_var, 7, 1, 0, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_delete_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_make_ref, 11, 0, 2, atom_label_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_get_ref, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_put_var_init, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_get_private_field, 7, 1, 1, atom_u16) /* obj -> value, emitted in phase 1, removed in phase 2 */ +def(scope_get_private_field2, 7, 1, 2, atom_u16) /* obj -> obj value, emitted in phase 1, removed in phase 2 */ +def(scope_put_private_field, 7, 2, 0, atom_u16) /* obj value ->, emitted in phase 1, removed in phase 2 */ +def(scope_in_private_field, 7, 1, 1, atom_u16) /* obj -> res emitted in phase 1, removed in phase 2 */ +def(get_field_opt_chain, 5, 1, 1, atom) /* emitted in phase 1, removed in phase 2 */ +def(get_array_el_opt_chain, 1, 2, 1, none) /* emitted in phase 1, removed in phase 2 */ +def( set_class_name, 5, 1, 1, u32) /* emitted in phase 1, removed in phase 2 */ + +def( source_loc, 9, 0, 0, u32x2) /* emitted in phase 1, removed in phase 3 */ + +DEF( push_minus1, 1, 0, 1, none_int) +DEF( push_0, 1, 0, 1, none_int) +DEF( push_1, 1, 0, 1, none_int) +DEF( push_2, 1, 0, 1, none_int) +DEF( push_3, 1, 0, 1, none_int) +DEF( push_4, 1, 0, 1, none_int) +DEF( push_5, 1, 0, 1, none_int) +DEF( push_6, 1, 0, 1, none_int) +DEF( push_7, 1, 0, 1, none_int) +DEF( push_i8, 2, 0, 1, i8) +DEF( push_i16, 3, 0, 1, i16) +DEF( push_const8, 2, 0, 1, const8) +DEF( fclosure8, 2, 0, 1, const8) /* must follow push_const8 */ +DEF(push_empty_string, 1, 0, 1, none) + +DEF( get_loc8, 2, 0, 1, loc8) +DEF( put_loc8, 2, 1, 0, loc8) +DEF( set_loc8, 2, 1, 1, loc8) + +DEF( get_loc0_loc1, 1, 0, 2, none_loc) +DEF( get_loc0, 1, 0, 1, none_loc) +DEF( get_loc1, 1, 0, 1, none_loc) +DEF( get_loc2, 1, 0, 1, none_loc) +DEF( get_loc3, 1, 0, 1, none_loc) +DEF( put_loc0, 1, 1, 0, none_loc) +DEF( put_loc1, 1, 1, 0, none_loc) +DEF( put_loc2, 1, 1, 0, none_loc) +DEF( put_loc3, 1, 1, 0, none_loc) +DEF( set_loc0, 1, 1, 1, none_loc) +DEF( set_loc1, 1, 1, 1, none_loc) +DEF( set_loc2, 1, 1, 1, none_loc) +DEF( set_loc3, 1, 1, 1, none_loc) +DEF( get_arg0, 1, 0, 1, none_arg) +DEF( get_arg1, 1, 0, 1, none_arg) +DEF( get_arg2, 1, 0, 1, none_arg) +DEF( get_arg3, 1, 0, 1, none_arg) +DEF( put_arg0, 1, 1, 0, none_arg) +DEF( put_arg1, 1, 1, 0, none_arg) +DEF( put_arg2, 1, 1, 0, none_arg) +DEF( put_arg3, 1, 1, 0, none_arg) +DEF( set_arg0, 1, 1, 1, none_arg) +DEF( set_arg1, 1, 1, 1, none_arg) +DEF( set_arg2, 1, 1, 1, none_arg) +DEF( set_arg3, 1, 1, 1, none_arg) +DEF( get_var_ref0, 1, 0, 1, none_var_ref) +DEF( get_var_ref1, 1, 0, 1, none_var_ref) +DEF( get_var_ref2, 1, 0, 1, none_var_ref) +DEF( get_var_ref3, 1, 0, 1, none_var_ref) +DEF( put_var_ref0, 1, 1, 0, none_var_ref) +DEF( put_var_ref1, 1, 1, 0, none_var_ref) +DEF( put_var_ref2, 1, 1, 0, none_var_ref) +DEF( put_var_ref3, 1, 1, 0, none_var_ref) +DEF( set_var_ref0, 1, 1, 1, none_var_ref) +DEF( set_var_ref1, 1, 1, 1, none_var_ref) +DEF( set_var_ref2, 1, 1, 1, none_var_ref) +DEF( set_var_ref3, 1, 1, 1, none_var_ref) + +DEF( get_length, 1, 1, 1, none) + +DEF( if_false8, 2, 1, 0, label8) +DEF( if_true8, 2, 1, 0, label8) /* must come after if_false8 */ +DEF( goto8, 2, 0, 0, label8) /* must come after if_true8 */ +DEF( goto16, 3, 0, 0, label16) + +DEF( call0, 1, 1, 1, npopx) +DEF( call1, 1, 1, 1, npopx) +DEF( call2, 1, 1, 1, npopx) +DEF( call3, 1, 1, 1, npopx) + +DEF( is_undefined, 1, 1, 1, none) +DEF( is_null, 1, 1, 1, none) +DEF(typeof_is_undefined, 1, 1, 1, none) +DEF( typeof_is_function, 1, 1, 1, none) + +#undef DEF +#undef def +#endif /* DEF */ + +#undef DEF +#undef FMT +}; + +/* After the final compilation pass, short opcodes are used. Their + opcodes overlap with the temporary opcodes which cannot appear in + the final bytecode. Their description is after the temporary + opcodes in opcode_info[]. */ +#define short_opcode_info(op) \ + opcode_info[(op) >= OP_TEMP_START ? \ + (op) + (OP_TEMP_END - OP_TEMP_START) : (op)] + +static void json_free_token(JSParseState *s, JSToken *token) { + // Only free actual allocated values + switch(token->val) { + case TOK_NUMBER: + JS_FreeValue(s->ctx, token->u.num.val); + break; + case TOK_STRING: + JS_FreeValue(s->ctx, token->u.str.str); + break; + case TOK_IDENT: + JS_FreeAtom(s->ctx, token->u.ident.atom); + break; + } +} + +static void free_token(JSParseState *s, JSToken *token) +{ + switch(token->val) { + case TOK_NUMBER: + JS_FreeValue(s->ctx, token->u.num.val); + break; + case TOK_STRING: + case TOK_TEMPLATE: + JS_FreeValue(s->ctx, token->u.str.str); + break; + case TOK_REGEXP: + JS_FreeValue(s->ctx, token->u.regexp.body); + JS_FreeValue(s->ctx, token->u.regexp.flags); + break; + case TOK_IDENT: + case TOK_PRIVATE_NAME: + JS_FreeAtom(s->ctx, token->u.ident.atom); + break; + default: + if (token->val >= TOK_FIRST_KEYWORD && + token->val <= TOK_LAST_KEYWORD) { + JS_FreeAtom(s->ctx, token->u.ident.atom); + } + break; + } +} + +static void __attribute((unused)) dump_token(JSParseState *s, + const JSToken *token) +{ + printf("%d:%d ", token->line_num, token->col_num); + switch(token->val) { + case TOK_NUMBER: + { + double d; + JS_ToFloat64(s->ctx, &d, token->u.num.val); /* no exception possible */ + printf("number: %.14g\n", d); + } + break; + case TOK_IDENT: + dump_atom: + { + char buf[ATOM_GET_STR_BUF_SIZE]; + printf("ident: '%s'\n", + JS_AtomGetStr(s->ctx, buf, sizeof(buf), token->u.ident.atom)); + } + break; + case TOK_STRING: + { + const char *str; + /* XXX: quote the string */ + str = JS_ToCString(s->ctx, token->u.str.str); + printf("string: '%s'\n", str); + JS_FreeCString(s->ctx, str); + } + break; + case TOK_TEMPLATE: + { + const char *str; + str = JS_ToCString(s->ctx, token->u.str.str); + printf("template: `%s`\n", str); + JS_FreeCString(s->ctx, str); + } + break; + case TOK_REGEXP: + { + const char *str, *str2; + str = JS_ToCString(s->ctx, token->u.regexp.body); + str2 = JS_ToCString(s->ctx, token->u.regexp.flags); + printf("regexp: '%s' '%s'\n", str, str2); + JS_FreeCString(s->ctx, str); + JS_FreeCString(s->ctx, str2); + } + break; + case TOK_EOF: + printf("eof\n"); + break; + default: + if (s->token.val >= TOK_NULL && s->token.val <= TOK_LAST_KEYWORD) { + goto dump_atom; + } else if (s->token.val >= 256) { + printf("token: %d\n", token->val); + } else { + printf("token: '%c'\n", token->val); + } + break; + } +} + +int JS_PRINTF_FORMAT_ATTR(2, 3) js_parse_error(JSParseState *s, JS_PRINTF_FORMAT const char *fmt, ...) +{ + JSContext *ctx = s->ctx; + va_list ap; + int backtrace_flags; + + va_start(ap, fmt); + JS_ThrowError2(ctx, JS_SYNTAX_ERROR, false, fmt, ap); + va_end(ap); + backtrace_flags = 0; + if (s->cur_func && s->cur_func->backtrace_barrier) + backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL; + build_backtrace(ctx, ctx->rt->current_exception, JS_UNDEFINED, s->filename, + s->line_num, s->col_num, backtrace_flags); + return -1; +} + +#ifndef QJS_DISABLE_PARSER + +static __exception int next_token(JSParseState *s); + +static int js_parse_expect(JSParseState *s, int tok) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + + if (s->token.val == tok) + return next_token(s); + + switch(s->token.val) { + case TOK_EOF: + return js_parse_error(s, "Unexpected end of input"); + case TOK_NUMBER: + return js_parse_error(s, "Unexpected number"); + case TOK_STRING: + return js_parse_error(s, "Unexpected string"); + case TOK_TEMPLATE: + return js_parse_error(s, "Unexpected string template"); + case TOK_REGEXP: + return js_parse_error(s, "Unexpected regexp"); + case TOK_IDENT: + return js_parse_error(s, "Unexpected identifier '%s'", + JS_AtomGetStr(s->ctx, buf, sizeof(buf), + s->token.u.ident.atom)); + case TOK_ERROR: + return js_parse_error(s, "Invalid or unexpected token"); + default: + return js_parse_error(s, "Unexpected token '%.*s'", + (int)(s->buf_ptr - s->token.ptr), + (const char *)s->token.ptr); + } +} + +static int js_parse_expect_semi(JSParseState *s) +{ + if (s->token.val != ';') { + /* automatic insertion of ';' */ + if (s->token.val == TOK_EOF || s->token.val == '}' || s->got_lf) { + return 0; + } + return js_parse_error(s, "expecting '%c'", ';'); + } + return next_token(s); +} + +static int js_parse_error_reserved_identifier(JSParseState *s) +{ + char buf1[ATOM_GET_STR_BUF_SIZE]; + return js_parse_error(s, "'%s' is a reserved identifier", + JS_AtomGetStr(s->ctx, buf1, sizeof(buf1), + s->token.u.ident.atom)); +} + +static __exception int js_parse_template_part(JSParseState *s, + const uint8_t *p) +{ + const uint8_t *p_next; + uint32_t c; + StringBuffer b_s, *b = &b_s; + JSValue str; + + /* p points to the first byte of the template part */ + if (string_buffer_init(s->ctx, b, 32)) + goto fail; + for(;;) { + if (p >= s->buf_end) + goto unexpected_eof; + c = *p++; + if (c == '`') { + /* template end part */ + break; + } + if (c == '$' && *p == '{') { + /* template start or middle part */ + p++; + break; + } + if (c == '\\') { + if (string_buffer_putc8(b, c)) + goto fail; + if (p >= s->buf_end) + goto unexpected_eof; + c = *p++; + } + /* newline sequences are normalized as single '\n' bytes */ + if (c == '\r') { + if (*p == '\n') + p++; + c = '\n'; + } + if (c == '\n') { + s->line_num++; + s->eol = &p[-1]; + s->mark = p; + } else if (c >= 0x80) { + c = utf8_decode(p - 1, &p_next); + if (p_next == p) { + js_parse_error(s, "invalid UTF-8 sequence"); + goto fail; + } + p = p_next; + } + if (string_buffer_putc(b, c)) + goto fail; + } + str = string_buffer_end(b); + if (JS_IsException(str)) + return -1; + s->token.val = TOK_TEMPLATE; + s->token.u.str.sep = c; + s->token.u.str.str = str; + s->buf_ptr = p; + return 0; + + unexpected_eof: + js_parse_error(s, "unexpected end of string"); + fail: + string_buffer_free(b); + return -1; +} + +static __exception int js_parse_string(JSParseState *s, int sep, + bool do_throw, const uint8_t *p, + JSToken *token, const uint8_t **pp) +{ + const uint8_t *p_next; + int ret; + uint32_t c; + StringBuffer b_s, *b = &b_s; + JSValue str; + + /* string */ + if (string_buffer_init(s->ctx, b, 32)) + goto fail; + for(;;) { + if (p >= s->buf_end) + goto invalid_char; + c = *p; + if (c < 0x20) { + if (sep == '`') { + if (c == '\r') { + if (p[1] == '\n') + p++; + c = '\n'; + } + /* do not update s->line_num */ + } else if (c == '\n' || c == '\r') + goto invalid_char; + } + p++; + if (c == sep) + break; + if (c == '$' && *p == '{' && sep == '`') { + /* template start or middle part */ + p++; + break; + } + if (c == '\\') { + c = *p; + switch(c) { + case '\0': + if (p >= s->buf_end) { + if (sep != '`') + goto invalid_char; + if (do_throw) + js_parse_error(s, "Unexpected end of input"); + goto fail; + } + p++; + break; + case '\'': + case '\"': + case '\\': + p++; + break; + case '\r': /* accept DOS and MAC newline sequences */ + if (p[1] == '\n') { + p++; + } + /* fall thru */ + case '\n': + /* ignore escaped newline sequence */ + p++; + if (sep != '`') { + s->line_num++; + s->eol = &p[-1]; + s->mark = p; + } + continue; + default: + if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) { + /* accept isolated \0 */ + p++; + c = '\0'; + } else + if ((c >= '0' && c <= '9') + && (s->cur_func->is_strict_mode || sep == '`')) { + if (do_throw) { + js_parse_error(s, "%s are not allowed in %s", + (c >= '8') ? "\\8 and \\9" : "Octal escape sequences", + (sep == '`') ? "template strings" : "strict mode"); + } + goto fail; + } else if (c >= 0x80) { + c = utf8_decode(p, &p_next); + if (p_next == p + 1) { + goto invalid_utf8; + } + p = p_next; + /* LS or PS are skipped */ + if (c == CP_LS || c == CP_PS) + continue; + } else { + ret = lre_parse_escape(&p, true); + if (ret == -1) { + if (do_throw) { + js_parse_error(s, "Invalid %s escape sequence", + c == 'u' ? "Unicode" : "hexadecimal"); + } + goto fail; + } else if (ret < 0) { + /* ignore the '\' (could output a warning) */ + p++; + } else { + c = ret; + } + } + break; + } + } else if (c >= 0x80) { + c = utf8_decode(p - 1, &p_next); + if (p_next == p) + goto invalid_utf8; + p = p_next; + } + if (string_buffer_putc(b, c)) + goto fail; + } + str = string_buffer_end(b); + if (JS_IsException(str)) + return -1; + token->val = TOK_STRING; + token->u.str.sep = c; + token->u.str.str = str; + *pp = p; + return 0; + + invalid_utf8: + if (do_throw) + js_parse_error(s, "invalid UTF-8 sequence"); + goto fail; + invalid_char: + if (do_throw) + js_parse_error(s, "unexpected end of string"); + fail: + string_buffer_free(b); + return -1; +} + +static inline bool token_is_pseudo_keyword(JSParseState *s, JSAtom atom) { + return s->token.val == TOK_IDENT && s->token.u.ident.atom == atom && + !s->token.u.ident.has_escape; +} + +static __exception int js_parse_regexp(JSParseState *s) +{ + const uint8_t *p, *p_next; + bool in_class; + StringBuffer b_s, *b = &b_s; + StringBuffer b2_s, *b2 = &b2_s; + uint32_t c; + JSValue body_str, flags_str; + + p = s->buf_ptr; + p++; + in_class = false; + if (string_buffer_init(s->ctx, b, 32)) + return -1; + if (string_buffer_init(s->ctx, b2, 1)) + goto fail; + for(;;) { + if (p >= s->buf_end) { + eof_error: + js_parse_error(s, "unexpected end of regexp"); + goto fail; + } + c = *p++; + if (c == '\n' || c == '\r') { + goto eol_error; + } else if (c == '/') { + if (!in_class) + break; + } else if (c == '[') { + in_class = true; + } else if (c == ']') { + /* XXX: incorrect as the first character in a class */ + in_class = false; + } else if (c == '\\') { + if (string_buffer_putc8(b, c)) + goto fail; + c = *p++; + if (c == '\n' || c == '\r') + goto eol_error; + else if (c == '\0' && p >= s->buf_end) + goto eof_error; + else if (c >= 0x80) { + c = utf8_decode(p - 1, &p_next); + if (p_next == p) { + goto invalid_utf8; + } + p = p_next; + if (c == CP_LS || c == CP_PS) + goto eol_error; + } + } else if (c >= 0x80) { + c = utf8_decode(p - 1, &p_next); + if (p_next == p) { + invalid_utf8: + js_parse_error(s, "invalid UTF-8 sequence"); + goto fail; + } + p = p_next; + /* LS or PS are considered as line terminator */ + if (c == CP_LS || c == CP_PS) { + eol_error: + js_parse_error(s, "unexpected line terminator in regexp"); + goto fail; + } + } + if (string_buffer_putc(b, c)) + goto fail; + } + + /* flags */ + for(;;) { + c = utf8_decode(p, &p_next); + /* no need to test for invalid UTF-8, 0xFFFD is not ident_next */ + if (!lre_js_is_ident_next(c)) + break; + if (string_buffer_putc(b2, c)) + goto fail; + p = p_next; + } + + body_str = string_buffer_end(b); + flags_str = string_buffer_end(b2); + if (JS_IsException(body_str) || + JS_IsException(flags_str)) { + JS_FreeValue(s->ctx, body_str); + JS_FreeValue(s->ctx, flags_str); + return -1; + } + s->token.val = TOK_REGEXP; + s->token.u.regexp.body = body_str; + s->token.u.regexp.flags = flags_str; + s->buf_ptr = p; + return 0; + fail: + string_buffer_free(b); + string_buffer_free(b2); + return -1; +} + +#endif // QJS_DISABLE_PARSER + +static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize, + char *static_buf) +{ + char *buf, *new_buf; + size_t size, new_size; + + buf = *pbuf; + size = *psize; + if (size >= (SIZE_MAX / 3) * 2) + new_size = SIZE_MAX; + else + new_size = size + (size >> 1); + if (buf == static_buf) { + new_buf = js_malloc(ctx, new_size); + if (!new_buf) + return -1; + memcpy(new_buf, buf, size); + } else { + new_buf = js_realloc(ctx, buf, new_size); + if (!new_buf) + return -1; + } + *pbuf = new_buf; + *psize = new_size; + return 0; +} + +#ifndef QJS_DISABLE_PARSER + +/* convert a TOK_IDENT to a keyword when needed */ +static void update_token_ident(JSParseState *s) +{ + if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD || + (s->token.u.ident.atom <= JS_ATOM_LAST_STRICT_KEYWORD && + s->cur_func->is_strict_mode) || + (s->token.u.ident.atom == JS_ATOM_yield && + ((s->cur_func->func_kind & JS_FUNC_GENERATOR) || + (s->cur_func->func_type == JS_PARSE_FUNC_ARROW && + !s->cur_func->in_function_body && s->cur_func->parent && + (s->cur_func->parent->func_kind & JS_FUNC_GENERATOR)))) || + (s->token.u.ident.atom == JS_ATOM_await && + (s->is_module || + (s->cur_func->func_kind & JS_FUNC_ASYNC) || + s->cur_func->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT || + (s->cur_func->func_type == JS_PARSE_FUNC_ARROW && + !s->cur_func->in_function_body && s->cur_func->parent && + ((s->cur_func->parent->func_kind & JS_FUNC_ASYNC) || + s->cur_func->parent->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT))))) { + if (s->token.u.ident.has_escape) { + s->token.u.ident.is_reserved = true; + s->token.val = TOK_IDENT; + } else { + /* The keywords atoms are pre allocated */ + s->token.val = s->token.u.ident.atom - 1 + TOK_FIRST_KEYWORD; + } + } +} + +/* if the current token is an identifier or keyword, reparse it + according to the current function type */ +static void reparse_ident_token(JSParseState *s) +{ + if (s->token.val == TOK_IDENT || + (s->token.val >= TOK_FIRST_KEYWORD && + s->token.val <= TOK_LAST_KEYWORD)) { + s->token.val = TOK_IDENT; + s->token.u.ident.is_reserved = false; + update_token_ident(s); + } +} + +/* 'c' is the first character. Return JS_ATOM_NULL in case of error */ +static JSAtom parse_ident(JSParseState *s, const uint8_t **pp, + bool *pident_has_escape, int c, bool is_private) +{ + const uint8_t *p, *p_next; + char ident_buf[128], *buf; + size_t ident_size, ident_pos; + JSAtom atom = JS_ATOM_NULL; + + p = *pp; + buf = ident_buf; + ident_size = sizeof(ident_buf); + ident_pos = 0; + if (is_private) + buf[ident_pos++] = '#'; + for(;;) { + if (c < 0x80) { + buf[ident_pos++] = c; + } else { + ident_pos += utf8_encode((uint8_t*)buf + ident_pos, c); + } + c = *p; + p_next = p + 1; + if (c == '\\' && *p_next == 'u') { + c = lre_parse_escape(&p_next, true); + *pident_has_escape = true; + } else if (c >= 0x80) { + c = utf8_decode(p, &p_next); + /* no need to test for invalid UTF-8, 0xFFFD is not ident_next */ + } + if (!lre_js_is_ident_next(c)) + break; + p = p_next; + if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) { + if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) + goto done; + } + } + /* buf is pure ASCII or UTF-8 encoded */ + atom = JS_NewAtomLen(s->ctx, buf, ident_pos); + done: + if (unlikely(buf != ident_buf)) + js_free(s->ctx, buf); + *pp = p; + return atom; +} + + +static __exception int next_token(JSParseState *s) +{ + const uint8_t *p, *p_next; + int c; + bool ident_has_escape; + JSAtom atom; + + if (js_check_stack_overflow(s->ctx->rt, 1000)) { + JS_ThrowStackOverflow(s->ctx); + return -1; + } + + free_token(s, &s->token); + + p = s->last_ptr = s->buf_ptr; + s->got_lf = false; + s->last_line_num = s->token.line_num; + s->last_col_num = s->token.col_num; + redo: + s->token.line_num = s->line_num; + s->token.col_num = s->col_num; + s->token.ptr = p; + c = *p; + switch(c) { + case 0: + if (p >= s->buf_end) { + s->token.val = TOK_EOF; + } else { + goto def_token; + } + break; + case '`': + if (js_parse_template_part(s, p + 1)) + goto fail; + p = s->buf_ptr; + break; + case '\'': + case '\"': + if (js_parse_string(s, c, true, p + 1, &s->token, &p)) + goto fail; + break; + case '\r': /* accept DOS and MAC newline sequences */ + if (p[1] == '\n') { + p++; + } + /* fall thru */ + case '\n': + p++; + line_terminator: + s->eol = &p[-1]; + s->mark = p; + s->got_lf = true; + s->line_num++; + goto redo; + case '\f': + case '\v': + case ' ': + case '\t': + s->mark = ++p; + goto redo; + case '/': + if (p[1] == '*') { + /* comment */ + p += 2; + for(;;) { + if (*p == '\0' && p >= s->buf_end) { + js_parse_error(s, "unexpected end of comment"); + goto fail; + } + if (p[0] == '*' && p[1] == '/') { + p += 2; + break; + } + if (*p == '\n') { + s->line_num++; + s->got_lf = true; /* considered as LF for ASI */ + s->eol = p++; + s->mark = p; + } else if (*p == '\r') { + s->got_lf = true; /* considered as LF for ASI */ + p++; + } else if (*p >= 0x80) { + c = utf8_decode(p, &p); + /* ignore invalid UTF-8 in comments */ + if (c == CP_LS || c == CP_PS) { + s->got_lf = true; /* considered as LF for ASI */ + } + } else { + p++; + } + } + s->mark = p; + goto redo; + } else if (p[1] == '/') { + /* line comment */ + p += 2; + skip_line_comment: + for(;;) { + if (*p == '\0' && p >= s->buf_end) + break; + if (*p == '\r' || *p == '\n') + break; + if (*p >= 0x80) { + c = utf8_decode(p, &p); + /* ignore invalid UTF-8 in comments */ + /* LS or PS are considered as line terminator */ + if (c == CP_LS || c == CP_PS) { + break; + } + } else { + p++; + } + } + s->mark = p; + goto redo; + } else if (p[1] == '=') { + p += 2; + s->token.val = TOK_DIV_ASSIGN; + } else { + p++; + s->token.val = c; + } + break; + case '\\': + if (p[1] == 'u') { + const uint8_t *p1 = p + 1; + int c1 = lre_parse_escape(&p1, true); + if (c1 >= 0 && lre_js_is_ident_first(c1)) { + c = c1; + p = p1; + ident_has_escape = true; + goto has_ident; + } else { + /* XXX: syntax error? */ + } + } + goto def_token; + case 'a': case 'b': case 'c': case 'd': + case 'e': case 'f': case 'g': case 'h': + case 'i': case 'j': case 'k': case 'l': + case 'm': case 'n': case 'o': case 'p': + case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': + case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D': + case 'E': case 'F': case 'G': case 'H': + case 'I': case 'J': case 'K': case 'L': + case 'M': case 'N': case 'O': case 'P': + case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': + case 'Y': case 'Z': + case '_': + case '$': + /* identifier */ + s->mark = p; + p++; + ident_has_escape = false; + has_ident: + atom = parse_ident(s, &p, &ident_has_escape, c, false); + if (atom == JS_ATOM_NULL) + goto fail; + s->token.u.ident.atom = atom; + s->token.u.ident.has_escape = ident_has_escape; + s->token.u.ident.is_reserved = false; + s->token.val = TOK_IDENT; + update_token_ident(s); + break; + case '#': + /* private name */ + { + p++; + c = *p; + p_next = p + 1; + if (c == '\\' && *p_next == 'u') { + c = lre_parse_escape(&p_next, true); + } else if (c >= 0x80) { + c = utf8_decode(p, &p_next); + if (p_next == p + 1) + goto invalid_utf8; + } + if (!lre_js_is_ident_first(c)) { + js_parse_error(s, "invalid first character of private name"); + goto fail; + } + p = p_next; + ident_has_escape = false; /* not used */ + atom = parse_ident(s, &p, &ident_has_escape, c, true); + if (atom == JS_ATOM_NULL) + goto fail; + s->token.u.ident.atom = atom; + s->token.val = TOK_PRIVATE_NAME; + } + break; + case '.': + if (p[1] == '.' && p[2] == '.') { + p += 3; + s->token.val = TOK_ELLIPSIS; + break; + } + if (p[1] >= '0' && p[1] <= '9') { + goto parse_number; + } else { + goto def_token; + } + break; + case '0': + /* in strict mode, octal literals are not accepted */ + if (is_digit(p[1]) && (s->cur_func->is_strict_mode)) { + js_parse_error(s, "Octal literals are not allowed in strict mode"); + goto fail; + } + goto parse_number; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': + case '9': + /* number */ + parse_number: + { + JSValue ret; + const uint8_t *p1; + int flags; + flags = ATOD_ACCEPT_BIN_OCT | ATOD_ACCEPT_LEGACY_OCTAL | + ATOD_ACCEPT_UNDERSCORES | ATOD_ACCEPT_SUFFIX; + ret = js_atof(s->ctx, (const char *)p, (const char **)&p, 0, + flags); + if (JS_IsException(ret)) + goto fail; + /* reject `10instanceof Number` */ + if (JS_VALUE_IS_NAN(ret) || + lre_js_is_ident_next(utf8_decode(p, &p1))) { + JS_FreeValue(s->ctx, ret); + js_parse_error(s, "invalid number literal"); + goto fail; + } + s->token.val = TOK_NUMBER; + s->token.u.num.val = ret; + } + break; + case '*': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_MUL_ASSIGN; + } else if (p[1] == '*') { + if (p[2] == '=') { + p += 3; + s->token.val = TOK_POW_ASSIGN; + } else { + p += 2; + s->token.val = TOK_POW; + } + } else { + goto def_token; + } + break; + case '%': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_MOD_ASSIGN; + } else { + goto def_token; + } + break; + case '+': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_PLUS_ASSIGN; + } else if (p[1] == '+') { + p += 2; + s->token.val = TOK_INC; + } else { + goto def_token; + } + break; + case '-': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_MINUS_ASSIGN; + } else if (p[1] == '-') { + if (s->allow_html_comments && p[2] == '>' && + (s->got_lf || s->last_ptr == s->buf_start)) { + /* Annex B: `-->` at beginning of line is an html comment end. + It extends to the end of the line. + */ + goto skip_line_comment; + } + p += 2; + s->token.val = TOK_DEC; + } else { + goto def_token; + } + break; + case '<': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_LTE; + } else if (p[1] == '<') { + if (p[2] == '=') { + p += 3; + s->token.val = TOK_SHL_ASSIGN; + } else { + p += 2; + s->token.val = TOK_SHL; + } + } else if (s->allow_html_comments && + p[1] == '!' && p[2] == '-' && p[3] == '-') { + /* Annex B: handle `